UNPKG

agents

Version:

A home for your AI agents

658 lines (657 loc) 28.8 kB
import { i as _classPrivateFieldInitSpec, n as _classPrivateFieldSet2, r as _assertClassBrand, t as _classPrivateFieldGet2 } from "./classPrivateFieldGet2-DZBYAB34.js"; import { t as LifecycleCapability } from "./capability-B4WbF81e.js"; import { t as _classPrivateMethodInitSpec } from "./classPrivateMethodInitSpec-qMjJ6sHQ.js"; import { isDurableObjectCodeUpdateReset, isPlatformFailure, resolveRetryConfig, tryN, validateRetryOptions } from "./retries.js"; import { parseCronExpression } from "cron-schedule"; //#region src/schedules/schedule-timing.ts /** * Pure timing rules for persistent schedules. * * A `ScheduleTiming` is the parsed "when and how it runs" half of a * `Schedule` — everything except identity, callback, and payload. Parsing * user-facing inputs here keeps Scheduler's storage code to one insert path. */ /** Longest allowed gap between interval executions: 30 days. */ const MAX_INTERVAL_SECONDS = 720 * 60 * 60; /** True when this timing repeats and therefore deduplicates by default. */ function isRecurring(timing) { return timing.type === "cron" || timing.type === "interval"; } /** * Next wall-clock execution time for a cron expression. * * @param cron - A standard cron expression. * @param nowMs - Current epoch time in milliseconds. * @returns The next execution epoch time in milliseconds. * @throws For an unparseable cron expression. */ function nextCronTimeMs(cron, nowMs) { return parseCronExpression(cron).getNextDate(new Date(nowMs)).getTime(); } /** * Parse a user-facing `when` into schedule timing. * * A `Date` runs once at that date, a number runs once after that many * seconds, and a string is a recurring cron expression. * * @param when - The requested execution time or recurrence. * @param nowMs - Current epoch time in milliseconds. * @param callback - Callback name, used only in error messages. * @returns The parsed timing. * @throws For a `when` value that is not a `Date`, number, or string. */ function parseWhen(when, nowMs, callback) { if (when instanceof Date) return { type: "scheduled", time: Math.floor(when.getTime() / 1e3) }; if (typeof when === "number") return { type: "delayed", time: Math.floor((nowMs + when * 1e3) / 1e3), delayInSeconds: when }; if (typeof when === "string") return { type: "cron", time: Math.floor(nextCronTimeMs(when, nowMs) / 1e3), cron: when }; throw new Error(`Invalid schedule type: ${JSON.stringify(when)}(${typeof when}) trying to schedule ${callback}`); } /** * Reject an interval outside the supported range. * * @param intervalSeconds - The requested gap between executions. * @throws For a non-positive interval or one longer than 30 days. */ function validateIntervalSeconds(intervalSeconds) { if (typeof intervalSeconds !== "number" || intervalSeconds <= 0) throw new Error("intervalSeconds must be a positive number"); if (intervalSeconds > 2592e3) throw new Error(`intervalSeconds cannot exceed ${MAX_INTERVAL_SECONDS} seconds (30 days)`); } /** * Parse a fixed interval into schedule timing. * * @param intervalSeconds - Seconds between executions. * @param nowMs - Current epoch time in milliseconds. * @returns The parsed timing with the first execution one interval from now. * @throws For an interval outside the supported range. */ function parseInterval(intervalSeconds, nowMs) { validateIntervalSeconds(intervalSeconds); return { type: "interval", time: Math.floor((nowMs + intervalSeconds * 1e3) / 1e3), intervalSeconds }; } //#endregion //#region src/schedules/scheduler.ts /** * Lifecycle scheduling vocabulary. Scheduler validates schedules, resolves * named callbacks, and pushes jobs into the Lifecycle-owned job queue; * Lifecycle runs the alarm event loop, retry policy, and physical alarm * arming. Scheduler owns no storage of its own — each schedule is one job * whose `fn` is the callback name and whose payload carries the schedule's * timing vocabulary. */ const schedulerCallbackResolvers = /* @__PURE__ */ new WeakMap(); /** * @internal Supply a composition-root fallback for callback names outside the * registered map. Agent uses this to keep its historical name-based * scheduling API (`this.schedule(60, "methodName")`) working: the resolver * looks the method up on the Agent, and the resolved handler still runs * inside the Lifecycle host boundary. */ function setSchedulerCallbackResolver(scheduler, resolver) { schedulerCallbackResolvers.set(scheduler, resolver); } const SCHEDULE_SCHEMA_VERSION_KEY = "cf_agents:schedules_schema_version"; /** Version 2: schedule rows live in the Lifecycle job queue. */ const CURRENT_SCHEDULE_SCHEMA_VERSION = 2; /** * Legacy schedule callbacks whose rows drive chat recovery loops from before * the job queue carried breaker membership. The migration is the one place * allowed to know legacy names (it already drops `_cf_keepAliveHeartbeat` * rows by name): a recovery row migrated without its `recoveryLoop` flag * would escape the alarm memory-limit breaker (#1825) and could re-trigger * a doomed loop on an upgraded object. */ const LEGACY_RECOVERY_LOOP_CALLBACKS = /* @__PURE__ */ new Set(["_chatRecoveryContinue", "_chatRecoveryRetry"]); const DEFAULT_RETRY = { maxAttempts: 3, baseDelayMs: 100, maxDelayMs: 3e3 }; function isSchedulerJobPayload(value) { return typeof value === "object" && value !== null && typeof value.type === "string"; } var _handlers = /* @__PURE__ */ new WeakMap(); var _retryDefaults = /* @__PURE__ */ new WeakMap(); var _hungScheduleTimeoutSeconds = /* @__PURE__ */ new WeakMap(); var _onError = /* @__PURE__ */ new WeakMap(); var _warnedStartupCallbacks = /* @__PURE__ */ new WeakMap(); var _Scheduler_brand = /* @__PURE__ */ new WeakSet(); /** * Persistent task scheduling for a Lifecycle Object. * * Register callbacks in the constructor and install the instance with * `Lifecycle.use()`. Scheduler validates schedules and pushes them into the * Lifecycle job queue; Lifecycle owns the physical alarm and the alarm * event loop, and Scheduler runs registered callbacks through Lifecycle's * host invocation boundary when its jobs come due. * * @experimental The API surface may change before stabilizing. */ var Scheduler = class extends LifecycleCapability { /** * Create a persistent Scheduler. * * @param options - Registered callbacks plus optional retry, * hung-interval, and error policy. Registering `callbacks` types * {@link set} and {@link every} against the map — names and * payloads are checked where the handlers are declared and where they are * scheduled. Names outside the map are rejected unless a composition-root * resolver supplies them — the internal aperture behind `Agent`'s * name-based scheduling API. */ constructor(options = {}) { super("scheduler"); _classPrivateMethodInitSpec(this, _Scheduler_brand); _classPrivateFieldInitSpec(this, _handlers, void 0); _classPrivateFieldInitSpec(this, _retryDefaults, void 0); _classPrivateFieldInitSpec(this, _hungScheduleTimeoutSeconds, void 0); _classPrivateFieldInitSpec(this, _onError, void 0); _classPrivateFieldInitSpec(this, _warnedStartupCallbacks, /* @__PURE__ */ new Set()); _classPrivateFieldSet2(_handlers, this, options.callbacks ?? {}); _classPrivateFieldSet2(_retryDefaults, this, resolveRetryConfig(options.retry, DEFAULT_RETRY)); _classPrivateFieldSet2(_hungScheduleTimeoutSeconds, this, options.hungScheduleTimeoutSeconds ?? 30); _classPrivateFieldSet2(_onError, this, options.onError); } /** Migrate legacy schedule storage into the Lifecycle job queue. */ async onStart() { _classPrivateFieldGet2(_warnedStartupCallbacks, this).clear(); const storage = this.lifecycle.storage; if ((await storage.get(SCHEDULE_SCHEMA_VERSION_KEY) ?? 0) >= CURRENT_SCHEDULE_SCHEMA_VERSION) return; await _assertClassBrand(_Scheduler_brand, this, _migrateLegacyScheduleTable).call(this, storage); await storage.put(SCHEDULE_SCHEMA_VERSION_KEY, CURRENT_SCHEDULE_SCHEMA_VERSION); } /** Drive one due schedule job dispatched by the Lifecycle event loop. */ async onJob(context) { const { job, attempt } = context; const timing = job.payload; if (!isSchedulerJobPayload(timing)) { console.error(`Malformed schedule job ${job.id}; dropping it`); return; } if (attempt === 1) _assertClassBrand(_Scheduler_brand, this, _emit).call(this, "schedule:execute", { callback: job.fn, id: job.id }); else _assertClassBrand(_Scheduler_brand, this, _emit).call(this, "schedule:retry", { callback: job.fn, id: job.id, attempt, maxAttempts: resolveRetryConfig(job.retry, _classPrivateFieldGet2(_retryDefaults, this)).maxAttempts }); if (timing.owner_path) { try { await this.lifecycle.routes.to({ key: timing.owner_path_key ?? timing.owner_path, data: timing.owner_path }, { type: "dispatch", id: job.id, fn: job.fn, job: timing, retry: job.retry }); } catch (error) { if (isPlatformFailure(error)) throw error; console.error(`error dispatching scheduled callback "${job.fn}"`, error); _assertClassBrand(_Scheduler_brand, this, _emit).call(this, "schedule:error", { callback: job.fn, id: job.id, error: error instanceof Error ? error.message : String(error), attempts: 0 }); try { await _classPrivateFieldGet2(_onError, this)?.call(this, error); } catch {} return "yield"; } return _assertClassBrand(_Scheduler_brand, this, _recurrenceOutcome).call(this, timing); } const handler = _assertClassBrand(_Scheduler_brand, this, _resolveCallback).call(this, job.fn); if (!handler) { console.error(`callback ${job.fn} not found`); return _assertClassBrand(_Scheduler_brand, this, _recurrenceOutcome).call(this, timing); } const schedule = _assertClassBrand(_Scheduler_brand, this, _jobToSchedule).call(this, job, timing); await this.lifecycle.runInHostContext(() => handler(timing.payload, schedule)); return _assertClassBrand(_Scheduler_brand, this, _recurrenceOutcome).call(this, timing); } /** Observe one schedule's terminal application failure. */ async onJobError(context, error) { const { job } = context; const timing = job.payload; if (!isSchedulerJobPayload(timing)) return void 0; const { maxAttempts } = resolveRetryConfig(job.retry, _classPrivateFieldGet2(_retryDefaults, this)); console.error(`error executing callback "${job.fn}" after ${maxAttempts} attempts`, error); _assertClassBrand(_Scheduler_brand, this, _emit).call(this, "schedule:error", { callback: job.fn, id: job.id, error: error instanceof Error ? error.message : String(error), attempts: maxAttempts }); try { await _classPrivateFieldGet2(_onError, this)?.call(this, error); } catch {} return _assertClassBrand(_Scheduler_brand, this, _recurrenceOutcome).call(this, timing); } /** Handle Scheduler protocol messages routed by another Lifecycle. */ async onRoute(context) { const message = context.payload; const owner = context.source ?? null; switch (message.type) { case "schedule": return _assertClassBrand(_Scheduler_brand, this, _insert).call(this, owner, parseWhen(message.when, Date.now(), message.callback), message.callback, message.payload, message.options); case "every": return _assertClassBrand(_Scheduler_brand, this, _insert).call(this, owner, parseInterval(message.intervalSeconds, Date.now()), message.callback, message.payload, message.options); case "get": return _assertClassBrand(_Scheduler_brand, this, _getForOwner).call(this, owner, message.id); case "list": return _assertClassBrand(_Scheduler_brand, this, _listForOwner).call(this, owner, message.criteria); case "cancel": return _assertClassBrand(_Scheduler_brand, this, _cancelForOwner).call(this, owner, message.id); case "dispatch": await _assertClassBrand(_Scheduler_brand, this, _executeRouted).call(this, message.id, message.fn, message.job, message.retry); return true; default: throw new Error("Unknown routed Scheduler message"); } } /** Set a delayed, dated, or cron schedule for a registered callback. */ async set(when, callback, payload, options) { await this.lifecycle.ready(); _assertClassBrand(_Scheduler_brand, this, _validateSchedule).call(this, when, callback, options); const result = this.lifecycle.routes.source ? await this.lifecycle.routes.toRoot({ type: "schedule", when, callback, payload, options }) : await _assertClassBrand(_Scheduler_brand, this, _insert).call(this, null, parseWhen(when, Date.now(), callback), callback, payload, options); _assertClassBrand(_Scheduler_brand, this, _emitCreated).call(this, result); return result.schedule; } /** Set a fixed-interval schedule for a registered callback. */ async every(intervalSeconds, callback, payload, options) { await this.lifecycle.ready(); _assertClassBrand(_Scheduler_brand, this, _validateInterval).call(this, intervalSeconds, callback, options?.retry); const result = this.lifecycle.routes.source ? await this.lifecycle.routes.toRoot({ type: "every", intervalSeconds, callback, payload, options }) : await _assertClassBrand(_Scheduler_brand, this, _insert).call(this, null, parseInterval(intervalSeconds, Date.now()), callback, payload, options); _assertClassBrand(_Scheduler_brand, this, _emitCreated).call(this, result); return result.schedule; } /** Get a schedule by ID. Works inside routed sub-agents. */ async get(id) { await this.lifecycle.ready(); return this.lifecycle.routes.source ? await this.lifecycle.routes.toRoot({ type: "get", id }) : _assertClassBrand(_Scheduler_brand, this, _getForOwner).call(this, null, id); } /** List schedules matching criteria. Works inside routed sub-agents. */ async list(criteria = {}) { await this.lifecycle.ready(); return this.lifecycle.routes.source ? await this.lifecycle.routes.toRoot({ type: "list", criteria }) : _assertClassBrand(_Scheduler_brand, this, _listForOwner).call(this, null, criteria); } /** * Cancel one schedule owned by this Scheduler. * * @param id - ID of the schedule to cancel. * @returns True when a schedule was cancelled, false when none matched. */ async cancel(id) { await this.lifecycle.ready(); const result = this.lifecycle.routes.source ? await this.lifecycle.routes.toRoot({ type: "cancel", id }) : await _assertClassBrand(_Scheduler_brand, this, _cancelForOwner).call(this, null, id); if (result.ok && result.callback) _assertClassBrand(_Scheduler_brand, this, _emit).call(this, "schedule:cancel", { callback: result.callback, id }); return result.ok; } /** * @internal Synchronous read backing Agent's deprecated `getSchedule()`. * Not part of the primitive's contract — use {@link get}. Cannot cross * Durable Object boundaries and throws inside routed sub-agents. */ __DO_NOT_USE_WILL_REMOVE__getSchedule(id) { if (this.lifecycle.routes.source) throw new Error("getSchedule() is synchronous and cannot read routed schedule storage. Use await getScheduleById(id) on Agent, or await scheduler.get(id) on a standalone Scheduler."); return _assertClassBrand(_Scheduler_brand, this, _getForOwner).call(this, null, id); } /** * @internal Synchronous read backing Agent's deprecated `getSchedules()`. * Not part of the primitive's contract — use {@link list}. Cannot cross * Durable Object boundaries and throws inside routed sub-agents. */ __DO_NOT_USE_WILL_REMOVE__getSchedules(criteria = {}) { if (this.lifecycle.routes.source) throw new Error("getSchedules() is synchronous and cannot read routed schedule storage. Use await listSchedules(criteria) on Agent, or await scheduler.list(criteria) on a standalone Scheduler."); return _assertClassBrand(_Scheduler_brand, this, _listForOwner).call(this, null, criteria); } /** @internal Remove schedules owned by one routed Lifecycle subtree. */ async __DO_NOT_USE_WILL_BREAK__cleanupRoutePrefix(prefix) { for (const { job, timing } of _assertClassBrand(_Scheduler_brand, this, _ownedJobs).call(this)) { const ownerKey = timing.owner_path_key ?? timing.owner_path; if (!timing.owner_path || ownerKey === null || ownerKey === void 0) continue; if (ownerKey !== prefix && !ownerKey.startsWith(`${prefix}/`)) continue; _assertClassBrand(_Scheduler_brand, this, _emit).call(this, "schedule:cancel", { callback: job.fn, id: job.id }); await this.lifecycle.jobs.cancel(job.id); } } }; /** * Move every `cf_agents_schedules` row into the job queue and drop the * table. Idempotent: a missing table means a fresh object or a completed * migration. Times convert from epoch seconds to epoch milliseconds. */ async function _migrateLegacyScheduleTable(storage) { if (storage.sql.exec("SELECT name FROM sqlite_master WHERE type='table' AND name='cf_agents_schedules'").toArray().length === 0) return; const rows = storage.sql.exec("SELECT * FROM cf_agents_schedules").toArray(); for (const row of rows) { if (row.callback === "_cf_keepAliveHeartbeat") continue; let payload; try { payload = typeof row.payload === "string" ? JSON.parse(row.payload) : void 0; } catch (error) { console.error(`Skipping schedule "${row.id}" during job-queue migration: its payload is not valid JSON`, error); continue; } let retry; try { retry = typeof row.retry_options === "string" ? JSON.parse(row.retry_options) : void 0; } catch { retry = void 0; } await this.lifecycle.jobs.push({ id: row.id, fn: row.callback, time: row.time * 1e3, payload: { payload, retry, type: row.type, delayInSeconds: row.delayInSeconds ?? void 0, cron: row.cron ?? void 0, intervalSeconds: row.intervalSeconds ?? void 0, owner_path: row.owner_path ?? null, owner_path_key: row.owner_path_key ?? null }, retry: resolveRetryConfig(retry, _classPrivateFieldGet2(_retryDefaults, this)), singleflight: row.type === "interval", hungTimeoutSeconds: _classPrivateFieldGet2(_hungScheduleTimeoutSeconds, this), recoveryLoop: LEGACY_RECOVERY_LOOP_CALLBACKS.has(row.callback) }); } storage.sql.exec("DROP TABLE cf_agents_schedules"); } /** Advance a recurring schedule; complete a one-shot. */ function _recurrenceOutcome(timing) { if (timing.type === "cron") return { rescheduleAt: nextCronTimeMs(timing.cron ?? "", Date.now()) }; if (timing.type === "interval") return { rescheduleAt: Date.now() + (timing.intervalSeconds ?? 0) * 1e3 }; } /** * Execute a routed schedule locally with the historical retry handling. * Runs on the owning (facet) Scheduler, outside the root's event loop, so * it applies its own in-process retry budget. Platform-class failures on a * one-shot re-throw so the root preserves the job and the durable alarm * retries on a fresh invocation. */ async function _executeRouted(id, fn, timing, retry) { const handler = _assertClassBrand(_Scheduler_brand, this, _resolveCallback).call(this, fn); if (!handler) { console.error(`callback ${fn} not found`); return; } const { maxAttempts, baseDelayMs, maxDelayMs } = resolveRetryConfig(retry, _classPrivateFieldGet2(_retryDefaults, this)); const isOneShot = timing.type === "delayed" || timing.type === "scheduled"; const schedule = _assertClassBrand(_Scheduler_brand, this, _payloadToSchedule).call(this, id, fn, timing); try { await tryN(maxAttempts, async (attempt) => { if (attempt > 1) _assertClassBrand(_Scheduler_brand, this, _emit).call(this, "schedule:retry", { callback: fn, id, attempt, maxAttempts }); await this.lifecycle.runInHostContext(() => handler(timing.payload, schedule)); }, { baseDelayMs, maxDelayMs, shouldRetry: (error) => !(isOneShot && isDurableObjectCodeUpdateReset(error)) }); } catch (error) { if (isOneShot && isPlatformFailure(error)) throw error; console.error(`error executing callback "${fn}" after ${maxAttempts} attempts`, error); _assertClassBrand(_Scheduler_brand, this, _emit).call(this, "schedule:error", { callback: fn, id, error: error instanceof Error ? error.message : String(error), attempts: maxAttempts }); try { await _classPrivateFieldGet2(_onError, this)?.call(this, error); } catch {} } } function _validateSchedule(when, callback, options) { if (typeof callback !== "string") throw new Error("Callback must be a string"); if (!_assertClassBrand(_Scheduler_brand, this, _hasCallback).call(this, callback)) throw new Error(`Unknown scheduled callback "${callback}": not registered on this Scheduler`); if (options?.retry) validateRetryOptions(options.retry, _classPrivateFieldGet2(_retryDefaults, this)); if (!(when instanceof Date) && typeof when !== "number" && typeof when !== "string") throw new Error(`Invalid schedule type: ${JSON.stringify(when)}(${typeof when}) trying to schedule ${callback}`); _assertClassBrand(_Scheduler_brand, this, _warnWhenScheduledDuringStartup).call(this, when, callback, options); } function _validateInterval(intervalSeconds, callback, retry) { validateIntervalSeconds(intervalSeconds); if (typeof callback !== "string") throw new Error("Callback must be a string"); if (!_assertClassBrand(_Scheduler_brand, this, _hasCallback).call(this, callback)) throw new Error(`Unknown scheduled callback "${callback}": not registered on this Scheduler`); if (retry) validateRetryOptions(retry, _classPrivateFieldGet2(_retryDefaults, this)); } /** * A non-idempotent one-shot created during startup accumulates one row per * Durable Object wake, whether it came from the host's onStart or another * startup hook. Warn once per callback; an explicit `idempotent` choice * (either value) opts out. */ function _warnWhenScheduledDuringStartup(when, callback, options) { if (this.lifecycle.status() !== "starting") return; if (options?.idempotent !== void 0) return; if (typeof when === "string") return; if (_classPrivateFieldGet2(_warnedStartupCallbacks, this).has(callback)) return; _classPrivateFieldGet2(_warnedStartupCallbacks, this).add(callback); console.warn(`Scheduling "${callback}" during startup (e.g. onStart()) without { idempotent: true } creates a new row on every Durable Object restart, which can cause duplicate executions. Pass { idempotent: true } to deduplicate, or use an interval schedule for recurring tasks.`); } /** Resolve a name to its registered or composition-root-supplied handler. */ function _resolveCallback(name) { const handler = _classPrivateFieldGet2(_handlers, this)[name]; if (handler) return handler; return schedulerCallbackResolvers.get(this)?.(name); } /** True when a name resolves to a runnable callback. */ function _hasCallback(name) { return _assertClassBrand(_Scheduler_brand, this, _resolveCallback).call(this, name) !== void 0; } /** Every schedule job this Scheduler owns, with its parsed vocabulary. */ function _ownedJobs() { const owned = []; for (const job of this.lifecycle.jobs.list()) if (isSchedulerJobPayload(job.payload)) owned.push({ job, timing: job.payload }); return owned; } /** * Push a schedule job for the given owner, or return the existing job * when an idempotent request matches one. One-shot timings deduplicate only * when `idempotent: true` is passed; recurring timings deduplicate unless * `idempotent: false` opts out. `created: false` marks a dedup hit so * callers suppress the `schedule:create` event. */ async function _insert(owner, timing, callback, payload, options) { const idempotent = isRecurring(timing) ? options?.idempotent !== false : Boolean(options?.idempotent); const recoveryLoop = options?.recoveryLoop; if (idempotent) { const existing = _assertClassBrand(_Scheduler_brand, this, _findMatchingJob).call(this, owner?.key ?? null, timing, callback, JSON.stringify(payload)); if (existing) { if (recoveryLoop && !existing.job.recoveryLoop) await this.lifecycle.jobs.push({ id: existing.job.id, fn: existing.job.fn, time: existing.job.time, payload: existing.job.payload, retry: existing.job.retry, singleflight: existing.job.singleflight, exclusive: existing.job.exclusive, hungTimeoutSeconds: _classPrivateFieldGet2(_hungScheduleTimeoutSeconds, this), recoveryLoop: true }); await this.lifecycle.jobs.rearm(); return { schedule: _assertClassBrand(_Scheduler_brand, this, _jobToSchedule).call(this, existing.job, existing.timing), created: false }; } } const jobPayload = { payload, retry: options?.retry, type: timing.type, delayInSeconds: timing.type === "delayed" ? timing.delayInSeconds : void 0, cron: timing.type === "cron" ? timing.cron : void 0, intervalSeconds: timing.type === "interval" ? timing.intervalSeconds : void 0, owner_path: owner?.data ?? null, owner_path_key: owner?.key ?? null }; return { schedule: { id: (await this.lifecycle.jobs.push({ fn: callback, time: timing.time * 1e3, payload: jobPayload, retry: resolveRetryConfig(options?.retry, _classPrivateFieldGet2(_retryDefaults, this)), singleflight: timing.type === "interval", hungTimeoutSeconds: _classPrivateFieldGet2(_hungScheduleTimeoutSeconds, this), recoveryLoop })).id, callback, payload, retry: options?.retry, ...timing }, created: true }; } /** Find the job an idempotent insert deduplicates onto, if any. */ function _findMatchingJob(ownerKey, timing, callback, payloadJson) { for (const owned of _assertClassBrand(_Scheduler_brand, this, _ownedJobs).call(this)) { const { job, timing: candidate } = owned; if (candidate.type !== timing.type) continue; if (job.fn !== callback) continue; if ((candidate.owner_path_key ?? null) !== ownerKey) continue; if (JSON.stringify(candidate.payload) !== payloadJson) continue; if (timing.type === "cron" && candidate.cron !== timing.cron) continue; if (timing.type === "interval" && candidate.intervalSeconds !== timing.intervalSeconds) continue; return owned; } } function _jobToSchedule(job, timing) { return _assertClassBrand(_Scheduler_brand, this, _payloadToSchedule).call(this, job.id, job.fn, timing, Math.floor(job.time / 1e3)); } function _payloadToSchedule(id, fn, timing, timeSeconds) { const base = { callback: fn, id, payload: timing.payload, retry: timing.retry }; const time = timeSeconds ?? 0; switch (timing.type) { case "scheduled": return { ...base, time, type: "scheduled" }; case "delayed": return { ...base, delayInSeconds: timing.delayInSeconds ?? 0, time, type: "delayed" }; case "cron": return { ...base, cron: timing.cron ?? "", time, type: "cron" }; case "interval": return { ...base, intervalSeconds: timing.intervalSeconds ?? 0, time, type: "interval" }; } } function _getForOwner(owner, id) { const ownerKey = owner?.key ?? null; const job = this.lifecycle.jobs.get(id); if (!job || !isSchedulerJobPayload(job.payload)) return void 0; if ((job.payload.owner_path_key ?? null) !== ownerKey) return void 0; return _assertClassBrand(_Scheduler_brand, this, _jobToSchedule).call(this, job, job.payload); } function _listForOwner(owner, criteria = {}) { const ownerKey = owner?.key ?? null; const startSeconds = criteria.timeRange ? Math.floor((criteria.timeRange.start ?? /* @__PURE__ */ new Date(0)).getTime() / 1e3) : null; const endSeconds = criteria.timeRange ? Math.floor((criteria.timeRange.end ?? /* @__PURE__ */ new Date(999999999999999)).getTime() / 1e3) : null; const schedules = []; for (const { job, timing } of _assertClassBrand(_Scheduler_brand, this, _ownedJobs).call(this)) { if ((timing.owner_path_key ?? null) !== ownerKey) continue; if (criteria.id && job.id !== criteria.id) continue; if (criteria.type && timing.type !== criteria.type) continue; const timeSeconds = Math.floor(job.time / 1e3); if (startSeconds !== null && timeSeconds < startSeconds) continue; if (endSeconds !== null && timeSeconds > endSeconds) continue; schedules.push(_assertClassBrand(_Scheduler_brand, this, _jobToSchedule).call(this, job, timing)); } return schedules; } async function _cancelForOwner(owner, id) { const ownerKey = owner?.key ?? null; const job = this.lifecycle.jobs.get(id); if (!job || !isSchedulerJobPayload(job.payload)) return { ok: false }; if ((job.payload.owner_path_key ?? null) !== ownerKey) return { ok: false }; const callback = job.fn; await this.lifecycle.jobs.cancel(id); return { ok: true, callback }; } function _emit(type, payload) { this.lifecycle.events.emit(type, payload); } function _emitCreated(result) { if (!result.created) return; _assertClassBrand(_Scheduler_brand, this, _emit).call(this, "schedule:create", { callback: result.schedule.callback, id: result.schedule.id }); } //#endregion export { setSchedulerCallbackResolver as n, Scheduler as t }; //# sourceMappingURL=scheduler-D_KHqxBa.js.map