effect-cf 0.53.0 → 0.54.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/CHANGELOG.md +10 -0
- package/README.md +6 -0
- package/dist/DurableObject.d.mts +1 -0
- package/dist/DurableObject.mjs +33 -11
- package/dist/DurableObjectAlarm.d.mts +74 -9
- package/dist/DurableObjectAlarm.mjs +245 -35
- package/dist/RateLimit.d.mts +50 -0
- package/dist/RateLimit.mjs +46 -0
- package/dist/index.d.mts +2 -1
- package/dist/index.mjs +2 -1
- package/dist/internal/Runtime.mjs +5 -3
- package/package.json +1 -1
- package/src/DurableObject.ts +93 -30
- package/src/DurableObjectAlarm.ts +601 -59
- package/src/RateLimit.ts +110 -0
- package/src/index.ts +1 -0
- package/src/internal/Entrypoint.ts +8 -4
- package/src/internal/Runtime.ts +12 -7
|
@@ -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,6 +63,7 @@ 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. */
|
|
@@ -65,7 +75,9 @@ const DEFAULT_MIN_REPEAT_INTERVAL_MS = 6e4;
|
|
|
65
75
|
/** Optional policy overrides. Provide with Layer.succeed in the Durable Object's application layer. */
|
|
66
76
|
const ScheduleConfiguration = Context.Reference("effect-cf/DurableObjectAlarm/ScheduleConfiguration", { defaultValue: () => ({}) });
|
|
67
77
|
const safeIntegerAtLeast = (minimum) => S.Int.check(S.isGreaterThanOrEqualTo(minimum), S.isLessThanOrEqualTo(Number.MAX_SAFE_INTEGER));
|
|
78
|
+
const AlarmLifecycleSchema = S.Literals(["automatic", "manual"]);
|
|
68
79
|
const ScheduleConfigurationSchema = S.Struct({
|
|
80
|
+
dispatchAfterEvent: S.Boolean,
|
|
69
81
|
minimumRetryDelay: safeIntegerAtLeast(MIN_RETRY_DELAY_MS),
|
|
70
82
|
unchangedAttemptBudget: safeIntegerAtLeast(1),
|
|
71
83
|
parkedRetryDelay: safeIntegerAtLeast(PARKED_RETRY_DELAY_MS),
|
|
@@ -89,6 +101,9 @@ const AlarmParked = S.TaggedStruct("AlarmParked", {
|
|
|
89
101
|
/** Install with Layer.succeed / Effect.provideService. Reporting cannot undo a committed guard. */
|
|
90
102
|
const AlarmReporter = Context.Reference("effect-cf/DurableObjectAlarm/AlarmReporter", { defaultValue: () => (event) => Effect.logWarning(event) });
|
|
91
103
|
const CurrentAlarmPass = Context.Reference("effect-cf/DurableObjectAlarm/CurrentAlarmPass", { defaultValue: () => void 0 });
|
|
104
|
+
/** One dispatcher's view of a registration pass: its tags, shared manual permits, raw tracking. */
|
|
105
|
+
const CurrentDispatch = Context.Reference("effect-cf/DurableObjectAlarm/CurrentDispatch", { defaultValue: () => void 0 });
|
|
106
|
+
const ManualTags = Symbol("effect-cf/DurableObjectAlarm/ManualTags");
|
|
92
107
|
var InvalidAlarmRefError = class extends Data.TaggedError("InvalidAlarmRefError") {
|
|
93
108
|
get message() {
|
|
94
109
|
return `Invalid Durable Object alarm ref: ${causeMessage(this.cause)}`;
|
|
@@ -114,6 +129,11 @@ var InvalidScheduleConfigurationError = class extends Data.TaggedError("InvalidS
|
|
|
114
129
|
return `Invalid Durable Object alarm configuration: ${causeMessage(this.cause)}`;
|
|
115
130
|
}
|
|
116
131
|
};
|
|
132
|
+
var InvalidAlarmRegistrationError = class extends Data.TaggedError("InvalidAlarmRegistrationError") {
|
|
133
|
+
get message() {
|
|
134
|
+
return `Invalid Durable Object alarm registration: ${causeMessage(this.cause)}`;
|
|
135
|
+
}
|
|
136
|
+
};
|
|
117
137
|
const getScheduleConfiguration = Effect.fnUntraced(function* (defaults) {
|
|
118
138
|
const input = {
|
|
119
139
|
...defaults,
|
|
@@ -121,6 +141,7 @@ const getScheduleConfiguration = Effect.fnUntraced(function* (defaults) {
|
|
|
121
141
|
};
|
|
122
142
|
return yield* Effect.try({
|
|
123
143
|
try: () => S.decodeUnknownSync(ScheduleConfigurationSchema)({
|
|
144
|
+
dispatchAfterEvent: input.dispatchAfterEvent ?? false,
|
|
124
145
|
minimumRetryDelay: Math.ceil(Duration.toMillis(input.minimumRetryDelay ?? 1e3)),
|
|
125
146
|
unchangedAttemptBudget: input.unchangedAttemptBudget ?? 8,
|
|
126
147
|
parkedRetryDelay: Math.ceil(Duration.toMillis(input.parkedRetryDelay ?? 36e5)),
|
|
@@ -146,7 +167,14 @@ const AlarmRefSchema = S.Struct({
|
|
|
146
167
|
tag: S.NonEmptyString
|
|
147
168
|
});
|
|
148
169
|
const decodeAlarmRef = (input) => S.decodeUnknownEffect(AlarmRefSchema)(input).pipe(Effect.mapError((cause) => new InvalidAlarmRefError({ cause })));
|
|
170
|
+
/** @internal */
|
|
171
|
+
const RunAlarm = Symbol("effect-cf/DurableObjectAlarm/RunAlarm");
|
|
172
|
+
/** @internal */
|
|
173
|
+
const DispatchAfterEvent = Symbol("effect-cf/DurableObjectAlarm/DispatchAfterEvent");
|
|
174
|
+
/** @internal */
|
|
175
|
+
const HasDueAlarms = Symbol("effect-cf/DurableObjectAlarm/HasDueAlarms");
|
|
149
176
|
const StoredPayloadString = S.fromJsonString(S.Json);
|
|
177
|
+
const StoredTags = S.fromJsonString(S.Array(S.String));
|
|
150
178
|
const decodeStoredPayload = (row) => S.decodeUnknownEffect(StoredPayloadString)(row.payload).pipe(Effect.mapError((cause) => new StoredAlarmDecodeError({
|
|
151
179
|
cause,
|
|
152
180
|
storageId: row.storage_id
|
|
@@ -154,15 +182,19 @@ const decodeStoredPayload = (row) => S.decodeUnknownEffect(StoredPayloadString)(
|
|
|
154
182
|
const encodeStoredPayload = (payload) => S.encodeEffect(StoredPayloadString)(payload).pipe(Effect.mapError((cause) => new InvalidAlarmPayloadError({ cause })));
|
|
155
183
|
const ensureTable = Effect.fnUntraced(function* (state) {
|
|
156
184
|
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 = ?",
|
|
185
|
+
if ((yield* (yield* state.storage.sql.exec("SELECT name FROM sqlite_master WHERE type = 'index' AND name = ?", LIFECYCLE_INDEX)).toArray()).length > 0) return;
|
|
158
186
|
const columns = yield* state.storage.sql.exec("SELECT name FROM pragma_table_info('effect_cf_scheduled_alarms')");
|
|
159
|
-
|
|
187
|
+
const names = new Set((yield* columns.toArray()).map((column) => column.name));
|
|
188
|
+
if (!names.has("lifecycle")) yield* state.storage.sql.exec("ALTER TABLE effect_cf_scheduled_alarms ADD COLUMN lifecycle TEXT NOT NULL DEFAULT 'automatic'");
|
|
189
|
+
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
190
|
yield* state.storage.sql.exec(`UPDATE effect_cf_scheduled_alarms SET
|
|
161
191
|
wake_at = MAX(run_at, COALESCE((SELECT retry_at FROM effect_cf_alarm_attempts s
|
|
162
192
|
WHERE s.storage_id = effect_cf_scheduled_alarms.storage_id), run_at))`);
|
|
163
193
|
yield* state.storage.sql.exec("DROP INDEX IF EXISTS idx_effect_cf_scheduled_alarms_run_at_storage_id");
|
|
164
194
|
yield* state.storage.sql.exec(`CREATE INDEX IF NOT EXISTS ${WAKE_INDEX}
|
|
165
195
|
ON effect_cf_scheduled_alarms (wake_at, storage_id)`);
|
|
196
|
+
yield* state.storage.sql.exec(`CREATE INDEX IF NOT EXISTS ${LIFECYCLE_INDEX}
|
|
197
|
+
ON effect_cf_scheduled_alarms (lifecycle, wake_at, storage_id)`);
|
|
166
198
|
});
|
|
167
199
|
const toRepeatEveryMillis = (input, minimum) => {
|
|
168
200
|
if (input === void 0) return Effect.succeed(null);
|
|
@@ -208,6 +240,71 @@ const processDue = (handle, options = {}) => Effect.gen(function* () {
|
|
|
208
240
|
return yield* (yield* DurableObjectAlarm).processDueAlarms(handle, options);
|
|
209
241
|
});
|
|
210
242
|
const TypedAlarmSchedulerTypeId = Symbol.for("effect-cf/DurableObjectAlarm/TypedScheduler");
|
|
243
|
+
const RegistrationParts = Symbol("effect-cf/DurableObjectAlarm/RegistrationParts");
|
|
244
|
+
const emptyResult = {
|
|
245
|
+
failed: [],
|
|
246
|
+
handled: [],
|
|
247
|
+
parked: []
|
|
248
|
+
};
|
|
249
|
+
const missingAlarm = (tag) => new InvalidAlarmRegistrationError({ cause: /* @__PURE__ */ new Error(`No manual handler registered for alarm tag "${tag}"`) });
|
|
250
|
+
const validateRegistration = (parts) => Effect.try({
|
|
251
|
+
try: () => {
|
|
252
|
+
if (parts.invalid !== void 0) throw new Error(parts.invalid);
|
|
253
|
+
const tags = parts.dispatchers.flatMap((dispatcher) => dispatcher.tags);
|
|
254
|
+
S.decodeUnknownSync(S.Array(S.NonEmptyString))([...parts.services, ...tags]);
|
|
255
|
+
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");
|
|
256
|
+
},
|
|
257
|
+
catch: (cause) => new InvalidAlarmRegistrationError({ cause })
|
|
258
|
+
});
|
|
259
|
+
const runRegistrations = (parts, rawAlarm) => Effect.gen(function* () {
|
|
260
|
+
yield* validateRegistration(parts);
|
|
261
|
+
const alarms = yield* DurableObjectAlarm;
|
|
262
|
+
const permits = yield* Semaphore.make(4);
|
|
263
|
+
const parentRaw = (yield* CurrentDispatch)?.raw;
|
|
264
|
+
const recoveryScope = {
|
|
265
|
+
tags: parts.dispatchers.flatMap((dispatcher) => dispatcher.tags),
|
|
266
|
+
exclude: true,
|
|
267
|
+
permits,
|
|
268
|
+
raw: parentRaw
|
|
269
|
+
};
|
|
270
|
+
const recovery = rawAlarm === void 0 ? makeDefinition({}).handlers({}).pipe(Effect.provideService(CurrentDispatch, recoveryScope)) : Effect.suspend(() => {
|
|
271
|
+
const raw = { processed: false };
|
|
272
|
+
return rawAlarm.pipe(Effect.onExit(() => raw.processed ? Effect.void : makeDefinition({}).handlers({}).pipe(Effect.asVoid)), Effect.provideService(CurrentDispatch, {
|
|
273
|
+
...recoveryScope,
|
|
274
|
+
raw
|
|
275
|
+
}), Effect.as(emptyResult));
|
|
276
|
+
});
|
|
277
|
+
return yield* alarms.deferWakes(Effect.gen(function* () {
|
|
278
|
+
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, {
|
|
279
|
+
tags: dispatcher.tags,
|
|
280
|
+
exclude: false,
|
|
281
|
+
permits,
|
|
282
|
+
raw: parentRaw
|
|
283
|
+
}), Effect.exit), { concurrency: "unbounded" });
|
|
284
|
+
const automatic = parts.dispatchers.filter((dispatcher) => rawAlarm !== void 0 && dispatcher.automatic);
|
|
285
|
+
const independent = parts.dispatchers.filter((dispatcher) => rawAlarm === void 0 || !dispatcher.automatic);
|
|
286
|
+
const exits = (yield* Effect.all([run(independent), run(automatic).pipe(Effect.flatMap((exits) => Effect.map(Effect.exit(recovery), (exit) => [...exits, exit])))], { concurrency: "unbounded" })).flat();
|
|
287
|
+
yield* Exit.asVoidAll(exits);
|
|
288
|
+
return {
|
|
289
|
+
failed: exits.flatMap((exit) => Exit.isSuccess(exit) ? exit.value.failed : []),
|
|
290
|
+
handled: exits.flatMap((exit) => Exit.isSuccess(exit) ? exit.value.handled : []),
|
|
291
|
+
parked: exits.flatMap((exit) => Exit.isSuccess(exit) ? exit.value.parked : [])
|
|
292
|
+
};
|
|
293
|
+
}));
|
|
294
|
+
});
|
|
295
|
+
const makeRegistration = (layer, parts) => ({
|
|
296
|
+
[RegistrationParts]: parts,
|
|
297
|
+
layer: layer.pipe(Layer.provide(Layer.effectDiscard(validateRegistration(parts)))),
|
|
298
|
+
run: runRegistrations(parts)
|
|
299
|
+
});
|
|
300
|
+
function mergeAll(registration, ...others) {
|
|
301
|
+
const parts = [registration, ...others].map((item) => item[RegistrationParts]);
|
|
302
|
+
return makeRegistration(Layer.mergeAll(registration.layer, ...others.map((item) => item.layer)), {
|
|
303
|
+
services: parts.flatMap((part) => part?.services ?? []),
|
|
304
|
+
dispatchers: parts.flatMap((part) => part?.dispatchers ?? []),
|
|
305
|
+
invalid: parts.some((part) => part === void 0) ? "mergeAll requires registrations returned by Tag.handlers" : parts.find((part) => part?.invalid !== void 0)?.invalid
|
|
306
|
+
});
|
|
307
|
+
}
|
|
211
308
|
const isAlarmDefinitionConfig = (definition) => Predicate.isObject(definition) && "payload" in definition;
|
|
212
309
|
const getAlarmDefinitionSchema = (definition) => isAlarmDefinitionConfig(definition) ? definition.payload : definition;
|
|
213
310
|
const getAlarmDefinitionFailureAction = (definition) => {
|
|
@@ -227,7 +324,8 @@ const makeDefinition = (definitions) => {
|
|
|
227
324
|
const payload = yield* S.encodeEffect(getAlarmDefinitionSchema(definition))(input.payload).pipe(Effect.flatMap(S.decodeUnknownEffect(S.Json)), Effect.mapError((cause) => new InvalidAlarmPayloadError({ cause })));
|
|
228
325
|
yield* mutations[method]({
|
|
229
326
|
...input,
|
|
230
|
-
payload
|
|
327
|
+
payload,
|
|
328
|
+
lifecycle: isAlarmDefinitionConfig(definition) ? definition.lifecycle : void 0
|
|
231
329
|
});
|
|
232
330
|
});
|
|
233
331
|
return {
|
|
@@ -243,6 +341,7 @@ const makeDefinition = (definitions) => {
|
|
|
243
341
|
make: (alarms) => ({
|
|
244
342
|
[TypedAlarmSchedulerTypeId]: TypedAlarmSchedulerTypeId,
|
|
245
343
|
...bind(alarms),
|
|
344
|
+
deferWakes: alarms.deferWakes,
|
|
246
345
|
getAlarmStatus: (input) => alarms.getAlarmStatus(input),
|
|
247
346
|
transaction: (closure) => alarms.transaction((tx) => closure(bind(tx)))
|
|
248
347
|
}),
|
|
@@ -264,6 +363,7 @@ const makeDefinition = (definitions) => {
|
|
|
264
363
|
});
|
|
265
364
|
}), {
|
|
266
365
|
...options,
|
|
366
|
+
[ManualTags]: new Set(Object.entries(definitions).filter(([, definition]) => isAlarmDefinitionConfig(definition) && definition.lifecycle === "manual").map(([tag]) => tag)),
|
|
267
367
|
onFailure: (failure) => Effect.gen(function* () {
|
|
268
368
|
const action = getAlarmDefinitionFailureAction(definitionFor(failure.tag));
|
|
269
369
|
const optionAction = options?.onFailure === void 0 ? void 0 : yield* options.onFailure(failure);
|
|
@@ -274,32 +374,70 @@ const makeDefinition = (definitions) => {
|
|
|
274
374
|
};
|
|
275
375
|
/** Handler-only definitions for applications using the raw scheduler. Prefer Tag for typed scheduling. */
|
|
276
376
|
const define = (definitions) => ({ handlers: makeDefinition(definitions).handlers });
|
|
377
|
+
const hasSchedulerTables = Effect.fnUntraced(function* (state) {
|
|
378
|
+
return yield* Effect.gen(function* () {
|
|
379
|
+
if (state.raw.storage.sql === void 0) return false;
|
|
380
|
+
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;
|
|
381
|
+
}).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)));
|
|
382
|
+
});
|
|
383
|
+
/** @internal Preserve custom dispatch while recovering retained alarms after deployments. */
|
|
384
|
+
const dispatchRawAlarm = (rawAlarm, registration) => Effect.gen(function* () {
|
|
385
|
+
const parts = registration?.[RegistrationParts];
|
|
386
|
+
if (parts !== void 0) return yield* runRegistrations(parts, rawAlarm).pipe(Effect.asVoid);
|
|
387
|
+
const custom = registration === void 0 ? rawAlarm : registration.run.pipe(Effect.andThen(rawAlarm));
|
|
388
|
+
const state = yield* DurableObjectState;
|
|
389
|
+
if (!(yield* hasSchedulerTables(state))) return yield* custom;
|
|
390
|
+
return yield* runRegistrations({
|
|
391
|
+
services: [],
|
|
392
|
+
dispatchers: []
|
|
393
|
+
}, custom).pipe(Effect.asVoid);
|
|
394
|
+
});
|
|
277
395
|
/** The typed scheduler becomes available when its handlers are registered on a Durable Object. */
|
|
278
396
|
const Tag = () => (id, definitions) => {
|
|
279
397
|
const tag = Context.Service()(id);
|
|
280
398
|
const definition = makeDefinition(definitions);
|
|
281
|
-
return Object.assign(tag, { handlers: (handlers, options) => ({
|
|
282
|
-
|
|
283
|
-
|
|
399
|
+
return Object.assign(tag, { handlers: (handlers, options) => makeRegistration(Layer.effect(tag, Effect.map(DurableObjectAlarm, definition.make)), {
|
|
400
|
+
services: [id],
|
|
401
|
+
dispatchers: [{
|
|
402
|
+
tags: Object.keys(definitions),
|
|
403
|
+
automatic: Object.values(definitions).some((entry) => !isAlarmDefinitionConfig(entry) || entry.lifecycle !== "manual"),
|
|
404
|
+
run: definition.handlers(handlers, options)
|
|
405
|
+
}]
|
|
284
406
|
}) });
|
|
285
407
|
};
|
|
286
408
|
var DurableObjectAlarm = class DurableObjectAlarm extends Context.Service()("effect-cf/DurableObjectAlarm") {
|
|
287
409
|
static layer = Layer.effect(DurableObjectAlarm, Effect.gen(function* () {
|
|
288
410
|
const state = yield* DurableObjectState;
|
|
289
411
|
const configurationDefaults = { ...yield* ScheduleConfiguration };
|
|
412
|
+
let wakeDeferrals = 0;
|
|
413
|
+
let wakesReady;
|
|
414
|
+
let pendingAlarm;
|
|
415
|
+
let dispatchActive = false;
|
|
416
|
+
let dispatchNeedsReconcile = false;
|
|
417
|
+
let deferredRecoveryDelay = PARKED_RETRY_DELAY_MS;
|
|
418
|
+
let deferredRecoveryAt = 0;
|
|
290
419
|
const readRow = Effect.fnUntraced(function* (storageId) {
|
|
291
420
|
return (yield* (yield* state.storage.sql.exec(`${alarmRowsSql} WHERE a.storage_id = ?`, storageId)).toArray())[0];
|
|
292
421
|
});
|
|
422
|
+
const writeNativeAlarm = (runAt) => Effect.tryPromise({
|
|
423
|
+
try: () => runAt === null ? state.raw.storage.deleteAlarm() : state.raw.storage.setAlarm(runAt),
|
|
424
|
+
catch: (cause) => new StorageOperationError({
|
|
425
|
+
operation: runAt === null ? "deleteAlarm" : "setAlarm",
|
|
426
|
+
cause
|
|
427
|
+
})
|
|
428
|
+
});
|
|
293
429
|
const reconcileAlarm = Effect.fn("DurableObjectAlarm.reconcileAlarm")(function* () {
|
|
430
|
+
if (wakeDeferrals > 0 || dispatchActive) {
|
|
431
|
+
if (dispatchActive) dispatchNeedsReconcile = true;
|
|
432
|
+
const current = yield* state.storage.getAlarm();
|
|
433
|
+
const now = yield* Clock.currentTimeMillis;
|
|
434
|
+
if (deferredRecoveryAt <= now) deferredRecoveryAt = now + deferredRecoveryDelay;
|
|
435
|
+
if (current === null || current <= now || current > deferredRecoveryAt) yield* writeNativeAlarm(deferredRecoveryAt);
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
294
438
|
const next = (yield* (yield* state.storage.sql.exec(`SELECT wake_at AS run_at FROM effect_cf_scheduled_alarms
|
|
295
|
-
|
|
296
|
-
yield*
|
|
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
|
-
});
|
|
439
|
+
ORDER BY wake_at, storage_id LIMIT 1`)).toArray())[0];
|
|
440
|
+
yield* writeNativeAlarm(next?.run_at ?? null);
|
|
303
441
|
});
|
|
304
442
|
const writeAttempts = Effect.fnUntraced(function* (storageId, attempts, parked, retryAt, progress) {
|
|
305
443
|
yield* state.storage.sql.exec(`INSERT OR REPLACE INTO effect_cf_alarm_attempts
|
|
@@ -318,17 +456,15 @@ var DurableObjectAlarm = class DurableObjectAlarm extends Context.Service()("eff
|
|
|
318
456
|
const scheduleAlarm = Effect.fn("DurableObjectAlarm.scheduleAlarm")(function* (input, earlier, reports) {
|
|
319
457
|
const configuration = yield* getScheduleConfiguration(configurationDefaults);
|
|
320
458
|
const ref = yield* decodeAlarmRef(input);
|
|
459
|
+
const lifecycle = yield* S.decodeUnknownEffect(AlarmLifecycleSchema)(input.lifecycle ?? "automatic").pipe(Effect.mapError((cause) => new InvalidAlarmRefError({ cause })));
|
|
460
|
+
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
461
|
const repeatEveryMillis = yield* toRepeatEveryMillis(input.repeatEvery, configuration.minimumRepeatInterval);
|
|
322
462
|
const payload = yield* encodeStoredPayload(input.payload);
|
|
323
463
|
const storageId = getScheduledEventId(ref);
|
|
324
464
|
const existing = yield* readRow(storageId);
|
|
325
465
|
const pass = yield* CurrentAlarmPass;
|
|
326
|
-
const source = pass?.row;
|
|
466
|
+
const source = lifecycle === "automatic" && pass?.row.lifecycle === "automatic" ? pass.row : void 0;
|
|
327
467
|
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
468
|
if (input.progress !== void 0) yield* S.decodeUnknownEffect(S.Natural)(input.progress).pipe(Effect.mapError((cause) => new InvalidAlarmRefError({ cause })));
|
|
333
469
|
if (source === void 0 && input.progress !== void 0 && input.progress <= progress) return;
|
|
334
470
|
const progressed = input.progress !== void 0 && input.progress > progress;
|
|
@@ -341,11 +477,11 @@ var DurableObjectAlarm = class DurableObjectAlarm extends Context.Service()("eff
|
|
|
341
477
|
const keepEarlier = earlier && existing !== void 0 && existing.run_at <= runAt;
|
|
342
478
|
const scheduledAt = keepEarlier ? existing.run_at : runAt;
|
|
343
479
|
const scheduledPayload = keepEarlier ? existing.payload : payload;
|
|
344
|
-
const scheduledRepeat = keepEarlier ? existing.repeat_every_ms : repeatEveryMillis;
|
|
345
|
-
const nextProgress = Math.max(progress, input.progress ?? -1);
|
|
480
|
+
const scheduledRepeat = lifecycle === "manual" ? null : keepEarlier ? existing.repeat_every_ms : repeatEveryMillis;
|
|
481
|
+
const nextProgress = lifecycle === "manual" ? -1 : Math.max(progress, input.progress ?? -1);
|
|
346
482
|
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);
|
|
483
|
+
(storage_id, alarm_id, tag, lifecycle, run_at, repeat_every_ms, payload)
|
|
484
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`, storageId, ref.id, ref.tag, lifecycle, scheduledAt, scheduledRepeat, scheduledPayload);
|
|
349
485
|
yield* writeAttempts(storageId, attempts, parked, retryAt, nextProgress);
|
|
350
486
|
if (parked === 1 && retryAt !== null && (existing?.parked ?? source?.parked ?? 0) === 0) reports.push(storageId);
|
|
351
487
|
});
|
|
@@ -365,10 +501,15 @@ var DurableObjectAlarm = class DurableObjectAlarm extends Context.Service()("eff
|
|
|
365
501
|
}
|
|
366
502
|
});
|
|
367
503
|
const transaction = Effect.fnUntraced(function* (closure) {
|
|
504
|
+
const pass = yield* CurrentAlarmPass;
|
|
505
|
+
if (pass !== void 0 && !pass.active) return yield* Effect.fail(new StorageOperationError({
|
|
506
|
+
operation: "alarm.transaction",
|
|
507
|
+
cause: /* @__PURE__ */ new Error("Alarm handlers cannot mutate detached work after their pass ends")
|
|
508
|
+
}));
|
|
368
509
|
const reports = [];
|
|
369
510
|
const result = yield* state.storage.transaction(() => Effect.withFiber((owner) => {
|
|
370
511
|
let active = true;
|
|
371
|
-
const requireActive = (effect) => Effect.withFiber((fiber) => active && fiber === owner ? effect : Effect.fail(new StorageOperationError({
|
|
512
|
+
const requireActive = (effect) => Effect.withFiber((fiber) => active && fiber === owner && (pass === void 0 || pass.active) ? effect : Effect.fail(new StorageOperationError({
|
|
372
513
|
operation: "alarm.transaction",
|
|
373
514
|
cause: /* @__PURE__ */ new Error("Alarm mutations require their active transaction callback")
|
|
374
515
|
})));
|
|
@@ -388,6 +529,50 @@ var DurableObjectAlarm = class DurableObjectAlarm extends Context.Service()("eff
|
|
|
388
529
|
yield* reportParked(reports);
|
|
389
530
|
return result;
|
|
390
531
|
});
|
|
532
|
+
const deferWakes = (body) => Effect.acquireUseRelease(Effect.gen(function* () {
|
|
533
|
+
const configuration = yield* getScheduleConfiguration(configurationDefaults);
|
|
534
|
+
const now = yield* Clock.currentTimeMillis;
|
|
535
|
+
if (wakeDeferrals === 0) {
|
|
536
|
+
deferredRecoveryDelay = configuration.parkedRetryDelay;
|
|
537
|
+
deferredRecoveryAt = now + deferredRecoveryDelay;
|
|
538
|
+
wakesReady ??= Deferred.makeUnsafe();
|
|
539
|
+
}
|
|
540
|
+
wakeDeferrals++;
|
|
541
|
+
}), () => transaction(() => Effect.void).pipe(Effect.andThen(body)), () => Effect.gen(function* () {
|
|
542
|
+
wakeDeferrals--;
|
|
543
|
+
if (wakeDeferrals === 0) yield* (dispatchActive ? Effect.void : transaction(() => Effect.void)).pipe(Effect.ensuring(Effect.sync(() => {
|
|
544
|
+
if (wakeDeferrals === 0 && wakesReady !== void 0) {
|
|
545
|
+
Deferred.doneUnsafe(wakesReady, Effect.void);
|
|
546
|
+
wakesReady = void 0;
|
|
547
|
+
}
|
|
548
|
+
})));
|
|
549
|
+
}));
|
|
550
|
+
const awaitWakes = Effect.suspend(() => wakesReady === void 0 ? Effect.void : Deferred.await(wakesReady).pipe(Effect.andThen(awaitWakes)));
|
|
551
|
+
const runAlarm = (event, native) => Effect.flatMap(hasSchedulerTables(state), (managed) => !managed ? native ? event(Effect.void) : Effect.void : Effect.uninterruptibleMask((restore) => Effect.suspend(() => {
|
|
552
|
+
const pending = pendingAlarm;
|
|
553
|
+
if (pending !== void 0) return restore((native ? transaction(() => Effect.void) : Effect.void).pipe(Effect.andThen(Deferred.await(pending))));
|
|
554
|
+
const completed = Deferred.makeUnsafe();
|
|
555
|
+
let dispatchStarted = false;
|
|
556
|
+
pendingAlarm = completed;
|
|
557
|
+
dispatchNeedsReconcile = native;
|
|
558
|
+
return restore(Effect.gen(function* () {
|
|
559
|
+
if (native && wakesReady !== void 0) yield* transaction(() => Effect.void);
|
|
560
|
+
yield* awaitWakes;
|
|
561
|
+
dispatchActive = true;
|
|
562
|
+
yield* event(Effect.gen(function* () {
|
|
563
|
+
yield* getScheduleConfiguration(configurationDefaults);
|
|
564
|
+
dispatchStarted = true;
|
|
565
|
+
}));
|
|
566
|
+
})).pipe(Effect.onExit((exit) => Effect.gen(function* () {
|
|
567
|
+
const reconcile = dispatchNeedsReconcile || dispatchStarted || Exit.isFailure(exit);
|
|
568
|
+
dispatchActive = Exit.isFailure(exit) && !dispatchStarted;
|
|
569
|
+
const reconciled = yield* (reconcile ? transaction(() => Effect.void) : Effect.void).pipe(Effect.exit);
|
|
570
|
+
dispatchActive = false;
|
|
571
|
+
pendingAlarm = void 0;
|
|
572
|
+
yield* Deferred.done(completed, Exit.asVoidAll([exit, reconciled]));
|
|
573
|
+
yield* reconciled;
|
|
574
|
+
})));
|
|
575
|
+
})));
|
|
391
576
|
const rescheduleFailedAlarm = Effect.fnUntraced(function* (row, initialDelay, configuration, reports) {
|
|
392
577
|
const current = yield* readRow(row.storage_id);
|
|
393
578
|
if (current === void 0 || current.revision !== row.revision) return;
|
|
@@ -411,8 +596,19 @@ var DurableObjectAlarm = class DurableObjectAlarm extends Context.Service()("eff
|
|
|
411
596
|
const limit = yield* getProcessLimit(options);
|
|
412
597
|
const initialDelay = yield* getFailureRetryDelay(options, configuration);
|
|
413
598
|
const now = yield* Clock.currentTimeMillis;
|
|
414
|
-
const
|
|
415
|
-
|
|
599
|
+
const dispatch = yield* CurrentDispatch;
|
|
600
|
+
if (dispatch?.raw !== void 0) dispatch.raw.processed = true;
|
|
601
|
+
const tagFilter = dispatch === void 0 ? "" : ` AND a.tag ${dispatch.exclude ? "NOT IN" : "IN"} (SELECT value FROM json_each(?))`;
|
|
602
|
+
const tagBindings = dispatch === void 0 ? [] : [S.encodeSync(StoredTags)([...dispatch.tags])];
|
|
603
|
+
const select = Effect.fnUntraced(function* (lifecycle, batchLimit) {
|
|
604
|
+
return yield* (yield* state.storage.sql.exec(`${alarmRowsSql} WHERE a.lifecycle = ? AND a.wake_at <= ?${tagFilter}
|
|
605
|
+
ORDER BY a.wake_at ASC, a.storage_id ASC LIMIT ?`, lifecycle, now, ...tagBindings, batchLimit)).toArray();
|
|
606
|
+
});
|
|
607
|
+
const dueRows = yield* select("automatic", limit);
|
|
608
|
+
const manualRows = yield* select("manual", DEFAULT_PROCESS_DUE_ALARMS_LIMIT);
|
|
609
|
+
if (manualRows.length > 0) yield* transaction(() => Effect.gen(function* () {
|
|
610
|
+
for (const row of manualRows) yield* state.storage.sql.exec(`UPDATE effect_cf_scheduled_alarms SET run_at = ?, wake_at = ? WHERE ${sameRevisionSql}`, now + configuration.parkedRetryDelay, now + configuration.parkedRetryDelay, row.storage_id, row.storage_id, row.revision);
|
|
611
|
+
}));
|
|
416
612
|
const handled = [];
|
|
417
613
|
const failed = [];
|
|
418
614
|
const parked = [];
|
|
@@ -437,9 +633,9 @@ var DurableObjectAlarm = class DurableObjectAlarm extends Context.Service()("eff
|
|
|
437
633
|
if (Exit.isFailure(actionExit)) return yield* Effect.failCause(actionExit.cause);
|
|
438
634
|
if (Exit.isFailure(delayExit)) return yield* Effect.failCause(delayExit.cause);
|
|
439
635
|
});
|
|
440
|
-
|
|
636
|
+
const handleRow = Effect.fnUntraced(function* (row) {
|
|
441
637
|
const current = yield* readRow(row.storage_id);
|
|
442
|
-
if (current === void 0 || current.revision !== row.revision)
|
|
638
|
+
if (current === void 0 || current.revision !== row.revision) return;
|
|
443
639
|
const inPass = (effect) => {
|
|
444
640
|
const pass = {
|
|
445
641
|
row,
|
|
@@ -450,21 +646,27 @@ var DurableObjectAlarm = class DurableObjectAlarm extends Context.Service()("eff
|
|
|
450
646
|
pass.active = false;
|
|
451
647
|
})));
|
|
452
648
|
};
|
|
649
|
+
const manual = row.lifecycle === "manual";
|
|
650
|
+
if (manual && !options?.[ManualTags]?.has(row.tag)) return yield* Effect.fail(missingAlarm(row.tag));
|
|
453
651
|
const eventExit = yield* Effect.exit(toAlarmDue(row));
|
|
454
652
|
if (Exit.isFailure(eventExit)) {
|
|
455
|
-
|
|
456
|
-
|
|
653
|
+
if (manual) return yield* Effect.failCause(eventExit.cause);
|
|
654
|
+
return yield* inPass(handleFailure(row, void 0, eventExit.cause));
|
|
457
655
|
}
|
|
458
656
|
const event = eventExit.value;
|
|
459
657
|
const handleExit = yield* Effect.exit(inPass(Effect.suspend(() => handle(event))));
|
|
460
658
|
if (Exit.isFailure(handleExit)) {
|
|
461
|
-
yield*
|
|
462
|
-
|
|
659
|
+
if (manual) return yield* Effect.failCause(handleExit.cause);
|
|
660
|
+
return yield* inPass(handleFailure(row, event, handleExit.cause));
|
|
463
661
|
}
|
|
464
|
-
yield* transaction(() => acknowledgeAlarm(row, configuration));
|
|
662
|
+
if (!manual) yield* transaction(() => acknowledgeAlarm(row, configuration));
|
|
465
663
|
handled.push(event);
|
|
466
|
-
}
|
|
664
|
+
});
|
|
665
|
+
const permits = dispatch?.permits ?? (yield* Semaphore.make(4));
|
|
666
|
+
const automatic = Effect.forEach(dueRows, handleRow, { discard: true }).pipe(Effect.exit);
|
|
667
|
+
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 });
|
|
467
668
|
yield* transaction(() => Effect.void);
|
|
669
|
+
yield* Exit.asVoidAll(exits);
|
|
468
670
|
return {
|
|
469
671
|
failed,
|
|
470
672
|
handled,
|
|
@@ -472,6 +674,14 @@ var DurableObjectAlarm = class DurableObjectAlarm extends Context.Service()("eff
|
|
|
472
674
|
};
|
|
473
675
|
});
|
|
474
676
|
return DurableObjectAlarm.of({
|
|
677
|
+
[RunAlarm]: runAlarm,
|
|
678
|
+
[DispatchAfterEvent]: Effect.map(ScheduleConfiguration, (configuration) => (configuration.dispatchAfterEvent ?? configurationDefaults.dispatchAfterEvent) === true),
|
|
679
|
+
[HasDueAlarms]: Effect.gen(function* () {
|
|
680
|
+
yield* ensureTable(state);
|
|
681
|
+
const now = yield* Clock.currentTimeMillis;
|
|
682
|
+
return (yield* (yield* state.storage.sql.exec("SELECT 1 FROM effect_cf_scheduled_alarms WHERE wake_at <= ? LIMIT 1", now)).toArray()).length > 0;
|
|
683
|
+
}),
|
|
684
|
+
deferWakes,
|
|
475
685
|
cancelAlarm: (input) => transaction((alarms) => alarms.cancelAlarm(input)),
|
|
476
686
|
getAlarmStatus: Effect.fnUntraced(function* (input) {
|
|
477
687
|
const ref = yield* decodeAlarmRef(input);
|
|
@@ -492,4 +702,4 @@ var DurableObjectAlarm = class DurableObjectAlarm extends Context.Service()("eff
|
|
|
492
702
|
}));
|
|
493
703
|
};
|
|
494
704
|
//#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 };
|
|
705
|
+
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 };
|
package/dist/index.mjs
CHANGED
|
@@ -35,6 +35,7 @@ import { Email_exports } from "./Email.mjs";
|
|
|
35
35
|
import { Hyperdrive_exports } from "./Hyperdrive.mjs";
|
|
36
36
|
import { Images_exports } from "./Images.mjs";
|
|
37
37
|
import { Kv_exports } from "./Kv.mjs";
|
|
38
|
+
import { RateLimit_exports } from "./RateLimit.mjs";
|
|
38
39
|
import { R2_exports } from "./R2.mjs";
|
|
39
40
|
import { RpcWebSocketClient_exports } from "./RpcWebSocketClient.mjs";
|
|
40
41
|
import { Vectorize_exports } from "./Vectorize.mjs";
|
|
@@ -42,4 +43,4 @@ import { WebTransport_exports } from "./WebTransport.mjs";
|
|
|
42
43
|
import { WorkflowBinding_exports } from "./WorkflowBinding.mjs";
|
|
43
44
|
import { WorkflowDefinition_exports } from "./WorkflowDefinition.mjs";
|
|
44
45
|
import { Workflow_exports } from "./Workflow.mjs";
|
|
45
|
-
export { AiGateway_exports as AiGateway, AnalyticsEngine_exports as AnalyticsEngine, Artifacts_exports as Artifacts, Binding_exports as Binding, BrowserRendering_exports as BrowserRendering, Cache_exports as Cache, CloudflareOtlp_exports as CloudflareOtlp, CloudflareTracer_exports as CloudflareTracer, ContainerFiles_exports as ContainerFiles, ContainerNamespace_exports as ContainerNamespace, D1_exports as D1, DurableObject_exports as DurableObject, DurableObjectAlarm_exports as DurableObjectAlarm, DurableObjectContainer_exports as DurableObjectContainer, DurableObjectDefinition_exports as DurableObjectDefinition, DurableObjectNamespace_exports as DurableObjectNamespace, DurableObjectRpcWebSocket_exports as DurableObjectRpcWebSocket, DurableObjectSqlite_exports as DurableObjectSqlite, DurableObjectState_exports as DurableObjectState, DurableObjectStorage_exports as DurableObjectStorage, DurableObjectWebSocket_exports as DurableObjectWebSocket, Email_exports as Email, Environment_exports as Environment, Hyperdrive_exports as Hyperdrive, Images_exports as Images, Kv_exports as Kv, Queue_exports as Queue, QueueBinding_exports as QueueBinding, QueueDefinition_exports as QueueDefinition, R2_exports as R2, Rpc_exports as Rpc, RpcDefinition_exports as RpcDefinition, RpcTargets_exports as RpcTargets, RpcTracing_exports as RpcTracing, RpcWebSocketClient_exports as RpcWebSocketClient, ServiceBinding_exports as ServiceBinding, Vectorize_exports as Vectorize, WebTransport_exports as WebTransport, Worker_exports as Worker, WorkerConfig, WorkerDefinition_exports as WorkerDefinition, WorkerEnvironment, WorkersAi_exports as WorkersAi, Workflow_exports as Workflow, WorkflowBinding_exports as WorkflowBinding, WorkflowDefinition_exports as WorkflowDefinition };
|
|
46
|
+
export { AiGateway_exports as AiGateway, AnalyticsEngine_exports as AnalyticsEngine, Artifacts_exports as Artifacts, Binding_exports as Binding, BrowserRendering_exports as BrowserRendering, Cache_exports as Cache, CloudflareOtlp_exports as CloudflareOtlp, CloudflareTracer_exports as CloudflareTracer, ContainerFiles_exports as ContainerFiles, ContainerNamespace_exports as ContainerNamespace, D1_exports as D1, DurableObject_exports as DurableObject, DurableObjectAlarm_exports as DurableObjectAlarm, DurableObjectContainer_exports as DurableObjectContainer, DurableObjectDefinition_exports as DurableObjectDefinition, DurableObjectNamespace_exports as DurableObjectNamespace, DurableObjectRpcWebSocket_exports as DurableObjectRpcWebSocket, DurableObjectSqlite_exports as DurableObjectSqlite, DurableObjectState_exports as DurableObjectState, DurableObjectStorage_exports as DurableObjectStorage, DurableObjectWebSocket_exports as DurableObjectWebSocket, Email_exports as Email, Environment_exports as Environment, Hyperdrive_exports as Hyperdrive, Images_exports as Images, Kv_exports as Kv, Queue_exports as Queue, QueueBinding_exports as QueueBinding, QueueDefinition_exports as QueueDefinition, R2_exports as R2, RateLimit_exports as RateLimit, Rpc_exports as Rpc, RpcDefinition_exports as RpcDefinition, RpcTargets_exports as RpcTargets, RpcTracing_exports as RpcTracing, RpcWebSocketClient_exports as RpcWebSocketClient, ServiceBinding_exports as ServiceBinding, Vectorize_exports as Vectorize, WebTransport_exports as WebTransport, Worker_exports as Worker, WorkerConfig, WorkerDefinition_exports as WorkerDefinition, WorkerEnvironment, WorkersAi_exports as WorkersAi, Workflow_exports as Workflow, WorkflowBinding_exports as WorkflowBinding, WorkflowDefinition_exports as WorkflowDefinition };
|