diff --git a/api/src/extensions.ts b/api/src/extensions.ts index 0aa1411b1c..77b0988134 100644 --- a/api/src/extensions.ts +++ b/api/src/extensions.ts @@ -51,20 +51,31 @@ export function getExtensionManager(): ExtensionManager { return extensionManager; } +type EventHandler = + | { type: 'filter'; name: string; handler: FilterHandler } + | { type: 'action'; name: string; handler: ActionHandler } + | { type: 'init'; name: string; handler: InitHandler } + | { type: 'schedule'; task: ScheduledTask }; + type AppExtensions = Partial>; type ApiExtensions = { - hooks: ( - | { type: 'filter'; path: string; event: string; handler: FilterHandler } - | { type: 'action'; path: string; event: string; handler: ActionHandler } - | { type: 'init'; path: string; event: string; handler: InitHandler } - | { type: 'schedule'; path: string; task: ScheduledTask } - )[]; + hooks: { path: string; events: EventHandler[] }[]; endpoints: { path: string }[]; }; +type Options = { + schedule: boolean; + watch: boolean; +}; + +const defaultOptions: Options = { + schedule: true, + watch: env.EXTENSIONS_AUTO_RELOAD && env.NODE_ENV !== 'development', +}; + class ExtensionManager { private isLoaded = false; - private isScheduleHookEnabled = true; + private options: Options; private extensions: Extension[] = []; @@ -77,16 +88,19 @@ class ExtensionManager { private watcher: FSWatcher | null = null; constructor() { + this.options = defaultOptions; + this.apiEmitter = new Emitter(); this.endpointRouter = Router(); } - public async initialize({ schedule, watch } = { schedule: true, watch: true }): Promise { - this.isScheduleHookEnabled = schedule; + public async initialize(options: Partial = {}): Promise { + this.options = { + ...defaultOptions, + ...options, + }; - if (watch) { - this.initializeWatcher(); - } + this.initializeWatcher(); if (!this.isLoaded) { await this.load(); @@ -181,7 +195,7 @@ class ExtensionManager { } private initializeWatcher(): void { - if (env.EXTENSIONS_AUTO_RELOAD && env.NODE_ENV !== 'development' && !this.watcher) { + if (this.options.watch && !this.watcher) { logger.info('Watching extensions for changes...'); const localExtensionPaths = (env.SERVE_APP ? EXTENSION_TYPES : API_EXTENSION_TYPES).map((type) => @@ -311,41 +325,43 @@ class ExtensionManager { const register = getModuleDefault(hookInstance); + const hookHandler: { path: string; events: EventHandler[] } = { + path: hookPath, + events: [], + }; + const registerFunctions = { filter: (event: string, handler: FilterHandler) => { emitter.onFilter(event, handler); - this.apiExtensions.hooks.push({ + hookHandler.events.push({ type: 'filter', - path: hookPath, - event, + name: event, handler, }); }, action: (event: string, handler: ActionHandler) => { emitter.onAction(event, handler); - this.apiExtensions.hooks.push({ + hookHandler.events.push({ type: 'action', - path: hookPath, - event, + name: event, handler, }); }, init: (event: string, handler: InitHandler) => { emitter.onInit(event, handler); - this.apiExtensions.hooks.push({ + hookHandler.events.push({ type: 'init', - path: hookPath, - event, + name: event, handler, }); }, schedule: (cron: string, handler: ScheduleHandler) => { if (validate(cron)) { const task = schedule(cron, async () => { - if (this.isScheduleHookEnabled) { + if (this.options.schedule) { try { await handler(); } catch (error: any) { @@ -354,9 +370,8 @@ class ExtensionManager { } }); - this.apiExtensions.hooks.push({ + hookHandler.events.push({ type: 'schedule', - path: hookPath, task, }); } else { @@ -374,6 +389,8 @@ class ExtensionManager { logger, getSchema, }); + + this.apiExtensions.hooks.push(hookHandler); } private registerEndpoint(endpoint: Extension, router: Router) { @@ -405,19 +422,21 @@ class ExtensionManager { private unregisterHooks(): void { for (const hook of this.apiExtensions.hooks) { - switch (hook.type) { - case 'filter': - emitter.offFilter(hook.event, hook.handler); - break; - case 'action': - emitter.offAction(hook.event, hook.handler); - break; - case 'init': - emitter.offInit(hook.event, hook.handler); - break; - case 'schedule': - hook.task.stop(); - break; + for (const event of hook.events) { + switch (event.type) { + case 'filter': + emitter.offFilter(event.name, event.handler); + break; + case 'action': + emitter.offAction(event.name, event.handler); + break; + case 'init': + emitter.offInit(event.name, event.handler); + break; + case 'schedule': + event.task.stop(); + break; + } } delete require.cache[require.resolve(hook.path)]; diff --git a/package-lock.json b/package-lock.json index 6b236864b0..918ef0d908 100644 --- a/package-lock.json +++ b/package-lock.json @@ -59,20 +59,20 @@ }, "api": { "name": "directus", - "version": "9.4.2", + "version": "9.4.3", "license": "GPL-3.0-only", "dependencies": { "@aws-sdk/client-ses": "^3.40.0", - "@directus/app": "9.4.2", - "@directus/drive": "9.4.2", - "@directus/drive-azure": "9.4.2", - "@directus/drive-gcs": "9.4.2", - "@directus/drive-s3": "9.4.2", - "@directus/extensions-sdk": "9.4.2", - "@directus/format-title": "9.4.2", - "@directus/schema": "9.4.2", - "@directus/shared": "9.4.2", - "@directus/specs": "9.4.2", + "@directus/app": "9.4.3", + "@directus/drive": "9.4.3", + "@directus/drive-azure": "9.4.3", + "@directus/drive-gcs": "9.4.3", + "@directus/drive-s3": "9.4.3", + "@directus/extensions-sdk": "9.4.3", + "@directus/format-title": "9.4.3", + "@directus/schema": "9.4.3", + "@directus/shared": "9.4.3", + "@directus/specs": "9.4.3", "@godaddy/terminus": "^4.9.0", "@rollup/plugin-alias": "^3.1.2", "@rollup/plugin-virtual": "^2.0.3", @@ -306,12 +306,12 @@ }, "app": { "name": "@directus/app", - "version": "9.4.2", + "version": "9.4.3", "devDependencies": { - "@directus/docs": "9.4.2", - "@directus/extensions-sdk": "9.4.2", - "@directus/format-title": "9.4.2", - "@directus/shared": "9.4.2", + "@directus/docs": "9.4.3", + "@directus/extensions-sdk": "9.4.3", + "@directus/format-title": "9.4.3", + "@directus/shared": "9.4.3", "@fortawesome/fontawesome-svg-core": "1.2.36", "@fortawesome/free-brands-svg-icons": "5.15.4", "@fullcalendar/core": "5.10.1", @@ -447,7 +447,7 @@ }, "docs": { "name": "@directus/docs", - "version": "9.4.2", + "version": "9.4.3", "license": "ISC", "devDependencies": { "directory-tree": "3.0.1", @@ -44957,11 +44957,11 @@ }, "packages/cli": { "name": "@directus/cli", - "version": "9.4.2", + "version": "9.4.3", "license": "MIT", "dependencies": { - "@directus/format-title": "9.4.2", - "@directus/sdk": "9.4.2", + "@directus/format-title": "9.4.3", + "@directus/sdk": "9.4.3", "@types/yargs": "^17.0.0", "app-module-path": "^2.2.0", "chalk": "^4.1.0", @@ -45096,11 +45096,11 @@ } }, "packages/create-directus-extension": { - "version": "9.4.2", + "version": "9.4.3", "license": "GPL-3.0-only", "dependencies": { - "@directus/extensions-sdk": "9.4.2", - "@directus/shared": "9.4.2", + "@directus/extensions-sdk": "9.4.3", + "@directus/shared": "9.4.3", "inquirer": "^8.1.2" }, "bin": { @@ -45143,7 +45143,7 @@ "license": "0BSD" }, "packages/create-directus-project": { - "version": "9.4.2", + "version": "9.4.3", "license": "GPL-3.0-only", "dependencies": { "chalk": "^4.1.1", @@ -45179,7 +45179,7 @@ }, "packages/drive": { "name": "@directus/drive", - "version": "9.4.2", + "version": "9.4.3", "license": "MIT", "dependencies": { "fs-extra": "^10.0.0", @@ -45198,11 +45198,11 @@ }, "packages/drive-azure": { "name": "@directus/drive-azure", - "version": "9.4.2", + "version": "9.4.3", "license": "MIT", "dependencies": { "@azure/storage-blob": "^12.6.0", - "@directus/drive": "9.4.2", + "@directus/drive": "9.4.3", "normalize-path": "^3.0.0" }, "devDependencies": { @@ -45233,10 +45233,10 @@ }, "packages/drive-gcs": { "name": "@directus/drive-gcs", - "version": "9.4.2", + "version": "9.4.3", "license": "MIT", "dependencies": { - "@directus/drive": "9.4.2", + "@directus/drive": "9.4.3", "@google-cloud/storage": "^5.8.5", "lodash": "4.17.21", "normalize-path": "^3.0.0" @@ -45256,10 +45256,10 @@ }, "packages/drive-s3": { "name": "@directus/drive-s3", - "version": "9.4.2", + "version": "9.4.3", "license": "MIT", "dependencies": { - "@directus/drive": "9.4.2", + "@directus/drive": "9.4.3", "aws-sdk": "^2.928.0", "normalize-path": "^3.0.0" }, @@ -45304,9 +45304,9 @@ }, "packages/extensions-sdk": { "name": "@directus/extensions-sdk", - "version": "9.4.2", + "version": "9.4.3", "dependencies": { - "@directus/shared": "9.4.2", + "@directus/shared": "9.4.3", "@rollup/plugin-commonjs": "^21.0.0", "@rollup/plugin-json": "^4.1.0", "@rollup/plugin-node-resolve": "^13.0.0", @@ -45356,7 +45356,7 @@ }, "packages/format-title": { "name": "@directus/format-title", - "version": "9.4.2", + "version": "9.4.3", "license": "MIT", "devDependencies": { "@rollup/plugin-commonjs": "21.0.1", @@ -45376,10 +45376,10 @@ }, "packages/gatsby-source-directus": { "name": "@directus/gatsby-source-directus", - "version": "9.4.2", + "version": "9.4.3", "license": "MIT", "dependencies": { - "@directus/sdk": "9.4.2", + "@directus/sdk": "9.4.3", "gatsby-source-filesystem": "4.2.0", "gatsby-source-graphql": "4.2.0", "ms": "2.1.3" @@ -45391,7 +45391,7 @@ }, "packages/schema": { "name": "@directus/schema", - "version": "9.4.2", + "version": "9.4.3", "license": "GPL-3.0", "dependencies": { "knex-schema-inspector": "^1.6.6", @@ -45404,7 +45404,7 @@ }, "packages/sdk": { "name": "@directus/sdk", - "version": "9.4.2", + "version": "9.4.3", "license": "MIT", "dependencies": { "axios": "^0.24.0" @@ -45433,7 +45433,7 @@ }, "packages/shared": { "name": "@directus/shared", - "version": "9.4.2", + "version": "9.4.3", "dependencies": { "axios": "*", "date-fns": "2.24.0", @@ -45492,7 +45492,7 @@ }, "packages/specs": { "name": "@directus/specs", - "version": "9.4.2", + "version": "9.4.3", "license": "GPL-3.0", "dependencies": { "openapi3-ts": "^2.0.1" @@ -47781,10 +47781,10 @@ "@directus/app": { "version": "file:app", "requires": { - "@directus/docs": "9.4.2", - "@directus/extensions-sdk": "9.4.2", - "@directus/format-title": "9.4.2", - "@directus/shared": "9.4.2", + "@directus/docs": "9.4.3", + "@directus/extensions-sdk": "9.4.3", + "@directus/format-title": "9.4.3", + "@directus/shared": "9.4.3", "@fortawesome/fontawesome-svg-core": "1.2.36", "@fortawesome/free-brands-svg-icons": "5.15.4", "@fullcalendar/core": "5.10.1", @@ -47887,8 +47887,8 @@ "@directus/cli": { "version": "file:packages/cli", "requires": { - "@directus/format-title": "9.4.2", - "@directus/sdk": "9.4.2", + "@directus/format-title": "9.4.3", + "@directus/sdk": "9.4.3", "@types/figlet": "1.5.4", "@types/fs-extra": "9.0.13", "@types/jest": "27.0.3", @@ -48040,7 +48040,7 @@ "version": "file:packages/drive-azure", "requires": { "@azure/storage-blob": "^12.6.0", - "@directus/drive": "9.4.2", + "@directus/drive": "9.4.3", "@types/fs-extra": "9.0.13", "@types/jest": "27.0.3", "@types/node": "16.11.9", @@ -48068,7 +48068,7 @@ "@directus/drive-gcs": { "version": "file:packages/drive-gcs", "requires": { - "@directus/drive": "9.4.2", + "@directus/drive": "9.4.3", "@google-cloud/storage": "^5.8.5", "@lukeed/uuid": "2.0.0", "@types/fs-extra": "9.0.13", @@ -48087,7 +48087,7 @@ "@directus/drive-s3": { "version": "file:packages/drive-s3", "requires": { - "@directus/drive": "9.4.2", + "@directus/drive": "9.4.3", "@lukeed/uuid": "2.0.0", "@types/fs-extra": "9.0.13", "@types/jest": "27.0.3", @@ -48117,7 +48117,7 @@ "@directus/extensions-sdk": { "version": "file:packages/extensions-sdk", "requires": { - "@directus/shared": "9.4.2", + "@directus/shared": "9.4.3", "@rollup/plugin-commonjs": "^21.0.0", "@rollup/plugin-json": "^4.1.0", "@rollup/plugin-node-resolve": "^13.0.0", @@ -48169,7 +48169,7 @@ "@directus/gatsby-source-directus": { "version": "file:packages/gatsby-source-directus", "requires": { - "@directus/sdk": "9.4.2", + "@directus/sdk": "9.4.3", "gatsby-source-filesystem": "4.2.0", "gatsby-source-graphql": "4.2.0", "ms": "2.1.3" @@ -56507,8 +56507,8 @@ "create-directus-extension": { "version": "file:packages/create-directus-extension", "requires": { - "@directus/extensions-sdk": "9.4.2", - "@directus/shared": "9.4.2", + "@directus/extensions-sdk": "9.4.3", + "@directus/shared": "9.4.3", "inquirer": "^8.1.2" }, "dependencies": { @@ -57854,16 +57854,16 @@ "version": "file:api", "requires": { "@aws-sdk/client-ses": "^3.40.0", - "@directus/app": "9.4.2", - "@directus/drive": "9.4.2", - "@directus/drive-azure": "9.4.2", - "@directus/drive-gcs": "9.4.2", - "@directus/drive-s3": "9.4.2", - "@directus/extensions-sdk": "9.4.2", - "@directus/format-title": "9.4.2", - "@directus/schema": "9.4.2", - "@directus/shared": "9.4.2", - "@directus/specs": "9.4.2", + "@directus/app": "9.4.3", + "@directus/drive": "9.4.3", + "@directus/drive-azure": "9.4.3", + "@directus/drive-gcs": "9.4.3", + "@directus/drive-s3": "9.4.3", + "@directus/extensions-sdk": "9.4.3", + "@directus/format-title": "9.4.3", + "@directus/schema": "9.4.3", + "@directus/shared": "9.4.3", + "@directus/specs": "9.4.3", "@godaddy/terminus": "^4.9.0", "@keyv/redis": "^2.1.2", "@rollup/plugin-alias": "^3.1.2",