effect-cf 0.53.0 → 0.55.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.
@@ -12,26 +12,34 @@ import * as Exit from "effect/Exit";
12
12
  import * as Duration from "effect/Duration";
13
13
  import * as Clock from "effect/Clock";
14
14
  import * as DateTime from "effect/DateTime";
15
+ import * as Deferred from "effect/Deferred";
16
+ import * as Semaphore from "effect/Semaphore";
15
17
  //#region src/DurableObjectAlarm.ts
16
18
  var DurableObjectAlarm_exports = /* @__PURE__ */ __exportAll({
17
19
  AlarmParked: () => AlarmParked,
18
20
  AlarmReporter: () => AlarmReporter,
19
21
  DEFAULT_MIN_REPEAT_INTERVAL_MS: () => DEFAULT_MIN_REPEAT_INTERVAL_MS,
22
+ DispatchAfterEvent: () => DispatchAfterEvent,
20
23
  DurableObjectAlarm: () => DurableObjectAlarm,
21
24
  DurableObjectAlarmEvent: () => DurableObjectAlarmEvent,
25
+ HasDueAlarms: () => HasDueAlarms,
22
26
  InvalidAlarmPayloadError: () => InvalidAlarmPayloadError,
23
27
  InvalidAlarmRefError: () => InvalidAlarmRefError,
28
+ InvalidAlarmRegistrationError: () => InvalidAlarmRegistrationError,
24
29
  InvalidProcessDueAlarmsOptionsError: () => InvalidProcessDueAlarmsOptionsError,
25
30
  InvalidRepeatEveryError: () => InvalidRepeatEveryError,
26
31
  InvalidScheduleConfigurationError: () => InvalidScheduleConfigurationError,
27
32
  MIN_REPEAT_INTERVAL_MS: () => MIN_REPEAT_INTERVAL_MS,
28
33
  MIN_RETRY_DELAY_MS: () => MIN_RETRY_DELAY_MS,
29
34
  PARKED_RETRY_DELAY_MS: () => PARKED_RETRY_DELAY_MS,
35
+ RunAlarm: () => RunAlarm,
30
36
  ScheduleConfiguration: () => ScheduleConfiguration,
31
37
  StoredAlarmDecodeError: () => StoredAlarmDecodeError,
32
38
  Tag: () => Tag,
33
39
  UNCHANGED_ATTEMPT_BUDGET: () => 8,
34
40
  define: () => define,
41
+ dispatchRawAlarm: () => dispatchRawAlarm,
42
+ mergeAll: () => mergeAll,
35
43
  processDue: () => processDue
36
44
  });
37
45
  const INIT_TABLE_SQL = `
@@ -39,6 +47,7 @@ CREATE TABLE IF NOT EXISTS effect_cf_scheduled_alarms (
39
47
  storage_id TEXT PRIMARY KEY,
40
48
  alarm_id TEXT NOT NULL,
41
49
  tag TEXT NOT NULL,
50
+ lifecycle TEXT NOT NULL DEFAULT 'automatic',
42
51
  run_at INTEGER NOT NULL,
43
52
  wake_at INTEGER NOT NULL DEFAULT 0,
44
53
  repeat_every_ms INTEGER,
@@ -54,10 +63,12 @@ CREATE TABLE IF NOT EXISTS effect_cf_alarm_attempts (
54
63
  );
55
64
  `;
56
65
  const WAKE_INDEX = "idx_effect_cf_scheduled_alarms_wake_at_storage_id";
66
+ const LIFECYCLE_INDEX = "idx_effect_cf_scheduled_alarms_lifecycle_wake_at_storage_id";
57
67
  const DEFAULT_PROCESS_DUE_ALARMS_LIMIT = 100;
58
68
  const MIN_RETRY_DELAY_MS = 1e3;
59
69
  /** Minimum and default parked recovery interval. */
60
70
  const PARKED_RETRY_DELAY_MS = 36e5;
71
+ const DEFAULT_IN_FLIGHT_RECOVERY_MS = 1e3;
61
72
  /** Absolute product repeat floor. */
62
73
  const MIN_REPEAT_INTERVAL_MS = 1e3;
63
74
  /** Default product schedule floor. Never use repeats to poll state. */
@@ -65,7 +76,10 @@ const DEFAULT_MIN_REPEAT_INTERVAL_MS = 6e4;
65
76
  /** Optional policy overrides. Provide with Layer.succeed in the Durable Object's application layer. */
66
77
  const ScheduleConfiguration = Context.Reference("effect-cf/DurableObjectAlarm/ScheduleConfiguration", { defaultValue: () => ({}) });
67
78
  const safeIntegerAtLeast = (minimum) => S.Int.check(S.isGreaterThanOrEqualTo(minimum), S.isLessThanOrEqualTo(Number.MAX_SAFE_INTEGER));
