@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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cron-loader-NHqRfKF0.js","names":[],"sources":["../src/cron/cron-loader.ts"],"sourcesContent":["import * as fs from \"fs\";\nimport * as path from \"path\";\nimport { pathToFileURL } from \"url\";\nimport { declareCron, type CronJobDefinition, type CronResourceOptions } from \"@rebasepro/types\";\nimport { logger } from \"../utils/logger.js\";\nimport { nativeDynamicImport, type ModuleImporter } from \"../utils/dynamic-import.js\";\nimport { describeLoadFailure, SYNTAX_ERROR_ADVICE } from \"../utils/load-failure.js\";\nimport { warnDeprecated } from \"@rebasepro/common\";\nimport type { LoadDirectoryOptions } from \"../functions/function-loader.js\";\n\n/**\n * Cron jobs as values: the job id, to its definition.\n *\n * `crons: { \"nightly-report\": nightlyReport }` schedules `nightlyReport` as the\n * job `nightly-report` — the same thing a file named `crons/nightly-report.ts`\n * does. {@link loadCrons} returns exactly this shape, so\n * `crons: await loadCrons(dir)` and `cronsDir: dir` schedule the same jobs.\n *\n * A value is what the author wrote — `defineCron({...})` — with no defaults\n * filled in. The scheduler fills them in when it registers the job.\n */\nexport type CronsRecord = Record<string, CronJobDefinition>;\n\n/** A job as the scheduler registers it: its id, and its definition with defaults filled in. */\nexport interface LoadedCronJob {\n /** Job ID derived from filename (e.g. \"cleanup-sessions\"). */\n id: string;\n /** The full definition. */\n definition: CronJobDefinition;\n}\n\n/** {@link loadCronJobsWithDiagnostics}' answer: what loaded, and what did not. */\nexport interface LoadedCronJobs {\n jobs: LoadedCronJob[];\n /** One entry per file that will NOT be scheduled, with the reason. */\n problems: string[];\n}\n\n/**\n * Load the cron jobs in a directory, as values.\n *\n * Each file default-exports a `CronJobDefinition` — `defineCron({...})`. The\n * filename without its extension is the record key, which is the job id:\n * `crons/cleanup-sessions.ts` → `{ \"cleanup-sessions\": definition }`.\n *\n * Loading declares nothing: the definitions come back exactly as written, and\n * whoever uses them decides what they are for — a backend schedules them, the\n * build's derive step records them in the resource graph ({@link declareCrons}).\n *\n * Never throws for a bad file: it is skipped, logged, and passed to\n * `options.onSkip`.\n *\n * @example\n * ```ts\n * const crons = await loadCrons(path.resolve(\"crons\"));\n * const backend = await createRebaseBackend({ database, crons, provision: true });\n * ```\n */\nexport async function loadCrons(directory: string, options: LoadDirectoryOptions = {}): Promise<CronsRecord> {\n const { crons, problems } = await readCronsDirectory(directory, options.importModule);\n if (options.onSkip) for (const problem of problems) options.onSkip(problem);\n return crons;\n}\n\n/**\n * Record each cron in the project's resource graph — what `rebase resources`\n * lists and a host reads to know a schedule exists, without running it.\n *\n * Keyed by the same id the scheduler, the routes and the Studio use, and built\n * from the definitions as written: a default the scheduler fills in\n * (`enabled: true`, `timeoutSeconds: 300`) is this process's, not something the\n * author declared.\n */\nexport function declareCrons(crons: CronsRecord): void {\n for (const [id, definition] of Object.entries(crons)) {\n declareCron(id, cronResourceOptions(definition));\n }\n}\n\n/**\n * The jobs a scheduler registers, from cron values: the definitions with this\n * process's defaults filled in.\n *\n * Spread first, then normalise. Rebuilding the object field by field dropped\n * `catchUpWindowSeconds` once — and since this is the path every job takes to\n * the scheduler, catch-up never ran for any job authored the documented way. A\n * field added to `CronJobDefinition` tomorrow would be dropped the same way.\n *\n * @throws when a value has no `schedule` or no `handler`. A file like that is\n * skipped by the loader with a reason; a value handed over in code is refused,\n * because the caller is right there to fix it.\n */\nexport function cronJobsFrom(crons: CronsRecord): LoadedCronJob[] {\n return Object.entries(crons).map(([id, definition]) => {\n const def = definition as Partial<CronJobDefinition> | undefined;\n if (!def || typeof def.schedule !== \"string\" || typeof def.handler !== \"function\") {\n throw new Error(\n `crons[\"${id}\"] is not a cron job: it needs a \\`schedule\\` string and a \\`handler\\` function. ` +\n \"Author it with `defineCron({ schedule, handler })`.\"\n );\n }\n return {\n id,\n definition: {\n ...def,\n schedule: def.schedule,\n name: def.name || id,\n enabled: def.enabled !== false,\n // `??`, not `||`: zero and NaN are values the author wrote, and\n // the scheduler refuses both by name (they end up under\n // `rejected`). `|| 300` swallowed them, so a job written with\n // `0` meaning \"no limit\" ran with five minutes and was listed as\n // healthy. `Infinity` is \"none\".\n timeoutSeconds: def.timeoutSeconds ?? 300,\n handler: def.handler\n }\n };\n });\n}\n\n/**\n * @deprecated Use `loadCrons` instead, which returns the jobs as a record keyed\n * by id. Will be removed in a future release.\n */\nexport async function loadCronJobsFromDirectory(\n directory: string,\n importModule: ModuleImporter = nativeDynamicImport\n): Promise<LoadedCronJob[]> {\n warnDeprecated(\n \"server.loadCronJobsFromDirectory\",\n \"`loadCronJobsFromDirectory` is deprecated; use `loadCrons(dir)`, which returns `{ [id]: definition }`.\"\n );\n return (await legacyLoad(directory, importModule)).jobs;\n}\n\n/**\n * @deprecated Use `loadCrons` instead, with `onSkip` to collect the files it\n * skipped — and `declareCrons` where the jobs belong in the resource graph,\n * which this used to do as a side effect of loading. Will be removed in a\n * future release.\n */\nexport async function loadCronJobsWithDiagnostics(\n directory: string,\n importModule: ModuleImporter = nativeDynamicImport\n): Promise<LoadedCronJobs> {\n warnDeprecated(\n \"server.loadCronJobsWithDiagnostics\",\n \"`loadCronJobsWithDiagnostics` is deprecated; use `loadCrons(dir, { onSkip })`, which returns \" +\n \"`{ [id]: definition }`, and `declareCrons(crons)` to record them in the resource graph.\"\n );\n return legacyLoad(directory, importModule);\n}\n\n/**\n * What the deprecated loaders did: load, declare every job in the resource\n * graph, and hand back normalised jobs. A job the graph refuses is skipped as a\n * problem, as it was when the declaration ran inside the loader.\n */\nasync function legacyLoad(directory: string, importModule: ModuleImporter): Promise<LoadedCronJobs> {\n const { crons, problems } = await readCronsDirectory(directory, importModule);\n const declared: CronsRecord = {};\n for (const [id, definition] of Object.entries(crons)) {\n try {\n declareCron(id, cronResourceOptions(definition));\n declared[id] = definition;\n } catch (err) {\n problems.push(describeLoadFailure(id, err));\n }\n }\n return { jobs: cronJobsFrom(declared), problems };\n}\n\n/**\n * Read a directory of cron files: the definitions that loaded, keyed by id, and\n * one `<file> (<reason>)` per file that will NOT be scheduled.\n *\n * A job that does not load does not run, and the only trace used to be one\n * `warn` per file in the boot log — next to nothing at 3am when the report that\n * should have been mailed was not. So the skips are counted, summarised in one\n * place, and answered by `GET /api/admin/cron`.\n *\n * Never throws, for the same reason the function loader does not: one\n * malformed file must not take the server down, and the other jobs in the\n * directory are still worth scheduling.\n */\nexport async function readCronsDirectory(\n directory: string,\n importModule: ModuleImporter = nativeDynamicImport\n): Promise<{ crons: CronsRecord; problems: string[] }> {\n const crons: CronsRecord = {};\n // Aggregated so a broken job surfaces as one loud summary rather than a\n // warning buried per-file among the boot noise.\n const problems: string[] = [];\n\n if (!fs.existsSync(directory)) {\n return { crons, problems };\n }\n\n const files = fs.readdirSync(directory);\n for (const file of files) {\n if (\n (file.endsWith(\".ts\") || file.endsWith(\".js\")) &&\n // Dotfiles: notably macOS bsdtar AppleDouble sidecars (`._foo.ts`),\n // binary blobs that cannot be imported.\n !file.startsWith(\".\") &&\n !file.includes(\".test.\") &&\n !file.endsWith(\".d.ts\") &&\n file !== \"index.ts\" &&\n file !== \"index.js\"\n ) {\n const filePath = path.join(directory, file);\n try {\n const fileUrl = pathToFileURL(filePath).href;\n\n const mod = await importModule(fileUrl);\n\n const exported: unknown = mod.default;\n\n if (!exported || typeof exported !== \"object\") {\n problems.push(`${file} (no default export)`);\n logger.warn(`[cron] ${file}: no valid default export. Skipping.`);\n continue;\n }\n\n const def = exported as Record<string, unknown>;\n if (typeof def.schedule !== \"string\" || def.schedule.trim() === \"\" || typeof def.handler !== \"function\") {\n problems.push(`${file} (default export has no 'schedule' or no 'handler')`);\n logger.warn(`[cron] ${file}: default export missing required 'schedule' or 'handler'. Skipping.`);\n continue;\n }\n\n const id = path.basename(file, path.extname(file));\n const definition = exported as CronJobDefinition;\n crons[id] = definition;\n logger.info(\n `⏰ Loaded cron job: ${id} (${definition.schedule}` +\n `${definition.timezone ? ` ${definition.timezone}` : \" in the process's local time\"})`\n );\n } catch (err: unknown) {\n const message =\n err instanceof Error ? err.message : String(err);\n problems.push(describeLoadFailure(file, err));\n // At `debug`: the aggregate warning below lists every problem\n // by file, so this line was the same text a second time — and\n // when the throw is a Zod error, that text is ten lines of\n // serialised issues. The reader met it twice before reaching\n // the sentence that says what to do.\n logger.debug(`[cron] Failed to load ${file}: ${message}`);\n }\n }\n }\n\n if (problems.length > 0) {\n // Advice keyed to what went wrong, as the functions loader does:\n // `defineCron` is the remedy for a wrong export shape, not for a typo.\n const syntax = problems.some(p => p.includes(\"(syntax error\"));\n const other = problems.some(p => !p.includes(\"(syntax error\"));\n logger.warn(\n `[cron] ${problems.length} cron file(s) were skipped and will NOT be scheduled:\\n` +\n problems.map((p) => ` - ${p}`).join(\"\\n\") + \"\\n\" +\n [\n ...(syntax ? [SYNTAX_ERROR_ADVICE] : []),\n ...(other ? [\" Fix these or author them with `defineCron(...)` for a typed, compile-checked contract.\"] : [])\n ].join(\"\\n\")\n );\n }\n\n return { crons, problems };\n}\n\n/**\n * The declaration a definition makes: every field a host can read without the\n * handler. Spelled once here so the graph and the scheduler cannot disagree\n * about what a cron is.\n */\nexport function cronResourceOptions(definition: CronJobDefinition): CronResourceOptions {\n return {\n schedule: definition.schedule,\n ...(definition.timezone !== undefined ? { timezone: definition.timezone } : {}),\n ...(definition.description !== undefined ? { description: definition.description } : {}),\n ...(definition.enabled !== undefined ? { enabled: definition.enabled } : {}),\n ...(definition.timeoutSeconds !== undefined ? { timeoutSeconds: definition.timeoutSeconds } : {}),\n ...(definition.catchUpWindowSeconds !== undefined ? { catchUpWindowSeconds: definition.catchUpWindowSeconds } : {}),\n ...(definition.name ? { label: definition.name } : {})\n };\n}\n"],"mappings":"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"}
|
|
@@ -3,10 +3,10 @@ import __rebaseProcess from "process";
|
|
|
3
3
|
globalThis.process ??= __rebaseProcess;
|
|
4
4
|
__rebaseCreateRequire(import.meta.url);
|
|
5
5
|
import { n as __exportAll } from "./rolldown-runtime-dW7B1o5h.js";
|
|
6
|
-
import { r as isAlreadyExecutingSkip } from "./cron-scheduler-
|
|
7
|
-
import { r as logger } from "./logger-
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
6
|
+
import { r as isAlreadyExecutingSkip } from "./cron-scheduler-D1DT0nw5.js";
|
|
7
|
+
import { r as logger } from "./logger-Bt32sHFw.js";
|
|
8
|
+
import { a as resolveListLimitParam } from "./query-parser-ECxP0nDw.js";
|
|
9
|
+
import { i as errorHandler, t as ApiError } from "./errors-Cqp86KnA.js";
|
|
10
10
|
import { Hono } from "hono";
|
|
11
11
|
//#region src/cron/cron-routes.ts
|
|
12
12
|
var cron_routes_exports = /* @__PURE__ */ __exportAll({ createCronRoutes: () => createCronRoutes });
|
|
@@ -18,12 +18,24 @@ function actorOf(c) {
|
|
|
18
18
|
return apiKey ? `api-key:${apiKey.id}` : void 0;
|
|
19
19
|
}
|
|
20
20
|
/**
|
|
21
|
+
* The manual trigger's `?async` switch. Absent means synchronous, as it always
|
|
22
|
+
* was; anything other than a plain yes or no is refused rather than read as
|
|
23
|
+
* one, so `?async=yes-please` does not quietly hold the request open.
|
|
24
|
+
*/
|
|
25
|
+
function readAsyncFlag(raw) {
|
|
26
|
+
if (raw === void 0) return false;
|
|
27
|
+
const value = raw.trim().toLowerCase();
|
|
28
|
+
if (value === "1" || value === "true") return true;
|
|
29
|
+
if (value === "" || value === "0" || value === "false") return false;
|
|
30
|
+
throw ApiError.badRequest(`?async must be 1, true, 0 or false — got "${raw}"`);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
21
33
|
* Create admin REST routes for managing cron jobs.
|
|
22
34
|
*
|
|
23
35
|
* Routes:
|
|
24
36
|
* GET / → list all cron jobs
|
|
25
37
|
* GET /:id → get a single job's status
|
|
26
|
-
* POST /:id/trigger → manually trigger a job
|
|
38
|
+
* POST /:id/trigger → manually trigger a job (`?async=1`: 202 once it has started)
|
|
27
39
|
* GET /:id/logs → get execution logs for a job
|
|
28
40
|
* PUT /:id → update job (enable/disable, or `null` to follow the code)
|
|
29
41
|
*
|
|
@@ -44,7 +56,7 @@ function createCronRoutes(scheduler, skipped = 0) {
|
|
|
44
56
|
...total > 0 && {
|
|
45
57
|
skipped: total,
|
|
46
58
|
...rejected.length > 0 && { rejected },
|
|
47
|
-
note: [skipped > 0 ? `${skipped} cron file(s) failed to load` : void 0, rejected.length > 0 ? `${rejected.length} job(s)
|
|
59
|
+
note: [skipped > 0 ? `${skipped} cron file(s) failed to load` : void 0, rejected.length > 0 ? `${rejected.length} job(s) were refused` : void 0].filter(Boolean).join(" and ") + " — NOT scheduled. " + (rejected.length > 0 ? "See `rejected` for the reason; " : "") + "the server log has the rest."
|
|
48
60
|
}
|
|
49
61
|
});
|
|
50
62
|
});
|
|
@@ -56,7 +68,18 @@ function createCronRoutes(scheduler, skipped = 0) {
|
|
|
56
68
|
});
|
|
57
69
|
router.post("/:id/trigger", async (c) => {
|
|
58
70
|
const id = c.req.param("id");
|
|
71
|
+
const asyncRun = readAsyncFlag(c.req.query("async"));
|
|
59
72
|
if (!scheduler.getJob(id)) throw ApiError.notFound(`Cron job "${id}" not found`);
|
|
73
|
+
if (asyncRun) {
|
|
74
|
+
const outcome = await scheduler.startJob(id);
|
|
75
|
+
if (!outcome) throw ApiError.notFound(`Cron job "${id}" not found`);
|
|
76
|
+
if (!outcome.started) throw ApiError.conflict(`Cron job "${id}" is already executing — try again when that run has finished`, "CRON_JOB_ALREADY_EXECUTING", { log: outcome.log });
|
|
77
|
+
return c.json({
|
|
78
|
+
accepted: true,
|
|
79
|
+
startedAt: outcome.startedAt,
|
|
80
|
+
job: await scheduler.fetchJob(id)
|
|
81
|
+
}, 202);
|
|
82
|
+
}
|
|
60
83
|
const log = await scheduler.triggerJob(id);
|
|
61
84
|
if (log && isAlreadyExecutingSkip(log)) throw ApiError.conflict(`Cron job "${id}" is already executing — try again when that run has finished`, "CRON_JOB_ALREADY_EXECUTING", { log });
|
|
62
85
|
return c.json({
|
|
@@ -68,7 +91,10 @@ function createCronRoutes(scheduler, skipped = 0) {
|
|
|
68
91
|
const id = c.req.param("id");
|
|
69
92
|
const limit = resolveListLimitParam(c.req.query("limit") ?? null, { defaultLimit: 50 });
|
|
70
93
|
if (!scheduler.getJob(id)) throw ApiError.notFound(`Cron job "${id}" not found`);
|
|
71
|
-
const logs = await scheduler.getJobLogsFromDb(id, limit)
|
|
94
|
+
const logs = await scheduler.getJobLogsFromDb(id, limit).catch((err) => {
|
|
95
|
+
logger.error(`[cron] Could not read the run history of "${id}"`, { error: err });
|
|
96
|
+
throw ApiError.serviceUnavailable(`The run history of cron job "${id}" could not be read. The server log has the reason.`);
|
|
97
|
+
});
|
|
72
98
|
return c.json({ logs });
|
|
73
99
|
});
|
|
74
100
|
router.put("/:id", async (c) => {
|
|
@@ -90,4 +116,4 @@ function createCronRoutes(scheduler, skipped = 0) {
|
|
|
90
116
|
//#endregion
|
|
91
117
|
export { cron_routes_exports as n, createCronRoutes as t };
|
|
92
118
|
|
|
93
|
-
//# sourceMappingURL=cron-routes-
|
|
119
|
+
//# sourceMappingURL=cron-routes-DsH8Jwn5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cron-routes-DsH8Jwn5.js","names":[],"sources":["../src/cron/cron-routes.ts"],"sourcesContent":["import { Hono } from \"hono\";\nimport type { Context } from \"hono\";\nimport type { HonoEnv } from \"../api/types\";\nimport { isAlreadyExecutingSkip, type CronScheduler } from \"./cron-scheduler\";\nimport { ApiError, errorHandler } from \"../api/errors\";\nimport { resolveListLimitParam } from \"../api/rest/query-parser\";\nimport { logger } from \"../utils/logger.js\";\n\n/** Who made a change, as `cron_job_state.updated_by` records it. */\nfunction actorOf(c: Context<HonoEnv>): string | undefined {\n const user = c.get(\"user\");\n if (typeof user === \"object\" && user !== null && typeof user.uid === \"string\") return user.uid;\n const apiKey = c.get(\"apiKey\");\n return apiKey ? `api-key:${apiKey.id}` : undefined;\n}\n\n/**\n * The manual trigger's `?async` switch. Absent means synchronous, as it always\n * was; anything other than a plain yes or no is refused rather than read as\n * one, so `?async=yes-please` does not quietly hold the request open.\n */\nfunction readAsyncFlag(raw: string | undefined): boolean {\n if (raw === undefined) return false;\n const value = raw.trim().toLowerCase();\n if (value === \"1\" || value === \"true\") return true;\n if (value === \"\" || value === \"0\" || value === \"false\") return false;\n throw ApiError.badRequest(`?async must be 1, true, 0 or false — got \"${raw}\"`);\n}\n\n/**\n * Create admin REST routes for managing cron jobs.\n *\n * Routes:\n * GET / → list all cron jobs\n * GET /:id → get a single job's status\n * POST /:id/trigger → manually trigger a job (`?async=1`: 202 once it has started)\n * GET /:id/logs → get execution logs for a job\n * PUT /:id → update job (enable/disable, or `null` to follow the code)\n *\n * Every answer about a job's state is read from the store, not from this\n * process's memory: the process serving these routes is often not the one\n * running the jobs (the `api` role never starts its scheduler), and is only\n * ever one replica of several.\n */\nexport function createCronRoutes(scheduler: CronScheduler, skipped = 0): Hono<HonoEnv> {\n const router = new Hono<HonoEnv>();\n // Hono's onError does NOT propagate from parent to child routers, so this\n // child router registers its own handler to format thrown ApiErrors.\n router.onError(errorHandler);\n\n // List all jobs\n router.get(\"/\", async (c) => {\n const jobs = await scheduler.fetchJobs();\n // A file that failed to load is not a job, so it appears nowhere in\n // this list — and \"my job is missing\" and \"my job is not scheduled\"\n // look identical from here. Say how many were dropped, as the\n // functions listing does, so the Studio panel and anyone with curl can\n // see it without boot-log access.\n //\n // Two ways to be dropped, counted together and reported apart. A file\n // the loader could not read has only a count: the failure happened\n // before there was a job to name. A schedule the scheduler refused has a\n // name and a reason — most often \"Expected 5 fields, got 6\", from an\n // expression copied out of a tool that supports seconds — and quoting it\n // here turns a job that silently never fires into a one-line fix.\n const rejected = scheduler.listRejectedJobs();\n const total = skipped + rejected.length;\n return c.json({\n jobs,\n ...(total > 0 && {\n skipped: total,\n ...(rejected.length > 0 && { rejected }),\n note: [\n skipped > 0 ? `${skipped} cron file(s) failed to load` : undefined,\n // Not \"an invalid schedule\": a job is also refused for an\n // unknown timezone or a timeout that is not a positive\n // number, and `rejected` says which.\n rejected.length > 0 ? `${rejected.length} job(s) were refused` : undefined\n ].filter(Boolean).join(\" and \") +\n \" — NOT scheduled. \" +\n (rejected.length > 0\n ? \"See `rejected` for the reason; \"\n : \"\") +\n \"the server log has the rest.\"\n })\n });\n });\n\n // Get single job\n router.get(\"/:id\", async (c) => {\n const id = c.req.param(\"id\");\n const job = await scheduler.fetchJob(id);\n if (!job) {\n throw ApiError.notFound(`Cron job \"${id}\" not found`);\n }\n return c.json({ job });\n });\n\n // Trigger a job manually\n router.post(\"/:id/trigger\", async (c) => {\n const id = c.req.param(\"id\");\n const asyncRun = readAsyncFlag(c.req.query(\"async\"));\n const job = scheduler.getJob(id);\n if (!job) {\n throw ApiError.notFound(`Cron job \"${id}\" not found`);\n }\n\n // `?async=1`: answer once the run has started rather than when it\n // ends. A long job's synchronous answer is cut by any proxy with a\n // shorter timeout than the job (60 s is common) while the run carries\n // on; this one is not. The outcome is in `GET /:id/logs`, as the entry\n // with `manual: true` and this `startedAt`.\n if (asyncRun) {\n const outcome = await scheduler.startJob(id);\n if (!outcome) throw ApiError.notFound(`Cron job \"${id}\" not found`);\n if (!outcome.started) {\n throw ApiError.conflict(\n `Cron job \"${id}\" is already executing — try again when that run has finished`,\n \"CRON_JOB_ALREADY_EXECUTING\",\n { log: outcome.log }\n );\n }\n return c.json({\n accepted: true,\n startedAt: outcome.startedAt,\n job: await scheduler.fetchJob(id)\n }, 202);\n }\n\n const log = await scheduler.triggerJob(id);\n // Nothing ran, so this is not a success: the job is executing already —\n // on this process, or on whichever one holds its run lease (the worker,\n // seen from the api role). The skip is in the run history either way;\n // `details.log` carries it, and its line names the process.\n if (log && isAlreadyExecutingSkip(log)) {\n throw ApiError.conflict(\n `Cron job \"${id}\" is already executing — try again when that run has finished`,\n \"CRON_JOB_ALREADY_EXECUTING\",\n { log }\n );\n }\n return c.json({ log,\njob: await scheduler.fetchJob(id) });\n });\n\n // Get job logs\n router.get(\"/:id/logs\", async (c) => {\n const id = c.req.param(\"id\");\n // Validated, not `parseInt`-ed. `?limit=abc` used to reach the store as\n // `NaN`, where Postgres refused `LIMIT NaN`, the store swallowed the\n // error and returned `[]` — a 200 with an empty list, which reads as\n // \"this job has never run\". The data plane answers 400 for the same\n // input; so does this now.\n const limit = resolveListLimitParam(c.req.query(\"limit\") ?? null, { defaultLimit: 50 });\n\n const job = scheduler.getJob(id);\n if (!job) {\n throw ApiError.notFound(`Cron job \"${id}\" not found`);\n }\n\n const logs = await scheduler.getJobLogsFromDb(id, limit).catch((err: unknown) => {\n // Not an empty 200: Studio shows that as \"No executions yet\".\n logger.error(`[cron] Could not read the run history of \"${id}\"`, { error: err });\n throw ApiError.serviceUnavailable(\n `The run history of cron job \"${id}\" could not be read. The server log has the reason.`\n );\n });\n return c.json({ logs });\n });\n\n // Enable/disable a job — for every process, and across redeploys\n router.put(\"/:id\", async (c) => {\n const id = c.req.param(\"id\");\n const body: unknown = await c.req.json().catch(() => ({}));\n const enabled = typeof body === \"object\" && body !== null && \"enabled\" in body ? body.enabled : undefined;\n\n if (typeof enabled !== \"boolean\" && enabled !== null) {\n throw ApiError.badRequest(\n \"Missing 'enabled' in body: true or false to override the job's code, or null to follow it again\"\n );\n }\n\n if (!scheduler.getJob(id)) {\n throw ApiError.notFound(`Cron job \"${id}\" not found`);\n }\n\n try {\n await scheduler.persistJobEnabled(id, enabled, actorOf(c));\n } catch (err) {\n logger.error(`[cron] Could not save the enabled state of \"${id}\"`, { error: err });\n throw ApiError.serviceUnavailable(\n `The change to cron job \"${id}\" could not be saved, so no process was changed. ` +\n \"The server log has the reason.\"\n );\n }\n\n return c.json({ job: await scheduler.fetchJob(id) });\n });\n\n return router;\n}\n"],"mappings":"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"}
|