@rebasepro/server 0.23.0 → 0.24.1-canary.g19596ef
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/README.md +28 -3
- package/bin/rebase-server.js +4 -2
- package/dist/GCSStorageController-DEga1ULx.js +402 -0
- package/dist/GCSStorageController-DEga1ULx.js.map +1 -0
- package/dist/S3StorageController-Btx-6bJ5.js +480 -0
- package/dist/S3StorageController-Btx-6bJ5.js.map +1 -0
- package/dist/api/ast-schema-editor.d.ts +92 -1
- package/dist/api/errors.d.ts +41 -0
- package/dist/api/live-schema-routes.d.ts +64 -10
- package/dist/api/logs-routes.d.ts +39 -1
- package/dist/api/mount-path.d.ts +30 -0
- package/dist/api/mount.d.ts +6 -13
- package/dist/api/openapi-generator.d.ts +17 -0
- package/dist/api/rest/api-generator.d.ts +97 -11
- package/dist/api/rest/auth-collection-writes.d.ts +28 -21
- package/dist/api/rest/field-access-query.d.ts +28 -8
- package/dist/api/rest/field-ops.d.ts +19 -7
- package/dist/api/rest/query-body.d.ts +10 -0
- package/dist/api/rest/write-validation.d.ts +9 -7
- package/dist/api/schema-editor-routes.d.ts +10 -1
- package/dist/api/sql-export-routes.d.ts +8 -0
- package/dist/api/types.d.ts +52 -1
- package/dist/{ast-schema-editor-Mvr50v_S.js → ast-schema-editor-mCeSC03x.js} +310 -12
- package/dist/ast-schema-editor-mCeSC03x.js.map +1 -0
- package/dist/auth/access.d.ts +156 -0
- package/dist/auth/adapter-middleware.d.ts +2 -1
- package/dist/auth/address-ownership.d.ts +16 -1
- package/dist/auth/admin-roles-route.d.ts +4 -2
- package/dist/auth/admin-roles.d.ts +17 -20
- package/dist/auth/admin-user-ops.d.ts +24 -2
- package/dist/auth/admin-users-route.d.ts +2 -0
- package/dist/auth/api-keys/api-key-middleware.d.ts +64 -56
- package/dist/auth/api-keys/api-key-routes.d.ts +44 -11
- package/dist/auth/api-keys/api-key-store.d.ts +31 -8
- package/dist/auth/api-keys/api-key-types.d.ts +14 -16
- package/dist/auth/api-keys/http-operation.d.ts +19 -0
- package/dist/auth/api-keys/index.d.ts +11 -11
- package/dist/auth/api-keys/key-grant.d.ts +41 -0
- package/dist/auth/api-keys/legacy-permissions.d.ts +33 -0
- package/dist/auth/auth-hooks.d.ts +46 -7
- package/dist/auth/builtin-auth-adapter.d.ts +21 -0
- package/dist/auth/cookie-utils.d.ts +7 -0
- package/dist/auth/deliverable-address.d.ts +6 -0
- package/dist/auth/email-change-routes.d.ts +44 -0
- package/dist/auth/expired-token-sweep.d.ts +71 -0
- package/dist/auth/impersonation.d.ts +110 -0
- package/dist/auth/index.d.ts +4 -2
- package/dist/auth/interfaces.d.ts +120 -59
- package/dist/auth/jwks-routes.d.ts +5 -1
- package/dist/auth/jwt-crypto.d.ts +7 -0
- package/dist/auth/jwt-keys.d.ts +4 -0
- package/dist/auth/jwt.d.ts +319 -157
- package/dist/auth/magic-link-routes.d.ts +5 -6
- package/dist/auth/mfa-crypto.d.ts +9 -3
- package/dist/auth/mfa-gate.d.ts +4 -2
- package/dist/auth/mfa-routes.d.ts +8 -9
- package/dist/auth/middleware.d.ts +26 -7
- package/dist/auth/otp-routes.d.ts +5 -6
- package/dist/auth/passwordless-signup.d.ts +27 -0
- package/dist/auth/platform-token.d.ts +122 -0
- package/dist/auth/rate-limiter.d.ts +99 -31
- package/dist/auth/registration-policy.d.ts +17 -0
- package/dist/auth/require-auth.d.ts +1 -1
- package/dist/auth/routes.d.ts +61 -0
- package/dist/auth/scope-routes.d.ts +22 -0
- package/dist/auth/session-routes.d.ts +17 -6
- package/dist/auth/token-revocation.d.ts +56 -1
- package/dist/auth/user-picking.d.ts +37 -0
- package/dist/auth/verify-credential.d.ts +49 -0
- package/dist/{auth-B-GIMpDG.js → auth-CSBky1mt.js} +4564 -1929
- package/dist/auth-CSBky1mt.js.map +1 -0
- package/dist/auth_adapter-CIItCTc8.js +21 -0
- package/dist/auth_adapter-CIItCTc8.js.map +1 -0
- package/dist/backend/create-backend.d.ts +281 -0
- package/dist/backend/default-backend.d.ts +35 -0
- package/dist/backend/no-collections.d.ts +19 -0
- package/dist/backend/options.d.ts +912 -0
- package/dist/backend/scope.d.ts +23 -0
- package/dist/backend/services.d.ts +54 -0
- package/dist/backend-DTAOsLQc.js.map +1 -1
- package/dist/backup/backup-common.d.ts +24 -3
- package/dist/backup/backup-routes.d.ts +46 -5
- package/dist/backup/backup-schedule.d.ts +33 -0
- package/dist/backup/backup-storage.d.ts +16 -0
- package/dist/backup/index.d.ts +5 -1
- package/dist/backup-DBGRUVTS.js +585 -0
- package/dist/backup-DBGRUVTS.js.map +1 -0
- package/dist/boot/boot.d.ts +2 -2
- package/dist/boot/bundle.d.ts +19 -0
- package/dist/boot/driver.d.ts +3 -0
- package/dist/boot/env.d.ts +58 -6
- package/dist/boot/http-server.d.ts +22 -0
- package/dist/boot/options.d.ts +16 -1
- package/dist/boot/provision.d.ts +4 -2
- package/dist/boot/resource-adapters.d.ts +11 -14
- package/dist/boot/runtime-cors.d.ts +17 -0
- package/dist/boot/security-headers.d.ts +26 -0
- package/dist/boot/sources.d.ts +9 -0
- package/dist/boot/static-routing.d.ts +65 -0
- package/dist/collection_patch-BRu-BvDv.js +472 -0
- package/dist/collection_patch-BRu-BvDv.js.map +1 -0
- package/dist/{contract-routes-CbFjuBwa.js → contract-routes-DiFwiLg2.js} +5 -8
- package/dist/contract-routes-DiFwiLg2.js.map +1 -0
- package/dist/cron/cron-loader.d.ts +77 -18
- package/dist/cron/cron-routes.d.ts +1 -1
- package/dist/cron/cron-scheduler.d.ts +94 -23
- package/dist/cron/cron-store.d.ts +37 -5
- package/dist/cron/define-cron.d.ts +6 -2
- package/dist/cron/index.d.ts +3 -3
- package/dist/cron-loader-NHqRfKF0.js +202 -0
- package/dist/cron-loader-NHqRfKF0.js.map +1 -0
- package/dist/{cron-routes-eE8nif_b.js → cron-routes-DsH8Jwn5.js} +34 -8
- package/dist/cron-routes-DsH8Jwn5.js.map +1 -0
- package/dist/{cron-scheduler-B0pLfAix.js → cron-scheduler-D1DT0nw5.js} +349 -65
- package/dist/cron-scheduler-D1DT0nw5.js.map +1 -0
- package/dist/cron-store-DEpVstUU.js +577 -0
- package/dist/cron-store-DEpVstUU.js.map +1 -0
- package/dist/{ddl-bootstrap-C6mo0Kmz.js → ddl-bootstrap-BNqi2NZe.js} +2 -2
- package/dist/{ddl-bootstrap-C6mo0Kmz.js.map → ddl-bootstrap-BNqi2NZe.js.map} +1 -1
- package/dist/email/index.d.ts +2 -2
- package/dist/email/smtp-email-service.d.ts +23 -0
- package/dist/email/templates.d.ts +22 -0
- package/dist/email/types.d.ts +44 -0
- package/dist/env.d.ts +1 -39
- package/dist/{errors-DWsX4yTd.js → errors-Cqp86KnA.js} +263 -10
- package/dist/errors-Cqp86KnA.js.map +1 -0
- package/dist/filter-dialect-O1HK6Nvr.js +1628 -0
- package/dist/filter-dialect-O1HK6Nvr.js.map +1 -0
- package/dist/{function-loader-xnbDAPfa.js → function-loader-DbCSoNJj.js} +69 -39
- package/dist/function-loader-DbCSoNJj.js.map +1 -0
- package/dist/{function-routes-Chet4-lB.js → function-routes-BSTClQNa.js} +28 -15
- package/dist/function-routes-BSTClQNa.js.map +1 -0
- package/dist/functions/context.d.ts +26 -6
- package/dist/functions/define-function.d.ts +10 -6
- package/dist/functions/function-loader.d.ts +75 -18
- package/dist/functions/function-routes.d.ts +2 -1
- package/dist/functions/guards.d.ts +22 -5
- package/dist/functions/index.d.ts +6 -5
- package/dist/functions/index.js +148 -50
- package/dist/functions/index.js.map +1 -1
- package/dist/functions/internal.d.ts +2 -2
- package/dist/functions/proxy.d.ts +6 -2
- package/dist/functions/request-timeout.d.ts +7 -5
- package/dist/history/history-routes.d.ts +9 -0
- package/dist/history/retention-sweep.d.ts +81 -0
- package/dist/{history-recorder-B4MpJfJK.js → history-recorder-RZ1Ow4YY.js} +4 -4
- package/dist/{history-recorder-B4MpJfJK.js.map → history-recorder-RZ1Ow4YY.js.map} +1 -1
- package/dist/{history-store-BhxWOuz9.js → history-store-DPofGVDp.js} +2 -2
- package/dist/{history-store-BhxWOuz9.js.map → history-store-DPofGVDp.js.map} +1 -1
- package/dist/index.d.ts +32 -13
- package/dist/index.es.js +25329 -18393
- package/dist/index.es.js.map +1 -1
- package/dist/init/docs.d.ts +35 -10
- package/dist/init/health.d.ts +17 -2
- package/dist/init/process-safety.d.ts +7 -2
- package/dist/init/shutdown.d.ts +26 -1
- package/dist/init.d.ts +49 -811
- package/dist/internal-tables-L2t39RdT.js +136 -0
- package/dist/internal-tables-L2t39RdT.js.map +1 -0
- package/dist/jobs/job-queue.d.ts +8 -0
- package/dist/jobs/job-store.d.ts +24 -0
- package/dist/jobs/types.d.ts +25 -4
- package/dist/{jobs-CazMYhyy.js → jobs-8G-_agld.js} +235 -25
- package/dist/jobs-8G-_agld.js.map +1 -0
- package/dist/{jwt-DnQHNFCl.js → jwt-CBaFc34Y.js} +546 -447
- package/dist/jwt-CBaFc34Y.js.map +1 -0
- package/dist/{keys-CogCQpxG.js → keys-COzQ2aE8.js} +3 -17
- package/dist/{keys-CogCQpxG.js.map → keys-COzQ2aE8.js.map} +1 -1
- package/dist/load-failure-L5hvfDoG.js +80 -0
- package/dist/load-failure-L5hvfDoG.js.map +1 -0
- package/dist/{logger-DO2PZc4i.js → logger-Bt32sHFw.js} +41 -5
- package/dist/logger-Bt32sHFw.js.map +1 -0
- package/dist/{logs-routes-Bj4TYYUl.js → logs-routes-DQJhrf-b.js} +48 -8
- package/dist/logs-routes-DQJhrf-b.js.map +1 -0
- package/dist/mcp/consent-page.d.ts +1 -1
- package/dist/mcp/mcp-routes.d.ts +13 -0
- package/dist/mcp/mcp-tools.d.ts +23 -9
- package/dist/mcp/oauth-metadata.d.ts +21 -16
- package/dist/mcp/oauth-routes.d.ts +14 -1
- package/dist/metrics/index.d.ts +18 -0
- package/dist/metrics/pool-wait.d.ts +50 -0
- package/dist/offline-QNQgsPOq-B2yGeXkn.js +3305 -0
- package/dist/offline-QNQgsPOq-B2yGeXkn.js.map +1 -0
- package/dist/offline-store-Cm5sAX81-CALiO4Gn.js +1915 -0
- package/dist/offline-store-Cm5sAX81-CALiO4Gn.js.map +1 -0
- package/dist/{openapi-generator-O_O24MAT.js → openapi-generator-CeGW2dLC.js} +455 -80
- package/dist/openapi-generator-CeGW2dLC.js.map +1 -0
- package/dist/policy-PdzX-Opd.js +121 -0
- package/dist/policy-PdzX-Opd.js.map +1 -0
- package/dist/project_manifest-R6y_KHms.js +59 -0
- package/dist/project_manifest-R6y_KHms.js.map +1 -0
- package/dist/{proxy-Czngl3p9.js → proxy-BEKbsil6.js} +5 -4
- package/dist/proxy-BEKbsil6.js.map +1 -0
- package/dist/{query-parser-DGRVFNM3.js → query-parser-ECxP0nDw.js} +497 -52
- package/dist/query-parser-ECxP0nDw.js.map +1 -0
- package/dist/{request-timeout-C_4C2BeR.js → request-timeout-BiRelY-U.js} +17 -10
- package/dist/request-timeout-BiRelY-U.js.map +1 -0
- package/dist/resumable-upload-threshold-CKv-Ry0l.js +24 -0
- package/dist/resumable-upload-threshold-CKv-Ry0l.js.map +1 -0
- package/dist/schema-edit/apply-schema-change.d.ts +73 -3
- package/dist/schema-edit/local-source.d.ts +29 -0
- package/dist/schema-edit/project-root.d.ts +3 -2
- package/dist/schema-edit/remote-source.d.ts +20 -5
- package/dist/schema-edit/source-editor.d.ts +28 -0
- package/dist/schema-editor-routes-DRXBAbZy.js +136 -0
- package/dist/schema-editor-routes-DRXBAbZy.js.map +1 -0
- package/dist/serve-spa.d.ts +58 -0
- package/dist/services/outbound-post.d.ts +50 -0
- package/dist/services/outbound-url-guard.d.ts +34 -5
- package/dist/services/routed-realtime-service.d.ts +16 -0
- package/dist/services/webhook-service.d.ts +66 -14
- package/dist/singleton.d.ts +9 -5
- package/dist/soft-delete-params-CBryZXMq.js +668 -0
- package/dist/soft-delete-params-CBryZXMq.js.map +1 -0
- package/dist/sql-export-routes-D8ZaRvoR.js +240 -0
- package/dist/sql-export-routes-D8ZaRvoR.js.map +1 -0
- package/dist/{src-vkcwKXbT.js → src-D5Y3N-S4.js} +440 -20
- package/dist/src-D5Y3N-S4.js.map +1 -0
- package/dist/storage/GCSStorageController.d.ts +48 -2
- package/dist/storage/LocalStorageController.d.ts +69 -2
- package/dist/storage/S3StorageController.d.ts +39 -3
- package/dist/storage/image-transform.d.ts +15 -0
- package/dist/storage/index.d.ts +2 -2
- package/dist/storage/maintenance-routes.d.ts +23 -0
- package/dist/storage/maintenance.d.ts +126 -0
- package/dist/storage/property-limits.d.ts +41 -6
- package/dist/storage/rendition-cache.d.ts +9 -11
- package/dist/storage/request-keys.d.ts +15 -0
- package/dist/storage/request-principals.d.ts +30 -0
- package/dist/storage/requested-object.d.ts +74 -0
- package/dist/storage/routes.d.ts +52 -14
- package/dist/storage/source-variables.d.ts +10 -0
- package/dist/storage/streams.d.ts +42 -0
- package/dist/storage/tus-handler.d.ts +76 -7
- package/dist/storage/types.d.ts +101 -7
- package/dist/storage/upload-validation.d.ts +13 -0
- package/dist/storage-resumable-VO-cMpse-C_YX71pO.js +125 -0
- package/dist/storage-resumable-VO-cMpse-C_YX71pO.js.map +1 -0
- package/dist/strings.es-BeZWAodn.js +256 -0
- package/dist/strings.es-BeZWAodn.js.map +1 -0
- package/dist/upload-validation-CVobI5xO.js +172 -0
- package/dist/upload-validation-CVobI5xO.js.map +1 -0
- package/dist/utils/byte-size.d.ts +10 -0
- package/dist/utils/compression.d.ts +6 -0
- package/dist/utils/load-failure.d.ts +14 -0
- package/dist/utils/logger.d.ts +21 -0
- package/package.json +5 -5
- package/dist/GCSStorageController-CjrA4PMo.js +0 -242
- package/dist/GCSStorageController-CjrA4PMo.js.map +0 -1
- package/dist/S3StorageController-B6pKDNVj.js +0 -264
- package/dist/S3StorageController-B6pKDNVj.js.map +0 -1
- package/dist/admin-roles-vYdp_Pil.js +0 -36
- package/dist/admin-roles-vYdp_Pil.js.map +0 -1
- package/dist/admin_block-DxKLmdiv.js +0 -206
- package/dist/admin_block-DxKLmdiv.js.map +0 -1
- package/dist/ast-schema-editor-Mvr50v_S.js.map +0 -1
- package/dist/auth/api-keys/api-key-permission-guard.d.ts +0 -65
- package/dist/auth-B-GIMpDG.js.map +0 -1
- package/dist/backup-D7YR94N3.js +0 -253
- package/dist/backup-D7YR94N3.js.map +0 -1
- package/dist/contract-routes-CbFjuBwa.js.map +0 -1
- package/dist/cron-loader-DfTj2Hbi.js +0 -118
- package/dist/cron-loader-DfTj2Hbi.js.map +0 -1
- package/dist/cron-routes-eE8nif_b.js.map +0 -1
- package/dist/cron-scheduler-B0pLfAix.js.map +0 -1
- package/dist/cron-store-TcoGz-xS.js +0 -305
- package/dist/cron-store-TcoGz-xS.js.map +0 -1
- package/dist/dynamic-import-X40pTZUQ.js +0 -23
- package/dist/dynamic-import-X40pTZUQ.js.map +0 -1
- package/dist/errors-DWsX4yTd.js.map +0 -1
- package/dist/function-loader-xnbDAPfa.js.map +0 -1
- package/dist/function-routes-Chet4-lB.js.map +0 -1
- package/dist/jobs-CazMYhyy.js.map +0 -1
- package/dist/jwt-DnQHNFCl.js.map +0 -1
- package/dist/logger-DO2PZc4i.js.map +0 -1
- package/dist/logs-routes-Bj4TYYUl.js.map +0 -1
- package/dist/openapi-generator-O_O24MAT.js.map +0 -1
- package/dist/proxy-Czngl3p9.js.map +0 -1
- package/dist/query-parser-DGRVFNM3.js.map +0 -1
- package/dist/request-timeout-C_4C2BeR.js.map +0 -1
- package/dist/schema-editor-routes-C5-lh_jO.js +0 -87
- package/dist/schema-editor-routes-C5-lh_jO.js.map +0 -1
- package/dist/src-pmvW7BFx.js +0 -6688
- package/dist/src-pmvW7BFx.js.map +0 -1
- package/dist/src-vkcwKXbT.js.map +0 -1
- package/dist/types-BfKcm9do.js +0 -39
- package/dist/types-BfKcm9do.js.map +0 -1
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
import { type CronJobDefinition, type CronResourceOptions } from "@rebasepro/types";
|
|
2
2
|
import { type ModuleImporter } from "../utils/dynamic-import.js";
|
|
3
|
+
import type { LoadDirectoryOptions } from "../functions/function-loader.js";
|
|
4
|
+
/**
|
|
5
|
+
* Cron jobs as values: the job id, to its definition.
|
|
6
|
+
*
|
|
7
|
+
* `crons: { "nightly-report": nightlyReport }` schedules `nightlyReport` as the
|
|
8
|
+
* job `nightly-report` — the same thing a file named `crons/nightly-report.ts`
|
|
9
|
+
* does. {@link loadCrons} returns exactly this shape, so
|
|
10
|
+
* `crons: await loadCrons(dir)` and `cronsDir: dir` schedule the same jobs.
|
|
11
|
+
*
|
|
12
|
+
* A value is what the author wrote — `defineCron({...})` — with no defaults
|
|
13
|
+
* filled in. The scheduler fills them in when it registers the job.
|
|
14
|
+
*/
|
|
15
|
+
export type CronsRecord = Record<string, CronJobDefinition>;
|
|
16
|
+
/** A job as the scheduler registers it: its id, and its definition with defaults filled in. */
|
|
3
17
|
export interface LoadedCronJob {
|
|
4
18
|
/** Job ID derived from filename (e.g. "cleanup-sessions"). */
|
|
5
19
|
id: string;
|
|
@@ -13,34 +27,79 @@ export interface LoadedCronJobs {
|
|
|
13
27
|
problems: string[];
|
|
14
28
|
}
|
|
15
29
|
/**
|
|
16
|
-
*
|
|
30
|
+
* Load the cron jobs in a directory, as values.
|
|
31
|
+
*
|
|
32
|
+
* Each file default-exports a `CronJobDefinition` — `defineCron({...})`. The
|
|
33
|
+
* filename without its extension is the record key, which is the job id:
|
|
34
|
+
* `crons/cleanup-sessions.ts` → `{ "cleanup-sessions": definition }`.
|
|
35
|
+
*
|
|
36
|
+
* Loading declares nothing: the definitions come back exactly as written, and
|
|
37
|
+
* whoever uses them decides what they are for — a backend schedules them, the
|
|
38
|
+
* build's derive step records them in the resource graph ({@link declareCrons}).
|
|
39
|
+
*
|
|
40
|
+
* Never throws for a bad file: it is skipped, logged, and passed to
|
|
41
|
+
* `options.onSkip`.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```ts
|
|
45
|
+
* const crons = await loadCrons(path.resolve("crons"));
|
|
46
|
+
* const backend = await createRebaseBackend({ database, crons, provision: true });
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export declare function loadCrons(directory: string, options?: LoadDirectoryOptions): Promise<CronsRecord>;
|
|
50
|
+
/**
|
|
51
|
+
* Record each cron in the project's resource graph — what `rebase resources`
|
|
52
|
+
* lists and a host reads to know a schedule exists, without running it.
|
|
17
53
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
54
|
+
* Keyed by the same id the scheduler, the routes and the Studio use, and built
|
|
55
|
+
* from the definitions as written: a default the scheduler fills in
|
|
56
|
+
* (`enabled: true`, `timeoutSeconds: 300`) is this process's, not something the
|
|
57
|
+
* author declared.
|
|
58
|
+
*/
|
|
59
|
+
export declare function declareCrons(crons: CronsRecord): void;
|
|
60
|
+
/**
|
|
61
|
+
* The jobs a scheduler registers, from cron values: the definitions with this
|
|
62
|
+
* process's defaults filled in.
|
|
63
|
+
*
|
|
64
|
+
* Spread first, then normalise. Rebuilding the object field by field dropped
|
|
65
|
+
* `catchUpWindowSeconds` once — and since this is the path every job takes to
|
|
66
|
+
* the scheduler, catch-up never ran for any job authored the documented way. A
|
|
67
|
+
* field added to `CronJobDefinition` tomorrow would be dropped the same way.
|
|
21
68
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
69
|
+
* @throws when a value has no `schedule` or no `handler`. A file like that is
|
|
70
|
+
* skipped by the loader with a reason; a value handed over in code is refused,
|
|
71
|
+
* because the caller is right there to fix it.
|
|
72
|
+
*/
|
|
73
|
+
export declare function cronJobsFrom(crons: CronsRecord): LoadedCronJob[];
|
|
74
|
+
/**
|
|
75
|
+
* @deprecated Use `loadCrons` instead, which returns the jobs as a record keyed
|
|
76
|
+
* by id. Will be removed in a future release.
|
|
25
77
|
*/
|
|
26
78
|
export declare function loadCronJobsFromDirectory(directory: string, importModule?: ModuleImporter): Promise<LoadedCronJob[]>;
|
|
27
79
|
/**
|
|
28
|
-
*
|
|
80
|
+
* @deprecated Use `loadCrons` instead, with `onSkip` to collect the files it
|
|
81
|
+
* skipped — and `declareCrons` where the jobs belong in the resource graph,
|
|
82
|
+
* which this used to do as a side effect of loading. Will be removed in a
|
|
83
|
+
* future release.
|
|
84
|
+
*/
|
|
85
|
+
export declare function loadCronJobsWithDiagnostics(directory: string, importModule?: ModuleImporter): Promise<LoadedCronJobs>;
|
|
86
|
+
/**
|
|
87
|
+
* Read a directory of cron files: the definitions that loaded, keyed by id, and
|
|
88
|
+
* one `<file> (<reason>)` per file that will NOT be scheduled.
|
|
29
89
|
*
|
|
30
|
-
* A job that does not load does not run, and
|
|
90
|
+
* A job that does not load does not run, and the only trace used to be one
|
|
31
91
|
* `warn` per file in the boot log — next to nothing at 3am when the report that
|
|
32
|
-
* should have been mailed was not.
|
|
33
|
-
*
|
|
34
|
-
* and a log line is not a surface.
|
|
92
|
+
* should have been mailed was not. So the skips are counted, summarised in one
|
|
93
|
+
* place, and answered by `GET /api/admin/cron`.
|
|
35
94
|
*
|
|
36
|
-
* Never throws, for the same reason
|
|
95
|
+
* Never throws, for the same reason the function loader does not: one
|
|
37
96
|
* malformed file must not take the server down, and the other jobs in the
|
|
38
|
-
* directory are still worth scheduling.
|
|
39
|
-
* counted, summarised in one place, and answered by `GET /api/cron` — so the
|
|
40
|
-
* question "why did nothing run?" has an answer that does not require log
|
|
41
|
-
* access.
|
|
97
|
+
* directory are still worth scheduling.
|
|
42
98
|
*/
|
|
43
|
-
export declare function
|
|
99
|
+
export declare function readCronsDirectory(directory: string, importModule?: ModuleImporter): Promise<{
|
|
100
|
+
crons: CronsRecord;
|
|
101
|
+
problems: string[];
|
|
102
|
+
}>;
|
|
44
103
|
/**
|
|
45
104
|
* The declaration a definition makes: every field a host can read without the
|
|
46
105
|
* handler. Spelled once here so the graph and the scheduler cannot disagree
|
|
@@ -7,7 +7,7 @@ import { type CronScheduler } from "./cron-scheduler.js";
|
|
|
7
7
|
* Routes:
|
|
8
8
|
* GET / → list all cron jobs
|
|
9
9
|
* GET /:id → get a single job's status
|
|
10
|
-
* POST /:id/trigger → manually trigger a job
|
|
10
|
+
* POST /:id/trigger → manually trigger a job (`?async=1`: 202 once it has started)
|
|
11
11
|
* GET /:id/logs → get execution logs for a job
|
|
12
12
|
* PUT /:id → update job (enable/disable, or `null` to follow the code)
|
|
13
13
|
*
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CronJobStatus, CronJobLogEntry } from "@rebasepro/types";
|
|
1
|
+
import type { CronJobDefinition, CronJobStatus, CronJobLogEntry, RejectedCronJob } from "@rebasepro/types";
|
|
2
2
|
import type { RebaseServerClient } from "@rebasepro/types";
|
|
3
3
|
import type { LoadedCronJob } from "./cron-loader.js";
|
|
4
4
|
import type { CronStore } from "./cron-store.js";
|
|
@@ -30,9 +30,9 @@ export declare function parseCronExpression(expression: string, after: Date, tim
|
|
|
30
30
|
* The latest slot matching `expression` within the inclusive window
|
|
31
31
|
* `[from, to]`, or `undefined` when the expression has no slot in it.
|
|
32
32
|
*
|
|
33
|
-
* Walks backwards
|
|
34
|
-
*
|
|
35
|
-
*
|
|
33
|
+
* Walks backwards from `to`, so the first hit is already the answer — in the
|
|
34
|
+
* common case (a job that ran normally moments ago) that is a handful of
|
|
35
|
+
* steps, not a scan of the whole window.
|
|
36
36
|
*
|
|
37
37
|
* `to`'s own minute is included: an instance booting at 06:00:30 has *not* run
|
|
38
38
|
* the 06:00 slot — `parseCronExpression` already skipped past it to tomorrow —
|
|
@@ -43,6 +43,29 @@ export declare function parseCronExpression(expression: string, after: Date, tim
|
|
|
43
43
|
* ISO string down either path. The claim key depends on that.
|
|
44
44
|
*/
|
|
45
45
|
export declare function findMostRecentSlot(expression: string, from: Date, to: Date, timezone?: string): Date | undefined;
|
|
46
|
+
/**
|
|
47
|
+
* What one run keeps of its `ctx.log` lines: up to 500 lines or 64 KB,
|
|
48
|
+
* whichever comes first. The lines past that are counted, and one line at the
|
|
49
|
+
* end says how many there were; every line still reaches the server log.
|
|
50
|
+
*
|
|
51
|
+
* Uncapped, a job logging one line per item kept them all — in the 50-run ring
|
|
52
|
+
* per job, in a `cron_logs` row, and in every history read: a per-minute sync
|
|
53
|
+
* of 1,000 items held 4 MB per job in memory and wrote 115 MB a day.
|
|
54
|
+
*/
|
|
55
|
+
export declare const MAX_CAPTURED_LOG_LINES = 500;
|
|
56
|
+
export declare const MAX_CAPTURED_LOG_BYTES: number;
|
|
57
|
+
/**
|
|
58
|
+
* What {@link CronScheduler.startJob} answers: the run began at `startedAt`
|
|
59
|
+
* (its history entry will carry the same timestamp), or it did not start and
|
|
60
|
+
* `log` is the skip that says why.
|
|
61
|
+
*/
|
|
62
|
+
export type StartedCronRun = {
|
|
63
|
+
started: true;
|
|
64
|
+
startedAt: string;
|
|
65
|
+
} | {
|
|
66
|
+
started: false;
|
|
67
|
+
log: CronJobLogEntry;
|
|
68
|
+
};
|
|
46
69
|
/**
|
|
47
70
|
* Whether a log entry records a run that did not happen because one was
|
|
48
71
|
* already executing — on this process, or on another that holds the job's run
|
|
@@ -50,19 +73,6 @@ export declare function findMostRecentSlot(expression: string, from: Date, to: D
|
|
|
50
73
|
* panel reads.
|
|
51
74
|
*/
|
|
52
75
|
export declare function isAlreadyExecutingSkip(entry: CronJobLogEntry): boolean;
|
|
53
|
-
/**
|
|
54
|
-
* A job the scheduler refused, and why.
|
|
55
|
-
*
|
|
56
|
-
* It is not a `CronJobStatus`: it has no state, no next run and no counters,
|
|
57
|
-
* because it was never registered. Reporting it as a job with `state: "error"`
|
|
58
|
-
* would be a lie in the other direction — nothing is going to run it.
|
|
59
|
-
*/
|
|
60
|
-
export interface RejectedCronJob {
|
|
61
|
-
id: string;
|
|
62
|
-
name: string;
|
|
63
|
-
schedule: string;
|
|
64
|
-
reason: string;
|
|
65
|
-
}
|
|
66
76
|
export declare class CronScheduler {
|
|
67
77
|
private jobs;
|
|
68
78
|
private rejected;
|
|
@@ -82,6 +92,10 @@ export declare class CronScheduler {
|
|
|
82
92
|
* ever clear the lease that run took.
|
|
83
93
|
*/
|
|
84
94
|
private readonly instanceLabel;
|
|
95
|
+
/** The hourly claim- and log-retention sweep, while the scheduler is started with a store. */
|
|
96
|
+
private claimSweepTimer?;
|
|
97
|
+
private sweepingClaims;
|
|
98
|
+
private sweepingLogs;
|
|
85
99
|
/**
|
|
86
100
|
* Set the server singleton to make it available to cron job handlers.
|
|
87
101
|
*
|
|
@@ -111,6 +125,14 @@ export declare class CronScheduler {
|
|
|
111
125
|
* Start the scheduler — begins ticking all enabled jobs.
|
|
112
126
|
*/
|
|
113
127
|
start(): void;
|
|
128
|
+
/** Delete slot claims past retention. Failures are logged; the next hour tries again. */
|
|
129
|
+
private sweepClaims;
|
|
130
|
+
/**
|
|
131
|
+
* Delete run history past retention, and count the runs not counted yet.
|
|
132
|
+
* Failures are logged; the next hour tries again, from where this one
|
|
133
|
+
* stopped — each batch it finished is committed.
|
|
134
|
+
*/
|
|
135
|
+
private sweepLogs;
|
|
114
136
|
/**
|
|
115
137
|
* Stop the scheduler: clear every timer, then wait for the runs executing
|
|
116
138
|
* right now — for at most `timeoutMs` when given.
|
|
@@ -128,6 +150,8 @@ export declare class CronScheduler {
|
|
|
128
150
|
* List all registered jobs with their current status.
|
|
129
151
|
*/
|
|
130
152
|
listJobs(): CronJobStatus[];
|
|
153
|
+
/** Keep a refused job, so the admin surface can say why it never runs. */
|
|
154
|
+
private refuse;
|
|
131
155
|
/**
|
|
132
156
|
* Jobs that loaded but whose schedule the scheduler refused.
|
|
133
157
|
*
|
|
@@ -136,6 +160,17 @@ export declare class CronScheduler {
|
|
|
136
160
|
* cron will never fire" look identical from the admin panel.
|
|
137
161
|
*/
|
|
138
162
|
listRejectedJobs(): RejectedCronJob[];
|
|
163
|
+
/**
|
|
164
|
+
* {@link listRejectedJobs}, those whose definition matches — how a surface
|
|
165
|
+
* finds a refused job by what it is, as {@link jobIdsWhere} does for the
|
|
166
|
+
* registered ones.
|
|
167
|
+
*/
|
|
168
|
+
rejectedJobsWhere(predicate: (definition: CronJobDefinition) => boolean): RejectedCronJob[];
|
|
169
|
+
/**
|
|
170
|
+
* The ids of the registered jobs whose definition matches — how a surface
|
|
171
|
+
* finds a job by what it is rather than by the file it happens to live in.
|
|
172
|
+
*/
|
|
173
|
+
jobIdsWhere(predicate: (definition: CronJobDefinition) => boolean): string[];
|
|
139
174
|
/**
|
|
140
175
|
* Get a single job status by ID.
|
|
141
176
|
*/
|
|
@@ -144,11 +179,11 @@ export declare class CronScheduler {
|
|
|
144
179
|
* {@link listJobs}, as the fleet sees it rather than this process alone.
|
|
145
180
|
*
|
|
146
181
|
* Reads the store: every job's enabled state — a pause another replica
|
|
147
|
-
* made —
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
182
|
+
* made — whether another process holds its run lease, and each job's run
|
|
183
|
+
* count and last run from `cron_logs`. The counters in this process's
|
|
184
|
+
* memory count only the runs it made: the `api` role runs nothing and
|
|
185
|
+
* would say 0 runs for a job the worker has run a thousand times, and one
|
|
186
|
+
* of two scheduling replicas never sees the slots the other one claimed.
|
|
152
187
|
*
|
|
153
188
|
* A store that cannot answer leaves this process's own view, with a
|
|
154
189
|
* warning: a listing is not worth failing.
|
|
@@ -162,7 +197,12 @@ export declare class CronScheduler {
|
|
|
162
197
|
getJobLogs(id: string, limit?: number): CronJobLogEntry[];
|
|
163
198
|
/**
|
|
164
199
|
* Get log entries for a job from the database (if store is available).
|
|
165
|
-
* Falls back to in-memory logs if no store is configured
|
|
200
|
+
* Falls back to in-memory logs if no store is configured, or if the store
|
|
201
|
+
* has none — a run whose row is still being written.
|
|
202
|
+
*
|
|
203
|
+
* Throws when the store cannot read the history. This process's memory is
|
|
204
|
+
* not a stand-in for it: it is empty on the `api` role and after a
|
|
205
|
+
* restart, and holds one replica's runs at most.
|
|
166
206
|
*/
|
|
167
207
|
getJobLogsFromDb(id: string, limit?: number): Promise<CronJobLogEntry[]>;
|
|
168
208
|
/**
|
|
@@ -199,6 +239,22 @@ export declare class CronScheduler {
|
|
|
199
239
|
* recognises it.
|
|
200
240
|
*/
|
|
201
241
|
triggerJob(id: string): Promise<CronJobLogEntry | undefined>;
|
|
242
|
+
/**
|
|
243
|
+
* Start a job now and answer as soon as the run has begun, not when it
|
|
244
|
+
* ends — the manual trigger's `?async=1`.
|
|
245
|
+
*
|
|
246
|
+
* The synchronous {@link triggerJob} holds its HTTP request open for the
|
|
247
|
+
* whole run, and a proxy in front of the server (many stop at 60 s) cuts
|
|
248
|
+
* a long job's answer while the run carries on. This resolves once the
|
|
249
|
+
* run lease is held and the handler is about to be called, so a run that
|
|
250
|
+
* cannot start — already executing here or on the lease holder — is still
|
|
251
|
+
* answered as the skip it is. The run itself is tracked like any other:
|
|
252
|
+
* `stop()` waits for it, and its outcome lands in the run history, where
|
|
253
|
+
* the entry carries `manual: true` and the `startedAt` returned here.
|
|
254
|
+
*
|
|
255
|
+
* Returns `undefined` if the job doesn't exist.
|
|
256
|
+
*/
|
|
257
|
+
startJob(id: string): Promise<StartedCronRun | undefined>;
|
|
202
258
|
/**
|
|
203
259
|
* Record a run that did not happen because the previous one had not
|
|
204
260
|
* finished.
|
|
@@ -262,6 +318,21 @@ export declare class CronScheduler {
|
|
|
262
318
|
* rest as a view for {@link toStatus}. Never throws.
|
|
263
319
|
*/
|
|
264
320
|
private readFleet;
|
|
321
|
+
/**
|
|
322
|
+
* The next slot of `job` after `now`, for a listing on a process whose
|
|
323
|
+
* scheduler is not started — the `api` role, which Studio's Cron panel
|
|
324
|
+
* polls every 15 seconds.
|
|
325
|
+
*
|
|
326
|
+
* Cached on the job. The key is the job's schedule and zone (a re-register
|
|
327
|
+
* replaces the job, and with it the cache); the entry holds while
|
|
328
|
+
* `computedAt <= now < slot`. That is the same answer, not an
|
|
329
|
+
* approximation: the search found no matching minute between
|
|
330
|
+
* `computedAt`'s next minute and `slot`, so from any later instant before
|
|
331
|
+
* `slot` it lands on `slot` again. A clock that moves back, or reaching the
|
|
332
|
+
* slot, recomputes. The cache holds a pure function of the schedule, so
|
|
333
|
+
* there is nothing user- or role-scoped in it.
|
|
334
|
+
*/
|
|
335
|
+
private listedNextRun;
|
|
265
336
|
/**
|
|
266
337
|
* Warn once at start when the process looks like it is running on a
|
|
267
338
|
* platform that freezes or evicts instances between requests, where the
|
|
@@ -11,12 +11,21 @@ import type { DataDriver } from "@rebasepro/types";
|
|
|
11
11
|
* occurs, and a pause or a trigger's "already executing" is per process.
|
|
12
12
|
*/
|
|
13
13
|
export interface CronStore {
|
|
14
|
-
/**
|
|
14
|
+
/**
|
|
15
|
+
* Ensure the backing tables exist and are locked away from end users. Runs
|
|
16
|
+
* once per process per database, however many stores ask: concurrent
|
|
17
|
+
* callers share the attempt in flight, and an attempt that fell short is
|
|
18
|
+
* made again on the next call.
|
|
19
|
+
*/
|
|
15
20
|
ensureTable(): Promise<void>;
|
|
16
21
|
/** Persist a single log entry after execution. */
|
|
17
22
|
insertLog(entry: CronJobLogEntry): Promise<void>;
|
|
18
23
|
/**
|
|
19
24
|
* Fetch the most recent logs for a job.
|
|
25
|
+
*
|
|
26
|
+
* Throws when the history cannot be read. An empty array is a claim — the
|
|
27
|
+
* job has no recorded runs — and a caller shows it as "never ran".
|
|
28
|
+
*
|
|
20
29
|
* @param jobId The job identifier
|
|
21
30
|
* @param limit Max entries to return (default 50)
|
|
22
31
|
* @returns Logs sorted newest-first
|
|
@@ -24,7 +33,8 @@ export interface CronStore {
|
|
|
24
33
|
fetchLogs(jobId: string, limit?: number): Promise<CronJobLogEntry[]>;
|
|
25
34
|
/**
|
|
26
35
|
* Fetch aggregate stats for all jobs (totalRuns, totalFailures, lastRunAt).
|
|
27
|
-
* Used to seed in-memory counters on startup.
|
|
36
|
+
* Used to seed in-memory counters on startup. The SQL store reads them from
|
|
37
|
+
* the all-time counters on `cron_job_state`, not from the history.
|
|
28
38
|
*/
|
|
29
39
|
fetchJobStats(): Promise<Map<string, {
|
|
30
40
|
totalRuns: number;
|
|
@@ -82,11 +92,33 @@ export interface CronStore {
|
|
|
82
92
|
/** Release a lease taken with {@link tryAcquireRunLease} — only if `holder` still holds it. */
|
|
83
93
|
releaseRunLease?(jobId: string, holder: string): Promise<void>;
|
|
84
94
|
/**
|
|
85
|
-
* Run count, failure count and last run of each job
|
|
86
|
-
*
|
|
87
|
-
*
|
|
95
|
+
* Run count, failure count and last run of each job — every process's
|
|
96
|
+
* runs, where the scheduler's own counters hold only the ones it made. The
|
|
97
|
+
* SQL store keeps them as all-time counters on `cron_job_state`, so they
|
|
98
|
+
* outlive the history's retention.
|
|
88
99
|
*/
|
|
89
100
|
fetchRunSummaries?(jobIds: readonly string[]): Promise<Map<string, CronJobRunSummary>>;
|
|
101
|
+
/**
|
|
102
|
+
* Delete slot claims past retention — all but each job's most recent one —
|
|
103
|
+
* in batches. Resolves with how many went. The scheduler runs it on a
|
|
104
|
+
* timer while it is started; a store without it keeps every claim.
|
|
105
|
+
*/
|
|
106
|
+
sweepClaims?(): Promise<number>;
|
|
107
|
+
/**
|
|
108
|
+
* Delete run history past retention, and count into the job counters any
|
|
109
|
+
* run that is not counted yet — a history written before the counters
|
|
110
|
+
* existed, or by an older server during a rolling deploy. Both in batches.
|
|
111
|
+
* The scheduler runs it with {@link sweepClaims}; a store without it keeps
|
|
112
|
+
* every run.
|
|
113
|
+
*/
|
|
114
|
+
sweepLogs?(): Promise<CronLogSweep>;
|
|
115
|
+
}
|
|
116
|
+
/** What one {@link CronStore.sweepLogs} did. */
|
|
117
|
+
export interface CronLogSweep {
|
|
118
|
+
/** History rows deleted past retention. */
|
|
119
|
+
deleted: number;
|
|
120
|
+
/** Runs counted into the job counters that were not counted before. */
|
|
121
|
+
counted: number;
|
|
90
122
|
}
|
|
91
123
|
/** One job's row in `rebase.cron_job_state`, as the scheduler reads it. */
|
|
92
124
|
export interface CronJobPersistedState {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CronJobDefinition } from "@rebasepro/types";
|
|
1
|
+
import type { CronJobDefinition, RegisteredDatabase } from "@rebasepro/types";
|
|
2
2
|
/**
|
|
3
3
|
* Typed authoring helper for a cron job file. Identity at runtime —
|
|
4
4
|
* a plain default-exported {@link CronJobDefinition} works identically;
|
|
@@ -38,5 +38,9 @@ import type { CronJobDefinition } from "@rebasepro/types";
|
|
|
38
38
|
* },
|
|
39
39
|
* });
|
|
40
40
|
* ```
|
|
41
|
+
*
|
|
42
|
+
* `ctx.rebase` is typed from the `Database` the project registered on
|
|
43
|
+
* `Register` (see the typed server data guide), and untyped when it registered
|
|
44
|
+
* none. `defineCron<Database>({…})` types one job without the registration.
|
|
41
45
|
*/
|
|
42
|
-
export declare function defineCron(definition: CronJobDefinition): CronJobDefinition
|
|
46
|
+
export declare function defineCron<DB = RegisteredDatabase>(definition: CronJobDefinition<DB>): CronJobDefinition<DB>;
|
package/dist/cron/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export { loadCronJobsFromDirectory, loadCronJobsWithDiagnostics } from "./cron-loader.js";
|
|
2
|
-
export type { LoadedCronJob, LoadedCronJobs } from "./cron-loader.js";
|
|
1
|
+
export { loadCrons, declareCrons, cronJobsFrom, loadCronJobsFromDirectory, loadCronJobsWithDiagnostics } from "./cron-loader.js";
|
|
2
|
+
export type { CronsRecord, LoadedCronJob, LoadedCronJobs } from "./cron-loader.js";
|
|
3
3
|
export { defineCron } from "./define-cron.js";
|
|
4
4
|
export { CronScheduler, validateCronExpression } from "./cron-scheduler.js";
|
|
5
5
|
export { createCronRoutes } from "./cron-routes.js";
|
|
6
6
|
export { createCronStore } from "./cron-store.js";
|
|
7
7
|
export { detectFreezableRuntime, buildScaleToZeroWarning, CRON_ALWAYS_ON_ENV } from "./scale-to-zero.js";
|
|
8
|
-
export type { CronStore, CronJobPersistedState, CronRunLease, CronJobRunSummary } from "./cron-store.js";
|
|
8
|
+
export type { CronStore, CronJobPersistedState, CronRunLease, CronJobRunSummary, CronLogSweep } from "./cron-store.js";
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { createRequire as __rebaseCreateRequire } from "module";
|
|
2
|
+
import __rebaseProcess from "process";
|
|
3
|
+
globalThis.process ??= __rebaseProcess;
|
|
4
|
+
__rebaseCreateRequire(import.meta.url);
|
|
5
|
+
import { n as __exportAll } from "./rolldown-runtime-dW7B1o5h.js";
|
|
6
|
+
import { x as declareCron } from "./src-D5Y3N-S4.js";
|
|
7
|
+
import { r as logger } from "./logger-Bt32sHFw.js";
|
|
8
|
+
import { i as warnDeprecated, n as describeLoadFailure, r as nativeDynamicImport, t as SYNTAX_ERROR_ADVICE } from "./load-failure-L5hvfDoG.js";
|
|
9
|
+
import * as fs$1 from "fs";
|
|
10
|
+
import * as path$1 from "path";
|
|
11
|
+
import { pathToFileURL } from "url";
|
|
12
|
+
//#region src/cron/cron-loader.ts
|
|
13
|
+
var cron_loader_exports = /* @__PURE__ */ __exportAll({
|
|
14
|
+
cronJobsFrom: () => cronJobsFrom,
|
|
15
|
+
cronResourceOptions: () => cronResourceOptions,
|
|
16
|
+
declareCrons: () => declareCrons,
|
|
17
|
+
loadCronJobsFromDirectory: () => loadCronJobsFromDirectory,
|
|
18
|
+
loadCronJobsWithDiagnostics: () => loadCronJobsWithDiagnostics,
|
|
19
|
+
loadCrons: () => loadCrons,
|
|
20
|
+
readCronsDirectory: () => readCronsDirectory
|
|
21
|
+
});
|
|
22
|
+
/**
|
|
23
|
+
* Load the cron jobs in a directory, as values.
|
|
24
|
+
*
|
|
25
|
+
* Each file default-exports a `CronJobDefinition` — `defineCron({...})`. The
|
|
26
|
+
* filename without its extension is the record key, which is the job id:
|
|
27
|
+
* `crons/cleanup-sessions.ts` → `{ "cleanup-sessions": definition }`.
|
|
28
|
+
*
|
|
29
|
+
* Loading declares nothing: the definitions come back exactly as written, and
|
|
30
|
+
* whoever uses them decides what they are for — a backend schedules them, the
|
|
31
|
+
* build's derive step records them in the resource graph ({@link declareCrons}).
|
|
32
|
+
*
|
|
33
|
+
* Never throws for a bad file: it is skipped, logged, and passed to
|
|
34
|
+
* `options.onSkip`.
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```ts
|
|
38
|
+
* const crons = await loadCrons(path.resolve("crons"));
|
|
39
|
+
* const backend = await createRebaseBackend({ database, crons, provision: true });
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
async function loadCrons(directory, options = {}) {
|
|
43
|
+
const { crons, problems } = await readCronsDirectory(directory, options.importModule);
|
|
44
|
+
if (options.onSkip) for (const problem of problems) options.onSkip(problem);
|
|
45
|
+
return crons;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Record each cron in the project's resource graph — what `rebase resources`
|
|
49
|
+
* lists and a host reads to know a schedule exists, without running it.
|
|
50
|
+
*
|
|
51
|
+
* Keyed by the same id the scheduler, the routes and the Studio use, and built
|
|
52
|
+
* from the definitions as written: a default the scheduler fills in
|
|
53
|
+
* (`enabled: true`, `timeoutSeconds: 300`) is this process's, not something the
|
|
54
|
+
* author declared.
|
|
55
|
+
*/
|
|
56
|
+
function declareCrons(crons) {
|
|
57
|
+
for (const [id, definition] of Object.entries(crons)) declareCron(id, cronResourceOptions(definition));
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* The jobs a scheduler registers, from cron values: the definitions with this
|
|
61
|
+
* process's defaults filled in.
|
|
62
|
+
*
|
|
63
|
+
* Spread first, then normalise. Rebuilding the object field by field dropped
|
|
64
|
+
* `catchUpWindowSeconds` once — and since this is the path every job takes to
|
|
65
|
+
* the scheduler, catch-up never ran for any job authored the documented way. A
|
|
66
|
+
* field added to `CronJobDefinition` tomorrow would be dropped the same way.
|
|
67
|
+
*
|
|
68
|
+
* @throws when a value has no `schedule` or no `handler`. A file like that is
|
|
69
|
+
* skipped by the loader with a reason; a value handed over in code is refused,
|
|
70
|
+
* because the caller is right there to fix it.
|
|
71
|
+
*/
|
|
72
|
+
function cronJobsFrom(crons) {
|
|
73
|
+
return Object.entries(crons).map(([id, definition]) => {
|
|
74
|
+
const def = definition;
|
|
75
|
+
if (!def || typeof def.schedule !== "string" || typeof def.handler !== "function") throw new Error(`crons["${id}"] is not a cron job: it needs a \`schedule\` string and a \`handler\` function. Author it with \`defineCron({ schedule, handler })\`.`);
|
|
76
|
+
return {
|
|
77
|
+
id,
|
|
78
|
+
definition: {
|
|
79
|
+
...def,
|
|
80
|
+
schedule: def.schedule,
|
|
81
|
+
name: def.name || id,
|
|
82
|
+
enabled: def.enabled !== false,
|
|
83
|
+
timeoutSeconds: def.timeoutSeconds ?? 300,
|
|
84
|
+
handler: def.handler
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* @deprecated Use `loadCrons` instead, which returns the jobs as a record keyed
|
|
91
|
+
* by id. Will be removed in a future release.
|
|
92
|
+
*/
|
|
93
|
+
async function loadCronJobsFromDirectory(directory, importModule = nativeDynamicImport) {
|
|
94
|
+
warnDeprecated("server.loadCronJobsFromDirectory", "`loadCronJobsFromDirectory` is deprecated; use `loadCrons(dir)`, which returns `{ [id]: definition }`.");
|
|
95
|
+
return (await legacyLoad(directory, importModule)).jobs;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* @deprecated Use `loadCrons` instead, with `onSkip` to collect the files it
|
|
99
|
+
* skipped — and `declareCrons` where the jobs belong in the resource graph,
|
|
100
|
+
* which this used to do as a side effect of loading. Will be removed in a
|
|
101
|
+
* future release.
|
|
102
|
+
*/
|
|
103
|
+
async function loadCronJobsWithDiagnostics(directory, importModule = nativeDynamicImport) {
|
|
104
|
+
warnDeprecated("server.loadCronJobsWithDiagnostics", "`loadCronJobsWithDiagnostics` is deprecated; use `loadCrons(dir, { onSkip })`, which returns `{ [id]: definition }`, and `declareCrons(crons)` to record them in the resource graph.");
|
|
105
|
+
return legacyLoad(directory, importModule);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* What the deprecated loaders did: load, declare every job in the resource
|
|
109
|
+
* graph, and hand back normalised jobs. A job the graph refuses is skipped as a
|
|
110
|
+
* problem, as it was when the declaration ran inside the loader.
|
|
111
|
+
*/
|
|
112
|
+
async function legacyLoad(directory, importModule) {
|
|
113
|
+
const { crons, problems } = await readCronsDirectory(directory, importModule);
|
|
114
|
+
const declared = {};
|
|
115
|
+
for (const [id, definition] of Object.entries(crons)) try {
|
|
116
|
+
declareCron(id, cronResourceOptions(definition));
|
|
117
|
+
declared[id] = definition;
|
|
118
|
+
} catch (err) {
|
|
119
|
+
problems.push(describeLoadFailure(id, err));
|
|
120
|
+
}
|
|
121
|
+
return {
|
|
122
|
+
jobs: cronJobsFrom(declared),
|
|
123
|
+
problems
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Read a directory of cron files: the definitions that loaded, keyed by id, and
|
|
128
|
+
* one `<file> (<reason>)` per file that will NOT be scheduled.
|
|
129
|
+
*
|
|
130
|
+
* A job that does not load does not run, and the only trace used to be one
|
|
131
|
+
* `warn` per file in the boot log — next to nothing at 3am when the report that
|
|
132
|
+
* should have been mailed was not. So the skips are counted, summarised in one
|
|
133
|
+
* place, and answered by `GET /api/admin/cron`.
|
|
134
|
+
*
|
|
135
|
+
* Never throws, for the same reason the function loader does not: one
|
|
136
|
+
* malformed file must not take the server down, and the other jobs in the
|
|
137
|
+
* directory are still worth scheduling.
|
|
138
|
+
*/
|
|
139
|
+
async function readCronsDirectory(directory, importModule = nativeDynamicImport) {
|
|
140
|
+
const crons = {};
|
|
141
|
+
const problems = [];
|
|
142
|
+
if (!fs$1.existsSync(directory)) return {
|
|
143
|
+
crons,
|
|
144
|
+
problems
|
|
145
|
+
};
|
|
146
|
+
const files = fs$1.readdirSync(directory);
|
|
147
|
+
for (const file of files) if ((file.endsWith(".ts") || file.endsWith(".js")) && !file.startsWith(".") && !file.includes(".test.") && !file.endsWith(".d.ts") && file !== "index.ts" && file !== "index.js") {
|
|
148
|
+
const filePath = path$1.join(directory, file);
|
|
149
|
+
try {
|
|
150
|
+
const fileUrl = pathToFileURL(filePath).href;
|
|
151
|
+
const exported = (await importModule(fileUrl)).default;
|
|
152
|
+
if (!exported || typeof exported !== "object") {
|
|
153
|
+
problems.push(`${file} (no default export)`);
|
|
154
|
+
logger.warn(`[cron] ${file}: no valid default export. Skipping.`);
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
const def = exported;
|
|
158
|
+
if (typeof def.schedule !== "string" || def.schedule.trim() === "" || typeof def.handler !== "function") {
|
|
159
|
+
problems.push(`${file} (default export has no 'schedule' or no 'handler')`);
|
|
160
|
+
logger.warn(`[cron] ${file}: default export missing required 'schedule' or 'handler'. Skipping.`);
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
const id = path$1.basename(file, path$1.extname(file));
|
|
164
|
+
const definition = exported;
|
|
165
|
+
crons[id] = definition;
|
|
166
|
+
logger.info(`⏰ Loaded cron job: ${id} (${definition.schedule}${definition.timezone ? ` ${definition.timezone}` : " in the process's local time"})`);
|
|
167
|
+
} catch (err) {
|
|
168
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
169
|
+
problems.push(describeLoadFailure(file, err));
|
|
170
|
+
logger.debug(`[cron] Failed to load ${file}: ${message}`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (problems.length > 0) {
|
|
174
|
+
const syntax = problems.some((p) => p.includes("(syntax error"));
|
|
175
|
+
const other = problems.some((p) => !p.includes("(syntax error"));
|
|
176
|
+
logger.warn(`[cron] ${problems.length} cron file(s) were skipped and will NOT be scheduled:\n` + problems.map((p) => ` - ${p}`).join("\n") + "\n" + [...syntax ? [SYNTAX_ERROR_ADVICE] : [], ...other ? [" Fix these or author them with `defineCron(...)` for a typed, compile-checked contract."] : []].join("\n"));
|
|
177
|
+
}
|
|
178
|
+
return {
|
|
179
|
+
crons,
|
|
180
|
+
problems
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* The declaration a definition makes: every field a host can read without the
|
|
185
|
+
* handler. Spelled once here so the graph and the scheduler cannot disagree
|
|
186
|
+
* about what a cron is.
|
|
187
|
+
*/
|
|
188
|
+
function cronResourceOptions(definition) {
|
|
189
|
+
return {
|
|
190
|
+
schedule: definition.schedule,
|
|
191
|
+
...definition.timezone !== void 0 ? { timezone: definition.timezone } : {},
|
|
192
|
+
...definition.description !== void 0 ? { description: definition.description } : {},
|
|
193
|
+
...definition.enabled !== void 0 ? { enabled: definition.enabled } : {},
|
|
194
|
+
...definition.timeoutSeconds !== void 0 ? { timeoutSeconds: definition.timeoutSeconds } : {},
|
|
195
|
+
...definition.catchUpWindowSeconds !== void 0 ? { catchUpWindowSeconds: definition.catchUpWindowSeconds } : {},
|
|
196
|
+
...definition.name ? { label: definition.name } : {}
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
//#endregion
|
|
200
|
+
export { loadCrons as a, loadCronJobsWithDiagnostics as i, declareCrons as n, loadCronJobsFromDirectory as r, cron_loader_exports as t };
|
|
201
|
+
|
|
202
|
+
//# sourceMappingURL=cron-loader-NHqRfKF0.js.map
|