@aphexcms/cms-core 9.5.2 → 9.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/documents.d.ts +28 -0
- package/dist/api/documents.d.ts.map +1 -1
- package/dist/api/documents.js +18 -0
- package/dist/api/schemas/documents.d.ts +19 -0
- package/dist/api/schemas/documents.d.ts.map +1 -1
- package/dist/api/schemas/documents.js +18 -0
- package/dist/client/api.d.ts +2 -0
- package/dist/client/api.d.ts.map +1 -0
- package/dist/client/api.js +11 -0
- package/dist/client/ui.d.ts +25 -0
- package/dist/client/ui.d.ts.map +1 -0
- package/dist/client/ui.js +53 -0
- package/dist/components/admin/ActivityView.svelte +227 -0
- package/dist/components/admin/ActivityView.svelte.d.ts +4 -0
- package/dist/components/admin/ActivityView.svelte.d.ts.map +1 -0
- package/dist/components/admin/DocumentEditor.svelte +197 -11
- package/dist/components/admin/DocumentEditor.svelte.d.ts.map +1 -1
- package/dist/components/admin/ScheduleDialog.svelte +174 -0
- package/dist/components/admin/ScheduleDialog.svelte.d.ts +20 -0
- package/dist/components/admin/ScheduleDialog.svelte.d.ts.map +1 -0
- package/dist/components/admin/fields/ArrayField.svelte +10 -2
- package/dist/components/admin/fields/ArrayField.svelte.d.ts.map +1 -1
- package/dist/components/layout/OrganizationSwitcher.svelte +3 -3
- package/dist/components/layout/sidebar/NavUser.svelte +5 -5
- package/dist/db/interfaces/events.d.ts +55 -0
- package/dist/db/interfaces/events.d.ts.map +1 -0
- package/dist/db/interfaces/events.js +1 -0
- package/dist/db/interfaces/index.d.ts +10 -2
- package/dist/db/interfaces/index.d.ts.map +1 -1
- package/dist/db/interfaces/plugin-storage.d.ts +39 -0
- package/dist/db/interfaces/plugin-storage.d.ts.map +1 -0
- package/dist/db/interfaces/plugin-storage.js +1 -0
- package/dist/events/catalog.d.ts +8 -0
- package/dist/events/catalog.d.ts.map +1 -0
- package/dist/events/catalog.js +11 -0
- package/dist/events/consumer.d.ts +79 -0
- package/dist/events/consumer.d.ts.map +1 -0
- package/dist/events/consumer.js +76 -0
- package/dist/events/define-event.d.ts +17 -0
- package/dist/events/define-event.d.ts.map +1 -0
- package/dist/events/define-event.js +7 -0
- package/dist/events/emit.d.ts +11 -0
- package/dist/events/emit.d.ts.map +1 -0
- package/dist/events/emit.js +20 -0
- package/dist/events/index.d.ts +4 -0
- package/dist/events/index.d.ts.map +1 -0
- package/dist/events/index.js +3 -0
- package/dist/field-validation/rule.d.ts.map +1 -1
- package/dist/field-validation/rule.js +15 -0
- package/dist/field-validation/utils.d.ts.map +1 -1
- package/dist/field-validation/utils.js +7 -1
- package/dist/forms/define-form.d.ts +25 -0
- package/dist/forms/define-form.d.ts.map +1 -0
- package/dist/forms/define-form.js +22 -0
- package/dist/forms/index.d.ts +4 -0
- package/dist/forms/index.d.ts.map +1 -0
- package/dist/forms/index.js +5 -0
- package/dist/forms/types.d.ts +49 -0
- package/dist/forms/types.d.ts.map +1 -0
- package/dist/forms/types.js +1 -0
- package/dist/forms/validate.d.ts +9 -0
- package/dist/forms/validate.d.ts.map +1 -0
- package/dist/forms/validate.js +18 -0
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +15 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/jobs/document-jobs.d.ts +27 -0
- package/dist/jobs/document-jobs.d.ts.map +1 -0
- package/dist/jobs/document-jobs.js +42 -0
- package/dist/jobs/embedded-runner.d.ts +25 -0
- package/dist/jobs/embedded-runner.d.ts.map +1 -0
- package/dist/jobs/embedded-runner.js +46 -0
- package/dist/jobs/index.d.ts +7 -0
- package/dist/jobs/index.d.ts.map +1 -0
- package/dist/jobs/index.js +8 -0
- package/dist/jobs/relay.d.ts +27 -0
- package/dist/jobs/relay.d.ts.map +1 -0
- package/dist/jobs/relay.js +60 -0
- package/dist/jobs/run-batch.d.ts +52 -0
- package/dist/jobs/run-batch.d.ts.map +1 -0
- package/dist/jobs/run-batch.js +61 -0
- package/dist/jobs/run-due-jobs.d.ts +30 -0
- package/dist/jobs/run-due-jobs.d.ts.map +1 -0
- package/dist/jobs/run-due-jobs.js +69 -0
- package/dist/jobs/types.d.ts +18 -0
- package/dist/jobs/types.d.ts.map +1 -0
- package/dist/jobs/types.js +1 -0
- package/dist/lib/api/documents.d.ts +28 -0
- package/dist/lib/api/documents.d.ts.map +1 -1
- package/dist/lib/api/documents.js +18 -0
- package/dist/lib/api/documents.js.map +1 -1
- package/dist/lib/api/schemas/documents.d.ts +19 -0
- package/dist/lib/api/schemas/documents.d.ts.map +1 -1
- package/dist/lib/api/schemas/documents.js +18 -0
- package/dist/lib/api/schemas/documents.js.map +1 -1
- package/dist/lib/client/api.d.ts +2 -0
- package/dist/lib/client/api.d.ts.map +1 -0
- package/dist/lib/client/api.js +12 -0
- package/dist/lib/client/api.js.map +1 -0
- package/dist/lib/client/ui.d.ts +25 -0
- package/dist/lib/client/ui.d.ts.map +1 -0
- package/dist/lib/client/ui.js +54 -0
- package/dist/lib/client/ui.js.map +1 -0
- package/dist/lib/db/interfaces/events.d.ts +55 -0
- package/dist/lib/db/interfaces/events.d.ts.map +1 -0
- package/dist/lib/db/interfaces/events.js +2 -0
- package/dist/lib/db/interfaces/events.js.map +1 -0
- package/dist/lib/db/interfaces/index.d.ts +10 -2
- package/dist/lib/db/interfaces/index.d.ts.map +1 -1
- package/dist/lib/db/interfaces/plugin-storage.d.ts +39 -0
- package/dist/lib/db/interfaces/plugin-storage.d.ts.map +1 -0
- package/dist/lib/db/interfaces/plugin-storage.js +2 -0
- package/dist/lib/db/interfaces/plugin-storage.js.map +1 -0
- package/dist/lib/events/catalog.d.ts +8 -0
- package/dist/lib/events/catalog.d.ts.map +1 -0
- package/dist/lib/events/catalog.js +12 -0
- package/dist/lib/events/catalog.js.map +1 -0
- package/dist/lib/events/consumer.d.ts +79 -0
- package/dist/lib/events/consumer.d.ts.map +1 -0
- package/dist/lib/events/consumer.js +77 -0
- package/dist/lib/events/consumer.js.map +1 -0
- package/dist/lib/events/define-event.d.ts +17 -0
- package/dist/lib/events/define-event.d.ts.map +1 -0
- package/dist/lib/events/define-event.js +8 -0
- package/dist/lib/events/define-event.js.map +1 -0
- package/dist/lib/events/emit.d.ts +11 -0
- package/dist/lib/events/emit.d.ts.map +1 -0
- package/dist/lib/events/emit.js +21 -0
- package/dist/lib/events/emit.js.map +1 -0
- package/dist/lib/events/index.d.ts +4 -0
- package/dist/lib/events/index.d.ts.map +1 -0
- package/dist/lib/events/index.js +4 -0
- package/dist/lib/events/index.js.map +1 -0
- package/dist/lib/field-validation/rule.d.ts.map +1 -1
- package/dist/lib/field-validation/rule.js +15 -0
- package/dist/lib/field-validation/rule.js.map +1 -1
- package/dist/lib/field-validation/utils.d.ts.map +1 -1
- package/dist/lib/field-validation/utils.js +7 -1
- package/dist/lib/field-validation/utils.js.map +1 -1
- package/dist/lib/forms/define-form.d.ts +25 -0
- package/dist/lib/forms/define-form.d.ts.map +1 -0
- package/dist/lib/forms/define-form.js +23 -0
- package/dist/lib/forms/define-form.js.map +1 -0
- package/dist/lib/forms/index.d.ts +4 -0
- package/dist/lib/forms/index.d.ts.map +1 -0
- package/dist/lib/forms/index.js +6 -0
- package/dist/lib/forms/index.js.map +1 -0
- package/dist/lib/forms/types.d.ts +49 -0
- package/dist/lib/forms/types.d.ts.map +1 -0
- package/dist/lib/forms/types.js +2 -0
- package/dist/lib/forms/types.js.map +1 -0
- package/dist/lib/forms/validate.d.ts +9 -0
- package/dist/lib/forms/validate.d.ts.map +1 -0
- package/dist/lib/forms/validate.js +19 -0
- package/dist/lib/forms/validate.js.map +1 -0
- package/dist/lib/hooks.d.ts.map +1 -1
- package/dist/lib/hooks.js +15 -0
- package/dist/lib/hooks.js.map +1 -1
- package/dist/lib/index.d.ts +2 -0
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +6 -0
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/jobs/document-jobs.d.ts +27 -0
- package/dist/lib/jobs/document-jobs.d.ts.map +1 -0
- package/dist/lib/jobs/document-jobs.js +43 -0
- package/dist/lib/jobs/document-jobs.js.map +1 -0
- package/dist/lib/jobs/embedded-runner.d.ts +25 -0
- package/dist/lib/jobs/embedded-runner.d.ts.map +1 -0
- package/dist/lib/jobs/embedded-runner.js +47 -0
- package/dist/lib/jobs/embedded-runner.js.map +1 -0
- package/dist/lib/jobs/index.d.ts +7 -0
- package/dist/lib/jobs/index.d.ts.map +1 -0
- package/dist/lib/jobs/index.js +9 -0
- package/dist/lib/jobs/index.js.map +1 -0
- package/dist/lib/jobs/relay.d.ts +27 -0
- package/dist/lib/jobs/relay.d.ts.map +1 -0
- package/dist/lib/jobs/relay.js +61 -0
- package/dist/lib/jobs/relay.js.map +1 -0
- package/dist/lib/jobs/run-batch.d.ts +52 -0
- package/dist/lib/jobs/run-batch.d.ts.map +1 -0
- package/dist/lib/jobs/run-batch.js +62 -0
- package/dist/lib/jobs/run-batch.js.map +1 -0
- package/dist/lib/jobs/run-due-jobs.d.ts +30 -0
- package/dist/lib/jobs/run-due-jobs.d.ts.map +1 -0
- package/dist/lib/jobs/run-due-jobs.js +70 -0
- package/dist/lib/jobs/run-due-jobs.js.map +1 -0
- package/dist/lib/jobs/types.d.ts +18 -0
- package/dist/lib/jobs/types.d.ts.map +1 -0
- package/dist/lib/jobs/types.js +2 -0
- package/dist/lib/jobs/types.js.map +1 -0
- package/dist/lib/local-api/collection-api.d.ts +42 -0
- package/dist/lib/local-api/collection-api.d.ts.map +1 -1
- package/dist/lib/local-api/collection-api.js +200 -25
- package/dist/lib/local-api/collection-api.js.map +1 -1
- package/dist/lib/local-api/hooks.d.ts +7 -0
- package/dist/lib/local-api/hooks.d.ts.map +1 -0
- package/dist/lib/local-api/hooks.js +20 -0
- package/dist/lib/local-api/hooks.js.map +1 -0
- package/dist/lib/mcp/tools.js +1 -1
- package/dist/lib/mcp/tools.js.map +1 -1
- package/dist/lib/plugins/resolver.d.ts +12 -1
- package/dist/lib/plugins/resolver.d.ts.map +1 -1
- package/dist/lib/plugins/resolver.js +4 -1
- package/dist/lib/plugins/resolver.js.map +1 -1
- package/dist/lib/plugins/types.d.ts +47 -2
- package/dist/lib/plugins/types.d.ts.map +1 -1
- package/dist/lib/plugins/types.js.map +1 -1
- package/dist/lib/schema-utils/define-type.d.ts +32 -0
- package/dist/lib/schema-utils/define-type.d.ts.map +1 -0
- package/dist/lib/schema-utils/define-type.js +41 -0
- package/dist/lib/schema-utils/define-type.js.map +1 -0
- package/dist/lib/schema-utils/index.d.ts +1 -0
- package/dist/lib/schema-utils/index.d.ts.map +1 -1
- package/dist/lib/schema-utils/index.js +1 -0
- package/dist/lib/schema-utils/index.js.map +1 -1
- package/dist/lib/server/api/index.d.ts.map +1 -1
- package/dist/lib/server/api/index.js +8 -0
- package/dist/lib/server/api/index.js.map +1 -1
- package/dist/lib/server/api/routes/documents-publish.d.ts.map +1 -1
- package/dist/lib/server/api/routes/documents-publish.js +110 -0
- package/dist/lib/server/api/routes/documents-publish.js.map +1 -1
- package/dist/lib/server/api/routes/jobs.d.ts +4 -0
- package/dist/lib/server/api/routes/jobs.d.ts.map +1 -0
- package/dist/lib/server/api/routes/jobs.js +93 -0
- package/dist/lib/server/api/routes/jobs.js.map +1 -0
- package/dist/lib/server/api/routes/workers-run.d.ts +4 -0
- package/dist/lib/server/api/routes/workers-run.d.ts.map +1 -0
- package/dist/lib/server/api/routes/workers-run.js +41 -0
- package/dist/lib/server/api/routes/workers-run.js.map +1 -0
- package/dist/lib/server/index.d.ts +1 -0
- package/dist/lib/server/index.d.ts.map +1 -1
- package/dist/lib/server/index.js +2 -0
- package/dist/lib/server/index.js.map +1 -1
- package/dist/lib/services/version-service.d.ts +16 -0
- package/dist/lib/services/version-service.d.ts.map +1 -1
- package/dist/lib/services/version-service.js +72 -48
- package/dist/lib/services/version-service.js.map +1 -1
- package/dist/lib/types/config.d.ts +43 -0
- package/dist/lib/types/config.d.ts.map +1 -1
- package/dist/lib/types/events.d.ts +134 -0
- package/dist/lib/types/events.d.ts.map +1 -0
- package/dist/lib/types/events.js +4 -0
- package/dist/lib/types/events.js.map +1 -0
- package/dist/lib/types/index.d.ts +1 -0
- package/dist/lib/types/index.d.ts.map +1 -1
- package/dist/lib/types/index.js +1 -0
- package/dist/lib/types/index.js.map +1 -1
- package/dist/lib/types/schemas.d.ts +71 -0
- package/dist/lib/types/schemas.d.ts.map +1 -1
- package/dist/lib/utils/index.d.ts +1 -1
- package/dist/lib/utils/index.d.ts.map +1 -1
- package/dist/lib/utils/index.js.map +1 -1
- package/dist/local-api/collection-api.d.ts +42 -0
- package/dist/local-api/collection-api.d.ts.map +1 -1
- package/dist/local-api/collection-api.js +200 -25
- package/dist/local-api/hooks.d.ts +7 -0
- package/dist/local-api/hooks.d.ts.map +1 -0
- package/dist/local-api/hooks.js +19 -0
- package/dist/mcp/tools.js +1 -1
- package/dist/plugins/resolver.d.ts +12 -1
- package/dist/plugins/resolver.d.ts.map +1 -1
- package/dist/plugins/resolver.js +4 -1
- package/dist/plugins/types.d.ts +47 -2
- package/dist/plugins/types.d.ts.map +1 -1
- package/dist/schema-utils/define-type.d.ts +32 -0
- package/dist/schema-utils/define-type.d.ts.map +1 -0
- package/dist/schema-utils/define-type.js +40 -0
- package/dist/schema-utils/index.d.ts +1 -0
- package/dist/schema-utils/index.d.ts.map +1 -1
- package/dist/schema-utils/index.js +1 -0
- package/dist/server/api/index.d.ts.map +1 -1
- package/dist/server/api/index.js +8 -0
- package/dist/server/api/routes/documents-publish.d.ts.map +1 -1
- package/dist/server/api/routes/documents-publish.js +110 -0
- package/dist/server/api/routes/jobs.d.ts +4 -0
- package/dist/server/api/routes/jobs.d.ts.map +1 -0
- package/dist/server/api/routes/jobs.js +92 -0
- package/dist/server/api/routes/workers-run.d.ts +4 -0
- package/dist/server/api/routes/workers-run.d.ts.map +1 -0
- package/dist/server/api/routes/workers-run.js +40 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +2 -0
- package/dist/services/version-service.d.ts +16 -0
- package/dist/services/version-service.d.ts.map +1 -1
- package/dist/services/version-service.js +72 -48
- package/dist/types/config.d.ts +43 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/events.d.ts +134 -0
- package/dist/types/events.d.ts.map +1 -0
- package/dist/types/events.js +3 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -0
- package/dist/types/schemas.d.ts +71 -0
- package/dist/types/schemas.d.ts.map +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/package.json +2 -1
package/dist/hooks.js
CHANGED
|
@@ -9,6 +9,7 @@ import { PluginSettingsService } from './services/plugin-settings-service.js';
|
|
|
9
9
|
import { createCMS } from './engine.js';
|
|
10
10
|
import { createLocalAPI } from './local-api/index.js';
|
|
11
11
|
import { createPartResolver } from './plugins/resolver.js';
|
|
12
|
+
import { startEmbeddedJobRunner } from './jobs/embedded-runner.js';
|
|
12
13
|
import { createAphexApi, mountAphexBuiltins, toHonoHandler } from './server/api/index.js';
|
|
13
14
|
/**
|
|
14
15
|
* Wrap a plugin route handler so it enforces `requiredCapabilities` before running.
|
|
@@ -35,6 +36,9 @@ let schemaError = null;
|
|
|
35
36
|
let initPromise = null;
|
|
36
37
|
let activeConfig = null;
|
|
37
38
|
let configDirty = false;
|
|
39
|
+
// Started once per process (not per request, and not restarted on schema-HMR re-init): the loop
|
|
40
|
+
// resolves the live `cmsInstances` at each tick, so a rebuilt instance is picked up automatically.
|
|
41
|
+
let embeddedRunner = null;
|
|
38
42
|
/**
|
|
39
43
|
* Called by the Vite HMR plugin (`@aphexcms/cms-core/vite`) when schema
|
|
40
44
|
* files or `aphex.config.ts` change. Replaces the captured config with the
|
|
@@ -200,6 +204,17 @@ export function createCMSHook(config) {
|
|
|
200
204
|
apiApp,
|
|
201
205
|
partResolver
|
|
202
206
|
};
|
|
207
|
+
// Start the embedded in-process job loop once, if opted in. It drives the queue
|
|
208
|
+
// directly (no HTTP, no worker secret) so scheduled publishes and event consumers
|
|
209
|
+
// run with zero setup — ideal for dev and single-instance self-hosting. It reads
|
|
210
|
+
// the live `cmsInstances` at each tick, so it survives schema-HMR re-init.
|
|
211
|
+
if (currentConfig.jobs?.embedded && !embeddedRunner) {
|
|
212
|
+
embeddedRunner = startEmbeddedJobRunner({
|
|
213
|
+
intervalMs: currentConfig.jobs.embeddedIntervalMs,
|
|
214
|
+
logger: cmsLogger,
|
|
215
|
+
getServices: () => cmsInstances
|
|
216
|
+
});
|
|
217
|
+
}
|
|
203
218
|
resolveInit();
|
|
204
219
|
}
|
|
205
220
|
// Attach schema error to instances so it can be accessed in load functions
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ export * from './field-validation/rule.js';
|
|
|
4
4
|
export * from './field-validation/utils.js';
|
|
5
5
|
export { createContentHash, hasUnpublishedChanges } from './utils/content-hash.js';
|
|
6
6
|
export * from './schema-utils/index.js';
|
|
7
|
+
export * from './events/index.js';
|
|
8
|
+
export * from './forms/index.js';
|
|
7
9
|
export * from './theme/index.js';
|
|
8
10
|
export * from './plugins/index.js';
|
|
9
11
|
export * from './utils/index.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/lib/index.ts"],"names":[],"mappings":"AAkBA,cAAc,eAAe,CAAC;AAC9B,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAGjG,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC;AAGzC,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAGhF,cAAc,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/lib/index.ts"],"names":[],"mappings":"AAkBA,cAAc,eAAe,CAAC;AAC9B,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAGjG,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC;AAGzC,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAGhF,cAAc,sBAAsB,CAAC;AAIrC,cAAc,gBAAgB,CAAC;AAI/B,cAAc,eAAe,CAAC;AAG9B,cAAc,eAAe,CAAC;AAI9B,cAAc,iBAAiB,CAAC;AAGhC,cAAc,eAAe,CAAC;AAG9B,cAAc,aAAa,CAAC;AAC5B,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -23,6 +23,12 @@ export * from './field-validation/utils.js';
|
|
|
23
23
|
export { createContentHash, hasUnpublishedChanges } from './utils/content-hash.js';
|
|
24
24
|
// Schema utilities
|
|
25
25
|
export * from './schema-utils/index.js';
|
|
26
|
+
// Event catalog — defineEvent() + built-in event definitions (document.published, …).
|
|
27
|
+
// Plain TS (zod only), safe for the universal barrel.
|
|
28
|
+
export * from './events/index.js';
|
|
29
|
+
// Forms primitives — defineForm(), InferForm, validateFormData. Plain TS, reuses the field
|
|
30
|
+
// model + validation engine, safe for the universal barrel.
|
|
31
|
+
export * from './forms/index.js';
|
|
26
32
|
// Theme design-system tokens — plain TS (Svelte-free), safe for the universal barrel.
|
|
27
33
|
export * from './theme/index.js';
|
|
28
34
|
// Plugin API — definePlugin(), part types, part resolver. Runtime is plain JS
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { LocalAPI } from '../local-api/index.js';
|
|
3
|
+
import type { JobHandlerMap } from './types.js';
|
|
4
|
+
/** Reserved built-in job types. Scheduling uses these; the worker maps them to the handlers below. */
|
|
5
|
+
export declare const DOCUMENT_PUBLISH_JOB = "document.publish";
|
|
6
|
+
export declare const DOCUMENT_UNPUBLISH_JOB = "document.unpublish";
|
|
7
|
+
/** Payload for document.publish / document.unpublish jobs — identifiers only, never content. */
|
|
8
|
+
export declare const documentJobPayload: z.ZodObject<{
|
|
9
|
+
documentId: z.ZodString;
|
|
10
|
+
documentType: z.ZodString;
|
|
11
|
+
}, z.core.$strip>;
|
|
12
|
+
export type DocumentJobPayload = z.infer<typeof documentJobPayload>;
|
|
13
|
+
export interface DocumentJobDeps {
|
|
14
|
+
localAPI: LocalAPI;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Built-in handlers for scheduled publish/unpublish.
|
|
18
|
+
*
|
|
19
|
+
* Runs as the system (override access) — the permission check already happened when the
|
|
20
|
+
* job was scheduled. Publish routes through `CollectionAPI.publish`, so it re-runs
|
|
21
|
+
* validation + reference guards + cache invalidation and emits `document.published`
|
|
22
|
+
* inside the publish transaction, exactly like a manual publish. A handler throw is a
|
|
23
|
+
* job failure: the runner retries with backoff or dead-letters it (e.g. a doc whose
|
|
24
|
+
* references became unpublished before the scheduled time fails validation and retries).
|
|
25
|
+
*/
|
|
26
|
+
export declare function createDocumentJobHandlers(deps: DocumentJobDeps): JobHandlerMap;
|
|
27
|
+
//# sourceMappingURL=document-jobs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"document-jobs.d.ts","sourceRoot":"","sources":["../../src/lib/jobs/document-jobs.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEnD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE7C,sGAAsG;AACtG,eAAO,MAAM,oBAAoB,qBAAqB,CAAC;AACvD,eAAO,MAAM,sBAAsB,uBAAuB,CAAC;AAE3D,gGAAgG;AAChG,eAAO,MAAM,kBAAkB;;;iBAG7B,CAAC;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAEpE,MAAM,WAAW,eAAe;IAC/B,QAAQ,EAAE,QAAQ,CAAC;CACnB;AAED;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,eAAe,GAAG,aAAa,CAgB9E"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Built-in job types for the scheduled document workflow: publish / unpublish at a
|
|
2
|
+
// future `runAt`. The handlers are provided by core but DI'd with the request-scoped
|
|
3
|
+
// LocalAPI at the worker endpoint (the runner itself only knows the database adapter).
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { systemContext } from '../local-api/auth-helpers.js';
|
|
6
|
+
/** Reserved built-in job types. Scheduling uses these; the worker maps them to the handlers below. */
|
|
7
|
+
export const DOCUMENT_PUBLISH_JOB = 'document.publish';
|
|
8
|
+
export const DOCUMENT_UNPUBLISH_JOB = 'document.unpublish';
|
|
9
|
+
/** Payload for document.publish / document.unpublish jobs — identifiers only, never content. */
|
|
10
|
+
export const documentJobPayload = z.object({
|
|
11
|
+
documentId: z.string(),
|
|
12
|
+
documentType: z.string()
|
|
13
|
+
});
|
|
14
|
+
/**
|
|
15
|
+
* Built-in handlers for scheduled publish/unpublish.
|
|
16
|
+
*
|
|
17
|
+
* Runs as the system (override access) — the permission check already happened when the
|
|
18
|
+
* job was scheduled. Publish routes through `CollectionAPI.publish`, so it re-runs
|
|
19
|
+
* validation + reference guards + cache invalidation and emits `document.published`
|
|
20
|
+
* inside the publish transaction, exactly like a manual publish. A handler throw is a
|
|
21
|
+
* job failure: the runner retries with backoff or dead-letters it (e.g. a doc whose
|
|
22
|
+
* references became unpublished before the scheduled time fails validation and retries).
|
|
23
|
+
*/
|
|
24
|
+
export function createDocumentJobHandlers(deps) {
|
|
25
|
+
const { localAPI } = deps;
|
|
26
|
+
return {
|
|
27
|
+
[DOCUMENT_PUBLISH_JOB]: async ({ job }) => {
|
|
28
|
+
const { documentId, documentType } = documentJobPayload.parse(job.payload);
|
|
29
|
+
const collection = localAPI.getCollection(documentType);
|
|
30
|
+
if (!collection)
|
|
31
|
+
throw new Error(`Unknown collection "${documentType}" for job ${job.id}`);
|
|
32
|
+
await collection.publish(systemContext(job.organizationId), documentId);
|
|
33
|
+
},
|
|
34
|
+
[DOCUMENT_UNPUBLISH_JOB]: async ({ job }) => {
|
|
35
|
+
const { documentId, documentType } = documentJobPayload.parse(job.payload);
|
|
36
|
+
const collection = localAPI.getCollection(documentType);
|
|
37
|
+
if (!collection)
|
|
38
|
+
throw new Error(`Unknown collection "${documentType}" for job ${job.id}`);
|
|
39
|
+
await collection.unpublish(systemContext(job.organizationId), documentId);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { JobRunnerServices } from './run-batch.js';
|
|
2
|
+
import type { Logger } from '../utils/logger.js';
|
|
3
|
+
/** Handle for a running embedded loop. Call `stop()` to clear the interval. */
|
|
4
|
+
export interface EmbeddedJobRunner {
|
|
5
|
+
stop(): void;
|
|
6
|
+
}
|
|
7
|
+
export interface EmbeddedJobRunnerOptions {
|
|
8
|
+
/** Milliseconds between ticks. Default 3000. */
|
|
9
|
+
intervalMs?: number;
|
|
10
|
+
logger: Logger;
|
|
11
|
+
/**
|
|
12
|
+
* Resolve the live services at tick time (rather than capturing them once), so a schema-HMR
|
|
13
|
+
* re-init that rebuilds the CMS instances is picked up automatically. Return `null` to skip a
|
|
14
|
+
* tick (e.g. the app hasn't finished initializing yet).
|
|
15
|
+
*/
|
|
16
|
+
getServices: () => JobRunnerServices | null;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Start the loop. Ticks NEVER overlap: while a tick is in flight the next interval fire is
|
|
20
|
+
* skipped, so a slow batch can't stack runs on top of each other. A thrown error in a tick is
|
|
21
|
+
* logged and swallowed — the loop must survive a transient DB blip and keep going. The interval
|
|
22
|
+
* is `unref`'d where supported so it never keeps the process alive on its own.
|
|
23
|
+
*/
|
|
24
|
+
export declare function startEmbeddedJobRunner(options: EmbeddedJobRunnerOptions): EmbeddedJobRunner;
|
|
25
|
+
//# sourceMappingURL=embedded-runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embedded-runner.d.ts","sourceRoot":"","sources":["../../src/lib/jobs/embedded-runner.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,iBAAiB,EAAsB,MAAM,aAAa,CAAC;AAEzE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAE9C,+EAA+E;AAC/E,MAAM,WAAW,iBAAiB;IACjC,IAAI,IAAI,IAAI,CAAC;CACb;AAED,MAAM,WAAW,wBAAwB;IACxC,gDAAgD;IAChD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,WAAW,EAAE,MAAM,iBAAiB,GAAG,IAAI,CAAC;CAC5C;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,wBAAwB,GAAG,iBAAiB,CAyC3F"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { runJobsBatch } from './run-batch.js';
|
|
2
|
+
/**
|
|
3
|
+
* Start the loop. Ticks NEVER overlap: while a tick is in flight the next interval fire is
|
|
4
|
+
* skipped, so a slow batch can't stack runs on top of each other. A thrown error in a tick is
|
|
5
|
+
* logged and swallowed — the loop must survive a transient DB blip and keep going. The interval
|
|
6
|
+
* is `unref`'d where supported so it never keeps the process alive on its own.
|
|
7
|
+
*/
|
|
8
|
+
export function startEmbeddedJobRunner(options) {
|
|
9
|
+
const intervalMs = options.intervalMs ?? 3000;
|
|
10
|
+
const { logger, getServices } = options;
|
|
11
|
+
let running = false;
|
|
12
|
+
let stopped = false;
|
|
13
|
+
const tick = async () => {
|
|
14
|
+
if (running || stopped)
|
|
15
|
+
return; // no overlap; nothing after stop()
|
|
16
|
+
const services = getServices();
|
|
17
|
+
if (!services)
|
|
18
|
+
return; // not initialized yet — try again next interval
|
|
19
|
+
running = true;
|
|
20
|
+
try {
|
|
21
|
+
const result = await runJobsBatch(services, {
|
|
22
|
+
workerId: 'embedded'
|
|
23
|
+
});
|
|
24
|
+
// Only surface a tick that actually did something, so an idle dev server stays quiet.
|
|
25
|
+
if (result.claimed > 0 || result.relay.enqueued > 0) {
|
|
26
|
+
logger.debug(`[jobs:embedded] relayed=${result.relay.enqueued} claimed=${result.claimed} completed=${result.completed} failed=${result.failed} retried=${result.retried}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
logger.error('[jobs:embedded] tick failed:', error);
|
|
31
|
+
}
|
|
32
|
+
finally {
|
|
33
|
+
running = false;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
const handle = setInterval(tick, intervalMs);
|
|
37
|
+
// Don't let the loop hold the process open by itself (Node/Bun); harmless where unsupported.
|
|
38
|
+
handle.unref?.();
|
|
39
|
+
logger.info(`[jobs:embedded] in-process job loop started (every ${intervalMs}ms)`);
|
|
40
|
+
return {
|
|
41
|
+
stop() {
|
|
42
|
+
stopped = true;
|
|
43
|
+
clearInterval(handle);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/jobs/index.ts"],"names":[],"mappings":"AAEA,cAAc,SAAS,CAAC;AACxB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,SAAS,CAAC;AACxB,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Job execution — the durable spine's runner. Plain TS (no Svelte), safe to import
|
|
2
|
+
// from the server barrel. Handlers and scheduling are wired via `CMSConfig.jobs`.
|
|
3
|
+
export * from './types.js';
|
|
4
|
+
export * from './run-due-jobs.js';
|
|
5
|
+
export * from './relay.js';
|
|
6
|
+
export * from './run-batch.js';
|
|
7
|
+
export * from './document-jobs.js';
|
|
8
|
+
export * from './embedded-runner.js';
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Logger } from '../utils/logger.js';
|
|
2
|
+
import type { DatabaseAdapter } from '../db/interfaces/index.js';
|
|
3
|
+
import type { PartResolver } from '../plugins/resolver.js';
|
|
4
|
+
/** The slice of the service container the relay needs — a structural subset of `JobRunnerServices`. */
|
|
5
|
+
export interface RelayServices {
|
|
6
|
+
databaseAdapter: DatabaseAdapter;
|
|
7
|
+
logger: Logger;
|
|
8
|
+
partResolver: PartResolver;
|
|
9
|
+
}
|
|
10
|
+
export interface RelayOutboxOptions {
|
|
11
|
+
/** Scope to one tenant; omit to relay across all orgs (worker context, override access). */
|
|
12
|
+
organizationId?: string;
|
|
13
|
+
/** Max outbox rows drained this pass. Default 100 (fan-out is cheap; relay generously). */
|
|
14
|
+
batchSize?: number;
|
|
15
|
+
}
|
|
16
|
+
export interface RelayOutboxResult {
|
|
17
|
+
/** Outbox rows processed this pass. */
|
|
18
|
+
relayed: number;
|
|
19
|
+
/** Delivery jobs enqueued (deduped enqueues still count as one each). */
|
|
20
|
+
enqueued: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Drain one bounded batch of the outbox, fanning each event out to its subscribed consumers.
|
|
24
|
+
* Runs one pass and returns — the caller (the same tick as `runJobsBatch`) owns cadence.
|
|
25
|
+
*/
|
|
26
|
+
export declare function relayOutbox(services: RelayServices, options?: RelayOutboxOptions): Promise<RelayOutboxResult>;
|
|
27
|
+
//# sourceMappingURL=relay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relay.d.ts","sourceRoot":"","sources":["../../src/lib/jobs/relay.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGxD,uGAAuG;AACvG,MAAM,WAAW,aAAa;IAC7B,eAAe,EAAE,eAAe,CAAC;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,YAAY,CAAC;CAC3B;AAED,MAAM,WAAW,kBAAkB;IAClC,4FAA4F;IAC5F,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,2FAA2F;IAC3F,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IACjC,uCAAuC;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,QAAQ,EAAE,MAAM,CAAC;CACjB;AAID;;;GAGG;AACH,wBAAsB,WAAW,CAChC,QAAQ,EAAE,aAAa,EACvB,OAAO,GAAE,kBAAuB,GAC9B,OAAO,CAAC,iBAAiB,CAAC,CA6D5B"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { consumerJobType, toDeliveryPayload } from '../events/consumer.js';
|
|
2
|
+
const DEFAULT_RELAY_BATCH_SIZE = 100;
|
|
3
|
+
/**
|
|
4
|
+
* Drain one bounded batch of the outbox, fanning each event out to its subscribed consumers.
|
|
5
|
+
* Runs one pass and returns — the caller (the same tick as `runJobsBatch`) owns cadence.
|
|
6
|
+
*/
|
|
7
|
+
export async function relayOutbox(services, options = {}) {
|
|
8
|
+
const { databaseAdapter, logger, partResolver } = services;
|
|
9
|
+
const batchSize = options.batchSize ?? DEFAULT_RELAY_BATCH_SIZE;
|
|
10
|
+
const rows = await databaseAdapter.listUnprocessedOutbox({
|
|
11
|
+
organizationId: options.organizationId,
|
|
12
|
+
limit: batchSize
|
|
13
|
+
});
|
|
14
|
+
const result = { relayed: 0, enqueued: 0 };
|
|
15
|
+
for (const row of rows) {
|
|
16
|
+
const consumers = partResolver.consumersForEvent(row.eventType);
|
|
17
|
+
// A row with no subscribers is still marked processed — the relay's contract is "every
|
|
18
|
+
// event is considered", not "every event produces a job". Nobody listening is a valid
|
|
19
|
+
// outcome, and leaving it pending would re-scan it forever.
|
|
20
|
+
const event = {
|
|
21
|
+
id: row.eventId,
|
|
22
|
+
type: row.eventType,
|
|
23
|
+
organizationId: row.organizationId,
|
|
24
|
+
payload: row.payload,
|
|
25
|
+
correlationId: row.correlationId,
|
|
26
|
+
causationId: row.causationId,
|
|
27
|
+
createdBy: row.createdBy,
|
|
28
|
+
createdAt: row.createdAt
|
|
29
|
+
};
|
|
30
|
+
const payload = toDeliveryPayload(event);
|
|
31
|
+
try {
|
|
32
|
+
await databaseAdapter.withTransaction(async (tx) => {
|
|
33
|
+
for (const consumer of consumers) {
|
|
34
|
+
await tx.scheduleJob({
|
|
35
|
+
organizationId: row.organizationId,
|
|
36
|
+
type: consumerJobType(consumer.id),
|
|
37
|
+
payload,
|
|
38
|
+
// Exactly-once per (event, consumer): a repeated key returns the existing job.
|
|
39
|
+
idempotencyKey: `evt:${row.eventId}:${consumer.id}`,
|
|
40
|
+
maxAttempts: consumer.maxAttempts,
|
|
41
|
+
correlationId: row.correlationId,
|
|
42
|
+
// The event is the cause of every delivery it spawns — chain it for tracing.
|
|
43
|
+
causationId: row.eventId,
|
|
44
|
+
createdBy: row.createdBy
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
await tx.markOutboxProcessed(row.organizationId, row.id);
|
|
48
|
+
});
|
|
49
|
+
result.relayed++;
|
|
50
|
+
result.enqueued += consumers.length;
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
// Leave the row unprocessed; the next pass retries it. Idempotent enqueue makes a
|
|
54
|
+
// partial failure safe to redo. Log and continue so one bad row can't stall the batch.
|
|
55
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
56
|
+
logger.error('[relay]', `Failed to relay event ${row.eventId} (${row.eventType}); will retry next pass: ${message}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return result;
|
|
60
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { DatabaseAdapter } from '../db/interfaces/index.js';
|
|
2
|
+
import type { EmailAdapter } from '../email/interfaces/email.js';
|
|
3
|
+
import type { Logger } from '../utils/logger.js';
|
|
4
|
+
import type { CMSConfig } from '../types/config.js';
|
|
5
|
+
import type { LocalAPI } from '../local-api/index.js';
|
|
6
|
+
import type { PartResolver } from '../plugins/resolver.js';
|
|
7
|
+
import { type RunDueJobsResult } from './run-due-jobs.js';
|
|
8
|
+
import { type RelayOutboxResult } from './relay.js';
|
|
9
|
+
import { type PluginSettingsReader } from '../events/consumer.js';
|
|
10
|
+
/**
|
|
11
|
+
* The slice of the CMS service container the runner needs. Structurally a subset
|
|
12
|
+
* of `CMSInstances`, so callers can pass `event.locals.aphexCMS` (or `c.var.aphexCMS`)
|
|
13
|
+
* straight through — no adapter, no cast.
|
|
14
|
+
*/
|
|
15
|
+
export interface JobRunnerServices {
|
|
16
|
+
config: CMSConfig;
|
|
17
|
+
databaseAdapter: DatabaseAdapter;
|
|
18
|
+
logger: Logger;
|
|
19
|
+
localAPI: LocalAPI;
|
|
20
|
+
partResolver: PartResolver;
|
|
21
|
+
/** Injected into event-consumer deliveries so a consumer can read its own decrypted settings. */
|
|
22
|
+
pluginSettingsService: PluginSettingsReader;
|
|
23
|
+
/** Handed to event consumers that send notifications; `null`/absent when email isn't configured. */
|
|
24
|
+
emailAdapter?: EmailAdapter | null;
|
|
25
|
+
}
|
|
26
|
+
export interface RunJobsBatchOptions {
|
|
27
|
+
/** Lease-owner label written to `cms_jobs.lease_owner` (crash-recovery visibility). */
|
|
28
|
+
workerId?: string;
|
|
29
|
+
/** Scope to one tenant; omit to claim across all orgs (worker context, override access). */
|
|
30
|
+
organizationId?: string;
|
|
31
|
+
}
|
|
32
|
+
/** One tick's combined outcome: the relay fan-out plus the job execution that followed. */
|
|
33
|
+
export interface RunJobsBatchResult extends RunDueJobsResult {
|
|
34
|
+
/** Outbox fan-out done at the top of this tick (events → delivery jobs). */
|
|
35
|
+
relay: RelayOutboxResult;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Run one full worker tick: relay the outbox, then run one bounded batch of due jobs with the
|
|
39
|
+
* fully-assembled handler map.
|
|
40
|
+
*
|
|
41
|
+
* Relaying FIRST means a delivery job an event spawns this tick is already `pending` when the
|
|
42
|
+
* job pass runs, so a just-published document's consumers can fire in the same tick rather than
|
|
43
|
+
* waiting for the next — at the cost of nothing, since a slow consumer is still its own job.
|
|
44
|
+
*
|
|
45
|
+
* Handler precedence (later wins): core's built-in handlers (scheduled publish/unpublish) →
|
|
46
|
+
* plugin event-consumer deliveries (`aphex/consumer:<id>`) → plugin job handlers
|
|
47
|
+
* (`aphex/job/handler`) → the app's `config.jobs.handlers`. So an app can override a plugin,
|
|
48
|
+
* and a plugin can override a built-in — the app always has the final say. Consumer delivery
|
|
49
|
+
* types are namespaced, so they can't actually collide with the others.
|
|
50
|
+
*/
|
|
51
|
+
export declare function runJobsBatch(services: JobRunnerServices, options?: RunJobsBatchOptions): Promise<RunJobsBatchResult>;
|
|
52
|
+
//# sourceMappingURL=run-batch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-batch.d.ts","sourceRoot":"","sources":["../../src/lib/jobs/run-batch.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,EAAc,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACnE,OAAO,EAAe,KAAK,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAE9D,OAAO,EAGN,KAAK,oBAAoB,EACzB,MAAM,oBAAoB,CAAC;AAE5B;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IACjC,MAAM,EAAE,SAAS,CAAC;IAClB,eAAe,EAAE,eAAe,CAAC;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,QAAQ,CAAC;IACnB,YAAY,EAAE,YAAY,CAAC;IAC3B,iGAAiG;IACjG,qBAAqB,EAAE,oBAAoB,CAAC;IAC5C,oGAAoG;IACpG,YAAY,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;CACnC;AAED,MAAM,WAAW,mBAAmB;IACnC,uFAAuF;IACvF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4FAA4F;IAC5F,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,2FAA2F;AAC3F,MAAM,WAAW,kBAAmB,SAAQ,gBAAgB;IAC3D,4EAA4E;IAC5E,KAAK,EAAE,iBAAiB,CAAC;CACzB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,YAAY,CACjC,QAAQ,EAAE,iBAAiB,EAC3B,OAAO,GAAE,mBAAwB,GAC/B,OAAO,CAAC,kBAAkB,CAAC,CAqC7B"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// The shared job-runner seam. Both the protected HTTP endpoint
|
|
2
|
+
// (`POST /api/internal/workers/run`) and the embedded in-process poll loop call
|
|
3
|
+
// THIS — never `runDueJobs` directly — so the handler wiring (built-in document
|
|
4
|
+
// jobs + app-registered handlers) is constructed in exactly one place and can
|
|
5
|
+
// never drift between the two invocation modes.
|
|
6
|
+
//
|
|
7
|
+
// `runDueJobs` is the pure engine (claim → run → settle, one batch). This wrapper
|
|
8
|
+
// is the only thing that knows *which* handlers exist and pulls the per-run knobs
|
|
9
|
+
// off `CMSConfig.jobs`.
|
|
10
|
+
import { randomUUID } from 'node:crypto';
|
|
11
|
+
import { runDueJobs } from './run-due-jobs.js';
|
|
12
|
+
import { relayOutbox } from './relay.js';
|
|
13
|
+
import { createDocumentJobHandlers } from './document-jobs.js';
|
|
14
|
+
import { consumerJobType, toConsumerJobHandler } from '../events/consumer.js';
|
|
15
|
+
/**
|
|
16
|
+
* Run one full worker tick: relay the outbox, then run one bounded batch of due jobs with the
|
|
17
|
+
* fully-assembled handler map.
|
|
18
|
+
*
|
|
19
|
+
* Relaying FIRST means a delivery job an event spawns this tick is already `pending` when the
|
|
20
|
+
* job pass runs, so a just-published document's consumers can fire in the same tick rather than
|
|
21
|
+
* waiting for the next — at the cost of nothing, since a slow consumer is still its own job.
|
|
22
|
+
*
|
|
23
|
+
* Handler precedence (later wins): core's built-in handlers (scheduled publish/unpublish) →
|
|
24
|
+
* plugin event-consumer deliveries (`aphex/consumer:<id>`) → plugin job handlers
|
|
25
|
+
* (`aphex/job/handler`) → the app's `config.jobs.handlers`. So an app can override a plugin,
|
|
26
|
+
* and a plugin can override a built-in — the app always has the final say. Consumer delivery
|
|
27
|
+
* types are namespaced, so they can't actually collide with the others.
|
|
28
|
+
*/
|
|
29
|
+
export async function runJobsBatch(services, options = {}) {
|
|
30
|
+
const { config, databaseAdapter, logger, localAPI, partResolver, pluginSettingsService } = services;
|
|
31
|
+
const emailAdapter = services.emailAdapter ?? null;
|
|
32
|
+
const relay = await relayOutbox(services, {
|
|
33
|
+
organizationId: options.organizationId,
|
|
34
|
+
batchSize: config.jobs?.relayBatchSize
|
|
35
|
+
});
|
|
36
|
+
// Turn each registered consumer into a delivery job handler, keyed by its reserved job type.
|
|
37
|
+
// Built here (not in the resolver) because the wrapper injects live services — the settings
|
|
38
|
+
// reader that lets a consumer read its own decrypted config.
|
|
39
|
+
const consumerHandlers = {};
|
|
40
|
+
for (const consumer of partResolver.eventConsumers()) {
|
|
41
|
+
consumerHandlers[consumerJobType(consumer.id)] = toConsumerJobHandler(consumer.handler, {
|
|
42
|
+
pluginSettingsService,
|
|
43
|
+
emailAdapter
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
const jobs = await runDueJobs({
|
|
47
|
+
databaseAdapter,
|
|
48
|
+
handlers: {
|
|
49
|
+
...createDocumentJobHandlers({ localAPI }),
|
|
50
|
+
...consumerHandlers,
|
|
51
|
+
...partResolver.jobHandlers(),
|
|
52
|
+
...(config.jobs?.handlers ?? {})
|
|
53
|
+
},
|
|
54
|
+
logger,
|
|
55
|
+
workerId: options.workerId ?? `runner-${randomUUID()}`,
|
|
56
|
+
organizationId: options.organizationId,
|
|
57
|
+
batchSize: config.jobs?.batchSize,
|
|
58
|
+
leaseMs: config.jobs?.leaseMs
|
|
59
|
+
});
|
|
60
|
+
return { ...jobs, relay };
|
|
61
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { DatabaseAdapter } from '../db/interfaces/index.js';
|
|
2
|
+
import type { Logger } from '../utils/logger.js';
|
|
3
|
+
import type { JobHandlerMap } from './types.js';
|
|
4
|
+
export interface RunDueJobsOptions {
|
|
5
|
+
databaseAdapter: DatabaseAdapter;
|
|
6
|
+
handlers: JobHandlerMap;
|
|
7
|
+
logger: Logger;
|
|
8
|
+
/** Identifies the lease owner in `cms_jobs.lease_owner` (for crash-recovery visibility). */
|
|
9
|
+
workerId: string;
|
|
10
|
+
/** Scope to one tenant; omit to claim across all orgs (worker context, override access). */
|
|
11
|
+
organizationId?: string;
|
|
12
|
+
/** Max jobs claimed this batch. Default 10. */
|
|
13
|
+
batchSize?: number;
|
|
14
|
+
/** Lease duration (ms) before a claimed-but-unfinished job is reclaimable. Default 30000. */
|
|
15
|
+
leaseMs?: number;
|
|
16
|
+
/** First-retry delay (ms); doubles per attempt. Default 1000. */
|
|
17
|
+
baseBackoffMs?: number;
|
|
18
|
+
/** Backoff ceiling (ms). Default 1h. */
|
|
19
|
+
maxBackoffMs?: number;
|
|
20
|
+
/** Injectable clock (tests). Default `new Date()`. */
|
|
21
|
+
now?: Date;
|
|
22
|
+
}
|
|
23
|
+
export interface RunDueJobsResult {
|
|
24
|
+
claimed: number;
|
|
25
|
+
completed: number;
|
|
26
|
+
retried: number;
|
|
27
|
+
failed: number;
|
|
28
|
+
}
|
|
29
|
+
export declare function runDueJobs(options: RunDueJobsOptions): Promise<RunDueJobsResult>;
|
|
30
|
+
//# sourceMappingURL=run-due-jobs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-due-jobs.d.ts","sourceRoot":"","sources":["../../src/lib/jobs/run-due-jobs.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE7C,MAAM,WAAW,iBAAiB;IACjC,eAAe,EAAE,eAAe,CAAC;IACjC,QAAQ,EAAE,aAAa,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,4FAA4F;IAC5F,QAAQ,EAAE,MAAM,CAAC;IACjB,4FAA4F;IAC5F,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,+CAA+C;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6FAA6F;IAC7F,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iEAAiE;IACjE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,wCAAwC;IACxC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,sDAAsD;IACtD,GAAG,CAAC,EAAE,IAAI,CAAC;CACX;AAED,MAAM,WAAW,gBAAgB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CACf;AAkBD,wBAAsB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAmEtF"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
const DEFAULT_BATCH_SIZE = 10;
|
|
2
|
+
const DEFAULT_LEASE_MS = 30_000;
|
|
3
|
+
const DEFAULT_BASE_BACKOFF_MS = 1_000;
|
|
4
|
+
const DEFAULT_MAX_BACKOFF_MS = 60 * 60 * 1_000;
|
|
5
|
+
/**
|
|
6
|
+
* Exponential backoff with jitter. `attempts` is 1-based (the claim already bumped it),
|
|
7
|
+
* so the first retry waits ~base, the second ~2×base, capped at `max`. Up to 25% of
|
|
8
|
+
* extra jitter spreads retries so a batch that failed together doesn't re-thunder as one.
|
|
9
|
+
*/
|
|
10
|
+
function backoffMs(attempts, base, max) {
|
|
11
|
+
const exp = Math.min(max, base * 2 ** (attempts - 1));
|
|
12
|
+
const jitter = Math.random() * exp * 0.25;
|
|
13
|
+
return Math.min(max, exp + jitter);
|
|
14
|
+
}
|
|
15
|
+
export async function runDueJobs(options) {
|
|
16
|
+
const { databaseAdapter, handlers, logger, workerId, organizationId } = options;
|
|
17
|
+
const batchSize = options.batchSize ?? DEFAULT_BATCH_SIZE;
|
|
18
|
+
const leaseMs = options.leaseMs ?? DEFAULT_LEASE_MS;
|
|
19
|
+
const baseBackoffMs = options.baseBackoffMs ?? DEFAULT_BASE_BACKOFF_MS;
|
|
20
|
+
const maxBackoffMs = options.maxBackoffMs ?? DEFAULT_MAX_BACKOFF_MS;
|
|
21
|
+
const now = options.now ?? new Date();
|
|
22
|
+
const claimed = await databaseAdapter.claimDueJobs({
|
|
23
|
+
organizationId,
|
|
24
|
+
limit: batchSize,
|
|
25
|
+
workerId,
|
|
26
|
+
leaseMs,
|
|
27
|
+
now
|
|
28
|
+
});
|
|
29
|
+
const result = {
|
|
30
|
+
claimed: claimed.length,
|
|
31
|
+
completed: 0,
|
|
32
|
+
retried: 0,
|
|
33
|
+
failed: 0
|
|
34
|
+
};
|
|
35
|
+
for (const job of claimed) {
|
|
36
|
+
const handler = handlers[job.type];
|
|
37
|
+
// Unknown type can never become runnable — dead-letter it now rather than burn retries.
|
|
38
|
+
if (!handler) {
|
|
39
|
+
logger.error('[jobs]', `No handler registered for job type "${job.type}" (job ${job.id}); dead-lettering.`);
|
|
40
|
+
await databaseAdapter.failJob(job.organizationId, job.id, {
|
|
41
|
+
error: `No handler registered for job type "${job.type}"`
|
|
42
|
+
});
|
|
43
|
+
result.failed++;
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
await handler({ job, databaseAdapter, logger });
|
|
48
|
+
await databaseAdapter.completeJob(job.organizationId, job.id);
|
|
49
|
+
result.completed++;
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
53
|
+
// `attempts` was incremented by the claim, so it already counts this run.
|
|
54
|
+
if (job.attempts >= job.maxAttempts) {
|
|
55
|
+
logger.error('[jobs]', `Job ${job.id} (${job.type}) failed permanently after ${job.attempts}/${job.maxAttempts} attempts: ${message}`);
|
|
56
|
+
await databaseAdapter.failJob(job.organizationId, job.id, { error: message });
|
|
57
|
+
result.failed++;
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
const delay = backoffMs(job.attempts, baseBackoffMs, maxBackoffMs);
|
|
61
|
+
const runAt = new Date(now.getTime() + delay);
|
|
62
|
+
logger.warn('[jobs]', `Job ${job.id} (${job.type}) failed (attempt ${job.attempts}/${job.maxAttempts}); retrying in ${Math.round(delay)}ms: ${message}`);
|
|
63
|
+
await databaseAdapter.retryJob(job.organizationId, job.id, { runAt, error: message });
|
|
64
|
+
result.retried++;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { DatabaseAdapter } from '../db/interfaces/index.js';
|
|
2
|
+
import type { Job } from '../types/events.js';
|
|
3
|
+
import type { Logger } from '../utils/logger.js';
|
|
4
|
+
/** What a job handler receives. `databaseAdapter` is the live adapter (org-scoped calls still take an org id). */
|
|
5
|
+
export interface JobContext {
|
|
6
|
+
job: Job;
|
|
7
|
+
databaseAdapter: DatabaseAdapter;
|
|
8
|
+
logger: Logger;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Runs one job. Resolve to complete it; throw to signal failure — the runner
|
|
12
|
+
* then retries with backoff (attempts remain) or dead-letters it (attempts exhausted).
|
|
13
|
+
* Handlers must be idempotent: at-least-once delivery means a job can run more than once.
|
|
14
|
+
*/
|
|
15
|
+
export type JobHandler = (ctx: JobContext) => Promise<void>;
|
|
16
|
+
/** Map of job `type` → handler. A claimed job whose type has no handler is dead-lettered. */
|
|
17
|
+
export type JobHandlerMap = Record<string, JobHandler>;
|
|
18
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lib/jobs/types.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAE9C,kHAAkH;AAClH,MAAM,WAAW,UAAU;IAC1B,GAAG,EAAE,GAAG,CAAC;IACT,eAAe,EAAE,eAAe,CAAC;IACjC,MAAM,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,GAAG,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAE5D,6FAA6F;AAC7F,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -46,6 +46,31 @@ export declare class DocumentsApi {
|
|
|
46
46
|
* Unpublish document (revert to draft only)
|
|
47
47
|
*/
|
|
48
48
|
static unpublish(id: string): Promise<ApiResponse<DocumentDTO>>;
|
|
49
|
+
/**
|
|
50
|
+
* Schedule a publish/unpublish for a future time (ISO-8601 `runAt`).
|
|
51
|
+
* Enqueues a job the worker runs at that time — the permission check happens now.
|
|
52
|
+
*/
|
|
53
|
+
static schedule(id: string, body: {
|
|
54
|
+
action: 'publish' | 'unpublish';
|
|
55
|
+
runAt: string;
|
|
56
|
+
}): Promise<ApiResponse<{
|
|
57
|
+
jobId: string;
|
|
58
|
+
type: string;
|
|
59
|
+
runAt: string;
|
|
60
|
+
status: string;
|
|
61
|
+
}>>;
|
|
62
|
+
/** Pending scheduled publish/unpublish for a document (for the editor's schedule indicator). */
|
|
63
|
+
static getSchedule(id: string): Promise<ApiResponse<Array<{
|
|
64
|
+
jobId: string;
|
|
65
|
+
type: string;
|
|
66
|
+
runAt: string;
|
|
67
|
+
status: string;
|
|
68
|
+
createdAt: string;
|
|
69
|
+
}>>>;
|
|
70
|
+
/** Cancel the pending schedule for a document. */
|
|
71
|
+
static cancelSchedule(id: string): Promise<ApiResponse<{
|
|
72
|
+
cancelled: number;
|
|
73
|
+
}>>;
|
|
49
74
|
/**
|
|
50
75
|
* Delete document by ID
|
|
51
76
|
*/
|
|
@@ -84,6 +109,9 @@ export declare const documents: {
|
|
|
84
109
|
updateById: typeof DocumentsApi.updateById;
|
|
85
110
|
publish: typeof DocumentsApi.publish;
|
|
86
111
|
unpublish: typeof DocumentsApi.unpublish;
|
|
112
|
+
schedule: typeof DocumentsApi.schedule;
|
|
113
|
+
getSchedule: typeof DocumentsApi.getSchedule;
|
|
114
|
+
cancelSchedule: typeof DocumentsApi.cancelSchedule;
|
|
87
115
|
deleteById: typeof DocumentsApi.deleteById;
|
|
88
116
|
getByType: typeof DocumentsApi.getByType;
|
|
89
117
|
getPublished: typeof DocumentsApi.getPublished;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"documents.d.ts","sourceRoot":"","sources":["../../../src/lib/api/documents.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,KAAK,EACX,WAAW,EACX,kBAAkB,EAClB,qBAAqB,EACrB,qBAAqB,EACrB,eAAe,EACf,iBAAiB,EACjB,MAAM,qBAAqB,CAAC;AAI7B,MAAM,MAAM,kBAAkB,GAAG,kBAAkB,CAAC;AAEpD,qBAAa,YAAY;IACxB;;;OAGG;WACU,IAAI,CAAC,MAAM,GAAE,kBAAuB,GAAG,OAAO,CAAC,WAAW,CAAC,WAAW,EAAE,CAAC,CAAC;IAUvF;;OAEG;WACU,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;IAInE;;;;OAIG;WACU,iBAAiB,CAC7B,EAAE,EAAE,MAAM,GACR,OAAO,CAAC,WAAW,CAAC,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC,CAAC;IAMnF;;;;;OAKG;WACU,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC,WAAW,EAAE,CAAC,CAAC;IAKxE;;OAEG;WACU,MAAM,CAAC,IAAI,EAAE,qBAAqB,GAAG,OAAO,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;IAInF;;;;OAIG;WACU,UAAU,CACtB,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,qBAAqB,GACzB,OAAO,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;IAIpC;;OAEG;WACU,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;IAInE;;OAEG;WACU,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;IAIrE;;OAEG;WACU,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IAI/D;;OAEG;WACU,SAAS,CACrB,OAAO,EAAE,MAAM,EACf,MAAM,GAAE,IAAI,CAAC,kBAAkB,EAAE,SAAS,CAAM,GAC9C,OAAO,CAAC,WAAW,CAAC,WAAW,EAAE,CAAC,CAAC;IAItC;;OAEG;WACU,YAAY,CACxB,MAAM,GAAE,IAAI,CAAC,kBAAkB,EAAE,QAAQ,CAAM,GAC7C,OAAO,CAAC,WAAW,CAAC,WAAW,EAAE,CAAC,CAAC;IAItC;;OAEG;WACU,SAAS,CACrB,MAAM,GAAE,IAAI,CAAC,kBAAkB,EAAE,QAAQ,CAAM,GAC7C,OAAO,CAAC,WAAW,CAAC,WAAW,EAAE,CAAC,CAAC;IAItC;;OAEG;WACU,YAAY,CACxB,EAAE,EAAE,MAAM,EACV,MAAM,CAAC,EAAE,iBAAiB,GACxB,OAAO,CAAC,WAAW,CAAC,eAAe,EAAE,CAAC,CAAC;IAI1C;;OAEG;WACU,UAAU,CACtB,EAAE,EAAE,MAAM,EACV,aAAa,EAAE,MAAM,GACnB,OAAO,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC;IAIxC;;OAEG;WACU,cAAc,CAC1B,EAAE,EAAE,MAAM,EACV,aAAa,EAAE,MAAM,GACnB,OAAO,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;CAGpC;AAGD,eAAO,MAAM,SAAS
|
|
1
|
+
{"version":3,"file":"documents.d.ts","sourceRoot":"","sources":["../../../src/lib/api/documents.ts"],"names":[],"mappings":"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"}
|