@rebasepro/server 0.24.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 +27 -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/errors.d.ts +32 -0
- package/dist/api/live-schema-routes.d.ts +27 -3
- package/dist/api/mount-path.d.ts +30 -0
- package/dist/api/mount.d.ts +6 -13
- package/dist/api/rest/api-generator.d.ts +53 -1
- 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 +7 -5
- 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 +35 -0
- package/dist/{ast-schema-editor-CWqS_sLJ.js → ast-schema-editor-mCeSC03x.js} +3 -3
- package/dist/{ast-schema-editor-CWqS_sLJ.js.map → ast-schema-editor-mCeSC03x.js.map} +1 -1
- package/dist/auth/access.d.ts +62 -11
- package/dist/auth/admin-user-ops.d.ts +24 -2
- package/dist/auth/admin-users-route.d.ts +1 -0
- package/dist/auth/api-keys/api-key-middleware.d.ts +10 -3
- package/dist/auth/api-keys/api-key-routes.d.ts +4 -1
- package/dist/auth/builtin-auth-adapter.d.ts +13 -0
- package/dist/auth/email-change-routes.d.ts +3 -0
- package/dist/auth/expired-token-sweep.d.ts +5 -1
- package/dist/auth/interfaces.d.ts +10 -0
- 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 +282 -166
- package/dist/auth/magic-link-routes.d.ts +3 -0
- 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 +6 -0
- package/dist/auth/middleware.d.ts +9 -2
- package/dist/auth/otp-routes.d.ts +3 -0
- package/dist/auth/rate-limiter.d.ts +61 -34
- package/dist/auth/registration-policy.d.ts +17 -0
- package/dist/auth/require-auth.d.ts +1 -1
- package/dist/auth/routes.d.ts +16 -0
- package/dist/auth/session-routes.d.ts +6 -0
- package/dist/auth/token-revocation.d.ts +6 -0
- package/dist/auth/user-picking.d.ts +37 -0
- package/dist/auth/verify-credential.d.ts +25 -4
- package/dist/{auth-DMLngxn_.js → auth-CSBky1mt.js} +2927 -1882
- 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 +14 -3
- package/dist/backup/backup-routes.d.ts +23 -2
- package/dist/backup/backup-storage.d.ts +3 -1
- package/dist/backup/index.d.ts +3 -1
- package/dist/{backup-CN0s50D2.js → backup-DBGRUVTS.js} +178 -37
- package/dist/backup-DBGRUVTS.js.map +1 -0
- package/dist/boot/boot.d.ts +2 -2
- package/dist/boot/driver.d.ts +3 -0
- package/dist/boot/env.d.ts +9 -2
- 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/sources.d.ts +9 -0
- package/dist/boot/static-routing.d.ts +9 -0
- package/dist/{contract-routes-fz8i4pxs.js → contract-routes-DiFwiLg2.js} +5 -21
- 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 +69 -3
- package/dist/cron/cron-store.d.ts +33 -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-Bc-SB0Se.js → cron-routes-DsH8Jwn5.js} +29 -6
- package/dist/cron-routes-DsH8Jwn5.js.map +1 -0
- package/dist/{cron-scheduler-CYQgco86.js → cron-scheduler-D1DT0nw5.js} +300 -34
- 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-BaqMSa4Y.js → ddl-bootstrap-BNqi2NZe.js} +2 -2
- package/dist/{ddl-bootstrap-BaqMSa4Y.js.map → ddl-bootstrap-BNqi2NZe.js.map} +1 -1
- package/dist/email/smtp-email-service.d.ts +23 -0
- package/dist/email/types.d.ts +18 -0
- package/dist/env.d.ts +0 -37
- package/dist/{errors-D6_y86c5.js → errors-Cqp86KnA.js} +166 -7
- 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-D7o5Epjj.js → function-loader-DbCSoNJj.js} +69 -39
- package/dist/function-loader-DbCSoNJj.js.map +1 -0
- package/dist/{function-routes-CaNG4waN.js → function-routes-BSTClQNa.js} +6 -5
- package/dist/function-routes-BSTClQNa.js.map +1 -0
- package/dist/functions/context.d.ts +9 -0
- 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/index.d.ts +5 -4
- package/dist/functions/index.js +59 -15
- 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-Nr8zLvoU.js → history-recorder-RZ1Ow4YY.js} +4 -4
- package/dist/{history-recorder-Nr8zLvoU.js.map → history-recorder-RZ1Ow4YY.js.map} +1 -1
- package/dist/{history-store-rcAm_xFR.js → history-store-DPofGVDp.js} +2 -2
- package/dist/{history-store-rcAm_xFR.js.map → history-store-DPofGVDp.js.map} +1 -1
- package/dist/index.d.ts +24 -11
- package/dist/index.es.js +24758 -20153
- package/dist/index.es.js.map +1 -1
- package/dist/init/docs.d.ts +35 -10
- package/dist/init/process-safety.d.ts +7 -2
- package/dist/init/shutdown.d.ts +18 -3
- package/dist/init.d.ts +49 -865
- 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-DqYNfquG.js → jobs-8G-_agld.js} +235 -25
- package/dist/jobs-8G-_agld.js.map +1 -0
- package/dist/{jwt-R6bSPMjk.js → jwt-CBaFc34Y.js} +530 -455
- package/dist/jwt-CBaFc34Y.js.map +1 -0
- package/dist/{keys-GAVZqbqx.js → keys-COzQ2aE8.js} +2 -2
- package/dist/{keys-GAVZqbqx.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-D-S-hO5e.js → logger-Bt32sHFw.js} +17 -4
- package/dist/logger-Bt32sHFw.js.map +1 -0
- package/dist/{logs-routes-DAdv37GI.js → logs-routes-DQJhrf-b.js} +3 -3
- package/dist/{logs-routes-DAdv37GI.js.map → logs-routes-DQJhrf-b.js.map} +1 -1
- package/dist/mcp/mcp-routes.d.ts +6 -0
- package/dist/mcp/mcp-tools.d.ts +8 -0
- package/dist/mcp/oauth-routes.d.ts +7 -0
- 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-DAq_XVDu.js → openapi-generator-CeGW2dLC.js} +366 -82
- 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-qRlqeUmO.js → proxy-BEKbsil6.js} +5 -4
- package/dist/proxy-BEKbsil6.js.map +1 -0
- package/dist/{query-parser-BgiKJKvc.js → query-parser-ECxP0nDw.js} +511 -16
- package/dist/query-parser-ECxP0nDw.js.map +1 -0
- package/dist/{request-timeout-DgH7j8qO.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 +10 -0
- package/dist/schema-edit/local-source.d.ts +29 -0
- package/dist/schema-edit/remote-source.d.ts +13 -3
- package/dist/schema-edit/source-editor.d.ts +28 -0
- package/dist/{schema-editor-routes-oIyuWl3L.js → schema-editor-routes-DRXBAbZy.js} +55 -11
- package/dist/schema-editor-routes-DRXBAbZy.js.map +1 -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 +5 -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-CatHFUym.js → src-D5Y3N-S4.js} +2 -1
- package/dist/{src-CatHFUym.js.map → src-D5Y3N-S4.js.map} +1 -1
- package/dist/storage/GCSStorageController.d.ts +46 -2
- package/dist/storage/LocalStorageController.d.ts +67 -2
- package/dist/storage/S3StorageController.d.ts +37 -3
- package/dist/storage/image-transform.d.ts +15 -0
- package/dist/storage/maintenance-routes.d.ts +23 -0
- package/dist/storage/maintenance.d.ts +126 -0
- package/dist/storage/rendition-cache.d.ts +9 -11
- package/dist/storage/request-principals.d.ts +30 -0
- package/dist/storage/routes.d.ts +20 -0
- package/dist/storage/source-variables.d.ts +10 -0
- package/dist/storage/streams.d.ts +42 -0
- package/dist/storage/tus-handler.d.ts +46 -2
- package/dist/storage/types.d.ts +82 -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 +9 -0
- package/package.json +5 -5
- package/dist/GCSStorageController-BSiP1c-f.js +0 -256
- package/dist/GCSStorageController-BSiP1c-f.js.map +0 -1
- package/dist/S3StorageController-CAwFRgjV.js +0 -276
- package/dist/S3StorageController-CAwFRgjV.js.map +0 -1
- package/dist/auth-DMLngxn_.js.map +0 -1
- package/dist/backup-CN0s50D2.js.map +0 -1
- package/dist/contract-routes-fz8i4pxs.js.map +0 -1
- package/dist/cron-loader-CwaANlOG.js +0 -118
- package/dist/cron-loader-CwaANlOG.js.map +0 -1
- package/dist/cron-routes-Bc-SB0Se.js.map +0 -1
- package/dist/cron-scheduler-CYQgco86.js.map +0 -1
- package/dist/cron-store-D2Q9-Aco.js +0 -300
- package/dist/cron-store-D2Q9-Aco.js.map +0 -1
- package/dist/dynamic-import-X40pTZUQ.js +0 -23
- package/dist/dynamic-import-X40pTZUQ.js.map +0 -1
- package/dist/errors-D6_y86c5.js.map +0 -1
- package/dist/function-loader-D7o5Epjj.js.map +0 -1
- package/dist/function-routes-CaNG4waN.js.map +0 -1
- package/dist/jobs-DqYNfquG.js.map +0 -1
- package/dist/jwt-R6bSPMjk.js.map +0 -1
- package/dist/logger-D-S-hO5e.js.map +0 -1
- package/dist/openapi-generator-DAq_XVDu.js.map +0 -1
- package/dist/proxy-qRlqeUmO.js.map +0 -1
- package/dist/query-parser-BgiKJKvc.js.map +0 -1
- package/dist/request-timeout-DgH7j8qO.js.map +0 -1
- package/dist/schema-editor-routes-oIyuWl3L.js.map +0 -1
- package/dist/soft-delete-params-BWPilMPF.js +0 -59
- package/dist/soft-delete-params-BWPilMPF.js.map +0 -1
- package/dist/src-I3aG1PcY.js +0 -6870
- package/dist/src-I3aG1PcY.js.map +0 -1
- package/dist/types-BfKcm9do.js +0 -39
- package/dist/types-BfKcm9do.js.map +0 -1
|
@@ -3,8 +3,8 @@ 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 "./src-
|
|
7
|
-
import { a as redactSensitiveText,
|
|
6
|
+
import "./src-D5Y3N-S4.js";
|
|
7
|
+
import { a as redactSensitiveText, c as stringifyForLog, r as logger } from "./logger-Bt32sHFw.js";
|
|
8
8
|
import { randomUUID } from "node:crypto";
|
|
9
9
|
import { hostname } from "node:os";
|
|
10
10
|
//#region ../types/src/types/env_boolean.ts
|
|
@@ -149,6 +149,8 @@ function buildScaleToZeroWarning(jobs, env = process.env) {
|
|
|
149
149
|
//#region src/cron/cron-scheduler.ts
|
|
150
150
|
var cron_scheduler_exports = /* @__PURE__ */ __exportAll({
|
|
151
151
|
CronScheduler: () => CronScheduler,
|
|
152
|
+
MAX_CAPTURED_LOG_BYTES: () => MAX_CAPTURED_LOG_BYTES,
|
|
153
|
+
MAX_CAPTURED_LOG_LINES: () => 500,
|
|
152
154
|
findMostRecentSlot: () => findMostRecentSlot,
|
|
153
155
|
isAlreadyExecutingSkip: () => isAlreadyExecutingSkip,
|
|
154
156
|
isValidTimeZone: () => isValidTimeZone,
|
|
@@ -295,6 +297,7 @@ function wallClockIn(candidate, zone) {
|
|
|
295
297
|
timeZone: zone,
|
|
296
298
|
hourCycle: "h23",
|
|
297
299
|
weekday: "short",
|
|
300
|
+
year: "numeric",
|
|
298
301
|
month: "numeric",
|
|
299
302
|
day: "numeric",
|
|
300
303
|
hour: "numeric",
|
|
@@ -305,6 +308,7 @@ function wallClockIn(candidate, zone) {
|
|
|
305
308
|
const parts = {};
|
|
306
309
|
for (const part of formatter.formatToParts(candidate)) parts[part.type] = part.value;
|
|
307
310
|
return {
|
|
311
|
+
year: Number(parts.year),
|
|
308
312
|
minute: Number(parts.minute),
|
|
309
313
|
hour: Number(parts.hour),
|
|
310
314
|
dom: Number(parts.day),
|
|
@@ -313,21 +317,69 @@ function wallClockIn(candidate, zone) {
|
|
|
313
317
|
};
|
|
314
318
|
}
|
|
315
319
|
/**
|
|
316
|
-
*
|
|
317
|
-
*
|
|
318
|
-
*
|
|
319
|
-
*
|
|
320
|
-
* names its zone means the same wall-clock hour on every host it runs on.
|
|
320
|
+
* The wall clock an instant reads in `zone` when one is named, else in the
|
|
321
|
+
* process's own zone — which is whatever the host is set to, UTC in nearly
|
|
322
|
+
* every container. A schedule that names its zone means the same wall-clock
|
|
323
|
+
* hour on every host it runs on.
|
|
321
324
|
*/
|
|
322
|
-
function
|
|
323
|
-
const
|
|
325
|
+
function wallClockOf(instantMs, zone) {
|
|
326
|
+
const candidate = new Date(instantMs);
|
|
327
|
+
if (zone) return wallClockIn(candidate, zone);
|
|
328
|
+
return {
|
|
329
|
+
year: candidate.getFullYear(),
|
|
324
330
|
minute: candidate.getMinutes(),
|
|
325
331
|
hour: candidate.getHours(),
|
|
326
332
|
dom: candidate.getDate(),
|
|
327
333
|
month: candidate.getMonth() + 1,
|
|
328
334
|
dow: candidate.getDay()
|
|
329
335
|
};
|
|
330
|
-
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* The zone's offset from UTC at an instant: its wall clock read back as UTC,
|
|
339
|
+
* minus the instant. Two instants with the same offset have wall clocks
|
|
340
|
+
* exactly as far apart as the instants themselves.
|
|
341
|
+
*/
|
|
342
|
+
function offsetAt(instantMs, wall) {
|
|
343
|
+
return Date.UTC(wall.year, wall.month - 1, wall.dom, wall.hour, wall.minute) - instantMs;
|
|
344
|
+
}
|
|
345
|
+
/** Whether a wall-clock day — month, day of month, weekday — can hold a slot. */
|
|
346
|
+
function dayMatches(wall, fields) {
|
|
347
|
+
return fields.months.includes(wall.month) && fields.doms.includes(wall.dom) && fields.dows.includes(wall.dow);
|
|
348
|
+
}
|
|
349
|
+
/** Whether a wall-clock minute matches every field of the expression. */
|
|
350
|
+
function wallMatches(wall, fields) {
|
|
351
|
+
return dayMatches(wall, fields) && fields.hours.includes(wall.hour) && fields.minutes.includes(wall.minute);
|
|
352
|
+
}
|
|
353
|
+
var MINUTES_PER_DAY = 1440;
|
|
354
|
+
/**
|
|
355
|
+
* How many wall-clock minutes from a non-matching `wall` to the first later
|
|
356
|
+
* wall minute that could match. Never past the next wall midnight, so the
|
|
357
|
+
* minutes in between share `wall`'s day and every one of them is a miss.
|
|
358
|
+
*/
|
|
359
|
+
function wallMinutesToNextCandidate(wall, fields) {
|
|
360
|
+
const minuteOfDay = wall.hour * 60 + wall.minute;
|
|
361
|
+
if (!dayMatches(wall, fields)) return MINUTES_PER_DAY - minuteOfDay;
|
|
362
|
+
if (!fields.hours.includes(wall.hour)) {
|
|
363
|
+
const nextHour = fields.hours.find((hour) => hour > wall.hour);
|
|
364
|
+
return nextHour === void 0 ? MINUTES_PER_DAY - minuteOfDay : nextHour * 60 - minuteOfDay;
|
|
365
|
+
}
|
|
366
|
+
const nextMinute = fields.minutes.find((minute) => minute > wall.minute);
|
|
367
|
+
return nextMinute === void 0 ? 60 - wall.minute : nextMinute - wall.minute;
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* The mirror of {@link wallMinutesToNextCandidate}: how many wall-clock
|
|
371
|
+
* minutes back from a non-matching `wall` to the latest earlier wall minute
|
|
372
|
+
* that could match. Never before 23:59 of the previous wall day.
|
|
373
|
+
*/
|
|
374
|
+
function wallMinutesToPreviousCandidate(wall, fields) {
|
|
375
|
+
const minuteOfDay = wall.hour * 60 + wall.minute;
|
|
376
|
+
if (!dayMatches(wall, fields)) return minuteOfDay + 1;
|
|
377
|
+
if (!fields.hours.includes(wall.hour)) {
|
|
378
|
+
const previousHour = [...fields.hours].reverse().find((hour) => hour < wall.hour);
|
|
379
|
+
return previousHour === void 0 ? minuteOfDay + 1 : minuteOfDay - (previousHour * 60 + 59);
|
|
380
|
+
}
|
|
381
|
+
const previousMinute = [...fields.minutes].reverse().find((minute) => minute < wall.minute);
|
|
382
|
+
return previousMinute === void 0 ? wall.minute + 1 : wall.minute - previousMinute;
|
|
331
383
|
}
|
|
332
384
|
/**
|
|
333
385
|
* How far forward to look for the next matching slot.
|
|
@@ -339,27 +391,86 @@ function matchesCronFields(candidate, fields, zone) {
|
|
|
339
391
|
var MAX_SLOT_SEARCH_MINUTES = 2105280;
|
|
340
392
|
var MINUTE_MS = 6e4;
|
|
341
393
|
/**
|
|
394
|
+
* The first real minute in `[startMs, endMs)`, stepping in `direction`, whose
|
|
395
|
+
* wall clock matches `fields` — the same answer as testing every minute in
|
|
396
|
+
* turn, without testing every minute.
|
|
397
|
+
*
|
|
398
|
+
* The search moves through *real* minutes and only reads the wall clock, never
|
|
399
|
+
* steps it: across a daylight-saving change a wall-clock minute is not a real
|
|
400
|
+
* one. Falling back, a wall-clock step from 02:59 summer time lands on 03:00
|
|
401
|
+
* winter time and skips the repeated hour; inside it, the "next" minute can be
|
|
402
|
+
* an hour in the past. That is why this used to test one real minute at a
|
|
403
|
+
* time — and a yearly job named in a zone cost half a million `Intl` calls,
|
|
404
|
+
* blocking the event loop for a third of a second on every re-arm, and two
|
|
405
|
+
* seconds for 29 February.
|
|
406
|
+
*
|
|
407
|
+
* Now it hops. From a miss, {@link wallMinutesToNextCandidate} says how many
|
|
408
|
+
* wall minutes cannot match (the rest of a day that is the wrong date, the
|
|
409
|
+
* rest of an hour that is the wrong hour, the minutes before the next listed
|
|
410
|
+
* one). While the zone's offset holds, that many wall minutes are exactly
|
|
411
|
+
* that many real minutes, so the hop is taken when the offset at both ends is
|
|
412
|
+
* the same. When it differs, a transition lies inside the hop: the search
|
|
413
|
+
* bisects for the first minute on the new offset and resumes there — every
|
|
414
|
+
* minute before it is on the old offset, so its wall clock is inside the
|
|
415
|
+
* stretch already known to miss. A hop is at most a day, and the one thing
|
|
416
|
+
* this assumes is that no zone changes its offset and changes it back within
|
|
417
|
+
* a single day, which none in the tz database does. Each day costs one
|
|
418
|
+
* wall-clock read, plus about a dozen for each transition crossed.
|
|
419
|
+
*
|
|
420
|
+
* `direction: -1` walks back from `startMs` to `endMs` (inclusive) with the
|
|
421
|
+
* mirror of the same hop, for {@link findMostRecentSlot}.
|
|
422
|
+
*/
|
|
423
|
+
function searchSlot(fields, startMs, endMs, direction, zone) {
|
|
424
|
+
const inWindow = (instantMs) => direction === 1 ? instantMs < endMs : instantMs >= endMs;
|
|
425
|
+
let current = startMs;
|
|
426
|
+
let wall = wallClockOf(current, zone);
|
|
427
|
+
while (inWindow(current)) {
|
|
428
|
+
if (wallMatches(wall, fields)) return current;
|
|
429
|
+
const skip = direction === 1 ? wallMinutesToNextCandidate(wall, fields) : wallMinutesToPreviousCandidate(wall, fields);
|
|
430
|
+
const target = current + direction * skip * MINUTE_MS;
|
|
431
|
+
const targetWall = wallClockOf(target, zone);
|
|
432
|
+
const offset = offsetAt(current, wall);
|
|
433
|
+
if (skip === 1 || offsetAt(target, targetWall) === offset) {
|
|
434
|
+
current = target;
|
|
435
|
+
wall = targetWall;
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
438
|
+
let held = current;
|
|
439
|
+
let changed = target;
|
|
440
|
+
let changedWall = targetWall;
|
|
441
|
+
while (Math.abs(changed - held) > MINUTE_MS) {
|
|
442
|
+
const middle = held + direction * Math.floor(Math.abs(changed - held) / MINUTE_MS / 2) * MINUTE_MS;
|
|
443
|
+
const middleWall = wallClockOf(middle, zone);
|
|
444
|
+
if (offsetAt(middle, middleWall) === offset) held = middle;
|
|
445
|
+
else {
|
|
446
|
+
changed = middle;
|
|
447
|
+
changedWall = middleWall;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
current = changed;
|
|
451
|
+
wall = changedWall;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
342
455
|
* Calculate the next Date after `after` that matches the cron expression.
|
|
343
456
|
* Throws on invalid expressions.
|
|
344
457
|
*/
|
|
345
458
|
function parseCronExpression(expression, after, timezone) {
|
|
346
459
|
const fields = parseCronFields(expression);
|
|
347
|
-
const
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
candidate.setTime(candidate.getTime() + MINUTE_MS);
|
|
353
|
-
}
|
|
460
|
+
const start = new Date(after);
|
|
461
|
+
start.setUTCSeconds(0, 0);
|
|
462
|
+
const startMs = start.getTime() + MINUTE_MS;
|
|
463
|
+
const slot = searchSlot(fields, startMs, startMs + MAX_SLOT_SEARCH_MINUTES * MINUTE_MS, 1, timezone);
|
|
464
|
+
if (slot !== void 0) return new Date(slot);
|
|
354
465
|
throw new Error(`Cron expression "${expression}" has no matching time within ${Math.round(MAX_SLOT_SEARCH_MINUTES / 525960)} years of ${after.toISOString()}. Check the day-of-month and month fields — a date such as 31 February never occurs.`);
|
|
355
466
|
}
|
|
356
467
|
/**
|
|
357
468
|
* The latest slot matching `expression` within the inclusive window
|
|
358
469
|
* `[from, to]`, or `undefined` when the expression has no slot in it.
|
|
359
470
|
*
|
|
360
|
-
* Walks backwards
|
|
361
|
-
*
|
|
362
|
-
*
|
|
471
|
+
* Walks backwards from `to`, so the first hit is already the answer — in the
|
|
472
|
+
* common case (a job that ran normally moments ago) that is a handful of
|
|
473
|
+
* steps, not a scan of the whole window.
|
|
363
474
|
*
|
|
364
475
|
* `to`'s own minute is included: an instance booting at 06:00:30 has *not* run
|
|
365
476
|
* the 06:00 slot — `parseCronExpression` already skipped past it to tomorrow —
|
|
@@ -371,14 +482,46 @@ function parseCronExpression(expression, after, timezone) {
|
|
|
371
482
|
*/
|
|
372
483
|
function findMostRecentSlot(expression, from, to, timezone) {
|
|
373
484
|
const fields = parseCronFields(expression);
|
|
374
|
-
const
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
}
|
|
485
|
+
const start = new Date(to);
|
|
486
|
+
start.setUTCSeconds(0, 0);
|
|
487
|
+
const startMs = start.getTime();
|
|
488
|
+
const slot = searchSlot(fields, startMs, Math.max(from.getTime(), startMs - (MAX_SLOT_SEARCH_MINUTES - 1) * MINUTE_MS), -1, timezone);
|
|
489
|
+
return slot === void 0 ? void 0 : new Date(slot);
|
|
380
490
|
}
|
|
381
491
|
var MAX_LOGS_PER_JOB = 50;
|
|
492
|
+
var MAX_CAPTURED_LOG_BYTES = 64 * 1024;
|
|
493
|
+
/**
|
|
494
|
+
* A run's `ctx.log` capture, capped at {@link MAX_CAPTURED_LOG_LINES} and
|
|
495
|
+
* {@link MAX_CAPTURED_LOG_BYTES}. The kept lines are the first ones: once one
|
|
496
|
+
* line is dropped, every later one is too, so the history reads as a prefix of
|
|
497
|
+
* the run's output and never as lines with gaps between them.
|
|
498
|
+
*/
|
|
499
|
+
function createLogCapture(firstLine) {
|
|
500
|
+
const lines = [];
|
|
501
|
+
let bytes = 0;
|
|
502
|
+
let dropped = 0;
|
|
503
|
+
let closed = false;
|
|
504
|
+
const capture = {
|
|
505
|
+
add(line) {
|
|
506
|
+
if (closed) return;
|
|
507
|
+
const size = Buffer.byteLength(line, "utf8");
|
|
508
|
+
if (dropped === 0 && lines.length < 500 && bytes + size <= 65536) {
|
|
509
|
+
lines.push(line);
|
|
510
|
+
bytes += size;
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
dropped++;
|
|
514
|
+
},
|
|
515
|
+
/** The lines kept, and the line that says what was not. Later `add`s are ignored. */
|
|
516
|
+
close() {
|
|
517
|
+
if (!closed && dropped > 0) lines.push(`… ${dropped} more line${dropped === 1 ? "" : "s"} not kept — see the server log`);
|
|
518
|
+
closed = true;
|
|
519
|
+
return lines;
|
|
520
|
+
}
|
|
521
|
+
};
|
|
522
|
+
if (firstLine !== void 0) capture.add(firstLine);
|
|
523
|
+
return capture;
|
|
524
|
+
}
|
|
382
525
|
/**
|
|
383
526
|
* Minimum milliseconds between scheduled executions of the same job.
|
|
384
527
|
* Prevents tight re-execution loops caused by jitter or clock drift.
|
|
@@ -410,6 +553,12 @@ function setLongTimeout(callback, delayMs) {
|
|
|
410
553
|
/** A run's timeout when its definition names none. */
|
|
411
554
|
var DEFAULT_TIMEOUT_SECONDS = 300;
|
|
412
555
|
/**
|
|
556
|
+
* How often a started scheduler deletes slot claims and run history past
|
|
557
|
+
* retention. Both accrue one row per job per fire, so a scheduler that lives
|
|
558
|
+
* for months needs to prune while it runs, not only when it next boots.
|
|
559
|
+
*/
|
|
560
|
+
var CLAIM_SWEEP_INTERVAL_MS = 60 * 6e4;
|
|
561
|
+
/**
|
|
413
562
|
* How long a run lease outlasts the run's own timeout.
|
|
414
563
|
*
|
|
415
564
|
* The lease is timed on the database's clock from the moment it is taken, the
|
|
@@ -469,6 +618,10 @@ var CronScheduler = class {
|
|
|
469
618
|
* ever clear the lease that run took.
|
|
470
619
|
*/
|
|
471
620
|
instanceLabel = `${hostname()}:${process.pid}`;
|
|
621
|
+
/** The hourly claim- and log-retention sweep, while the scheduler is started with a store. */
|
|
622
|
+
claimSweepTimer;
|
|
623
|
+
sweepingClaims = false;
|
|
624
|
+
sweepingLogs = false;
|
|
472
625
|
/**
|
|
473
626
|
* Set the server singleton to make it available to cron job handlers.
|
|
474
627
|
*
|
|
@@ -559,8 +712,48 @@ var CronScheduler = class {
|
|
|
559
712
|
if (!this.store) logger.warn("[cron] No cron store attached — runs are uncoordinated; with multiple app instances every instance will execute every job");
|
|
560
713
|
this.warnIfScaleToZero();
|
|
561
714
|
this.catchUpMissedSlots();
|
|
715
|
+
if (this.store?.sweepClaims || this.store?.sweepLogs) {
|
|
716
|
+
const sweep = () => {
|
|
717
|
+
this.sweepClaims();
|
|
718
|
+
this.sweepLogs();
|
|
719
|
+
};
|
|
720
|
+
sweep();
|
|
721
|
+
this.claimSweepTimer = setInterval(sweep, CLAIM_SWEEP_INTERVAL_MS);
|
|
722
|
+
this.claimSweepTimer.unref?.();
|
|
723
|
+
}
|
|
562
724
|
logger.info(`⏰ Cron scheduler started with ${this.jobs.size} job(s)`);
|
|
563
725
|
}
|
|
726
|
+
/** Delete slot claims past retention. Failures are logged; the next hour tries again. */
|
|
727
|
+
async sweepClaims() {
|
|
728
|
+
const store = this.store;
|
|
729
|
+
if (!store?.sweepClaims || this.sweepingClaims) return;
|
|
730
|
+
this.sweepingClaims = true;
|
|
731
|
+
try {
|
|
732
|
+
await store.sweepClaims();
|
|
733
|
+
} catch (err) {
|
|
734
|
+
logger.warn("[cron] The claim retention sweep failed; it runs again within the hour", { error: err });
|
|
735
|
+
} finally {
|
|
736
|
+
this.sweepingClaims = false;
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
/**
|
|
740
|
+
* Delete run history past retention, and count the runs not counted yet.
|
|
741
|
+
* Failures are logged; the next hour tries again, from where this one
|
|
742
|
+
* stopped — each batch it finished is committed.
|
|
743
|
+
*/
|
|
744
|
+
async sweepLogs() {
|
|
745
|
+
const store = this.store;
|
|
746
|
+
if (!store?.sweepLogs || this.sweepingLogs) return;
|
|
747
|
+
this.sweepingLogs = true;
|
|
748
|
+
try {
|
|
749
|
+
const { counted } = await store.sweepLogs();
|
|
750
|
+
if (counted > 0) logger.info(`[cron] Counted ${counted} earlier run(s) into the jobs' run totals`);
|
|
751
|
+
} catch (err) {
|
|
752
|
+
logger.warn("[cron] The run history sweep failed; it runs again within the hour", { error: err });
|
|
753
|
+
} finally {
|
|
754
|
+
this.sweepingLogs = false;
|
|
755
|
+
}
|
|
756
|
+
}
|
|
564
757
|
/**
|
|
565
758
|
* Stop the scheduler: clear every timer, then wait for the runs executing
|
|
566
759
|
* right now — for at most `timeoutMs` when given.
|
|
@@ -576,6 +769,10 @@ var CronScheduler = class {
|
|
|
576
769
|
async stop(timeoutMs) {
|
|
577
770
|
this.started = false;
|
|
578
771
|
for (const [id] of this.jobs) this.stopJob(id);
|
|
772
|
+
if (this.claimSweepTimer) {
|
|
773
|
+
clearInterval(this.claimSweepTimer);
|
|
774
|
+
this.claimSweepTimer = void 0;
|
|
775
|
+
}
|
|
579
776
|
if (this.inFlight.size === 0) return;
|
|
580
777
|
const settled = Promise.allSettled([...this.inFlight]).then(() => true);
|
|
581
778
|
if (timeoutMs === void 0) {
|
|
@@ -747,6 +944,46 @@ var CronScheduler = class {
|
|
|
747
944
|
return this.executeJob(job, true);
|
|
748
945
|
}
|
|
749
946
|
/**
|
|
947
|
+
* Start a job now and answer as soon as the run has begun, not when it
|
|
948
|
+
* ends — the manual trigger's `?async=1`.
|
|
949
|
+
*
|
|
950
|
+
* The synchronous {@link triggerJob} holds its HTTP request open for the
|
|
951
|
+
* whole run, and a proxy in front of the server (many stop at 60 s) cuts
|
|
952
|
+
* a long job's answer while the run carries on. This resolves once the
|
|
953
|
+
* run lease is held and the handler is about to be called, so a run that
|
|
954
|
+
* cannot start — already executing here or on the lease holder — is still
|
|
955
|
+
* answered as the skip it is. The run itself is tracked like any other:
|
|
956
|
+
* `stop()` waits for it, and its outcome lands in the run history, where
|
|
957
|
+
* the entry carries `manual: true` and the `startedAt` returned here.
|
|
958
|
+
*
|
|
959
|
+
* Returns `undefined` if the job doesn't exist.
|
|
960
|
+
*/
|
|
961
|
+
async startJob(id) {
|
|
962
|
+
const job = this.jobs.get(id);
|
|
963
|
+
if (!job) return void 0;
|
|
964
|
+
if (job.executing) {
|
|
965
|
+
logger.warn(`[cron] Skipping manual trigger of "${id}" — already executing`);
|
|
966
|
+
return {
|
|
967
|
+
started: false,
|
|
968
|
+
log: this.recordSkip(job, "already_executing", true)
|
|
969
|
+
};
|
|
970
|
+
}
|
|
971
|
+
let onStart;
|
|
972
|
+
const begun = new Promise((resolve) => {
|
|
973
|
+
onStart = resolve;
|
|
974
|
+
});
|
|
975
|
+
const run = this.executeJob(job, true, void 0, onStart);
|
|
976
|
+
const first = await Promise.race([begun.then((startedAt) => ({ startedAt })), run.then((log) => ({ log }))]);
|
|
977
|
+
if ("startedAt" in first) return {
|
|
978
|
+
started: true,
|
|
979
|
+
startedAt: first.startedAt
|
|
980
|
+
};
|
|
981
|
+
return {
|
|
982
|
+
started: false,
|
|
983
|
+
log: first.log
|
|
984
|
+
};
|
|
985
|
+
}
|
|
986
|
+
/**
|
|
750
987
|
* Record a run that did not happen because the previous one had not
|
|
751
988
|
* finished.
|
|
752
989
|
*
|
|
@@ -899,7 +1136,7 @@ var CronScheduler = class {
|
|
|
899
1136
|
if (!this.started) {
|
|
900
1137
|
const now = /* @__PURE__ */ new Date();
|
|
901
1138
|
for (const job of jobs) try {
|
|
902
|
-
viewOf(job.id).nextRunAt =
|
|
1139
|
+
viewOf(job.id).nextRunAt = this.listedNextRun(job, now);
|
|
903
1140
|
} catch {}
|
|
904
1141
|
}
|
|
905
1142
|
if (store?.fetchRunSummaries && ids.length > 0) try {
|
|
@@ -911,6 +1148,34 @@ var CronScheduler = class {
|
|
|
911
1148
|
return views;
|
|
912
1149
|
}
|
|
913
1150
|
/**
|
|
1151
|
+
* The next slot of `job` after `now`, for a listing on a process whose
|
|
1152
|
+
* scheduler is not started — the `api` role, which Studio's Cron panel
|
|
1153
|
+
* polls every 15 seconds.
|
|
1154
|
+
*
|
|
1155
|
+
* Cached on the job. The key is the job's schedule and zone (a re-register
|
|
1156
|
+
* replaces the job, and with it the cache); the entry holds while
|
|
1157
|
+
* `computedAt <= now < slot`. That is the same answer, not an
|
|
1158
|
+
* approximation: the search found no matching minute between
|
|
1159
|
+
* `computedAt`'s next minute and `slot`, so from any later instant before
|
|
1160
|
+
* `slot` it lands on `slot` again. A clock that moves back, or reaching the
|
|
1161
|
+
* slot, recomputes. The cache holds a pure function of the schedule, so
|
|
1162
|
+
* there is nothing user- or role-scoped in it.
|
|
1163
|
+
*/
|
|
1164
|
+
listedNextRun(job, now) {
|
|
1165
|
+
const { schedule, timezone } = job.definition;
|
|
1166
|
+
const nowMs = now.getTime();
|
|
1167
|
+
const cached = job.listedSlot;
|
|
1168
|
+
if (cached && cached.schedule === schedule && cached.timezone === timezone && cached.computedAt <= nowMs && nowMs < cached.slot.getTime()) return cached.slot;
|
|
1169
|
+
const slot = parseCronExpression(schedule, now, timezone);
|
|
1170
|
+
job.listedSlot = {
|
|
1171
|
+
schedule,
|
|
1172
|
+
timezone,
|
|
1173
|
+
computedAt: nowMs,
|
|
1174
|
+
slot
|
|
1175
|
+
};
|
|
1176
|
+
return slot;
|
|
1177
|
+
}
|
|
1178
|
+
/**
|
|
914
1179
|
* Warn once at start when the process looks like it is running on a
|
|
915
1180
|
* platform that freezes or evicts instances between requests, where the
|
|
916
1181
|
* in-process timers this scheduler relies on never fire.
|
|
@@ -1064,10 +1329,10 @@ var CronScheduler = class {
|
|
|
1064
1329
|
* - Persists to store (non-blocking) if available
|
|
1065
1330
|
* - Always restores state even on catastrophic errors
|
|
1066
1331
|
*/
|
|
1067
|
-
executeJob(job, manual, seedLog) {
|
|
1332
|
+
executeJob(job, manual, seedLog, onStart) {
|
|
1068
1333
|
const abort = new AbortController();
|
|
1069
1334
|
this.runControllers.add(abort);
|
|
1070
|
-
const run = this.runJob(job, manual, abort, seedLog);
|
|
1335
|
+
const run = this.runJob(job, manual, abort, seedLog, onStart);
|
|
1071
1336
|
this.track(run);
|
|
1072
1337
|
return run.finally(() => {
|
|
1073
1338
|
this.runControllers.delete(abort);
|
|
@@ -1081,7 +1346,7 @@ var CronScheduler = class {
|
|
|
1081
1346
|
};
|
|
1082
1347
|
work.then(release, release);
|
|
1083
1348
|
}
|
|
1084
|
-
async runJob(job, manual, abort, seedLog) {
|
|
1349
|
+
async runJob(job, manual, abort, seedLog, onStart) {
|
|
1085
1350
|
if (job.executing) return this.recordSkip(job, "already_executing", manual);
|
|
1086
1351
|
job.executing = true;
|
|
1087
1352
|
const lease = await this.acquireLease(job);
|
|
@@ -1091,14 +1356,14 @@ var CronScheduler = class {
|
|
|
1091
1356
|
return this.recordSkip(job, "already_executing", manual, lease.holder ?? "another process");
|
|
1092
1357
|
}
|
|
1093
1358
|
const startedAt = /* @__PURE__ */ new Date();
|
|
1094
|
-
const capturedLogs = seedLog
|
|
1359
|
+
const capturedLogs = createLogCapture(seedLog || void 0);
|
|
1095
1360
|
const ctx = {
|
|
1096
1361
|
jobId: job.id,
|
|
1097
1362
|
scheduledAt: startedAt,
|
|
1098
1363
|
signal: abort.signal,
|
|
1099
1364
|
log: (...args) => {
|
|
1100
1365
|
const line = args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
|
|
1101
|
-
capturedLogs.
|
|
1366
|
+
capturedLogs.add(line);
|
|
1102
1367
|
logger.info(args.map((a) => typeof a === "string" ? a : stringifyForLog(a)).join(" "), { cron: job.id });
|
|
1103
1368
|
},
|
|
1104
1369
|
rebase: this.client
|
|
@@ -1106,6 +1371,7 @@ var CronScheduler = class {
|
|
|
1106
1371
|
job.state = "running";
|
|
1107
1372
|
job.lastRunAt = startedAt;
|
|
1108
1373
|
job.totalRuns++;
|
|
1374
|
+
onStart?.(startedAt.toISOString());
|
|
1109
1375
|
let success = true;
|
|
1110
1376
|
let error;
|
|
1111
1377
|
let result;
|
|
@@ -1144,7 +1410,7 @@ var CronScheduler = class {
|
|
|
1144
1410
|
success,
|
|
1145
1411
|
error,
|
|
1146
1412
|
result: result !== void 0 ? result : void 0,
|
|
1147
|
-
logs: capturedLogs,
|
|
1413
|
+
logs: capturedLogs.close(),
|
|
1148
1414
|
manual
|
|
1149
1415
|
};
|
|
1150
1416
|
job.logs.push(logEntry);
|
|
@@ -1188,4 +1454,4 @@ var CronScheduler = class {
|
|
|
1188
1454
|
//#endregion
|
|
1189
1455
|
export { parseEnvBoolean as a, validateCronExpression as i, cron_scheduler_exports as n, isAlreadyExecutingSkip as r, CronScheduler as t };
|
|
1190
1456
|
|
|
1191
|
-
//# sourceMappingURL=cron-scheduler-
|
|
1457
|
+
//# sourceMappingURL=cron-scheduler-D1DT0nw5.js.map
|