@kiwa-test/edge 1.0.1 → 1.1.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/dist/index.cjs +866 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +519 -1
- package/dist/index.d.ts +519 -1
- package/dist/index.js +828 -1
- package/dist/index.js.map +1 -1
- package/package.json +4 -5
package/dist/index.d.ts
CHANGED
|
@@ -63,4 +63,522 @@ interface InvokeEdgeHandlerResult {
|
|
|
63
63
|
*/
|
|
64
64
|
declare function invokeEdgeHandler<TEnv extends EdgeEnvBindings = EdgeEnvBindings>(opts: InvokeEdgeHandlerOptions<TEnv>): Promise<InvokeEdgeHandlerResult>;
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
/**
|
|
67
|
+
* Advanced edge semantics — platform-neutral axis SSOT.
|
|
68
|
+
*
|
|
69
|
+
* v0.1 edge mocks only carried fetch invocation + lightweight KV helpers.
|
|
70
|
+
* v0.2 adds 8 production semantics that edge runtimes expose differently —
|
|
71
|
+
* Durable Objects, websocket upgrades, edge KV, geo replication, cron triggers,
|
|
72
|
+
* subrequest limits, CPU time limits, and streaming responses. Each axis is
|
|
73
|
+
* expressed as a small pure state-machine helper that returns a neutral
|
|
74
|
+
* envelope, so downstream tests can drive the axis without knowing the
|
|
75
|
+
* platform's payload dialect.
|
|
76
|
+
*/
|
|
77
|
+
type EdgePlatform = 'cloudflare' | 'vercel' | 'deno';
|
|
78
|
+
type EdgeAxis = 'durable-object' | 'websocket-edge' | 'edge-kv' | 'geo-replicated' | 'cron-trigger' | 'subrequest-limit' | 'cpu-time-limit' | 'streaming-response';
|
|
79
|
+
/**
|
|
80
|
+
* Platform-neutral event names used inside the axis helpers. Real edge
|
|
81
|
+
* platforms expose different string ids (Cloudflare `durable_object.fetch`,
|
|
82
|
+
* Vercel `edge_function.session_affinity`, Deno Deploy `deploy.stateful_fetch`)
|
|
83
|
+
* — the {@link platformEventName} map handles the translation. Tests can
|
|
84
|
+
* assert on the neutral name via `step.neutralEvent` or on the
|
|
85
|
+
* platform-specific one via `step.platformEvent`.
|
|
86
|
+
*/
|
|
87
|
+
type NeutralEventName = 'durable-object.created' | 'durable-object.requested' | 'durable-object.alarm-fired' | 'durable-object.storage-written' | 'websocket.upgrade-requested' | 'websocket.accepted' | 'websocket.message' | 'websocket.closed' | 'kv.read' | 'kv.write' | 'kv.cache-hit' | 'kv.cache-miss' | 'geo.primary-write' | 'geo.replica-lagged' | 'geo.replica-synced' | 'geo.conflict-resolved' | 'cron.scheduled' | 'cron.started' | 'cron.completed' | 'cron.failed' | 'subrequest.started' | 'subrequest.counted' | 'subrequest.limited' | 'subrequest.completed' | 'cpu.started' | 'cpu.budget-warning' | 'cpu.limited' | 'cpu.completed' | 'stream.opened' | 'stream.chunk-sent' | 'stream.backpressure' | 'stream.closed';
|
|
88
|
+
/**
|
|
89
|
+
* Translate a neutral event name to the platform dialect. Falls back to the
|
|
90
|
+
* neutral name if the platform has no specific dialect entry — this makes
|
|
91
|
+
* the map partial-safe without silent typos.
|
|
92
|
+
*/
|
|
93
|
+
declare function platformEventName(platform: EdgePlatform, neutral: NeutralEventName): string;
|
|
94
|
+
/**
|
|
95
|
+
* Axis result envelope returned by every state-machine step. Edge semantics
|
|
96
|
+
* are pure helpers (no adapters); the envelope surfaces the next state
|
|
97
|
+
* transition metadata so tests can drive the next call without re-reading
|
|
98
|
+
* runtime-specific telemetry.
|
|
99
|
+
*/
|
|
100
|
+
interface AxisStep<TState> {
|
|
101
|
+
neutralEvent: NeutralEventName;
|
|
102
|
+
platformEvent: string;
|
|
103
|
+
state: TState;
|
|
104
|
+
platform: EdgePlatform;
|
|
105
|
+
metadata: Record<string, string | number | boolean>;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Fidelity harness — collects the platform × axis coverage grid that
|
|
110
|
+
* downstream release-gate reports on. Not a runner (no side effect emit);
|
|
111
|
+
* pure inspection so tests / release-gate can assert "3 platform × 8 axis"
|
|
112
|
+
* without walking every neutral event by hand.
|
|
113
|
+
*/
|
|
114
|
+
interface FidelityRow {
|
|
115
|
+
platform: EdgePlatform;
|
|
116
|
+
axis: EdgeAxis;
|
|
117
|
+
neutralEvents: NeutralEventName[];
|
|
118
|
+
platformEvents: string[];
|
|
119
|
+
}
|
|
120
|
+
interface FidelityCoverage {
|
|
121
|
+
platforms: EdgePlatform[];
|
|
122
|
+
axes: EdgeAxis[];
|
|
123
|
+
rows: FidelityRow[];
|
|
124
|
+
}
|
|
125
|
+
declare const AXIS_TO_EVENTS: Record<EdgeAxis, NeutralEventName[]>;
|
|
126
|
+
/**
|
|
127
|
+
* Collect the platform × axis coverage grid. `platforms` is the list of
|
|
128
|
+
* platforms to inspect — usually all 3 (`cloudflare`, `vercel`, `deno`).
|
|
129
|
+
*
|
|
130
|
+
* The output is a flat row list `platforms.length * 8 = 24` for the default
|
|
131
|
+
* setup, plus `platforms` + `axes` roll-up lists so callers can assert on
|
|
132
|
+
* the grid dimensions.
|
|
133
|
+
*/
|
|
134
|
+
declare function collectFidelityCoverage(platforms: EdgePlatform[]): FidelityCoverage;
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Durable Object — stateful, single-instance actor pinned to one edge
|
|
138
|
+
* location. Cloudflare Durable Objects are the canonical example; Vercel's
|
|
139
|
+
* closest analogue is a session-affine edge function, Deno Deploy exposes
|
|
140
|
+
* stateful objects backed by Deno KV. The mock reproduces the user-observable
|
|
141
|
+
* lifecycle: an instance is created once, receives fetch requests (which pin
|
|
142
|
+
* it "active"), can wake on a scheduled alarm, and persists to transactional
|
|
143
|
+
* storage. Hibernation / eviction is intentionally out of scope for v0.2 — the
|
|
144
|
+
* axis only exposes the 4 neutral events the fidelity grid tracks.
|
|
145
|
+
*
|
|
146
|
+
* State transitions:
|
|
147
|
+
* created → 'initialized'
|
|
148
|
+
* requestDurableObject → 'active' (from initialized or active)
|
|
149
|
+
* fireAlarm → 'active' (an alarm wakes the object)
|
|
150
|
+
* writeStorage → 'active' (a storage write implies an active handler)
|
|
151
|
+
*/
|
|
152
|
+
type DoState = 'initialized' | 'active' | 'hibernated' | 'terminated';
|
|
153
|
+
interface DurableObjectSession {
|
|
154
|
+
id: string;
|
|
155
|
+
platform: EdgePlatform;
|
|
156
|
+
state: DoState;
|
|
157
|
+
requestCount: number;
|
|
158
|
+
storageKeys: Map<string, string>;
|
|
159
|
+
scheduledAlarmAt: number | null;
|
|
160
|
+
history: AxisStep<DoState>[];
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Create a durable object instance. State starts at 'initialized' and no
|
|
164
|
+
* request has been served yet. Emits `durable-object.created`.
|
|
165
|
+
*/
|
|
166
|
+
declare function createDurableObject(input: {
|
|
167
|
+
id: string;
|
|
168
|
+
platform: EdgePlatform;
|
|
169
|
+
}): DurableObjectSession;
|
|
170
|
+
/**
|
|
171
|
+
* Route a fetch request to the object. Pins the instance 'active' and bumps
|
|
172
|
+
* the request counter. Emits `durable-object.requested`.
|
|
173
|
+
*/
|
|
174
|
+
declare function requestDurableObject(session: DurableObjectSession, input: {
|
|
175
|
+
url: string;
|
|
176
|
+
}): AxisStep<DoState>;
|
|
177
|
+
/**
|
|
178
|
+
* Fire the scheduled alarm. Wakes the object into 'active' regardless of the
|
|
179
|
+
* prior state and clears the pending alarm. Emits `durable-object.alarm-fired`.
|
|
180
|
+
*/
|
|
181
|
+
declare function fireAlarm(session: DurableObjectSession): AxisStep<DoState>;
|
|
182
|
+
/**
|
|
183
|
+
* Write a key to transactional storage. Implies an active handler, so the
|
|
184
|
+
* object stays 'active'. Emits `durable-object.storage-written`.
|
|
185
|
+
*/
|
|
186
|
+
declare function writeStorage(session: DurableObjectSession, input: {
|
|
187
|
+
key: string;
|
|
188
|
+
value: string;
|
|
189
|
+
}): AxisStep<DoState>;
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* WebSocket at the edge — the HTTP-upgrade handshake plus the message /
|
|
193
|
+
* close lifecycle. All three runtimes accept a `101 Switching Protocols`
|
|
194
|
+
* upgrade (Cloudflare `WebSocketPair`, Vercel edge websockets, Deno
|
|
195
|
+
* `Deno.upgradeWebSocket`) but expose different telemetry strings. The mock
|
|
196
|
+
* drives the neutral lifecycle so a test can assert the handshake ordering
|
|
197
|
+
* without a live socket.
|
|
198
|
+
*
|
|
199
|
+
* State transitions:
|
|
200
|
+
* requestWebSocketUpgrade → 'pending'
|
|
201
|
+
* acceptWebSocket → 'open' (only from 'pending')
|
|
202
|
+
* sendMessage → 'open' (only while 'open')
|
|
203
|
+
* closeWebSocket → 'closed'
|
|
204
|
+
*/
|
|
205
|
+
type WsState = 'pending' | 'open' | 'closing' | 'closed';
|
|
206
|
+
interface WebSocketSession {
|
|
207
|
+
id: string;
|
|
208
|
+
platform: EdgePlatform;
|
|
209
|
+
state: WsState;
|
|
210
|
+
messages: string[];
|
|
211
|
+
history: AxisStep<WsState>[];
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Begin the upgrade handshake. State starts 'pending' until the server
|
|
215
|
+
* accepts. Emits `websocket.upgrade-requested`.
|
|
216
|
+
*/
|
|
217
|
+
declare function requestWebSocketUpgrade(input: {
|
|
218
|
+
id: string;
|
|
219
|
+
platform: EdgePlatform;
|
|
220
|
+
}): WebSocketSession;
|
|
221
|
+
/**
|
|
222
|
+
* Accept the pending upgrade, moving the socket 'open'. Rejects if the socket
|
|
223
|
+
* is not awaiting acceptance. Emits `websocket.accepted`.
|
|
224
|
+
*/
|
|
225
|
+
declare function acceptWebSocket(session: WebSocketSession): AxisStep<WsState>;
|
|
226
|
+
/**
|
|
227
|
+
* Send a frame over the open socket. Rejects unless the socket is 'open'.
|
|
228
|
+
* Emits `websocket.message`.
|
|
229
|
+
*/
|
|
230
|
+
declare function sendMessage(session: WebSocketSession, input: {
|
|
231
|
+
data: string;
|
|
232
|
+
}): AxisStep<WsState>;
|
|
233
|
+
/**
|
|
234
|
+
* Close the socket with a status code. Rejects if already closed. Emits
|
|
235
|
+
* `websocket.closed`.
|
|
236
|
+
*/
|
|
237
|
+
declare function closeWebSocket(session: WebSocketSession, input: {
|
|
238
|
+
code: number;
|
|
239
|
+
}): AxisStep<WsState>;
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Edge KV — a globally replicated key/value store with a read-through cache.
|
|
243
|
+
* Cloudflare KV, Vercel Edge Config, and Deno KV all trade strong consistency
|
|
244
|
+
* for low-latency edge reads: a write may take time to propagate, and reads
|
|
245
|
+
* are served from a per-POP cache when warm. The mock models the observable
|
|
246
|
+
* surface: a backing `store`, a `cache` layer that a read populates and a
|
|
247
|
+
* write invalidates, and a range query over a key prefix.
|
|
248
|
+
*
|
|
249
|
+
* There is no state machine per se — the store is always usable. `state`
|
|
250
|
+
* records the consistency model the caller declared so downstream tests can
|
|
251
|
+
* assert on it. The 4 neutral events distinguish a cold read, a write, a warm
|
|
252
|
+
* cache hit, and a miss on an absent key.
|
|
253
|
+
*/
|
|
254
|
+
type KvState = 'consistent' | 'eventually-consistent';
|
|
255
|
+
interface EdgeKvSession {
|
|
256
|
+
platform: EdgePlatform;
|
|
257
|
+
store: Map<string, string>;
|
|
258
|
+
cache: Map<string, string>;
|
|
259
|
+
state: KvState;
|
|
260
|
+
history: AxisStep<KvState>[];
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Construct a KV session. No event is emitted — the store is simply opened.
|
|
264
|
+
* Defaults to eventual consistency, the common edge-KV replication model.
|
|
265
|
+
*/
|
|
266
|
+
declare function createEdgeKvSession(input: {
|
|
267
|
+
platform: EdgePlatform;
|
|
268
|
+
state?: KvState;
|
|
269
|
+
}): EdgeKvSession;
|
|
270
|
+
/**
|
|
271
|
+
* Read a key. Three outcomes:
|
|
272
|
+
* - cache warm → `kv.cache-hit`
|
|
273
|
+
* - store only → `kv.read` and the cache is populated (read-through)
|
|
274
|
+
* - absent → `kv.cache-miss`
|
|
275
|
+
*/
|
|
276
|
+
declare function kvRead(session: EdgeKvSession, input: {
|
|
277
|
+
key: string;
|
|
278
|
+
}): AxisStep<KvState>;
|
|
279
|
+
/**
|
|
280
|
+
* Write a key. Updates the backing store and invalidates the cache entry so
|
|
281
|
+
* the next read goes through to the store. Emits `kv.write`.
|
|
282
|
+
*/
|
|
283
|
+
declare function kvWrite(session: EdgeKvSession, input: {
|
|
284
|
+
key: string;
|
|
285
|
+
value: string;
|
|
286
|
+
}): AxisStep<KvState>;
|
|
287
|
+
/**
|
|
288
|
+
* Range query over a key prefix. Returns the matching keys (sorted, up to
|
|
289
|
+
* `limit`) alongside the emitted step. Emits `kv.read` since a range scan is a
|
|
290
|
+
* store read. `limit` defaults to no cap.
|
|
291
|
+
*/
|
|
292
|
+
declare function kvRangeQuery(session: EdgeKvSession, input: {
|
|
293
|
+
prefix: string;
|
|
294
|
+
limit?: number;
|
|
295
|
+
}): {
|
|
296
|
+
matches: string[];
|
|
297
|
+
step: AxisStep<KvState>;
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Geo-replicated store — a primary region that accepts writes and N replica
|
|
302
|
+
* regions that catch up asynchronously. This is the multi-region consistency
|
|
303
|
+
* model behind Cloudflare Smart Placement + KV replication, Vercel Edge Config
|
|
304
|
+
* replication, and Deno KV's primary/replica topology. The mock exposes the
|
|
305
|
+
* observable lifecycle a test cares about: a primary write bumps a version and
|
|
306
|
+
* leaves replicas lagging, each replica is marked lagged then synced, and a
|
|
307
|
+
* write conflict can be explicitly resolved.
|
|
308
|
+
*
|
|
309
|
+
* State transitions:
|
|
310
|
+
* createGeoReplicatedSession → 'in-sync' (version 0, no lag)
|
|
311
|
+
* geoPrimaryWrite → 'lagging' (replicas fall behind)
|
|
312
|
+
* markReplicaLagged → 'lagging'
|
|
313
|
+
* syncReplica → 'in-sync' (only once every replica lag = 0)
|
|
314
|
+
* resolveConflict → 'in-sync'
|
|
315
|
+
*/
|
|
316
|
+
type GeoRegion = string;
|
|
317
|
+
type GeoState = 'in-sync' | 'lagging' | 'conflict-detected';
|
|
318
|
+
interface GeoReplicatedSession {
|
|
319
|
+
platform: EdgePlatform;
|
|
320
|
+
primaryRegion: GeoRegion;
|
|
321
|
+
replicaRegions: GeoRegion[];
|
|
322
|
+
state: GeoState;
|
|
323
|
+
version: number;
|
|
324
|
+
lagMs: Record<GeoRegion, number>;
|
|
325
|
+
history: AxisStep<GeoState>[];
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Construct a geo-replicated session. Starts 'in-sync' at version 0 with every
|
|
329
|
+
* replica at zero lag. No event is emitted.
|
|
330
|
+
*/
|
|
331
|
+
declare function createGeoReplicatedSession(input: {
|
|
332
|
+
platform: EdgePlatform;
|
|
333
|
+
primaryRegion: GeoRegion;
|
|
334
|
+
replicaRegions: GeoRegion[];
|
|
335
|
+
}): GeoReplicatedSession;
|
|
336
|
+
/**
|
|
337
|
+
* Write to the primary region. Bumps the version and marks every replica as
|
|
338
|
+
* lagging (they have not yet received the new version). Emits
|
|
339
|
+
* `geo.primary-write`.
|
|
340
|
+
*/
|
|
341
|
+
declare function geoPrimaryWrite(session: GeoReplicatedSession, input: {
|
|
342
|
+
data: string;
|
|
343
|
+
}): AxisStep<GeoState>;
|
|
344
|
+
/**
|
|
345
|
+
* Report replication lag for a specific replica. Rejects an unknown region.
|
|
346
|
+
* Emits `geo.replica-lagged`.
|
|
347
|
+
*/
|
|
348
|
+
declare function markReplicaLagged(session: GeoReplicatedSession, input: {
|
|
349
|
+
region: GeoRegion;
|
|
350
|
+
lagMs: number;
|
|
351
|
+
}): AxisStep<GeoState>;
|
|
352
|
+
/**
|
|
353
|
+
* Mark a replica caught up (lag → 0). When every replica has zero lag the
|
|
354
|
+
* session returns 'in-sync'. Rejects an unknown region. Emits
|
|
355
|
+
* `geo.replica-synced`.
|
|
356
|
+
*/
|
|
357
|
+
declare function syncReplica(session: GeoReplicatedSession, input: {
|
|
358
|
+
region: GeoRegion;
|
|
359
|
+
}): AxisStep<GeoState>;
|
|
360
|
+
/**
|
|
361
|
+
* Resolve a write conflict for a region by picking a winning version. Rejects
|
|
362
|
+
* an unknown region. Adopts the chosen version, clears every replica's lag and
|
|
363
|
+
* forces the session back to 'in-sync'. Emits `geo.conflict-resolved`.
|
|
364
|
+
*/
|
|
365
|
+
declare function resolveConflict(session: GeoReplicatedSession, input: {
|
|
366
|
+
region: GeoRegion;
|
|
367
|
+
chosenVersion: number;
|
|
368
|
+
}): AxisStep<GeoState>;
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Cron trigger — scheduled invocation lifecycle. Edge platforms fire scheduled
|
|
372
|
+
* handlers from distinct sources (Cloudflare Cron Triggers + Queue consumers +
|
|
373
|
+
* Email routing, Vercel Cron jobs, Deno Deploy cron) yet share the same
|
|
374
|
+
* observable lifecycle: an event is scheduled, starts running, then either
|
|
375
|
+
* completes or fails. A failed run re-enters the schedule until `maxRetries`
|
|
376
|
+
* is exhausted, at which point it terminates in `failed`.
|
|
377
|
+
*/
|
|
378
|
+
type CronState = 'scheduled' | 'running' | 'completed' | 'failed';
|
|
379
|
+
/** Which trigger source fired the scheduled handler. */
|
|
380
|
+
type CronTriggerType = 'scheduled' | 'queue' | 'email';
|
|
381
|
+
interface CronSession {
|
|
382
|
+
id: string;
|
|
383
|
+
platform: EdgePlatform;
|
|
384
|
+
triggerType: CronTriggerType;
|
|
385
|
+
cronSpec: string;
|
|
386
|
+
state: CronState;
|
|
387
|
+
startedAt: number | null;
|
|
388
|
+
retryCount: number;
|
|
389
|
+
maxRetries: number;
|
|
390
|
+
history: AxisStep<CronState>[];
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Schedule a cron invocation. Emits `cron.scheduled` and seeds the session in
|
|
394
|
+
* the `scheduled` state. `triggerType` defaults to `scheduled` (a plain time
|
|
395
|
+
* trigger) and `maxRetries` defaults to 3.
|
|
396
|
+
*/
|
|
397
|
+
declare function scheduleCron(input: {
|
|
398
|
+
id: string;
|
|
399
|
+
platform: EdgePlatform;
|
|
400
|
+
triggerType?: CronTriggerType;
|
|
401
|
+
cronSpec: string;
|
|
402
|
+
maxRetries?: number;
|
|
403
|
+
}): CronSession;
|
|
404
|
+
/**
|
|
405
|
+
* Begin executing a scheduled invocation. Transitions `scheduled` → `running`,
|
|
406
|
+
* stamps `startedAt`, and emits `cron.started`. Rejects if the session is not
|
|
407
|
+
* currently `scheduled` (already running / terminal).
|
|
408
|
+
*/
|
|
409
|
+
declare function startCron(session: CronSession): AxisStep<CronState>;
|
|
410
|
+
/**
|
|
411
|
+
* Finish a running invocation successfully. Transitions `running` →
|
|
412
|
+
* `completed` and emits `cron.completed`. Rejects if not `running`.
|
|
413
|
+
*/
|
|
414
|
+
declare function completeCron(session: CronSession, input: {
|
|
415
|
+
durationMs: number;
|
|
416
|
+
}): AxisStep<CronState>;
|
|
417
|
+
/**
|
|
418
|
+
* Fail a running invocation. Increments `retryCount`; if retries remain the
|
|
419
|
+
* session re-enters the `scheduled` state (to be picked up again), otherwise it
|
|
420
|
+
* terminates in `failed`. Emits `cron.failed` with `willRetry` reflecting the
|
|
421
|
+
* decision. Rejects if the session already `completed`.
|
|
422
|
+
*/
|
|
423
|
+
declare function failCron(session: CronSession, input: {
|
|
424
|
+
reason: string;
|
|
425
|
+
}): AxisStep<CronState>;
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Subrequest limit — outbound fetch budget per invocation. Edge runtimes cap
|
|
429
|
+
* how many subrequests a single handler may issue (Cloudflare Workers default
|
|
430
|
+
* 50 on the free plan, Vercel + Deno enforce comparable ceilings). The axis
|
|
431
|
+
* tracks a running count against the limit: below a warning threshold the
|
|
432
|
+
* session is `ok`, at the threshold it is `approaching-limit`, and once the
|
|
433
|
+
* count reaches the hard limit it is `limited` and further fetches are refused.
|
|
434
|
+
*/
|
|
435
|
+
type SubrequestState = 'ok' | 'approaching-limit' | 'limited';
|
|
436
|
+
interface SubrequestSession {
|
|
437
|
+
platform: EdgePlatform;
|
|
438
|
+
count: number;
|
|
439
|
+
limit: number;
|
|
440
|
+
warningThreshold: number;
|
|
441
|
+
state: SubrequestState;
|
|
442
|
+
history: AxisStep<SubrequestState>[];
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Open a subrequest budget. `limit` defaults to 50 (Workers free-plan default)
|
|
446
|
+
* and `warningThreshold` to 40 (80% of the default limit). Emits nothing — the
|
|
447
|
+
* budget is inert until the first {@link startSubrequest}.
|
|
448
|
+
*/
|
|
449
|
+
declare function startSubrequestBudget(input: {
|
|
450
|
+
platform: EdgePlatform;
|
|
451
|
+
limit?: number;
|
|
452
|
+
warningThreshold?: number;
|
|
453
|
+
}): SubrequestSession;
|
|
454
|
+
/**
|
|
455
|
+
* Announce an outbound subrequest. Emits `subrequest.started` but does not
|
|
456
|
+
* advance the count (starting is distinct from counting — a started request
|
|
457
|
+
* only counts once it is admitted via {@link countSubrequest}). Rejects when
|
|
458
|
+
* the budget is already `limited`.
|
|
459
|
+
*/
|
|
460
|
+
declare function startSubrequest(session: SubrequestSession, input: {
|
|
461
|
+
url: string;
|
|
462
|
+
}): AxisStep<SubrequestState>;
|
|
463
|
+
/**
|
|
464
|
+
* Count an admitted subrequest against the budget. Increments the count and
|
|
465
|
+
* emits `subrequest.limited` when the count reaches the hard limit (state →
|
|
466
|
+
* `limited`), otherwise `subrequest.counted` — flipping to `approaching-limit`
|
|
467
|
+
* once the warning threshold is crossed.
|
|
468
|
+
*/
|
|
469
|
+
declare function countSubrequest(session: SubrequestSession): AxisStep<SubrequestState>;
|
|
470
|
+
/**
|
|
471
|
+
* Mark an outbound subrequest as finished. Emits `subrequest.completed` with
|
|
472
|
+
* the final count. Does not mutate state — a completed request that already
|
|
473
|
+
* tripped the limit stays `limited`.
|
|
474
|
+
*/
|
|
475
|
+
declare function completeSubrequest(session: SubrequestSession, input: {
|
|
476
|
+
url: string;
|
|
477
|
+
durationMs: number;
|
|
478
|
+
}): AxisStep<SubrequestState>;
|
|
479
|
+
/** Remaining subrequest budget (never negative). */
|
|
480
|
+
declare function remainingBudget(session: SubrequestSession): number;
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* CPU time limit — per-invocation compute budget. Edge runtimes bill wall-clock
|
|
484
|
+
* loosely but enforce a hard CPU budget (Cloudflare Workers 50ms on the free
|
|
485
|
+
* plan, Vercel + Deno enforce comparable ceilings). The axis accumulates
|
|
486
|
+
* elapsed CPU time across ticks: below a warning threshold it is `running`, at
|
|
487
|
+
* the threshold it flips to `warning`, and once the budget is exhausted the
|
|
488
|
+
* invocation is `throttled` and no further work is admitted.
|
|
489
|
+
*/
|
|
490
|
+
type CpuState = 'idle' | 'running' | 'warning' | 'throttled' | 'completed';
|
|
491
|
+
interface CpuSession {
|
|
492
|
+
platform: EdgePlatform;
|
|
493
|
+
budgetMs: number;
|
|
494
|
+
warningAtMs: number;
|
|
495
|
+
elapsedMs: number;
|
|
496
|
+
state: CpuState;
|
|
497
|
+
history: AxisStep<CpuState>[];
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* Open a CPU budget. `budgetMs` defaults to 50 (Workers free-plan default) and
|
|
501
|
+
* `warningAtMs` to 40 (80% of the default budget). Emits nothing — the budget
|
|
502
|
+
* is `idle` until {@link startCpu}.
|
|
503
|
+
*/
|
|
504
|
+
declare function startCpuBudget(input: {
|
|
505
|
+
platform: EdgePlatform;
|
|
506
|
+
budgetMs?: number;
|
|
507
|
+
warningAtMs?: number;
|
|
508
|
+
}): CpuSession;
|
|
509
|
+
/**
|
|
510
|
+
* Begin consuming the CPU budget. Transitions `idle` → `running` and emits
|
|
511
|
+
* `cpu.started`. Rejects if the session is not `idle`.
|
|
512
|
+
*/
|
|
513
|
+
declare function startCpu(session: CpuSession): AxisStep<CpuState>;
|
|
514
|
+
/**
|
|
515
|
+
* Advance the CPU clock by `deltaMs`. Emits `cpu.limited` when the accumulated
|
|
516
|
+
* time reaches the budget (state → `throttled`), `cpu.budget-warning` when it
|
|
517
|
+
* crosses the warning threshold (state → `warning`), otherwise a `cpu.started`
|
|
518
|
+
* heartbeat carrying the remaining budget. Rejects once the session is
|
|
519
|
+
* `throttled` or `completed`.
|
|
520
|
+
*/
|
|
521
|
+
declare function tickCpu(session: CpuSession, input: {
|
|
522
|
+
deltaMs: number;
|
|
523
|
+
}): AxisStep<CpuState>;
|
|
524
|
+
/**
|
|
525
|
+
* Finish the invocation. Transitions to `completed` and emits `cpu.completed`
|
|
526
|
+
* with the used ratio. Rejects if the session never started (`idle`).
|
|
527
|
+
*/
|
|
528
|
+
declare function completeCpu(session: CpuSession): AxisStep<CpuState>;
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* Streaming response — chunked / SSE / websocket body delivery with
|
|
532
|
+
* backpressure. Edge runtimes stream responses through a bounded buffer: while
|
|
533
|
+
* buffered bytes stay under the high-water mark the stream is `open` and chunks
|
|
534
|
+
* flow freely; once the mark is exceeded the stream enters `backpressure` and
|
|
535
|
+
* the producer must wait for the consumer to drain before resuming.
|
|
536
|
+
*/
|
|
537
|
+
type StreamState = 'open' | 'backpressure' | 'closed';
|
|
538
|
+
/** Delivery mechanism for the streamed body. */
|
|
539
|
+
type StreamKind = 'chunked' | 'sse' | 'websocket';
|
|
540
|
+
interface StreamSession {
|
|
541
|
+
id: string;
|
|
542
|
+
platform: EdgePlatform;
|
|
543
|
+
kind: StreamKind;
|
|
544
|
+
state: StreamState;
|
|
545
|
+
chunksSent: number;
|
|
546
|
+
bytesSent: number;
|
|
547
|
+
highWaterMark: number;
|
|
548
|
+
history: AxisStep<StreamState>[];
|
|
549
|
+
}
|
|
550
|
+
/**
|
|
551
|
+
* Open a response stream. `kind` defaults to `chunked` and `highWaterMark` to
|
|
552
|
+
* 65536 bytes (64 KiB). Emits `stream.opened` and seeds counters at zero.
|
|
553
|
+
*/
|
|
554
|
+
declare function openStream(input: {
|
|
555
|
+
id: string;
|
|
556
|
+
platform: EdgePlatform;
|
|
557
|
+
kind?: StreamKind;
|
|
558
|
+
highWaterMark?: number;
|
|
559
|
+
}): StreamSession;
|
|
560
|
+
/**
|
|
561
|
+
* Write a chunk to the stream. Advances `chunksSent` + `bytesSent`; when the
|
|
562
|
+
* buffered byte total exceeds the high-water mark the stream flips to
|
|
563
|
+
* `backpressure` and emits `stream.backpressure`, otherwise `stream.chunk-sent`.
|
|
564
|
+
* Rejects if the stream is already `closed`.
|
|
565
|
+
*/
|
|
566
|
+
declare function sendChunk(session: StreamSession, input: {
|
|
567
|
+
data: string;
|
|
568
|
+
}): AxisStep<StreamState>;
|
|
569
|
+
/**
|
|
570
|
+
* Resume a back-pressured stream after the consumer drained. Transitions
|
|
571
|
+
* `backpressure` → `open`, drains one high-water mark worth of buffered bytes,
|
|
572
|
+
* and re-emits `stream.chunk-sent` tagged `resumed: true` (there is no distinct
|
|
573
|
+
* neutral resume event). Rejects unless the stream is `backpressure`.
|
|
574
|
+
*/
|
|
575
|
+
declare function resumeStream(session: StreamSession): AxisStep<StreamState>;
|
|
576
|
+
/**
|
|
577
|
+
* Close the stream. Transitions to `closed` and emits `stream.closed` with the
|
|
578
|
+
* final chunk + byte totals. Rejects if the stream is already `closed`.
|
|
579
|
+
*/
|
|
580
|
+
declare function closeStream(session: StreamSession, input: {
|
|
581
|
+
reason: string;
|
|
582
|
+
}): AxisStep<StreamState>;
|
|
583
|
+
|
|
584
|
+
export { AXIS_TO_EVENTS, type AxisStep, type CpuSession, type CpuState, type CronSession, type CronState, type CronTriggerType, type DoState, type DurableObjectSession, type EdgeAxis, type EdgeEnvBindings, type EdgeFetchHandler, type EdgeKvSession, type EdgePlatform, type FidelityCoverage, type FidelityRow, type GeoRegion, type GeoReplicatedSession, type GeoState, type InvokeEdgeHandlerOptions, type InvokeEdgeHandlerResult, type KVMockEntry, type KVNamespace, type KVNamespaceListOptions, type KVNamespaceListResult, type KVNamespacePutOptions, type KvState, type NeutralEventName, type SimulatedExecutionContext, type StreamKind, type StreamSession, type StreamState, type SubrequestSession, type SubrequestState, type WebSocketSession, type WsState, acceptWebSocket, closeStream, closeWebSocket, collectFidelityCoverage, completeCpu, completeCron, completeSubrequest, countSubrequest, createDurableObject, createEdgeKvSession, createGeoReplicatedSession, createKvNamespace, failCron, fireAlarm, geoPrimaryWrite, invokeEdgeHandler, kvRangeQuery, kvRead, kvWrite, markReplicaLagged, openStream, platformEventName, remainingBudget, requestDurableObject, requestWebSocketUpgrade, resolveConflict, resumeStream, scheduleCron, sendChunk, sendMessage, startCpu, startCpuBudget, startCron, startSubrequest, startSubrequestBudget, syncReplica, tickCpu, writeStorage };
|