79
+ const AlarmLifecycleSchema = S.Literals(["automatic", "manual"]);
68
80
  const ScheduleConfigurationSchema = S.Struct({
81
+ dispatchAfterEvent: S.Boolean,
82
+ inFlightRecovery: safeIntegerAtLeast(1),
69
83
  minimumRetryDelay: safeIntegerAtLeast(MIN_RETRY_DELAY_MS),
70
84
  unchangedAttemptBudget: safeIntegerAtLeast(1),
71
85
  parkedRetryDelay: safeIntegerAtLeast(PARKED_RETRY_DELAY_MS),
@@ -89,6 +103,9 @@ const AlarmParked = S.TaggedStruct("AlarmParked", {
89
103
  /** Install with Layer.succeed / Effect.provideService. Reporting cannot undo a committed guard. */
90
104
  const AlarmReporter = Context.Reference("effect-cf/DurableObjectAlarm/AlarmReporter", { defaultValue: () => (event) => Effect.logWarning(event) });
91
105
  const CurrentAlarmPass = Context.Reference("effect-cf/DurableObjectAlarm/CurrentAlarmPass", { defaultValue: () => void 0 });
106
+ /** One dispatcher's view of a registration pass: its tags, shared manual permits, raw tracking. */
107
+ const CurrentDispatch = Context.Reference("effect-cf/DurableObjectAlarm/CurrentDispatch", { defaultValue: () => void 0 });
108
+ const ManualTags = Symbol("effect-cf/DurableObjectAlarm/ManualTags");
92
109
  var InvalidAlarmRefError = class extends Data.TaggedError("InvalidAlarmRefError") {
93
110
  get message() {
94
111
  return `Invalid Durable Object alarm ref: ${causeMessage(this.cause)}`;
@@ -114,6 +131,11 @@ var InvalidScheduleConfigurationError = class extends Data.TaggedError("InvalidS
114
131
  return `Invalid Durable Object alarm configuration: ${causeMessage(this.cause)}`;
115
132
  }
116
133
  };
134
+ var InvalidAlarmRegistrationError = class extends Data.TaggedError("InvalidAlarmRegistrationError") {
135
+ get message() {
136
+ return `Invalid Durable Object alarm registration: ${causeMessage(this.cause)}`;
137
+ }
138
+ };
117
139
  const getScheduleConfiguration = Effect.fnUntraced(function* (defaults) {
118
140
  const input = {
119
141
  ...defaults,
@@ -121,6 +143,8 @@ const getScheduleConfiguration = Effect.fnUntraced(function* (defaults) {
121
143
  };
122
144
  return yield* Effect.try({
123
145
  try: () => S.decodeUnknownSync(ScheduleConfigurationSchema)({
146
+ dispatchAfterEvent: input.dispatchAfterEvent ?? false,
147
+ inFlightRecovery: Math.ceil(Duration.toMillis(input.inFlightRecovery ?? DEFAULT_IN_FLIGHT_RECOVERY_MS)),
124
148
  minimumRetryDelay: Math.ceil(Duration.toMillis(input.minimumRetryDelay ?? 1e3)),
125
149
  unchangedAttemptBudget: input.unchangedAttemptBudget ?? 8,
126
150
  parkedRetryDelay: Math.ceil(Duration.toMillis(input.parkedRetryDelay ?? 36e5)),
@@ -146,7 +170,14 @@ const AlarmRefSchema = S.Struct({
146
170
  tag: S.NonEmptyString
147
171
  });
148
172
  const decodeAlarmRef = (input) => S.decodeUnknownEffect(AlarmRefSchema)(input).pipe(Effect.mapError((cause) => new InvalidAlarmRefError({ cause })));
173
+ /** @internal */
174
+ const RunAlarm = Symbol("effect-cf/DurableObjectAlarm/RunAlarm");
175
+ /** @internal */
176
+ const DispatchAfterEvent = Symbol("effect-cf/DurableObjectAlarm/DispatchAfterEvent");
177
+ /** @internal */
178
+ const HasDueAlarms = Symbol("effect-cf/DurableObjectAlarm/HasDueAlarms");
149
179
  const StoredPayloadString = S.fromJsonString(S.Json);
180
+ const StoredTags = S.fromJsonString(S.Array(S.String));
150
181
  const decodeStoredPayload = (row) => S.decodeUnknownEffect(StoredPayloadString)(row.payload).pipe(Effect.mapError((cause) => new StoredAlarmDecodeError({
151
182
  cause,
152
183
  storageId: row.storage_id
@@ -154,15 +185,19 @@ const decodeStoredPayload = (row) => S.decodeUnknownEffect(StoredPayloadString)(
154
185
  const encodeStoredPayload = (payload) => S.encodeEffect(StoredPayloadString)(payload).pipe(Effect.mapError((cause) => new InvalidAlarmPayloadError({ cause })));
155
186
  const ensureTable = Effect.fnUntraced(function* (state) {
156
187
  yield* state.storage.sql.exec(INIT_TABLE_SQL);
157
- if ((yield* (yield* state.storage.sql.exec("SELECT name FROM sqlite_master WHERE type = 'index' AND name = ?", WAKE_INDEX)).toArray()).length > 0) return;
188
+ if ((yield* (yield* state.storage.sql.exec("SELECT name FROM sqlite_master WHERE type = 'index' AND name = ?", LIFECYCLE_INDEX)).toArray()).length > 0) return;
158
189
  const columns = yield* state.storage.sql.exec("SELECT name FROM pragma_table_info('effect_cf_scheduled_alarms')");
159
- if (!new Set((yield* columns.toArray()).map((column) => column.name)).has("wake_at")) yield* state.storage.sql.exec("ALTER TABLE effect_cf_scheduled_alarms ADD COLUMN wake_at INTEGER NOT NULL DEFAULT 0");
190
+ const names = new Set((yield* columns.toArray()).map((column) => column.name));
191
+ if (!names.has("lifecycle")) yield* state.storage.sql.exec("ALTER TABLE effect_cf_scheduled_alarms ADD COLUMN lifecycle TEXT NOT NULL DEFAULT 'automatic'");
192
+ if (!names.has("wake_at")) yield* state.storage.sql.exec("ALTER TABLE effect_cf_scheduled_alarms ADD COLUMN wake_at INTEGER NOT NULL DEFAULT 0");
160
193
  yield* state.storage.sql.exec(`UPDATE effect_cf_scheduled_alarms SET
161
194
  wake_at = MAX(run_at, COALESCE((SELECT retry_at FROM effect_cf_alarm_attempts s
162
195
  WHERE s.storage_id = effect_cf_scheduled_alarms.storage_id), run_at))`);
163
196
  yield* state.storage.sql.exec("DROP INDEX IF EXISTS idx_effect_cf_scheduled_alarms_run_at_storage_id");
164
197
  yield* state.storage.sql.exec(`CREATE INDEX IF NOT EXISTS ${WAKE_INDEX}
165
198
  ON effect_cf_scheduled_alarms (wake_at, storage_id)`);
199
+ yield* state.storage.sql.exec(`CREATE INDEX IF NOT EXISTS ${LIFECYCLE_INDEX}
200
+ ON effect_cf_scheduled_alarms (lifecycle, wake_at, storage_id)`);
166
201
  });
167
202
  const toRepeatEveryMillis = (input, minimum) => {
168
203
  if (input === void 0) return Effect.succeed(null);
@@ -208,6 +243,71 @@ const processDue = (handle, options = {}) => Effect.gen(function* () {
208
243
  return yield* (yield* DurableObjectAlarm).processDueAlarms(handle, options);
209
244
  });
210
245
  const TypedAlarmSchedulerTypeId = Symbol.for("effect-cf/DurableObjectAlarm/TypedScheduler");
246
+ const RegistrationParts = Symbol("effect-cf/DurableObjectAlarm/RegistrationParts");
247
+ const emptyResult = {
248
+ failed: [],
249
+ handled: [],
250
+ parked: []
251
+ };
252
+ const missingAlarm = (tag) => new InvalidAlarmRegistrationError({ cause: /* @__PURE__ */ new Error(`No manual handler registered for alarm tag "${tag}"`) });
253
+ const validateRegistration = (parts) => Effect.try({
254
+ try: () => {
255
+ if (parts.invalid !== void 0) throw new Error(parts.invalid);
256
+ const tags = parts.dispatchers.flatMap((dispatcher) => dispatcher.tags);
257
+ S.decodeUnknownSync(S.Array(S.NonEmptyString))([...parts.services, ...tags]);
258
+ if (new Set(parts.services).size !== parts.services.length || new Set(tags).size !== tags.length) throw new Error("Alarm service keys and definition tags must be unique within a Durable Object");
259
+ },
260
+ catch: (cause) => new InvalidAlarmRegistrationError({ cause })
261
+ });
262
+ const runRegistrations = (parts, rawAlarm) => Effect.gen(function* () {
263
+ yield* validateRegistration(parts);
264
+ const alarms = yield* DurableObjectAlarm;
265
+ const permits = yield* Semaphore.make(4);
266
+ const parentRaw = (yield* CurrentDispatch)?.raw;
267
+ const recoveryScope = {
268
+ tags: parts.dispatchers.flatMap((dispatcher) => dispatcher.tags),
269
+ exclude: true,
270
+ permits,
271
+ raw: parentRaw
272
+ };
273
+ const recovery = rawAlarm === void 0 ? makeDefinition({}).handlers({}).pipe(Effect.provideService(CurrentDispatch, recoveryScope)) : Effect.suspend(() => {
274
+ const raw = { processed: false };
275
+ return rawAlarm.pipe(Effect.onExit(() => raw.processed ? Effect.void : makeDefinition({}).handlers({}).pipe(Effect.asVoid)), Effect.provideService(CurrentDispatch, {
276
+ ...recoveryScope,
277
+ raw
278
+ }), Effect.as(emptyResult));
279
+ });
280
+ return yield* alarms.deferWakes(Effect.gen(function* () {
281
+ const run = (dispatchers) => Effect.forEach(dispatchers, (dispatcher) => dispatcher.run.pipe(Effect.catchTag("InvalidProcessDueAlarmsOptionsError", (error) => Effect.fail(error).pipe(Effect.onExit(() => makeDefinition({}).handlers({}).pipe(Effect.asVoid)))), Effect.provideService(CurrentDispatch, {
282
+ tags: dispatcher.tags,
283
+ exclude: false,
284
+ permits,
285
+ raw: parentRaw
286
+ }), Effect.exit), { concurrency: "unbounded" });
287
+ const automatic = parts.dispatchers.filter((dispatcher) => rawAlarm !== void 0 && dispatcher.automatic);
288
+ const independent = parts.dispatchers.filter((dispatcher) => rawAlarm === void 0 || !dispatcher.automatic);
289
+ const exits = (yield* Effect.all([run(independent), run(automatic).pipe(Effect.flatMap((exits) => Effect.map(Effect.exit(recovery), (exit) => [...exits, exit])))], { concurrency: "unbounded" })).flat();
290
+ yield* Exit.asVoidAll(exits);
291
+ return {
292
+ failed: exits.flatMap((exit) => Exit.isSuccess(exit) ? exit.value.failed : []),
293
+ handled: exits.flatMap((exit) => Exit.isSuccess(exit) ? exit.value.handled : []),
294
+ parked: exits.flatMap((exit) => Exit.isSuccess(exit) ? exit.value.parked : [])
295
+ };
296
+ }));
297
+ });
298
+ const makeRegistration = (layer, parts) => ({
299
+ [RegistrationParts]: parts,
300
+ layer: layer.pipe(Layer.provide(Layer.effectDiscard(validateRegistration(parts)))),
301
+ run: runRegistrations(parts)
302
+ });
303
+ function mergeAll(registration, ...others) {
304
+ const parts = [registration, ...others].map((item) => item[RegistrationParts]);
305
+ return makeRegistration(Layer.mergeAll(registration.layer, ...others.map((item) => item.layer)), {
306
+ services: parts.flatMap((part) => part?.services ?? []),
307
+ dispatchers: parts.flatMap((part) => part?.dispatchers ?? []),
308
+ invalid: parts.some((part) => part === void 0) ? "mergeAll requires registrations returned by Tag.handlers" : parts.find((part) => part?.invalid !== void 0)?.invalid
309
+ });
310
+ }
211
311
  const isAlarmDefinitionConfig = (definition) => Predicate.isObject(definition) && "payload" in definition;
212
312
  const getAlarmDefinitionSchema = (definition) => isAlarmDefinitionConfig(definition) ? definition.payload : definition;
213
313
  const getAlarmDefinitionFailureAction = (definition) => {
@@ -227,7 +327,8 @@ const makeDefinition = (definitions) => {
227
327
  const payload = yield* S.encodeEffect(getAlarmDefinitionSchema(definition))(input.payload).pipe(Effect.flatMap(S.decodeUnknownEffect(S.Json)), Effect.mapError((cause) => new InvalidAlarmPayloadError({ cause })));
228
328
  yield* mutations[method]({
229
329
  ...input,
230
- payload
330
+ payload,
331
+ lifecycle: isAlarmDefinitionConfig(definition) ? definition.lifecycle : void 0
231
332
  });
232
333
  });
233
334
  return {
@@ -243,6 +344,7 @@ const makeDefinition = (definitions) => {
243
344
  make: (alarms) => ({
244
345
  [TypedAlarmSchedulerTypeId]: TypedAlarmSchedulerTypeId,
245
346
  ...bind(alarms),
347
+ deferWakes: alarms.deferWakes,
246
348
  getAlarmStatus: (input) => alarms.getAlarmStatus(input),
247
349
  transaction: (closure) => alarms.transaction((tx) => closure(bind(tx)))
248
350
  }),
@@ -264,6 +366,7 @@ const makeDefinition = (definitions) => {
264
366
  });
265
367
  }), {
266
368
  ...options,
369
+ [ManualTags]: new Set(Object.entries(definitions).filter(([, definition]) => isAlarmDefinitionConfig(definition) && definition.lifecycle === "manual").map(([tag]) => tag)),
267
370
  onFailure: (failure) => Effect.gen(function* () {
268
371
  const action = getAlarmDefinitionFailureAction(definitionFor(failure.tag));
269
372
  const optionAction = options?.onFailure === void 0 ? void 0 : yield* options.onFailure(failure);
@@ -274,32 +377,70 @@ const makeDefinition = (definitions) => {
274
377
  };
275
378
  /** Handler-only definitions for applications using the raw scheduler. Prefer Tag for typed scheduling. */
276
379
  const define = (definitions) => ({ handlers: makeDefinition(definitions).handlers });
380
+ const hasSchedulerTables = Effect.fnUntraced(function* (state) {
381
+ return yield* Effect.gen(function* () {
382
+ if (state.raw.storage.sql === void 0) return false;
383
+ return (yield* (yield* state.storage.sql.exec("SELECT name FROM sqlite_master WHERE type = 'table' AND name = ? LIMIT 1", "effect_cf_scheduled_alarms")).toArray()).length > 0;
384
+ }).pipe(Effect.catchIf((error) => error.cause instanceof Error && (error.cause.message.startsWith("SQL is not enabled for this Durable Object class") || error.cause.message.startsWith("This Durable Object is not backed by SQLite storage")), () => Effect.succeed(false)));
385
+ });
386
+ /** @internal Preserve custom dispatch while recovering retained alarms after deployments. */
387
+ const dispatchRawAlarm = (rawAlarm, registration) => Effect.gen(function* () {
388
+ const parts = registration?.[RegistrationParts];
389
+ if (parts !== void 0) return yield* runRegistrations(parts, rawAlarm).pipe(Effect.asVoid);
390
+ const custom = registration === void 0 ? rawAlarm : registration.run.pipe(Effect.andThen(rawAlarm));
391
+ const state = yield* DurableObjectState;
392
+ if (!(yield* hasSchedulerTables(state))) return yield* custom;
393
+ return yield* runRegistrations({
394
+ services: [],
395
+ dispatchers: []
396
+ }, custom).pipe(Effect.asVoid);
397
+ });
277
398
  /** The typed scheduler becomes available when its handlers are registered on a Durable Object. */
278
399
  const Tag = () => (id, definitions) => {
279
400
  const tag = Context.Service()(id);
280
401
  const definition = makeDefinition(definitions);
281
- return Object.assign(tag, { handlers: (handlers, options) => ({
282
- layer: Layer.effect(tag, Effect.map(DurableObjectAlarm, definition.make)),
283
- run: definition.handlers(handlers, options)
402
+ return Object.assign(tag, { handlers: (handlers, options) => makeRegistration(Layer.effect(tag, Effect.map(DurableObjectAlarm, definition.make)), {
403
+ services: [id],
404
+ dispatchers: [{
405
+ tags: Object.keys(definitions),
406
+ automatic: Object.values(definitions).some((entry) => !isAlarmDefinitionConfig(entry) || entry.lifecycle !== "manual"),
407
+ run: definition.handlers(handlers, options)
408
+ }]
284
409
  }) });
285
410
  };
286
411
  var DurableObjectAlarm = class DurableObjectAlarm extends Context.Service()("effect-cf/DurableObjectAlarm") {
287
412
  static layer = Layer.effect(DurableObjectAlarm, Effect.gen(function* () {
288
413
  const state = yield* DurableObjectState;
289
414
  const configurationDefaults = { ...yield* ScheduleConfiguration };
415
+ let wakeDeferrals = 0;
416
+ let wakesReady;
417
+ let pendingAlarm;
418
+ let dispatchActive = false;
419
+ let dispatchNeedsReconcile = false;
420
+ let deferredRecoveryDelay = PARKED_RETRY_DELAY_MS;
421
+ let deferredRecoveryAt = 0;
290
422
  const readRow = Effect.fnUntraced(function* (storageId) {
291
423
  return (yield* (yield* state.storage.sql.exec(`${alarmRowsSql} WHERE a.storage_id = ?`, storageId)).toArray())[0];
292
424
  });
425
+ const writeNativeAlarm = (runAt) => Effect.tryPromise({
426
+ try: () => runAt === null ? state.raw.storage.deleteAlarm() : state.raw.storage.setAlarm(runAt),
427
+ catch: (cause) => new StorageOperationError({
428
+ operation: runAt === null ? "deleteAlarm" : "setAlarm",
429
+ cause
430
+ })
431
+ });
293
432
  const reconcileAlarm = Effect.fn("DurableObjectAlarm.reconcileAlarm")(function* () {
433
+ if (wakeDeferrals > 0 || dispatchActive) {
434
+ if (dispatchActive) dispatchNeedsReconcile = true;
435
+ const current = yield* state.storage.getAlarm();
436
+ const now = yield* Clock.currentTimeMillis;
437
+ if (deferredRecoveryAt <= now) deferredRecoveryAt = now + deferredRecoveryDelay;
438
+ if (current === null || current <= now || current > deferredRecoveryAt) yield* writeNativeAlarm(deferredRecoveryAt);
439
+ return;
440
+ }
294
441
  const next = (yield* (yield* state.storage.sql.exec(`SELECT wake_at AS run_at FROM effect_cf_scheduled_alarms
295
- ORDER BY wake_at, storage_id LIMIT 1`)).toArray())[0];
296
- yield* Effect.tryPromise({
297
- try: () => next === void 0 ? state.raw.storage.deleteAlarm() : state.raw.storage.setAlarm(next.run_at),
298
- catch: (cause) => new StorageOperationError({
299
- operation: next === void 0 ? "deleteAlarm" : "setAlarm",
300
- cause
301
- })
302
- });
442
+ ORDER BY wake_at, storage_id LIMIT 1`)).toArray())[0];
443
+ yield* writeNativeAlarm(next?.run_at ?? null);
303
444
  });
304
445
  const writeAttempts = Effect.fnUntraced(function* (storageId, attempts, parked, retryAt, progress) {
305
446
  yield* state.storage.sql.exec(`INSERT OR REPLACE INTO effect_cf_alarm_attempts
@@ -318,17 +459,15 @@ var DurableObjectAlarm = class DurableObjectAlarm extends Context.Service()("eff
318
459
  const scheduleAlarm = Effect.fn("DurableObjectAlarm.scheduleAlarm")(function* (input, earlier, reports) {
319
460
  const configuration = yield* getScheduleConfiguration(configurationDefaults);
320
461
  const ref = yield* decodeAlarmRef(input);
462
+ const lifecycle = yield* S.decodeUnknownEffect(AlarmLifecycleSchema)(input.lifecycle ?? "automatic").pipe(Effect.mapError((cause) => new InvalidAlarmRefError({ cause })));
463
+ if (lifecycle === "manual" && (input.repeatEvery !== void 0 || input.progress !== void 0)) return yield* Effect.fail(new InvalidAlarmRefError({ cause: /* @__PURE__ */ new Error("Manual alarms own their repeats and progress budgets") }));
321
464
  const repeatEveryMillis = yield* toRepeatEveryMillis(input.repeatEvery, configuration.minimumRepeatInterval);
322
465
  const payload = yield* encodeStoredPayload(input.payload);
323
466
  const storageId = getScheduledEventId(ref);
324
467
  const existing = yield* readRow(storageId);
325
468
  const pass = yield* CurrentAlarmPass;
326
- const source = pass?.row;
469
+ const source = lifecycle === "automatic" && pass?.row.lifecycle === "automatic" ? pass.row : void 0;
327
470
  const progress = existing?.progress ?? source?.progress ?? -1;
328
- if (pass !== void 0 && !pass.active) return yield* Effect.fail(new StorageOperationError({
329
- operation: "alarm.schedule",
330
- cause: /* @__PURE__ */ new Error("Alarm handlers cannot schedule detached work after their pass ends")
331
- }));
332
471
  if (input.progress !== void 0) yield* S.decodeUnknownEffect(S.Natural)(input.progress).pipe(Effect.mapError((cause) => new InvalidAlarmRefError({ cause })));
333
472
  if (source === void 0 && input.progress !== void 0 && input.progress <= progress) return;
334
473
  const progressed = input.progress !== void 0 && input.progress > progress;
@@ -341,11 +480,11 @@ var DurableObjectAlarm = class DurableObjectAlarm extends Context.Service()("eff
341
480
  const keepEarlier = earlier && existing !== void 0 && existing.run_at <= runAt;
342
481
  const scheduledAt = keepEarlier ? existing.run_at : runAt;
343
482
  const scheduledPayload = keepEarlier ? existing.payload : payload;
344
- const scheduledRepeat = keepEarlier ? existing.repeat_every_ms : repeatEveryMillis;
345
- const nextProgress = Math.max(progress, input.progress ?? -1);
483
+ const scheduledRepeat = lifecycle === "manual" ? null : keepEarlier ? existing.repeat_every_ms : repeatEveryMillis;
484
+ const nextProgress = lifecycle === "manual" ? -1 : Math.max(progress, input.progress ?? -1);
346
485
  yield* state.storage.sql.exec(`INSERT OR REPLACE INTO effect_cf_scheduled_alarms
347
- (storage_id, alarm_id, tag, run_at, repeat_every_ms, payload)
348
- VALUES (?, ?, ?, ?, ?, ?)`, storageId, ref.id, ref.tag, scheduledAt, scheduledRepeat, scheduledPayload);
486
+ (storage_id, alarm_id, tag, lifecycle, run_at, repeat_every_ms, payload)
487
+ VALUES (?, ?, ?, ?, ?, ?, ?)`, storageId, ref.id, ref.tag, lifecycle, scheduledAt, scheduledRepeat, scheduledPayload);
349
488
  yield* writeAttempts(storageId, attempts, parked, retryAt, nextProgress);
350
489
  if (parked === 1 && retryAt !== null && (existing?.parked ?? source?.parked ?? 0) === 0) reports.push(storageId);
351
490
  });
@@ -365,10 +504,15 @@ var DurableObjectAlarm = class DurableObjectAlarm extends Context.Service()("eff
365
504
  }
366
505
  });
367
506
  const transaction = Effect.fnUntraced(function* (closure) {
507
+ const pass = yield* CurrentAlarmPass;
508
+ if (pass !== void 0 && !pass.active) return yield* Effect.fail(new StorageOperationError({
509
+ operation: "alarm.transaction",
510
+ cause: /* @__PURE__ */ new Error("Alarm handlers cannot mutate detached work after their pass ends")
511
+ }));
368
512
  const reports = [];
369
513
  const result = yield* state.storage.transaction(() => Effect.withFiber((owner) => {
370
514
  let active = true;
371
- const requireActive = (effect) => Effect.withFiber((fiber) => active && fiber === owner ? effect : Effect.fail(new StorageOperationError({
515
+ const requireActive = (effect) => Effect.withFiber((fiber) => active && fiber === owner && (pass === void 0 || pass.active) ? effect : Effect.fail(new StorageOperationError({
372
516
  operation: "alarm.transaction",
373
517
  cause: /* @__PURE__ */ new Error("Alarm mutations require their active transaction callback")
374
518
  })));
@@ -388,6 +532,60 @@ var DurableObjectAlarm = class DurableObjectAlarm extends Context.Service()("eff
388
532
  yield* reportParked(reports);
389
533
  return result;
390
534
  });
535
+ const deferWakes = (body) => Effect.acquireUseRelease(Effect.gen(function* () {
536
+ const configuration = yield* getScheduleConfiguration(configurationDefaults);
537
+ const now = yield* Clock.currentTimeMillis;
538
+ if (wakeDeferrals === 0) {
539
+ deferredRecoveryDelay = configuration.inFlightRecovery;
540
+ deferredRecoveryAt = now + deferredRecoveryDelay;
541
+ wakesReady ??= Deferred.makeUnsafe();
542
+ }
543
+ wakeDeferrals++;
544
+ }), () => transaction(() => Effect.void).pipe(Effect.andThen(body)), () => Effect.gen(function* () {
545
+ wakeDeferrals--;
546
+ if (wakeDeferrals === 0) yield* (dispatchActive ? Effect.void : transaction(() => Effect.void)).pipe(Effect.ensuring(Effect.sync(() => {
547
+ if (wakeDeferrals === 0 && wakesReady !== void 0) {
548
+ Deferred.doneUnsafe(wakesReady, Effect.void);
549
+ wakesReady = void 0;
550
+ }
551
+ })));
552
+ }));
553
+ const awaitWakes = Effect.suspend(() => wakesReady === void 0 ? Effect.void : Deferred.await(wakesReady).pipe(Effect.andThen(awaitWakes)));
554
+ const runAlarm = (event, native) => Effect.flatMap(hasSchedulerTables(state), (managed) => !managed ? native ? event(Effect.void) : Effect.void : Effect.uninterruptibleMask((restore) => Effect.suspend(() => {
555
+ const pending = pendingAlarm;
556
+ if (pending !== void 0) return restore((native ? transaction(() => Effect.void) : Effect.void).pipe(Effect.andThen(Deferred.await(pending))));
557
+ const completed = Deferred.makeUnsafe();
558
+ let dispatchStarted = false;
559
+ pendingAlarm = completed;
560
+ dispatchNeedsReconcile = native;
561
+ return restore(Effect.gen(function* () {
562
+ if (native && wakesReady !== void 0) yield* transaction(() => Effect.void);
563
+ yield* awaitWakes;
564
+ dispatchActive = true;
565
+ yield* event(Effect.gen(function* () {
566
+ yield* getScheduleConfiguration(configurationDefaults);
567
+ dispatchStarted = true;
568
+ }));
569
+ })).pipe(Effect.onExit((exit) => Effect.gen(function* () {
570
+ const reconcile = dispatchNeedsReconcile || dispatchStarted || Exit.isFailure(exit);
571
+ dispatchActive = Exit.isFailure(exit) && !dispatchStarted;
572
+ if (dispatchActive) {
573
+ const recoveryDelay = yield* getScheduleConfiguration(configurationDefaults).pipe(Effect.match({
574
+ onFailure: () => PARKED_RETRY_DELAY_MS,
575
+ onSuccess: (configuration) => configuration.parkedRetryDelay
576
+ }));
577
+ if (wakeDeferrals === 0) {
578
+ deferredRecoveryDelay = recoveryDelay;
579
+ deferredRecoveryAt = 0;
580
+ }
581
+ }
582
+ const reconciled = yield* (reconcile ? transaction(() => Effect.void) : Effect.void).pipe(Effect.exit);
583
+ dispatchActive = false;
584
+ pendingAlarm = void 0;
585
+ yield* Deferred.done(completed, Exit.asVoidAll([exit, reconciled]));
586
+ yield* reconciled;
587
+ })));
588
+ })));
391
589
  const rescheduleFailedAlarm = Effect.fnUntraced(function* (row, initialDelay, configuration, reports) {
392
590
  const current = yield* readRow(row.storage_id);
393
591
  if (current === void 0 || current.revision !== row.revision) return;
@@ -411,8 +609,22 @@ var DurableObjectAlarm = class DurableObjectAlarm extends Context.Service()("eff
411
609
  const limit = yield* getProcessLimit(options);
412
610
  const initialDelay = yield* getFailureRetryDelay(options, configuration);
413
611
  const now = yield* Clock.currentTimeMillis;
414
- const dueRows = yield* (yield* state.storage.sql.exec(`${alarmRowsSql}
415
- WHERE a.wake_at <= ? ORDER BY a.wake_at ASC, a.storage_id ASC LIMIT ?`, now, limit)).toArray();
612
+ const dispatch = yield* CurrentDispatch;
613
+ if (dispatch?.raw !== void 0) dispatch.raw.processed = true;
614
+ const tagFilter = dispatch === void 0 ? "" : ` AND a.tag ${dispatch.exclude ? "NOT IN" : "IN"} (SELECT value FROM json_each(?))`;
615
+ const tagBindings = dispatch === void 0 ? [] : [S.encodeSync(StoredTags)([...dispatch.tags])];
616
+ const select = Effect.fnUntraced(function* (lifecycle, batchLimit) {
617
+ return yield* (yield* state.storage.sql.exec(`${alarmRowsSql} WHERE a.lifecycle = ? AND a.wake_at <= ?${tagFilter}
618
+ ORDER BY a.wake_at ASC, a.storage_id ASC LIMIT ?`, lifecycle, now, ...tagBindings, batchLimit)).toArray();
619
+ });
620
+ const dueRows = yield* select("automatic", limit);
621
+ const manualRows = yield* select("manual", DEFAULT_PROCESS_DUE_ALARMS_LIMIT);
622
+ if (manualRows.length > 0) yield* transaction(() => Effect.gen(function* () {
623
+ for (const row of manualRows) {
624
+ const recoveryAt = now + (options?.[ManualTags]?.has(row.tag) ? configuration.inFlightRecovery : configuration.parkedRetryDelay);
625
+ yield* state.storage.sql.exec(`UPDATE effect_cf_scheduled_alarms SET run_at = ?, wake_at = ? WHERE ${sameRevisionSql}`, recoveryAt, recoveryAt, row.storage_id, row.storage_id, row.revision);
626
+ }
627
+ }));
416
628
  const handled = [];
417
629
  const failed = [];
418
630
  const parked = [];
@@ -437,9 +649,9 @@ var DurableObjectAlarm = class DurableObjectAlarm extends Context.Service()("eff
437
649
  if (Exit.isFailure(actionExit)) return yield* Effect.failCause(actionExit.cause);
438
650
  if (Exit.isFailure(delayExit)) return yield* Effect.failCause(delayExit.cause);
439
651
  });
440
- for (const row of dueRows) {
652
+ const handleRow = Effect.fnUntraced(function* (row) {
441
653
  const current = yield* readRow(row.storage_id);
442
- if (current === void 0 || current.revision !== row.revision) continue;
654
+ if (current === void 0 || current.revision !== row.revision) return;
443
655
  const inPass = (effect) => {
444
656
  const pass = {
445
657
  row,
@@ -450,21 +662,30 @@ var DurableObjectAlarm = class DurableObjectAlarm extends Context.Service()("eff
450
662
  pass.active = false;
451
663
  })));
452
664
  };
665
+ const manual = row.lifecycle === "manual";
666
+ if (manual && !options?.[ManualTags]?.has(row.tag)) return yield* Effect.fail(missingAlarm(row.tag));
453
667
  const eventExit = yield* Effect.exit(toAlarmDue(row));
454
668
  if (Exit.isFailure(eventExit)) {
455
- yield* inPass(handleFailure(row, void 0, eventExit.cause));
456
- continue;
669
+ if (manual) return yield* Effect.failCause(eventExit.cause);
670
+ return yield* inPass(handleFailure(row, void 0, eventExit.cause));
457
671
  }
458
672
  const event = eventExit.value;
459
673
  const handleExit = yield* Effect.exit(inPass(Effect.suspend(() => handle(event))));
460
674
  if (Exit.isFailure(handleExit)) {
461
- yield* inPass(handleFailure(row, event, handleExit.cause));
462
- continue;
675
+ if (manual) return yield* Effect.failCause(handleExit.cause);
676
+ return yield* inPass(handleFailure(row, event, handleExit.cause));
463
677
  }
464
- yield* transaction(() => acknowledgeAlarm(row, configuration));
678
+ if (!manual) yield* transaction(() => acknowledgeAlarm(row, configuration));
465
679
  handled.push(event);
466
- }
467
- yield* transaction(() => Effect.void);
680
+ });
681
+ const permits = dispatch?.permits ?? (yield* Semaphore.make(4));
682
+ const automatic = Effect.forEach(dueRows, handleRow, { discard: true }).pipe(Effect.exit);
683
+ const exits = yield* manualRows.length === 0 ? automatic.pipe(Effect.map((exit) => [exit])) : Effect.all([automatic, Effect.forEach(manualRows, (row) => permits.withPermits(1)(handleRow(row)).pipe(Effect.exit), { concurrency: 4 }).pipe(Effect.flatMap(Exit.asVoidAll), Effect.exit)], { concurrency: 2 });
684
+ yield* transaction(() => Effect.gen(function* () {
685
+ const recoveryAt = (yield* Clock.currentTimeMillis) + configuration.parkedRetryDelay;
686
+ for (const row of manualRows) yield* state.storage.sql.exec(`UPDATE effect_cf_scheduled_alarms SET run_at = ?, wake_at = ? WHERE ${sameRevisionSql}`, recoveryAt, recoveryAt, row.storage_id, row.storage_id, row.revision);
687
+ }));
688
+ yield* Exit.asVoidAll(exits);
468
689
  return {
469
690
  failed,
470
691
  handled,
@@ -472,6 +693,14 @@ var DurableObjectAlarm = class DurableObjectAlarm extends Context.Service()("eff
472
693
  };
473
694
  });
474
695
  return DurableObjectAlarm.of({
696
+ [RunAlarm]: runAlarm,
697
+ [DispatchAfterEvent]: Effect.map(ScheduleConfiguration, (configuration) => (configuration.dispatchAfterEvent ?? configurationDefaults.dispatchAfterEvent) === true),
698
+ [HasDueAlarms]: Effect.gen(function* () {
699
+ yield* ensureTable(state);
700
+ const now = yield* Clock.currentTimeMillis;
701
+ return (yield* (yield* state.storage.sql.exec("SELECT 1 FROM effect_cf_scheduled_alarms WHERE wake_at <= ? LIMIT 1", now)).toArray()).length > 0;
702
+ }),
703
+ deferWakes,
475
704
  cancelAlarm: (input) => transaction((alarms) => alarms.cancelAlarm(input)),
476
705
  getAlarmStatus: Effect.fnUntraced(function* (input) {
477
706
  const ref = yield* decodeAlarmRef(input);
@@ -492,4 +721,4 @@ var DurableObjectAlarm = class DurableObjectAlarm extends Context.Service()("eff
492
721
  }));
493
722
  };
494
723
  //#endregion
495
- export { AlarmParked, AlarmReporter, DEFAULT_MIN_REPEAT_INTERVAL_MS, DurableObjectAlarm, DurableObjectAlarmEvent, DurableObjectAlarm_exports, InvalidAlarmPayloadError, InvalidAlarmRefError, InvalidProcessDueAlarmsOptionsError, InvalidRepeatEveryError, InvalidScheduleConfigurationError, MIN_REPEAT_INTERVAL_MS, MIN_RETRY_DELAY_MS, PARKED_RETRY_DELAY_MS, ScheduleConfiguration, StoredAlarmDecodeError, Tag, define, processDue };
724
+ export { AlarmParked, AlarmReporter, DEFAULT_MIN_REPEAT_INTERVAL_MS, DispatchAfterEvent, DurableObjectAlarm, DurableObjectAlarmEvent, DurableObjectAlarm_exports, HasDueAlarms, InvalidAlarmPayloadError, InvalidAlarmRefError, InvalidAlarmRegistrationError, InvalidProcessDueAlarmsOptionsError, InvalidRepeatEveryError, InvalidScheduleConfigurationError, MIN_REPEAT_INTERVAL_MS, MIN_RETRY_DELAY_MS, PARKED_RETRY_DELAY_MS, RunAlarm, ScheduleConfiguration, StoredAlarmDecodeError, Tag, define, dispatchRawAlarm, mergeAll, processDue };
@@ -0,0 +1,50 @@
1
+ import { WorkerEnvironment } from "./Environment.mjs";
2
+ import { BindingNotFoundError, BindingValidationError } from "./Binding.mjs";
3
+ import * as Context$1 from "effect/Context";
4
+ import * as Effect from "effect/Effect";
5
+ import { Layer } from "effect";
6
+ declare namespace RateLimit_d_exports {
7
+ export { BindingNotFoundError, BindingValidationError, LayerOptions, RateLimitBinding, RateLimitClient, RateLimitDefinition, RateLimitOperationError, RateLimitOptions, RateLimitOutcome, RateLimitService, Tag, TagClass, isRateLimit, layer, make, makeClient };
8
+ }
9
+ type RateLimitBinding = globalThis.RateLimit;
10
+ type RateLimitOptions = globalThis.RateLimitOptions;
11
+ type RateLimitOutcome = globalThis.RateLimitOutcome;
12
+ declare const RateLimitOperationError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
13
+ readonly _tag: "RateLimitOperationError";
14
+ } & Readonly<A>;
15
+ declare class RateLimitOperationError extends RateLimitOperationError_base<{
16
+ readonly binding: string;
17
+ readonly operation: "limit";
18
+ readonly cause: unknown;
19
+ }> {
20
+ get message(): string;
21
+ }
22
+ interface RateLimitDefinition {
23
+ readonly binding: string;
24
+ }
25
+ interface RateLimitClient {
26
+ /** Denial is a successful result with success: false. Calls are never retried. */
27
+ readonly limit: (options: RateLimitOptions) => Effect.Effect<RateLimitOutcome, RateLimitOperationError>;
28
+ readonly rawUnsafe: Effect.Effect<RateLimitBinding>;
29
+ readonly definition: RateLimitDefinition;
30
+ }
31
+ declare const RateLimitServiceTypeId: unique symbol;
32
+ interface RateLimitService<Id extends string> {
33
+ readonly [RateLimitServiceTypeId]: {
34
+ readonly id: Id;
35
+ };
36
+ }
37
+ type LayerOptions = {
38
+ readonly binding: string;
39
+ };
40
+ interface TagClass<Self, Id extends string> extends Context$1.ServiceClass<Self, Id, RateLimitClient> {
41
+ readonly id: Id;
42
+ readonly layer: (options: LayerOptions) => Layer.Layer<Self, BindingNotFoundError | BindingValidationError, WorkerEnvironment>;
43
+ }
44
+ declare const isRateLimit: <Candidate>(value: Candidate) => value is Candidate & RateLimitBinding;
45
+ declare const makeClient: (definition: RateLimitDefinition) => (rateLimit: RateLimitBinding) => RateLimitClient;
46
+ declare const layer: <Self>(tag: Context$1.Service<Self, RateLimitClient>, definition: RateLimitDefinition) => Layer.Layer<Self, BindingNotFoundError | BindingValidationError, WorkerEnvironment>;
47
+ declare const make: <Id extends string>(id: Id) => TagClass<RateLimitService<Id>, Id>;
48
+ declare const Tag: <Self>() => <Id extends string>(id: Id) => TagClass<Self, Id>;
49
+ //#endregion
50
+ export { type BindingNotFoundError, type BindingValidationError, LayerOptions, RateLimitBinding, RateLimitClient, RateLimitDefinition, RateLimitOperationError, RateLimitOptions, RateLimitOutcome, RateLimitService, RateLimit_d_exports, Tag, TagClass, isRateLimit, layer, make, makeClient };
@@ -0,0 +1,46 @@
1
+ import { __exportAll } from "./_virtual/_rolldown/runtime.mjs";
2
+ import { layer as layer$1 } from "./Binding.mjs";
3
+ import * as Context from "effect/Context";
4
+ import * as Data from "effect/Data";
5
+ import * as Effect from "effect/Effect";
6
+ import * as Predicate from "effect/Predicate";
7
+ //#region src/RateLimit.ts
8
+ var RateLimit_exports = /* @__PURE__ */ __exportAll({
9
+ RateLimitOperationError: () => RateLimitOperationError,
10
+ Tag: () => Tag,
11
+ isRateLimit: () => isRateLimit,
12
+ layer: () => layer,
13
+ make: () => make,
14
+ makeClient: () => makeClient
15
+ });
16
+ const expectedRateLimit = "Rate Limiting binding with limit()";
17
+ var RateLimitOperationError = class extends Data.TaggedError("RateLimitOperationError") {
18
+ get message() {
19
+ return `Cloudflare rate limit binding "${this.binding}" operation "limit" failed`;
20
+ }
21
+ };
22
+ const isRateLimit = (value) => Predicate.hasProperty(value, "limit") && Predicate.isFunction(value.limit);
23
+ const makeClient = (definition) => (rateLimit) => ({
24
+ definition,
25
+ limit: (options) => Effect.tryPromise({
26
+ try: () => rateLimit.limit(options),
27
+ catch: (cause) => new RateLimitOperationError({
28
+ binding: definition.binding,
29
+ operation: "limit",
30
+ cause
31
+ })
32
+ }),
33
+ rawUnsafe: Effect.succeed(rateLimit)
34
+ });
35
+ const layer = (tag, definition) => layer$1(tag, definition.binding, isRateLimit, makeClient(definition), { expected: expectedRateLimit });
36
+ const make = (id) => Tag()(id);
37
+ const Tag = () => (id) => {
38
+ const tag = Context.Service()(id);
39
+ const makeLayer = (definition) => layer(tag, definition);
40
+ return Object.assign(tag, {
41
+ id,
42
+ layer: makeLayer
43
+ });
44
+ };
45
+ //#endregion
46
+ export { RateLimitOperationError, RateLimit_exports, Tag, isRateLimit, layer, make, makeClient };
package/dist/index.d.mts CHANGED
@@ -36,10 +36,11 @@ import { DurableObjectSqlite_d_exports } from "./DurableObjectSqlite.mjs";
36
36
  import { Email_d_exports } from "./Email.mjs";
37
37
  import { Images_d_exports } from "./Images.mjs";
38
38
  import { Kv_d_exports } from "./Kv.mjs";
39
+ import { RateLimit_d_exports } from "./RateLimit.mjs";
39
40
  import { R2_d_exports } from "./R2.mjs";
40
41
  import { RpcTargets_d_exports } from "./RpcTargets.mjs";
41
42
  import { Vectorize_d_exports } from "./Vectorize.mjs";
42
43
  import { WebTransport_d_exports } from "./WebTransport.mjs";
43
44
  import { WorkflowDefinition_d_exports } from "./WorkflowDefinition.mjs";
44
45
  import { Workflow_d_exports } from "./Workflow.mjs";
45
- export { AiGateway_d_exports as AiGateway, AnalyticsEngine_d_exports as AnalyticsEngine, Artifacts_d_exports as Artifacts, Binding_d_exports as Binding, BrowserRendering_d_exports as BrowserRendering, Cache_d_exports as Cache, CloudflareOtlp_d_exports as CloudflareOtlp, CloudflareTracer_d_exports as CloudflareTracer, ContainerFiles_d_exports as ContainerFiles, ContainerNamespace_d_exports as ContainerNamespace, D1_d_exports as D1, DurableObject_d_exports as DurableObject, DurableObjectAlarm_d_exports as DurableObjectAlarm, DurableObjectContainer_d_exports as DurableObjectContainer, DurableObjectDefinition_d_exports as DurableObjectDefinition, DurableObjectNamespace_d_exports as DurableObjectNamespace, DurableObjectRpcWebSocket_d_exports as DurableObjectRpcWebSocket, DurableObjectSqlite_d_exports as DurableObjectSqlite, DurableObjectState_d_exports as DurableObjectState, DurableObjectStorage_d_exports as DurableObjectStorage, DurableObjectWebSocket_d_exports as DurableObjectWebSocket, Email_d_exports as Email, Environment_d_exports as Environment, Hyperdrive_d_exports as Hyperdrive, Images_d_exports as Images, Kv_d_exports as Kv, Queue_d_exports as Queue, QueueBinding_d_exports as QueueBinding, QueueDefinition_d_exports as QueueDefinition, R2_d_exports as R2, Rpc_d_exports as Rpc, RpcDefinition_d_exports as RpcDefinition, RpcTargets_d_exports as RpcTargets, RpcTracing_d_exports as RpcTracing, RpcWebSocketClient_d_exports as RpcWebSocketClient, ServiceBinding_d_exports as ServiceBinding, Vectorize_d_exports as Vectorize, WebTransport_d_exports as WebTransport, Worker_d_exports as Worker, WorkerConfig, WorkerDefinition_d_exports as WorkerDefinition, type WorkerEnv, WorkerEnvironment, WorkersAi_d_exports as WorkersAi, Workflow_d_exports as Workflow, WorkflowBinding_d_exports as WorkflowBinding, WorkflowDefinition_d_exports as WorkflowDefinition };
46
+ export { AiGateway_d_exports as AiGateway, AnalyticsEngine_d_exports as AnalyticsEngine, Artifacts_d_exports as Artifacts, Binding_d_exports as Binding, BrowserRendering_d_exports as BrowserRendering, Cache_d_exports as Cache, CloudflareOtlp_d_exports as CloudflareOtlp, CloudflareTracer_d_exports as CloudflareTracer, ContainerFiles_d_exports as ContainerFiles, ContainerNamespace_d_exports as ContainerNamespace, D1_d_exports as D1, DurableObject_d_exports as DurableObject, DurableObjectAlarm_d_exports as DurableObjectAlarm, DurableObjectContainer_d_exports as DurableObjectContainer, DurableObjectDefinition_d_exports as DurableObjectDefinition, DurableObjectNamespace_d_exports as DurableObjectNamespace, DurableObjectRpcWebSocket_d_exports as DurableObjectRpcWebSocket, DurableObjectSqlite_d_exports as DurableObjectSqlite, DurableObjectState_d_exports as DurableObjectState, DurableObjectStorage_d_exports as DurableObjectStorage, DurableObjectWebSocket_d_exports as DurableObjectWebSocket, Email_d_exports as Email, Environment_d_exports as Environment, Hyperdrive_d_exports as Hyperdrive, Images_d_exports as Images, Kv_d_exports as Kv, Queue_d_exports as Queue, QueueBinding_d_exports as QueueBinding, QueueDefinition_d_exports as QueueDefinition, R2_d_exports as R2, RateLimit_d_exports as RateLimit, Rpc_d_exports as Rpc, RpcDefinition_d_exports as RpcDefinition, RpcTargets_d_exports as RpcTargets, RpcTracing_d_exports as RpcTracing, RpcWebSocketClient_d_exports as RpcWebSocketClient, ServiceBinding_d_exports as ServiceBinding, Vectorize_d_exports as Vectorize, WebTransport_d_exports as WebTransport, Worker_d_exports as Worker, WorkerConfig, WorkerDefinition_d_exports as WorkerDefinition, type WorkerEnv, WorkerEnvironment, WorkersAi_d_exports as WorkersAi, Workflow_d_exports as Workflow, WorkflowBinding_d_exports as WorkflowBinding, WorkflowDefinition_d_exports as WorkflowDefinition };