agents 0.21.0 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -1
- package/dist/{agent-tool-types-CzGGB-20.d.ts → agent-routing-DE5zmCQ8.d.ts} +1844 -1336
- package/dist/agent-routing.d.ts +14 -0
- package/dist/agent-routing.js +187 -0
- package/dist/agent-routing.js.map +1 -0
- package/dist/agent-tool-types.d.ts +26 -26
- package/dist/{agent-tools-zR2d5uij.d.ts → agent-tools-DtXMTDGM.d.ts} +7 -7
- package/dist/agent-tools.d.ts +21 -21
- package/dist/agent-tools.js +2 -1
- package/dist/agent-tools.js.map +1 -1
- package/dist/browser/ai.js +6 -2
- package/dist/browser/ai.js.map +1 -1
- package/dist/browser/tanstack-ai.js.map +1 -1
- package/dist/callable-decorator-DP__HhBA.d.ts +72 -0
- package/dist/callable-decorator.d.ts +18 -0
- package/dist/callable-decorator.js +71 -0
- package/dist/callable-decorator.js.map +1 -0
- package/dist/capability-BjSKYpzg.js +42 -0
- package/dist/capability-BjSKYpzg.js.map +1 -0
- package/dist/capability-runner-Be_-PLR1.d.ts +459 -0
- package/dist/channel-Bnm4S7T2.d.ts +491 -0
- package/dist/channels/ai-sdk.d.ts +46 -0
- package/dist/channels/ai-sdk.js +120 -0
- package/dist/channels/ai-sdk.js.map +1 -0
- package/dist/channels/email.d.ts +95 -0
- package/dist/channels/email.js +323 -0
- package/dist/channels/email.js.map +1 -0
- package/dist/channels/index.d.ts +233 -0
- package/dist/channels/index.js +608 -0
- package/dist/channels/index.js.map +1 -0
- package/dist/channels/slack.d.ts +140 -0
- package/dist/channels/slack.js +614 -0
- package/dist/channels/slack.js.map +1 -0
- package/dist/channels/tanstack-ai.d.ts +39 -0
- package/dist/channels/tanstack-ai.js +17 -0
- package/dist/channels/tanstack-ai.js.map +1 -0
- package/dist/channels/telegram.d.ts +106 -0
- package/dist/channels/telegram.js +427 -0
- package/dist/channels/telegram.js.map +1 -0
- package/dist/channels/voice.d.ts +45 -0
- package/dist/channels/voice.js +122 -0
- package/dist/channels/voice.js.map +1 -0
- package/dist/chat/index.d.ts +2328 -2015
- package/dist/chat/index.js +891 -521
- package/dist/chat/index.js.map +1 -1
- package/dist/chat/react.d.ts +14 -1
- package/dist/chat/react.js +82 -52
- package/dist/chat/react.js.map +1 -1
- package/dist/chat/transport.js +1 -1
- package/dist/chat-sdk/index.d.ts +7 -7
- package/dist/chat-sdk/index.js +1 -1
- package/dist/{client-zqKcsyFa.js → client-jagG8a9_.js} +129 -37
- package/dist/client-jagG8a9_.js.map +1 -0
- package/dist/client.d.ts +1 -1
- package/dist/client.js +1 -1
- package/dist/{cloudflare-BduZwmYK.js → cloudflare-Dzvc7V2N.js} +10 -3
- package/dist/{cloudflare-BduZwmYK.js.map → cloudflare-Dzvc7V2N.js.map} +1 -1
- package/dist/context/index.d.ts +216 -0
- package/dist/context/index.js +454 -0
- package/dist/context/index.js.map +1 -0
- package/dist/current-agent-Da_C9a3b.d.ts +266 -0
- package/dist/current-agent-DhoDkSnH.js +51 -0
- package/dist/current-agent-DhoDkSnH.js.map +1 -0
- package/dist/diagnostics-BzvaX2UT.js +45 -0
- package/dist/diagnostics-BzvaX2UT.js.map +1 -0
- package/dist/diagnostics-C4jcz3VK.js +360 -0
- package/dist/diagnostics-C4jcz3VK.js.map +1 -0
- package/dist/{do-oauth-client-provider-VTZj2VtM.d.ts → do-oauth-client-provider-Tmf1vgKz.d.ts} +2 -2
- package/dist/{email-CL27preh.d.ts → email-7TatiTnl.d.ts} +38 -9
- package/dist/email-send.d.ts +15 -0
- package/dist/email-send.js +32 -0
- package/dist/email-send.js.map +1 -0
- package/dist/email.d.ts +14 -10
- package/dist/email.js.map +1 -1
- package/dist/{handler-stateless-C_bo-Ytq.d.ts → handler-stateless-DxYpJ_XF.d.ts} +3 -3
- package/dist/{handler-stateless-CIkKPETH.js → handler-stateless-VvrWSAVA.js} +5 -5
- package/dist/handler-stateless-VvrWSAVA.js.map +1 -0
- package/dist/index-BB0kqhIz.d.ts +101 -0
- package/dist/index-XDkuQ7zm.d.ts +89 -0
- package/dist/{index-BRnybD6X.d.ts → index-YSKgfgg9.d.ts} +21 -31
- package/dist/index.d.ts +113 -101
- package/dist/index.js +11 -7234
- package/dist/ingress-BfetZbMO.js +83 -0
- package/dist/ingress-BfetZbMO.js.map +1 -0
- package/dist/internal-CYlgHl1l.js +59 -0
- package/dist/internal-CYlgHl1l.js.map +1 -0
- package/dist/internal_context-BlxFEWfn.d.ts +19 -0
- package/dist/internal_context.d.ts +10 -4
- package/dist/internal_context.js +1 -10
- package/dist/{client-invoker-BNSZxAkv.d.ts → invoker-CG0_p_Wq.d.ts} +2 -2
- package/dist/{client-invoker-VNZ7X0nn.js → invoker-CHMnoxIA.js} +2 -2
- package/dist/invoker-CHMnoxIA.js.map +1 -0
- package/dist/lifecycle/index.d.ts +66 -0
- package/dist/lifecycle/index.js +4 -0
- package/dist/lifecycle-CMRGjZdw.js +1299 -0
- package/dist/lifecycle-CMRGjZdw.js.map +1 -0
- package/dist/mcp/{do-oauth-client-provider.d.ts → client/do-oauth-client-provider.d.ts} +1 -1
- package/dist/mcp/{do-oauth-client-provider.js → client/do-oauth-client-provider.js} +1 -1
- package/dist/mcp/client/do-oauth-client-provider.js.map +1 -0
- package/dist/mcp/client/index.d.ts +42 -0
- package/dist/mcp/{client.js → client/index.js} +1 -1
- package/dist/mcp/{x402.d.ts → client/x402.d.ts} +2 -2
- package/dist/mcp/{x402.js → client/x402.js} +2 -2
- package/dist/mcp/client/x402.js.map +1 -0
- package/dist/mcp/index.d.ts +36 -36
- package/dist/mcp/index.js +14 -16
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/{server.d.ts → server/index.d.ts} +1 -1
- package/dist/mcp/{server.js → server/index.js} +1 -1
- package/dist/observability/ai/index.js +50 -35
- package/dist/observability/ai/index.js.map +1 -1
- package/dist/observability/index.d.ts +4 -4
- package/dist/observability/index.js +3 -50
- package/dist/observability/index.js.map +1 -1
- package/dist/{protocol-Dqc2MQxo.js → protocol-B0nh6KNf.js} +19 -21
- package/dist/protocol-B0nh6KNf.js.map +1 -0
- package/dist/react.d.ts +4 -4
- package/dist/react.js +1 -1
- package/dist/{retries-CAvxtG9d.d.ts → retries-D9Ds-1lz.d.ts} +17 -6
- package/dist/retries.d.ts +8 -6
- package/dist/retries.js +13 -1
- package/dist/retries.js.map +1 -1
- package/dist/routing/index.d.ts +137 -0
- package/dist/routing/index.js +244 -0
- package/dist/routing/index.js.map +1 -0
- package/dist/sanitize-D9TujEK8.js +79 -0
- package/dist/sanitize-D9TujEK8.js.map +1 -0
- package/dist/schedule.d.ts +25 -94
- package/dist/schedule.js +1 -98
- package/dist/schedule.js.map +1 -1
- package/dist/scheduler-DD9NdYbF.js +665 -0
- package/dist/scheduler-DD9NdYbF.js.map +1 -0
- package/dist/scheduler-Dwh85ZGl.d.ts +223 -0
- package/dist/schedules/index.d.ts +22 -0
- package/dist/schedules/index.js +2 -0
- package/dist/schedules/parser.d.ts +79 -0
- package/dist/schedules/parser.js +103 -0
- package/dist/schedules/parser.js.map +1 -0
- package/dist/sentence-chunker-BAidJ4DA.d.ts +68 -0
- package/dist/serializable.d.ts +1 -1
- package/dist/sessions/index.d.ts +441 -0
- package/dist/sessions/index.js +2063 -0
- package/dist/sessions/index.js.map +1 -0
- package/dist/skills/index.d.ts +99 -0
- package/dist/skills/index.js +254 -5
- package/dist/skills/index.js.map +1 -1
- package/dist/sql-error-CPY-GXyI.d.ts +12 -0
- package/dist/sql-error.d.ts +2 -0
- package/dist/sql-error.js +16 -0
- package/dist/sql-error.js.map +1 -0
- package/dist/src-DlSHshb2.js +6963 -0
- package/dist/src-DlSHshb2.js.map +1 -0
- package/dist/streams/index.d.ts +120 -0
- package/dist/streams/index.js +107 -0
- package/dist/streams/index.js.map +1 -0
- package/dist/streams-D6tJ0NN9.d.ts +370 -0
- package/dist/streams-DZKgAj9b.js +709 -0
- package/dist/streams-DZKgAj9b.js.map +1 -0
- package/dist/sub-routing.d.ts +12 -12
- package/dist/surface-bZZJqBka.js +17 -0
- package/dist/surface-bZZJqBka.js.map +1 -0
- package/dist/tasks/index.d.ts +64 -0
- package/dist/tasks/index.js +2 -0
- package/dist/tasks-BRJ5zgya.d.ts +517 -0
- package/dist/tasks-ylZgBjhj.js +1656 -0
- package/dist/tasks-ylZgBjhj.js.map +1 -0
- package/dist/text-segment-joiner-BtAFQSA_.js +57 -0
- package/dist/text-segment-joiner-BtAFQSA_.js.map +1 -0
- package/dist/text-stream-CpdiKrJB.js +272 -0
- package/dist/text-stream-CpdiKrJB.js.map +1 -0
- package/dist/tokens-nHAKcN6M.js +52 -0
- package/dist/tokens-nHAKcN6M.js.map +1 -0
- package/dist/tool-schema-CBjGPrsQ.js +31 -0
- package/dist/tool-schema-CBjGPrsQ.js.map +1 -0
- package/dist/types-B7LojTe4.d.ts +202 -0
- package/dist/types-_Faxb570.d.ts +439 -0
- package/dist/voice/client.d.ts +226 -0
- package/dist/voice/client.js +932 -0
- package/dist/voice/client.js.map +1 -0
- package/dist/voice/errors.d.ts +43 -0
- package/dist/voice/errors.js +41 -0
- package/dist/voice/errors.js.map +1 -0
- package/dist/voice/index.d.ts +271 -0
- package/dist/voice/index.js +1812 -0
- package/dist/voice/index.js.map +1 -0
- package/dist/voice/react.d.ts +167 -0
- package/dist/voice/react.js +234 -0
- package/dist/voice/react.js.map +1 -0
- package/dist/voice/sfu.d.ts +71 -0
- package/dist/voice/sfu.js +157 -0
- package/dist/voice/sfu.js.map +1 -0
- package/dist/voice/text.d.ts +6 -0
- package/dist/voice/text.js +2 -0
- package/dist/voice/types.d.ts +58 -0
- package/dist/voice/types.js +18 -0
- package/dist/voice/types.js.map +1 -0
- package/dist/voice/workers-ai.d.ts +136 -0
- package/dist/voice/workers-ai.js +568 -0
- package/dist/voice/workers-ai.js.map +1 -0
- package/dist/websockets/index.d.ts +192 -0
- package/dist/websockets/index.js +2 -0
- package/dist/websockets-DUfRHPRq.js +502 -0
- package/dist/websockets-DUfRHPRq.js.map +1 -0
- package/dist/workflow-types.d.ts +25 -25
- package/dist/workflows.d.ts +22 -22
- package/dist/workflows.js +2 -1
- package/dist/workflows.js.map +1 -1
- package/dist/{ws-chat-transport-CIoOBbO7.js → ws-chat-transport-rWwta645.js} +152 -15
- package/dist/ws-chat-transport-rWwta645.js.map +1 -0
- package/docs/agent-class.md +29 -87
- package/docs/agent-tools.md +2 -1
- package/docs/channels.md +323 -0
- package/docs/chat-agents.md +19 -25
- package/docs/context.md +131 -0
- package/docs/durable-execution.md +1 -1
- package/docs/http-websockets.md +1 -11
- package/docs/human-in-the-loop.md +1 -1
- package/docs/index.md +16 -12
- package/docs/lifecycle.md +370 -0
- package/docs/long-running-agents.md +4 -6
- package/docs/mcp-client.md +55 -0
- package/docs/mcp-servers.md +5 -1
- package/docs/observability.md +11 -11
- package/docs/resumable-streaming.md +2 -2
- package/docs/routing.md +105 -0
- package/docs/scheduling.md +175 -15
- package/docs/server-driven-messages.md +1 -1
- package/docs/sessions.md +237 -871
- package/docs/streams.md +213 -0
- package/docs/sub-agents.md +185 -125
- package/docs/tasks.md +246 -0
- package/docs/voice.md +745 -0
- package/package.json +144 -33
- package/dist/cli/index.js +0 -26
- package/dist/cli/index.js.map +0 -1
- package/dist/client-invoker-VNZ7X0nn.js.map +0 -1
- package/dist/client-zqKcsyFa.js.map +0 -1
- package/dist/compaction-helpers-iiKMr2TQ.js +0 -340
- package/dist/compaction-helpers-iiKMr2TQ.js.map +0 -1
- package/dist/compaction-helpers-wUz6M3us.d.ts +0 -621
- package/dist/experimental/memory/session/index.d.ts +0 -670
- package/dist/experimental/memory/session/index.js +0 -2374
- package/dist/experimental/memory/session/index.js.map +0 -1
- package/dist/experimental/memory/utils/index.d.ts +0 -96
- package/dist/experimental/memory/utils/index.js +0 -79
- package/dist/experimental/memory/utils/index.js.map +0 -1
- package/dist/handler-stateless-CIkKPETH.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/internal_context-Dg4Cgjcu.d.ts +0 -37
- package/dist/internal_context.js.map +0 -1
- package/dist/mcp/client.d.ts +0 -42
- package/dist/mcp/do-oauth-client-provider.js.map +0 -1
- package/dist/mcp/x402.js.map +0 -1
- package/dist/protocol-Dqc2MQxo.js.map +0 -1
- package/dist/tool-output-truncation-CNnnGZQ3.js +0 -98
- package/dist/tool-output-truncation-CNnnGZQ3.js.map +0 -1
- package/dist/ws-chat-transport-CIoOBbO7.js.map +0 -1
- /package/dist/{cli/index.d.ts → index-BVVgDSdq.d.ts} +0 -0
|
@@ -0,0 +1,665 @@
|
|
|
1
|
+
import { i as _classPrivateFieldInitSpec, n as _classPrivateFieldSet2, r as _assertClassBrand, t as _classPrivateFieldGet2 } from "./classPrivateFieldGet2-DZBYAB34.js";
|
|
2
|
+
import { t as LifecycleCapability } from "./capability-BjSKYpzg.js";
|
|
3
|
+
import { t as _classPrivateMethodInitSpec } from "./classPrivateMethodInitSpec-qMjJ6sHQ.js";
|
|
4
|
+
import { isDurableObjectCodeUpdateReset, isPlatformFailure, tryN, validateRetryOptions } from "./retries.js";
|
|
5
|
+
import { parseCronExpression } from "cron-schedule";
|
|
6
|
+
//#region src/schedules/schedule-timing.ts
|
|
7
|
+
/**
|
|
8
|
+
* Pure timing rules for persistent schedules.
|
|
9
|
+
*
|
|
10
|
+
* A `ScheduleTiming` is the parsed "when and how it runs" half of a
|
|
11
|
+
* `Schedule` — everything except identity, callback, and payload. Parsing
|
|
12
|
+
* user-facing inputs here keeps Scheduler's storage code to one insert path.
|
|
13
|
+
*/
|
|
14
|
+
/** Longest allowed gap between interval executions: 30 days. */
|
|
15
|
+
const MAX_INTERVAL_SECONDS = 720 * 60 * 60;
|
|
16
|
+
/** True when this timing repeats and therefore deduplicates by default. */
|
|
17
|
+
function isRecurring(timing) {
|
|
18
|
+
return timing.type === "cron" || timing.type === "interval";
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Next wall-clock execution time for a cron expression.
|
|
22
|
+
*
|
|
23
|
+
* @param cron - A standard cron expression.
|
|
24
|
+
* @param nowMs - Current epoch time in milliseconds.
|
|
25
|
+
* @returns The next execution epoch time in milliseconds.
|
|
26
|
+
* @throws For an unparseable cron expression.
|
|
27
|
+
*/
|
|
28
|
+
function nextCronTimeMs(cron, nowMs) {
|
|
29
|
+
return parseCronExpression(cron).getNextDate(new Date(nowMs)).getTime();
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Parse a user-facing `when` into schedule timing.
|
|
33
|
+
*
|
|
34
|
+
* A `Date` runs once at that date, a number runs once after that many
|
|
35
|
+
* seconds, and a string is a recurring cron expression.
|
|
36
|
+
*
|
|
37
|
+
* @param when - The requested execution time or recurrence.
|
|
38
|
+
* @param nowMs - Current epoch time in milliseconds.
|
|
39
|
+
* @param callback - Callback name, used only in error messages.
|
|
40
|
+
* @returns The parsed timing.
|
|
41
|
+
* @throws For a `when` value that is not a `Date`, number, or string.
|
|
42
|
+
*/
|
|
43
|
+
function parseWhen(when, nowMs, callback) {
|
|
44
|
+
if (when instanceof Date) return {
|
|
45
|
+
type: "scheduled",
|
|
46
|
+
time: Math.floor(when.getTime() / 1e3)
|
|
47
|
+
};
|
|
48
|
+
if (typeof when === "number") return {
|
|
49
|
+
type: "delayed",
|
|
50
|
+
time: Math.floor((nowMs + when * 1e3) / 1e3),
|
|
51
|
+
delayInSeconds: when
|
|
52
|
+
};
|
|
53
|
+
if (typeof when === "string") return {
|
|
54
|
+
type: "cron",
|
|
55
|
+
time: Math.floor(nextCronTimeMs(when, nowMs) / 1e3),
|
|
56
|
+
cron: when
|
|
57
|
+
};
|
|
58
|
+
throw new Error(`Invalid schedule type: ${JSON.stringify(when)}(${typeof when}) trying to schedule ${callback}`);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Reject an interval outside the supported range.
|
|
62
|
+
*
|
|
63
|
+
* @param intervalSeconds - The requested gap between executions.
|
|
64
|
+
* @throws For a non-positive interval or one longer than 30 days.
|
|
65
|
+
*/
|
|
66
|
+
function validateIntervalSeconds(intervalSeconds) {
|
|
67
|
+
if (typeof intervalSeconds !== "number" || intervalSeconds <= 0) throw new Error("intervalSeconds must be a positive number");
|
|
68
|
+
if (intervalSeconds > 2592e3) throw new Error(`intervalSeconds cannot exceed ${MAX_INTERVAL_SECONDS} seconds (30 days)`);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Parse a fixed interval into schedule timing.
|
|
72
|
+
*
|
|
73
|
+
* @param intervalSeconds - Seconds between executions.
|
|
74
|
+
* @param nowMs - Current epoch time in milliseconds.
|
|
75
|
+
* @returns The parsed timing with the first execution one interval from now.
|
|
76
|
+
* @throws For an interval outside the supported range.
|
|
77
|
+
*/
|
|
78
|
+
function parseInterval(intervalSeconds, nowMs) {
|
|
79
|
+
validateIntervalSeconds(intervalSeconds);
|
|
80
|
+
return {
|
|
81
|
+
type: "interval",
|
|
82
|
+
time: Math.floor((nowMs + intervalSeconds * 1e3) / 1e3),
|
|
83
|
+
intervalSeconds
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
//#endregion
|
|
87
|
+
//#region src/schedules/scheduler.ts
|
|
88
|
+
/**
|
|
89
|
+
* Lifecycle scheduling vocabulary. Scheduler validates schedules, resolves
|
|
90
|
+
* named callbacks, and pushes jobs into the Lifecycle-owned job queue;
|
|
91
|
+
* Lifecycle runs the alarm event loop, retry policy, and physical alarm
|
|
92
|
+
* arming. Scheduler owns no storage of its own — each schedule is one job
|
|
93
|
+
* whose `fn` is the callback name and whose payload carries the schedule's
|
|
94
|
+
* timing vocabulary.
|
|
95
|
+
*/
|
|
96
|
+
const schedulerCallbackResolvers = /* @__PURE__ */ new WeakMap();
|
|
97
|
+
/**
|
|
98
|
+
* @internal Supply a composition-root fallback for callback names outside the
|
|
99
|
+
* registered map. Agent uses this to keep its historical name-based
|
|
100
|
+
* scheduling API (`this.schedule(60, "methodName")`) working: the resolver
|
|
101
|
+
* looks the method up on the Agent, and the resolved handler still runs
|
|
102
|
+
* inside the Lifecycle host boundary.
|
|
103
|
+
*/
|
|
104
|
+
function setSchedulerCallbackResolver(scheduler, resolver) {
|
|
105
|
+
schedulerCallbackResolvers.set(scheduler, resolver);
|
|
106
|
+
}
|
|
107
|
+
const SCHEDULE_SCHEMA_VERSION_KEY = "cf_agents:schedules_schema_version";
|
|
108
|
+
/** Version 2: schedule rows live in the Lifecycle job queue. */
|
|
109
|
+
const CURRENT_SCHEDULE_SCHEMA_VERSION = 2;
|
|
110
|
+
/**
|
|
111
|
+
* Legacy schedule callbacks whose rows drive chat recovery loops from before
|
|
112
|
+
* the job queue carried breaker membership. The migration is the one place
|
|
113
|
+
* allowed to know legacy names (it already drops `_cf_keepAliveHeartbeat`
|
|
114
|
+
* rows by name): a recovery row migrated without its `recoveryLoop` flag
|
|
115
|
+
* would escape the alarm memory-limit breaker (#1825) and could re-trigger
|
|
116
|
+
* a doomed loop on an upgraded object.
|
|
117
|
+
*/
|
|
118
|
+
const LEGACY_RECOVERY_LOOP_CALLBACKS = /* @__PURE__ */ new Set(["_chatRecoveryContinue", "_chatRecoveryRetry"]);
|
|
119
|
+
const DEFAULT_RETRY = {
|
|
120
|
+
maxAttempts: 3,
|
|
121
|
+
baseDelayMs: 100,
|
|
122
|
+
maxDelayMs: 3e3
|
|
123
|
+
};
|
|
124
|
+
function resolveRetryConfig(retry, defaults) {
|
|
125
|
+
return {
|
|
126
|
+
maxAttempts: retry?.maxAttempts ?? defaults.maxAttempts,
|
|
127
|
+
baseDelayMs: retry?.baseDelayMs ?? defaults.baseDelayMs,
|
|
128
|
+
maxDelayMs: retry?.maxDelayMs ?? defaults.maxDelayMs
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
function isSchedulerJobPayload(value) {
|
|
132
|
+
return typeof value === "object" && value !== null && typeof value.type === "string";
|
|
133
|
+
}
|
|
134
|
+
var _handlers = /* @__PURE__ */ new WeakMap();
|
|
135
|
+
var _retryDefaults = /* @__PURE__ */ new WeakMap();
|
|
136
|
+
var _hungScheduleTimeoutSeconds = /* @__PURE__ */ new WeakMap();
|
|
137
|
+
var _onError = /* @__PURE__ */ new WeakMap();
|
|
138
|
+
var _warnedStartupCallbacks = /* @__PURE__ */ new WeakMap();
|
|
139
|
+
var _Scheduler_brand = /* @__PURE__ */ new WeakSet();
|
|
140
|
+
/**
|
|
141
|
+
* Persistent task scheduling for a Lifecycle Object.
|
|
142
|
+
*
|
|
143
|
+
* Register callbacks in the constructor and install the instance with
|
|
144
|
+
* `Lifecycle.use()`. Scheduler validates schedules and pushes them into the
|
|
145
|
+
* Lifecycle job queue; Lifecycle owns the physical alarm and the alarm
|
|
146
|
+
* event loop, and Scheduler runs registered callbacks through Lifecycle's
|
|
147
|
+
* host invocation boundary when its jobs come due.
|
|
148
|
+
*
|
|
149
|
+
* @experimental The API surface may change before stabilizing.
|
|
150
|
+
*/
|
|
151
|
+
var Scheduler = class extends LifecycleCapability {
|
|
152
|
+
/**
|
|
153
|
+
* Create a persistent Scheduler.
|
|
154
|
+
*
|
|
155
|
+
* @param options - Registered callbacks plus optional retry,
|
|
156
|
+
* hung-interval, and error policy. Registering `callbacks` types
|
|
157
|
+
* {@link set} and {@link every} against the map — names and
|
|
158
|
+
* payloads are checked where the handlers are declared and where they are
|
|
159
|
+
* scheduled. Names outside the map are rejected unless a composition-root
|
|
160
|
+
* resolver supplies them — the internal aperture behind `Agent`'s
|
|
161
|
+
* name-based scheduling API.
|
|
162
|
+
*/
|
|
163
|
+
constructor(options = {}) {
|
|
164
|
+
super("scheduler");
|
|
165
|
+
_classPrivateMethodInitSpec(this, _Scheduler_brand);
|
|
166
|
+
_classPrivateFieldInitSpec(this, _handlers, void 0);
|
|
167
|
+
_classPrivateFieldInitSpec(this, _retryDefaults, void 0);
|
|
168
|
+
_classPrivateFieldInitSpec(this, _hungScheduleTimeoutSeconds, void 0);
|
|
169
|
+
_classPrivateFieldInitSpec(this, _onError, void 0);
|
|
170
|
+
_classPrivateFieldInitSpec(this, _warnedStartupCallbacks, /* @__PURE__ */ new Set());
|
|
171
|
+
_classPrivateFieldSet2(_handlers, this, options.callbacks ?? {});
|
|
172
|
+
_classPrivateFieldSet2(_retryDefaults, this, resolveRetryConfig(options.retry, DEFAULT_RETRY));
|
|
173
|
+
_classPrivateFieldSet2(_hungScheduleTimeoutSeconds, this, options.hungScheduleTimeoutSeconds ?? 30);
|
|
174
|
+
_classPrivateFieldSet2(_onError, this, options.onError);
|
|
175
|
+
}
|
|
176
|
+
/** Migrate legacy schedule storage into the Lifecycle job queue. */
|
|
177
|
+
async onStart() {
|
|
178
|
+
_classPrivateFieldGet2(_warnedStartupCallbacks, this).clear();
|
|
179
|
+
const storage = this.lifecycle.storage;
|
|
180
|
+
if ((await storage.get(SCHEDULE_SCHEMA_VERSION_KEY) ?? 0) >= CURRENT_SCHEDULE_SCHEMA_VERSION) return;
|
|
181
|
+
await _assertClassBrand(_Scheduler_brand, this, _migrateLegacyScheduleTable).call(this, storage);
|
|
182
|
+
await storage.put(SCHEDULE_SCHEMA_VERSION_KEY, CURRENT_SCHEDULE_SCHEMA_VERSION);
|
|
183
|
+
}
|
|
184
|
+
/** Drive one due schedule job dispatched by the Lifecycle event loop. */
|
|
185
|
+
async onJob(context) {
|
|
186
|
+
const { job, attempt } = context;
|
|
187
|
+
const timing = job.payload;
|
|
188
|
+
if (!isSchedulerJobPayload(timing)) {
|
|
189
|
+
console.error(`Malformed schedule job ${job.id}; dropping it`);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
if (attempt === 1) _assertClassBrand(_Scheduler_brand, this, _emit).call(this, "schedule:execute", {
|
|
193
|
+
callback: job.fn,
|
|
194
|
+
id: job.id
|
|
195
|
+
});
|
|
196
|
+
else _assertClassBrand(_Scheduler_brand, this, _emit).call(this, "schedule:retry", {
|
|
197
|
+
callback: job.fn,
|
|
198
|
+
id: job.id,
|
|
199
|
+
attempt,
|
|
200
|
+
maxAttempts: resolveRetryConfig(job.retry, _classPrivateFieldGet2(_retryDefaults, this)).maxAttempts
|
|
201
|
+
});
|
|
202
|
+
if (timing.owner_path) {
|
|
203
|
+
try {
|
|
204
|
+
await this.lifecycle.routes.to({
|
|
205
|
+
key: timing.owner_path_key ?? timing.owner_path,
|
|
206
|
+
data: timing.owner_path
|
|
207
|
+
}, {
|
|
208
|
+
type: "dispatch",
|
|
209
|
+
id: job.id,
|
|
210
|
+
fn: job.fn,
|
|
211
|
+
job: timing,
|
|
212
|
+
retry: job.retry
|
|
213
|
+
});
|
|
214
|
+
} catch (error) {
|
|
215
|
+
if (isPlatformFailure(error)) throw error;
|
|
216
|
+
console.error(`error dispatching scheduled callback "${job.fn}"`, error);
|
|
217
|
+
_assertClassBrand(_Scheduler_brand, this, _emit).call(this, "schedule:error", {
|
|
218
|
+
callback: job.fn,
|
|
219
|
+
id: job.id,
|
|
220
|
+
error: error instanceof Error ? error.message : String(error),
|
|
221
|
+
attempts: 0
|
|
222
|
+
});
|
|
223
|
+
try {
|
|
224
|
+
await _classPrivateFieldGet2(_onError, this)?.call(this, error);
|
|
225
|
+
} catch {}
|
|
226
|
+
return "yield";
|
|
227
|
+
}
|
|
228
|
+
return _assertClassBrand(_Scheduler_brand, this, _recurrenceOutcome).call(this, timing);
|
|
229
|
+
}
|
|
230
|
+
const handler = _assertClassBrand(_Scheduler_brand, this, _resolveCallback).call(this, job.fn);
|
|
231
|
+
if (!handler) {
|
|
232
|
+
console.error(`callback ${job.fn} not found`);
|
|
233
|
+
return _assertClassBrand(_Scheduler_brand, this, _recurrenceOutcome).call(this, timing);
|
|
234
|
+
}
|
|
235
|
+
const schedule = _assertClassBrand(_Scheduler_brand, this, _jobToSchedule).call(this, job, timing);
|
|
236
|
+
await this.lifecycle.runInHostContext(() => handler(timing.payload, schedule));
|
|
237
|
+
return _assertClassBrand(_Scheduler_brand, this, _recurrenceOutcome).call(this, timing);
|
|
238
|
+
}
|
|
239
|
+
/** Observe one schedule's terminal application failure. */
|
|
240
|
+
async onJobError(context, error) {
|
|
241
|
+
const { job } = context;
|
|
242
|
+
const timing = job.payload;
|
|
243
|
+
if (!isSchedulerJobPayload(timing)) return void 0;
|
|
244
|
+
const { maxAttempts } = resolveRetryConfig(job.retry, _classPrivateFieldGet2(_retryDefaults, this));
|
|
245
|
+
console.error(`error executing callback "${job.fn}" after ${maxAttempts} attempts`, error);
|
|
246
|
+
_assertClassBrand(_Scheduler_brand, this, _emit).call(this, "schedule:error", {
|
|
247
|
+
callback: job.fn,
|
|
248
|
+
id: job.id,
|
|
249
|
+
error: error instanceof Error ? error.message : String(error),
|
|
250
|
+
attempts: maxAttempts
|
|
251
|
+
});
|
|
252
|
+
try {
|
|
253
|
+
await _classPrivateFieldGet2(_onError, this)?.call(this, error);
|
|
254
|
+
} catch {}
|
|
255
|
+
return _assertClassBrand(_Scheduler_brand, this, _recurrenceOutcome).call(this, timing);
|
|
256
|
+
}
|
|
257
|
+
/** Handle Scheduler protocol messages routed by another Lifecycle. */
|
|
258
|
+
async onRoute(context) {
|
|
259
|
+
const message = context.payload;
|
|
260
|
+
const owner = context.source ?? null;
|
|
261
|
+
switch (message.type) {
|
|
262
|
+
case "schedule": return _assertClassBrand(_Scheduler_brand, this, _insert).call(this, owner, parseWhen(message.when, Date.now(), message.callback), message.callback, message.payload, message.options);
|
|
263
|
+
case "every": return _assertClassBrand(_Scheduler_brand, this, _insert).call(this, owner, parseInterval(message.intervalSeconds, Date.now()), message.callback, message.payload, message.options);
|
|
264
|
+
case "get": return _assertClassBrand(_Scheduler_brand, this, _getForOwner).call(this, owner, message.id);
|
|
265
|
+
case "list": return _assertClassBrand(_Scheduler_brand, this, _listForOwner).call(this, owner, message.criteria);
|
|
266
|
+
case "cancel": return _assertClassBrand(_Scheduler_brand, this, _cancelForOwner).call(this, owner, message.id);
|
|
267
|
+
case "dispatch":
|
|
268
|
+
await _assertClassBrand(_Scheduler_brand, this, _executeRouted).call(this, message.id, message.fn, message.job, message.retry);
|
|
269
|
+
return true;
|
|
270
|
+
default: throw new Error("Unknown routed Scheduler message");
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
/** Set a delayed, dated, or cron schedule for a registered callback. */
|
|
274
|
+
async set(when, callback, payload, options) {
|
|
275
|
+
await this.lifecycle.ready();
|
|
276
|
+
_assertClassBrand(_Scheduler_brand, this, _validateSchedule).call(this, when, callback, options);
|
|
277
|
+
const result = this.lifecycle.routes.source ? await this.lifecycle.routes.toRoot({
|
|
278
|
+
type: "schedule",
|
|
279
|
+
when,
|
|
280
|
+
callback,
|
|
281
|
+
payload,
|
|
282
|
+
options
|
|
283
|
+
}) : await _assertClassBrand(_Scheduler_brand, this, _insert).call(this, null, parseWhen(when, Date.now(), callback), callback, payload, options);
|
|
284
|
+
_assertClassBrand(_Scheduler_brand, this, _emitCreated).call(this, result);
|
|
285
|
+
return result.schedule;
|
|
286
|
+
}
|
|
287
|
+
/** Set a fixed-interval schedule for a registered callback. */
|
|
288
|
+
async every(intervalSeconds, callback, payload, options) {
|
|
289
|
+
await this.lifecycle.ready();
|
|
290
|
+
_assertClassBrand(_Scheduler_brand, this, _validateInterval).call(this, intervalSeconds, callback, options?.retry);
|
|
291
|
+
const result = this.lifecycle.routes.source ? await this.lifecycle.routes.toRoot({
|
|
292
|
+
type: "every",
|
|
293
|
+
intervalSeconds,
|
|
294
|
+
callback,
|
|
295
|
+
payload,
|
|
296
|
+
options
|
|
297
|
+
}) : await _assertClassBrand(_Scheduler_brand, this, _insert).call(this, null, parseInterval(intervalSeconds, Date.now()), callback, payload, options);
|
|
298
|
+
_assertClassBrand(_Scheduler_brand, this, _emitCreated).call(this, result);
|
|
299
|
+
return result.schedule;
|
|
300
|
+
}
|
|
301
|
+
/** Get a schedule by ID. Works inside routed sub-agents. */
|
|
302
|
+
async get(id) {
|
|
303
|
+
await this.lifecycle.ready();
|
|
304
|
+
return this.lifecycle.routes.source ? await this.lifecycle.routes.toRoot({
|
|
305
|
+
type: "get",
|
|
306
|
+
id
|
|
307
|
+
}) : _assertClassBrand(_Scheduler_brand, this, _getForOwner).call(this, null, id);
|
|
308
|
+
}
|
|
309
|
+
/** List schedules matching criteria. Works inside routed sub-agents. */
|
|
310
|
+
async list(criteria = {}) {
|
|
311
|
+
await this.lifecycle.ready();
|
|
312
|
+
return this.lifecycle.routes.source ? await this.lifecycle.routes.toRoot({
|
|
313
|
+
type: "list",
|
|
314
|
+
criteria
|
|
315
|
+
}) : _assertClassBrand(_Scheduler_brand, this, _listForOwner).call(this, null, criteria);
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Cancel one schedule owned by this Scheduler.
|
|
319
|
+
*
|
|
320
|
+
* @param id - ID of the schedule to cancel.
|
|
321
|
+
* @returns True when a schedule was cancelled, false when none matched.
|
|
322
|
+
*/
|
|
323
|
+
async cancel(id) {
|
|
324
|
+
await this.lifecycle.ready();
|
|
325
|
+
const result = this.lifecycle.routes.source ? await this.lifecycle.routes.toRoot({
|
|
326
|
+
type: "cancel",
|
|
327
|
+
id
|
|
328
|
+
}) : await _assertClassBrand(_Scheduler_brand, this, _cancelForOwner).call(this, null, id);
|
|
329
|
+
if (result.ok && result.callback) _assertClassBrand(_Scheduler_brand, this, _emit).call(this, "schedule:cancel", {
|
|
330
|
+
callback: result.callback,
|
|
331
|
+
id
|
|
332
|
+
});
|
|
333
|
+
return result.ok;
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* @internal Synchronous read backing Agent's deprecated `getSchedule()`.
|
|
337
|
+
* Not part of the primitive's contract — use {@link get}. Cannot cross
|
|
338
|
+
* Durable Object boundaries and throws inside routed sub-agents.
|
|
339
|
+
*/
|
|
340
|
+
__DO_NOT_USE_WILL_REMOVE__getSchedule(id) {
|
|
341
|
+
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.");
|
|
342
|
+
return _assertClassBrand(_Scheduler_brand, this, _getForOwner).call(this, null, id);
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* @internal Synchronous read backing Agent's deprecated `getSchedules()`.
|
|
346
|
+
* Not part of the primitive's contract — use {@link list}. Cannot cross
|
|
347
|
+
* Durable Object boundaries and throws inside routed sub-agents.
|
|
348
|
+
*/
|
|
349
|
+
__DO_NOT_USE_WILL_REMOVE__getSchedules(criteria = {}) {
|
|
350
|
+
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.");
|
|
351
|
+
return _assertClassBrand(_Scheduler_brand, this, _listForOwner).call(this, null, criteria);
|
|
352
|
+
}
|
|
353
|
+
/** @internal Remove schedules owned by one routed Lifecycle subtree. */
|
|
354
|
+
async __DO_NOT_USE_WILL_BREAK__cleanupRoutePrefix(prefix) {
|
|
355
|
+
for (const { job, timing } of _assertClassBrand(_Scheduler_brand, this, _ownedJobs).call(this)) {
|
|
356
|
+
const ownerKey = timing.owner_path_key ?? timing.owner_path;
|
|
357
|
+
if (!timing.owner_path || ownerKey === null || ownerKey === void 0) continue;
|
|
358
|
+
if (ownerKey !== prefix && !ownerKey.startsWith(`${prefix}/`)) continue;
|
|
359
|
+
_assertClassBrand(_Scheduler_brand, this, _emit).call(this, "schedule:cancel", {
|
|
360
|
+
callback: job.fn,
|
|
361
|
+
id: job.id
|
|
362
|
+
});
|
|
363
|
+
await this.lifecycle.jobs.cancel(job.id);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
};
|
|
367
|
+
/**
|
|
368
|
+
* Move every `cf_agents_schedules` row into the job queue and drop the
|
|
369
|
+
* table. Idempotent: a missing table means a fresh object or a completed
|
|
370
|
+
* migration. Times convert from epoch seconds to epoch milliseconds.
|
|
371
|
+
*/
|
|
372
|
+
async function _migrateLegacyScheduleTable(storage) {
|
|
373
|
+
if (storage.sql.exec("SELECT name FROM sqlite_master WHERE type='table' AND name='cf_agents_schedules'").toArray().length === 0) return;
|
|
374
|
+
const rows = storage.sql.exec("SELECT * FROM cf_agents_schedules").toArray();
|
|
375
|
+
for (const row of rows) {
|
|
376
|
+
if (row.callback === "_cf_keepAliveHeartbeat") continue;
|
|
377
|
+
let payload;
|
|
378
|
+
try {
|
|
379
|
+
payload = typeof row.payload === "string" ? JSON.parse(row.payload) : void 0;
|
|
380
|
+
} catch (error) {
|
|
381
|
+
console.error(`Skipping schedule "${row.id}" during job-queue migration: its payload is not valid JSON`, error);
|
|
382
|
+
continue;
|
|
383
|
+
}
|
|
384
|
+
let retry;
|
|
385
|
+
try {
|
|
386
|
+
retry = typeof row.retry_options === "string" ? JSON.parse(row.retry_options) : void 0;
|
|
387
|
+
} catch {
|
|
388
|
+
retry = void 0;
|
|
389
|
+
}
|
|
390
|
+
await this.lifecycle.jobs.push({
|
|
391
|
+
id: row.id,
|
|
392
|
+
fn: row.callback,
|
|
393
|
+
time: row.time * 1e3,
|
|
394
|
+
payload: {
|
|
395
|
+
payload,
|
|
396
|
+
retry,
|
|
397
|
+
type: row.type,
|
|
398
|
+
delayInSeconds: row.delayInSeconds ?? void 0,
|
|
399
|
+
cron: row.cron ?? void 0,
|
|
400
|
+
intervalSeconds: row.intervalSeconds ?? void 0,
|
|
401
|
+
owner_path: row.owner_path ?? null,
|
|
402
|
+
owner_path_key: row.owner_path_key ?? null
|
|
403
|
+
},
|
|
404
|
+
retry: resolveRetryConfig(retry, _classPrivateFieldGet2(_retryDefaults, this)),
|
|
405
|
+
singleflight: row.type === "interval",
|
|
406
|
+
hungTimeoutSeconds: _classPrivateFieldGet2(_hungScheduleTimeoutSeconds, this),
|
|
407
|
+
recoveryLoop: LEGACY_RECOVERY_LOOP_CALLBACKS.has(row.callback)
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
storage.sql.exec("DROP TABLE cf_agents_schedules");
|
|
411
|
+
}
|
|
412
|
+
/** Advance a recurring schedule; complete a one-shot. */
|
|
413
|
+
function _recurrenceOutcome(timing) {
|
|
414
|
+
if (timing.type === "cron") return { rescheduleAt: nextCronTimeMs(timing.cron ?? "", Date.now()) };
|
|
415
|
+
if (timing.type === "interval") return { rescheduleAt: Date.now() + (timing.intervalSeconds ?? 0) * 1e3 };
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Execute a routed schedule locally with the historical retry handling.
|
|
419
|
+
* Runs on the owning (facet) Scheduler, outside the root's event loop, so
|
|
420
|
+
* it applies its own in-process retry budget. Platform-class failures on a
|
|
421
|
+
* one-shot re-throw so the root preserves the job and the durable alarm
|
|
422
|
+
* retries on a fresh invocation.
|
|
423
|
+
*/
|
|
424
|
+
async function _executeRouted(id, fn, timing, retry) {
|
|
425
|
+
const handler = _assertClassBrand(_Scheduler_brand, this, _resolveCallback).call(this, fn);
|
|
426
|
+
if (!handler) {
|
|
427
|
+
console.error(`callback ${fn} not found`);
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
const { maxAttempts, baseDelayMs, maxDelayMs } = resolveRetryConfig(retry, _classPrivateFieldGet2(_retryDefaults, this));
|
|
431
|
+
const isOneShot = timing.type === "delayed" || timing.type === "scheduled";
|
|
432
|
+
const schedule = _assertClassBrand(_Scheduler_brand, this, _payloadToSchedule).call(this, id, fn, timing);
|
|
433
|
+
try {
|
|
434
|
+
await tryN(maxAttempts, async (attempt) => {
|
|
435
|
+
if (attempt > 1) _assertClassBrand(_Scheduler_brand, this, _emit).call(this, "schedule:retry", {
|
|
436
|
+
callback: fn,
|
|
437
|
+
id,
|
|
438
|
+
attempt,
|
|
439
|
+
maxAttempts
|
|
440
|
+
});
|
|
441
|
+
await this.lifecycle.runInHostContext(() => handler(timing.payload, schedule));
|
|
442
|
+
}, {
|
|
443
|
+
baseDelayMs,
|
|
444
|
+
maxDelayMs,
|
|
445
|
+
shouldRetry: (error) => !(isOneShot && isDurableObjectCodeUpdateReset(error))
|
|
446
|
+
});
|
|
447
|
+
} catch (error) {
|
|
448
|
+
if (isOneShot && isPlatformFailure(error)) throw error;
|
|
449
|
+
console.error(`error executing callback "${fn}" after ${maxAttempts} attempts`, error);
|
|
450
|
+
_assertClassBrand(_Scheduler_brand, this, _emit).call(this, "schedule:error", {
|
|
451
|
+
callback: fn,
|
|
452
|
+
id,
|
|
453
|
+
error: error instanceof Error ? error.message : String(error),
|
|
454
|
+
attempts: maxAttempts
|
|
455
|
+
});
|
|
456
|
+
try {
|
|
457
|
+
await _classPrivateFieldGet2(_onError, this)?.call(this, error);
|
|
458
|
+
} catch {}
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
function _validateSchedule(when, callback, options) {
|
|
462
|
+
if (typeof callback !== "string") throw new Error("Callback must be a string");
|
|
463
|
+
if (!_assertClassBrand(_Scheduler_brand, this, _hasCallback).call(this, callback)) throw new Error(`Unknown scheduled callback "${callback}": not registered on this Scheduler`);
|
|
464
|
+
if (options?.retry) validateRetryOptions(options.retry, _classPrivateFieldGet2(_retryDefaults, this));
|
|
465
|
+
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}`);
|
|
466
|
+
_assertClassBrand(_Scheduler_brand, this, _warnWhenScheduledDuringStartup).call(this, when, callback, options);
|
|
467
|
+
}
|
|
468
|
+
function _validateInterval(intervalSeconds, callback, retry) {
|
|
469
|
+
validateIntervalSeconds(intervalSeconds);
|
|
470
|
+
if (typeof callback !== "string") throw new Error("Callback must be a string");
|
|
471
|
+
if (!_assertClassBrand(_Scheduler_brand, this, _hasCallback).call(this, callback)) throw new Error(`Unknown scheduled callback "${callback}": not registered on this Scheduler`);
|
|
472
|
+
if (retry) validateRetryOptions(retry, _classPrivateFieldGet2(_retryDefaults, this));
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* A non-idempotent one-shot created during startup accumulates one row per
|
|
476
|
+
* Durable Object wake, whether it came from the host's onStart or another
|
|
477
|
+
* startup hook. Warn once per callback; an explicit `idempotent` choice
|
|
478
|
+
* (either value) opts out.
|
|
479
|
+
*/
|
|
480
|
+
function _warnWhenScheduledDuringStartup(when, callback, options) {
|
|
481
|
+
if (!this.lifecycle.starting()) return;
|
|
482
|
+
if (options?.idempotent !== void 0) return;
|
|
483
|
+
if (typeof when === "string") return;
|
|
484
|
+
if (_classPrivateFieldGet2(_warnedStartupCallbacks, this).has(callback)) return;
|
|
485
|
+
_classPrivateFieldGet2(_warnedStartupCallbacks, this).add(callback);
|
|
486
|
+
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.`);
|
|
487
|
+
}
|
|
488
|
+
/** Resolve a name to its registered or composition-root-supplied handler. */
|
|
489
|
+
function _resolveCallback(name) {
|
|
490
|
+
const handler = _classPrivateFieldGet2(_handlers, this)[name];
|
|
491
|
+
if (handler) return handler;
|
|
492
|
+
return schedulerCallbackResolvers.get(this)?.(name);
|
|
493
|
+
}
|
|
494
|
+
/** True when a name resolves to a runnable callback. */
|
|
495
|
+
function _hasCallback(name) {
|
|
496
|
+
return _assertClassBrand(_Scheduler_brand, this, _resolveCallback).call(this, name) !== void 0;
|
|
497
|
+
}
|
|
498
|
+
/** Every schedule job this Scheduler owns, with its parsed vocabulary. */
|
|
499
|
+
function _ownedJobs() {
|
|
500
|
+
const owned = [];
|
|
501
|
+
for (const job of this.lifecycle.jobs.list()) if (isSchedulerJobPayload(job.payload)) owned.push({
|
|
502
|
+
job,
|
|
503
|
+
timing: job.payload
|
|
504
|
+
});
|
|
505
|
+
return owned;
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* Push a schedule job for the given owner, or return the existing job
|
|
509
|
+
* when an idempotent request matches one. One-shot timings deduplicate only
|
|
510
|
+
* when `idempotent: true` is passed; recurring timings deduplicate unless
|
|
511
|
+
* `idempotent: false` opts out. `created: false` marks a dedup hit so
|
|
512
|
+
* callers suppress the `schedule:create` event.
|
|
513
|
+
*/
|
|
514
|
+
async function _insert(owner, timing, callback, payload, options) {
|
|
515
|
+
const idempotent = isRecurring(timing) ? options?.idempotent !== false : Boolean(options?.idempotent);
|
|
516
|
+
const recoveryLoop = options?.recoveryLoop;
|
|
517
|
+
if (idempotent) {
|
|
518
|
+
const existing = _assertClassBrand(_Scheduler_brand, this, _findMatchingJob).call(this, owner?.key ?? null, timing, callback, JSON.stringify(payload));
|
|
519
|
+
if (existing) {
|
|
520
|
+
if (recoveryLoop && !existing.job.recoveryLoop) await this.lifecycle.jobs.push({
|
|
521
|
+
id: existing.job.id,
|
|
522
|
+
fn: existing.job.fn,
|
|
523
|
+
time: existing.job.time,
|
|
524
|
+
payload: existing.job.payload,
|
|
525
|
+
retry: existing.job.retry,
|
|
526
|
+
singleflight: existing.job.singleflight,
|
|
527
|
+
exclusive: existing.job.exclusive,
|
|
528
|
+
hungTimeoutSeconds: _classPrivateFieldGet2(_hungScheduleTimeoutSeconds, this),
|
|
529
|
+
recoveryLoop: true
|
|
530
|
+
});
|
|
531
|
+
await this.lifecycle.jobs.rearm();
|
|
532
|
+
return {
|
|
533
|
+
schedule: _assertClassBrand(_Scheduler_brand, this, _jobToSchedule).call(this, existing.job, existing.timing),
|
|
534
|
+
created: false
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
const jobPayload = {
|
|
539
|
+
payload,
|
|
540
|
+
retry: options?.retry,
|
|
541
|
+
type: timing.type,
|
|
542
|
+
delayInSeconds: timing.type === "delayed" ? timing.delayInSeconds : void 0,
|
|
543
|
+
cron: timing.type === "cron" ? timing.cron : void 0,
|
|
544
|
+
intervalSeconds: timing.type === "interval" ? timing.intervalSeconds : void 0,
|
|
545
|
+
owner_path: owner?.data ?? null,
|
|
546
|
+
owner_path_key: owner?.key ?? null
|
|
547
|
+
};
|
|
548
|
+
return {
|
|
549
|
+
schedule: {
|
|
550
|
+
id: (await this.lifecycle.jobs.push({
|
|
551
|
+
fn: callback,
|
|
552
|
+
time: timing.time * 1e3,
|
|
553
|
+
payload: jobPayload,
|
|
554
|
+
retry: resolveRetryConfig(options?.retry, _classPrivateFieldGet2(_retryDefaults, this)),
|
|
555
|
+
singleflight: timing.type === "interval",
|
|
556
|
+
hungTimeoutSeconds: _classPrivateFieldGet2(_hungScheduleTimeoutSeconds, this),
|
|
557
|
+
recoveryLoop
|
|
558
|
+
})).id,
|
|
559
|
+
callback,
|
|
560
|
+
payload,
|
|
561
|
+
retry: options?.retry,
|
|
562
|
+
...timing
|
|
563
|
+
},
|
|
564
|
+
created: true
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
/** Find the job an idempotent insert deduplicates onto, if any. */
|
|
568
|
+
function _findMatchingJob(ownerKey, timing, callback, payloadJson) {
|
|
569
|
+
for (const owned of _assertClassBrand(_Scheduler_brand, this, _ownedJobs).call(this)) {
|
|
570
|
+
const { job, timing: candidate } = owned;
|
|
571
|
+
if (candidate.type !== timing.type) continue;
|
|
572
|
+
if (job.fn !== callback) continue;
|
|
573
|
+
if ((candidate.owner_path_key ?? null) !== ownerKey) continue;
|
|
574
|
+
if (JSON.stringify(candidate.payload) !== payloadJson) continue;
|
|
575
|
+
if (timing.type === "cron" && candidate.cron !== timing.cron) continue;
|
|
576
|
+
if (timing.type === "interval" && candidate.intervalSeconds !== timing.intervalSeconds) continue;
|
|
577
|
+
return owned;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
function _jobToSchedule(job, timing) {
|
|
581
|
+
return _assertClassBrand(_Scheduler_brand, this, _payloadToSchedule).call(this, job.id, job.fn, timing, Math.floor(job.time / 1e3));
|
|
582
|
+
}
|
|
583
|
+
function _payloadToSchedule(id, fn, timing, timeSeconds) {
|
|
584
|
+
const base = {
|
|
585
|
+
callback: fn,
|
|
586
|
+
id,
|
|
587
|
+
payload: timing.payload,
|
|
588
|
+
retry: timing.retry
|
|
589
|
+
};
|
|
590
|
+
const time = timeSeconds ?? 0;
|
|
591
|
+
switch (timing.type) {
|
|
592
|
+
case "scheduled": return {
|
|
593
|
+
...base,
|
|
594
|
+
time,
|
|
595
|
+
type: "scheduled"
|
|
596
|
+
};
|
|
597
|
+
case "delayed": return {
|
|
598
|
+
...base,
|
|
599
|
+
delayInSeconds: timing.delayInSeconds ?? 0,
|
|
600
|
+
time,
|
|
601
|
+
type: "delayed"
|
|
602
|
+
};
|
|
603
|
+
case "cron": return {
|
|
604
|
+
...base,
|
|
605
|
+
cron: timing.cron ?? "",
|
|
606
|
+
time,
|
|
607
|
+
type: "cron"
|
|
608
|
+
};
|
|
609
|
+
case "interval": return {
|
|
610
|
+
...base,
|
|
611
|
+
intervalSeconds: timing.intervalSeconds ?? 0,
|
|
612
|
+
time,
|
|
613
|
+
type: "interval"
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
function _getForOwner(owner, id) {
|
|
618
|
+
const ownerKey = owner?.key ?? null;
|
|
619
|
+
const job = this.lifecycle.jobs.get(id);
|
|
620
|
+
if (!job || !isSchedulerJobPayload(job.payload)) return void 0;
|
|
621
|
+
if ((job.payload.owner_path_key ?? null) !== ownerKey) return void 0;
|
|
622
|
+
return _assertClassBrand(_Scheduler_brand, this, _jobToSchedule).call(this, job, job.payload);
|
|
623
|
+
}
|
|
624
|
+
function _listForOwner(owner, criteria = {}) {
|
|
625
|
+
const ownerKey = owner?.key ?? null;
|
|
626
|
+
const startSeconds = criteria.timeRange ? Math.floor((criteria.timeRange.start ?? /* @__PURE__ */ new Date(0)).getTime() / 1e3) : null;
|
|
627
|
+
const endSeconds = criteria.timeRange ? Math.floor((criteria.timeRange.end ?? /* @__PURE__ */ new Date(999999999999999)).getTime() / 1e3) : null;
|
|
628
|
+
const schedules = [];
|
|
629
|
+
for (const { job, timing } of _assertClassBrand(_Scheduler_brand, this, _ownedJobs).call(this)) {
|
|
630
|
+
if ((timing.owner_path_key ?? null) !== ownerKey) continue;
|
|
631
|
+
if (criteria.id && job.id !== criteria.id) continue;
|
|
632
|
+
if (criteria.type && timing.type !== criteria.type) continue;
|
|
633
|
+
const timeSeconds = Math.floor(job.time / 1e3);
|
|
634
|
+
if (startSeconds !== null && timeSeconds < startSeconds) continue;
|
|
635
|
+
if (endSeconds !== null && timeSeconds > endSeconds) continue;
|
|
636
|
+
schedules.push(_assertClassBrand(_Scheduler_brand, this, _jobToSchedule).call(this, job, timing));
|
|
637
|
+
}
|
|
638
|
+
return schedules;
|
|
639
|
+
}
|
|
640
|
+
async function _cancelForOwner(owner, id) {
|
|
641
|
+
const ownerKey = owner?.key ?? null;
|
|
642
|
+
const job = this.lifecycle.jobs.get(id);
|
|
643
|
+
if (!job || !isSchedulerJobPayload(job.payload)) return { ok: false };
|
|
644
|
+
if ((job.payload.owner_path_key ?? null) !== ownerKey) return { ok: false };
|
|
645
|
+
const callback = job.fn;
|
|
646
|
+
await this.lifecycle.jobs.cancel(id);
|
|
647
|
+
return {
|
|
648
|
+
ok: true,
|
|
649
|
+
callback
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
function _emit(type, payload) {
|
|
653
|
+
this.lifecycle.events.emit(type, payload);
|
|
654
|
+
}
|
|
655
|
+
function _emitCreated(result) {
|
|
656
|
+
if (!result.created) return;
|
|
657
|
+
_assertClassBrand(_Scheduler_brand, this, _emit).call(this, "schedule:create", {
|
|
658
|
+
callback: result.schedule.callback,
|
|
659
|
+
id: result.schedule.id
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
//#endregion
|
|
663
|
+
export { setSchedulerCallbackResolver as n, Scheduler as t };
|
|
664
|
+
|
|
665
|
+
//# sourceMappingURL=scheduler-DD9NdYbF.js.map
|