@lunora/platform 1.0.0-alpha.5 → 1.0.0-alpha.50
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/conformance/index.d.mts +1 -1
- package/dist/conformance/index.d.ts +1 -1
- package/dist/conformance/index.mjs +1 -1
- package/dist/conformance/suite.d.mts +57 -18
- package/dist/conformance/suite.d.ts +57 -18
- package/dist/conformance/suite.mjs +1 -1
- package/dist/index.d.mts +737 -53
- package/dist/index.d.ts +737 -53
- package/dist/index.mjs +1 -1
- package/dist/packem_shared/CELLD_CAPABILITIES--dZeylT-.mjs +1 -0
- package/dist/packem_shared/CLOUDFLARE_CAPABILITIES-B0h9qH2z.mjs +1 -0
- package/dist/packem_shared/NODE_CAPABILITIES-ChAHZ-QD.mjs +1 -0
- package/dist/packem_shared/createReferenceHost-B9XE4Gpl.mjs +1 -0
- package/dist/packem_shared/resolveShard-BzKOUEO4.mjs +1 -0
- package/dist/packem_shared/{socket-host.d-DCVFvmXn.d.ts → socket-host.d-BsnGTydH.d.mts} +121 -41
- package/dist/packem_shared/{socket-host.d-DCVFvmXn.d.mts → socket-host.d-BsnGTydH.d.ts} +121 -41
- package/package.json +1 -1
- package/dist/packem_shared/CLOUDFLARE_CAPABILITIES-DMLgo_TI.mjs +0 -1
- package/dist/packem_shared/createReferenceHost-fs0Q8_VA.mjs +0 -1
- package/dist/packem_shared/resolveShard-uGhAKuTB.mjs +0 -1
|
@@ -141,6 +141,16 @@ interface SchedulerHost {
|
|
|
141
141
|
* presence is the host's declaration that dynamic cron works, exactly as
|
|
142
142
|
* with `SocketHost.setTag`. A caller that finds it absent must fall
|
|
143
143
|
* back to the target's declarative configuration.
|
|
144
|
+
*
|
|
145
|
+
* **Nothing in the framework calls this today.** `@lunora/platform-node`
|
|
146
|
+
* implements it (parser, persistence, timer re-arm) and the conformance
|
|
147
|
+
* suite is its only caller: no runtime walks an app's declared crons — the
|
|
148
|
+
* generated `LUNORA_CRONS` map — into it. The only cron dispatch that ships
|
|
149
|
+
* is `@lunora/runtime`'s, reached from Cloudflare's `scheduled()` handler,
|
|
150
|
+
* which is declarative and never touches this method. So a host that
|
|
151
|
+
* implements `cron` gains nothing until that wiring exists, and an app
|
|
152
|
+
* declaring crons on such a host has none of them fire. Wire it before
|
|
153
|
+
* treating a `cron`-bearing host as one that runs declared schedules.
|
|
144
154
|
*/
|
|
145
155
|
cron?: (cron: string, functionPath: string, args?: Record<string, unknown>) => Promise<void>;
|
|
146
156
|
/**
|
|
@@ -180,20 +190,32 @@ interface SchedulerHost {
|
|
|
180
190
|
schedule: (functionPath: string, args: Record<string, unknown>, options?: ScheduleOptions) => Promise<ScheduledJob>;
|
|
181
191
|
}
|
|
182
192
|
/**
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
*
|
|
186
|
-
*
|
|
193
|
+
* Edge geography → placement region, shared by `@lunora/runtime` (which reads
|
|
194
|
+
* `request.cf` to pick where a shard, replica, or region-local socket should
|
|
195
|
+
* live) and `@lunora/do` (which parses a region out of its own DO name). Kept
|
|
196
|
+
* here — inlined into each consumer's bundle — so the two sides can never drift
|
|
197
|
+
* on the region vocabulary without creating a runtime dependency edge between
|
|
198
|
+
* the packages.
|
|
187
199
|
*
|
|
188
|
-
* The
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
*
|
|
192
|
-
*
|
|
200
|
+
* The values are Cloudflare's Durable Object location hints, which is also the
|
|
201
|
+
* only vocabulary a Lunora deployment needs today: a region is *only* ever used
|
|
202
|
+
* as a placement hint and as a name segment, never as data. Wrong-but-close is
|
|
203
|
+
* fine by construction — a misrouted read is one longer hop, never a wrong
|
|
204
|
+
* answer — so this maps coarsely and returns `undefined` rather than guessing
|
|
205
|
+
* when the request carries no usable geography.
|
|
193
206
|
*
|
|
194
|
-
*
|
|
195
|
-
*
|
|
207
|
+
* Zero-dependency by design (see the repo's `shared/` rules): only relative /
|
|
208
|
+
* builtin imports, named exports, no `.js` extensions.
|
|
209
|
+
*/
|
|
210
|
+
/**
|
|
211
|
+
* The placement regions a name may carry and a hint may request — Cloudflare's
|
|
212
|
+
* `DurableObjectLocationHint` values, listed so the set can be validated at a
|
|
213
|
+
* trust boundary (a region parsed out of a DO name is attacker-influenced input
|
|
214
|
+
* on any route that mints names from a client-supplied shard key).
|
|
196
215
|
*/
|
|
216
|
+
declare const REGION_HINTS: readonly ["wnam", "enam", "sam", "weur", "eeur", "apac", "apac-ne", "apac-se", "oc", "afr", "me"];
|
|
217
|
+
/** One placement region. Structurally identical to Cloudflare's `DurableObjectLocationHint`. */
|
|
218
|
+
type RegionHint = (typeof REGION_HINTS)[number];
|
|
197
219
|
/**
|
|
198
220
|
* Cloudflare Durable Object jurisdictions restrict where a DO runs and
|
|
199
221
|
* persists data, for data-residency / compliance regimes (GDPR, FedRAMP, US
|
|
@@ -206,6 +228,23 @@ interface SchedulerHost {
|
|
|
206
228
|
* leave them unsupported.
|
|
207
229
|
*/
|
|
208
230
|
type ShardJurisdiction = "eu" | "fedramp" | "us" | (Record<never, never> & string);
|
|
231
|
+
/**
|
|
232
|
+
* A geographic placement region — where a shard should be created, when the
|
|
233
|
+
* caller has an opinion.
|
|
234
|
+
*
|
|
235
|
+
* One vocabulary, defined once: `shared/region-hint.ts` owns the region list
|
|
236
|
+
* (it is also what derives a region from edge geography), and this contract
|
|
237
|
+
* re-exports it rather than restating the strings. A second list is how the two
|
|
238
|
+
* ends of a placement request drift apart.
|
|
239
|
+
*
|
|
240
|
+
* Unlike a jurisdiction — a hard constraint the caller must fail closed on — a
|
|
241
|
+
* region is **best effort and advisory**: a provider may ignore it, and on
|
|
242
|
+
* Cloudflare it is honoured only by the call that first creates the object.
|
|
243
|
+
* Everything downstream must work identically whether the hint was honoured,
|
|
244
|
+
* ignored, or never supplied, and no caller may treat a resolved stub's
|
|
245
|
+
* location as known.
|
|
246
|
+
*/
|
|
247
|
+
type ShardRegionHint = RegionHint;
|
|
209
248
|
/**
|
|
210
249
|
* A resolved shard stub. The engine calls `fetch` (or an equivalent RPC
|
|
211
250
|
* method) to dispatch work to the shard.
|
|
@@ -222,9 +261,9 @@ interface ShardStub {
|
|
|
222
261
|
*/
|
|
223
262
|
interface DirectShardDirectory {
|
|
224
263
|
/** Resolve an opaque id (from `idForName`) to a stub, when the provider has ids. */
|
|
225
|
-
get?: (id: unknown) => ShardStub;
|
|
264
|
+
get?: (id: unknown, locationHint?: ShardRegionHint) => ShardStub;
|
|
226
265
|
/** Resolve a shard key to a stub. */
|
|
227
|
-
getByName: (name: string) => ShardStub;
|
|
266
|
+
getByName: (name: string, locationHint?: ShardRegionHint) => ShardStub;
|
|
228
267
|
/** Derive a stable, opaque shard id from a shard key, when the provider has ids. */
|
|
229
268
|
idForName?: (name: string) => unknown;
|
|
230
269
|
/** See {@link ShardDirectory}. */
|
|
@@ -237,7 +276,7 @@ interface DirectShardDirectory {
|
|
|
237
276
|
*/
|
|
238
277
|
interface TwoStepShardDirectory {
|
|
239
278
|
/** Resolve an opaque id (from `idForName`) to a stub. */
|
|
240
|
-
get: (id: unknown) => ShardStub;
|
|
279
|
+
get: (id: unknown, locationHint?: ShardRegionHint) => ShardStub;
|
|
241
280
|
/**
|
|
242
281
|
* Absent — the discriminant that selects the two-step branch.
|
|
243
282
|
*/
|
|
@@ -269,8 +308,13 @@ type ShardDirectory = DirectShardDirectory | TwoStepShardDirectory;
|
|
|
269
308
|
* Resolve a shard key to a stub against either directory shape. Uses direct
|
|
270
309
|
* name lookup when the provider has it, and falls back to the two-step
|
|
271
310
|
* `idForName` + `get` dance otherwise.
|
|
311
|
+
*
|
|
312
|
+
* `locationHint` is forwarded to whichever branch runs. It is advisory in
|
|
313
|
+
* both: a provider with no placement concept ignores the extra argument, which
|
|
314
|
+
* is exactly what an implementation written against the pre-placement
|
|
315
|
+
* signature does.
|
|
272
316
|
*/
|
|
273
|
-
declare const resolveShard: (directory: ShardDirectory, name: string) => ShardStub;
|
|
317
|
+
declare const resolveShard: (directory: ShardDirectory, name: string, locationHint?: ShardRegionHint) => ShardStub;
|
|
274
318
|
/**
|
|
275
319
|
* `ShardHost` — the provider-neutral contract for a single-writer, durable
|
|
276
320
|
* shard execution slot. On Cloudflare this is backed by one Durable Object
|
|
@@ -301,22 +345,47 @@ type SqlRow = Record<string, unknown>;
|
|
|
301
345
|
/**
|
|
302
346
|
* The result of one statement: a cursor over its rows.
|
|
303
347
|
*
|
|
304
|
-
*
|
|
305
|
-
*
|
|
306
|
-
*
|
|
307
|
-
*
|
|
308
|
-
*
|
|
348
|
+
* The three members the ENGINE uses are required, because a host that omits one
|
|
349
|
+
* fails at runtime rather than at compile time. That is not hypothetical — an
|
|
350
|
+
* earlier revision of this contract made `toArray` optional and offered a
|
|
351
|
+
* `rowsAffected` nothing reads, which meant a host could satisfy the type and
|
|
352
|
+
* still be unusable.
|
|
309
353
|
*
|
|
310
354
|
* Iteration is part of the contract because read paths stream cursors directly
|
|
311
355
|
* rather than buffering; `toArray` is the buffered form, and `one` is the
|
|
312
356
|
* exactly-one-row form used by lookups and aggregates.
|
|
357
|
+
*
|
|
358
|
+
* `columnNames` / `raw` are the exception and are deliberately optional: no
|
|
359
|
+
* engine path needs them, one admin read-back reports a better result when they
|
|
360
|
+
* are there, and that surface degrades explicitly when they are not. Requiring
|
|
361
|
+
* them would make a host unusable over a detail it can live without.
|
|
313
362
|
*/
|
|
314
363
|
interface ShardSqlCursor<Row = SqlRow> extends Iterable<Row> {
|
|
364
|
+
/**
|
|
365
|
+
* The result columns in SELECT order, as the statement declares them, before
|
|
366
|
+
* a row object collapses them into keys. Mirrors Cloudflare
|
|
367
|
+
* `SqlStorageCursor`'s field of the same name.
|
|
368
|
+
*
|
|
369
|
+
* Optional, and absent is a supported answer rather than a bug: a host
|
|
370
|
+
* supplies it when its driver has one (Cloudflare's cursor, better-sqlite3's
|
|
371
|
+
* `Statement#columns`, `node:sqlite`'s `columns()` from Node 22.14 on) and
|
|
372
|
+
* omits it otherwise. A reader with it can report a result faithfully;
|
|
373
|
+
* without it, a zero-row result has no column list at all and two same-named
|
|
374
|
+
* result columns (`SELECT u.id, o.id`) collapse to one.
|
|
375
|
+
*/
|
|
376
|
+
readonly columnNames?: string[];
|
|
315
377
|
/**
|
|
316
378
|
* The single row this statement produced.
|
|
317
379
|
* @throws when the result does not hold exactly one row.
|
|
318
380
|
*/
|
|
319
381
|
one: () => Row;
|
|
382
|
+
/**
|
|
383
|
+
* The rows as positional value arrays aligned with `columnNames` rather
|
|
384
|
+
* than as objects — the only shape that survives two result columns sharing
|
|
385
|
+
* a name. Optional alongside `columnNames` (a host offers both or neither),
|
|
386
|
+
* and consuming it consumes the cursor: call it *or* `toArray`, never both.
|
|
387
|
+
*/
|
|
388
|
+
raw?: () => IterableIterator<unknown[]>;
|
|
320
389
|
/** Buffer every row. */
|
|
321
390
|
toArray: () => Row[];
|
|
322
391
|
}
|
|
@@ -340,17 +409,6 @@ interface ShardSqlExec {
|
|
|
340
409
|
/** Execute a SQL statement with optional bound parameters. */
|
|
341
410
|
exec: <Row = SqlRow>(query: string, ...bindings: ReadonlyArray<unknown>) => ShardSqlCursor<Row>;
|
|
342
411
|
}
|
|
343
|
-
/**
|
|
344
|
-
* Async SQL executor used by the engine's higher-level paths (global tables,
|
|
345
|
-
* metrics, auth). Already defined in `@lunora/sql-store` as `SqlExec`; this
|
|
346
|
-
* alias keeps the platform contract self-contained.
|
|
347
|
-
*/
|
|
348
|
-
interface ShardAsyncSqlExec {
|
|
349
|
-
all: (sql: string, params: ReadonlyArray<unknown>) => Promise<SqlRow[]>;
|
|
350
|
-
run: (sql: string, params: ReadonlyArray<unknown>) => Promise<{
|
|
351
|
-
rowsAffected: number;
|
|
352
|
-
}>;
|
|
353
|
-
}
|
|
354
412
|
/**
|
|
355
413
|
* Alarm scheduling for a shard. Alarms are durable: they survive host
|
|
356
414
|
* recycling and fire at the requested timestamp.
|
|
@@ -368,16 +426,21 @@ interface ShardAlarms {
|
|
|
368
426
|
}
|
|
369
427
|
/**
|
|
370
428
|
* The core shard host contract. One instance per shard key.
|
|
429
|
+
*
|
|
430
|
+
* **At most one LIVE instance per shard key, at any moment — a requirement,
|
|
431
|
+
* not a convention.** `ShardDO` keeps correctness-bearing state in memory on
|
|
432
|
+
* that basis: its in-flight dispatch claim (`@lunora/do`'s `InFlightClaims`)
|
|
433
|
+
* declines a second concurrent delivery of the same idempotency id only because
|
|
434
|
+
* every delivery for the shard reaches the same instance, and treats a missing
|
|
435
|
+
* claim as "the holder is gone" only because a replaced instance takes its
|
|
436
|
+
* running handlers with it. A host that could run two instances of one shard
|
|
437
|
+
* side by side (a second Node process, a celld failover that overlaps the old
|
|
438
|
+
* cell) would let an action run twice concurrently. Cloudflare Durable Objects
|
|
439
|
+
* provide this natively; every other host must enforce it.
|
|
371
440
|
*/
|
|
372
441
|
interface ShardHost {
|
|
373
442
|
/** Durable alarm scheduling for the shard. */
|
|
374
443
|
alarms: ShardAlarms;
|
|
375
|
-
/**
|
|
376
|
-
* Async SQL executor for engine paths that need promise-based row access
|
|
377
|
-
* (global tables, metrics, auth). Hosts may implement this over the same
|
|
378
|
-
* underlying storage as `sql`.
|
|
379
|
-
*/
|
|
380
|
-
asyncSql?: ShardAsyncSqlExec;
|
|
381
444
|
/**
|
|
382
445
|
* Run `fn` with exclusive ownership of the shard. Concurrent calls are
|
|
383
446
|
* queued; no two closures run at once for the same shard key. On
|
|
@@ -560,9 +623,26 @@ interface SocketHost {
|
|
|
560
623
|
* durable tag minted at accept (Cloudflare), a registry key, a `WeakMap`.
|
|
561
624
|
*
|
|
562
625
|
* Must answer consistently for the same socket within a wake AND across a
|
|
563
|
-
* recycle
|
|
564
|
-
*
|
|
565
|
-
*
|
|
626
|
+
* recycle. Callers outside the O(subscribers) loops are the intended
|
|
627
|
+
* consumers; do not reach for this per socket per frame.
|
|
628
|
+
*
|
|
629
|
+
* **Who actually calls it, since the previous wording named the wrong
|
|
630
|
+
* caller.** It said "the engine uses it to reassociate a rehydrated socket
|
|
631
|
+
* with its subscription state"; the engine does no such thing. Per-socket
|
|
632
|
+
* state is keyed on the handle object itself (`ShardRunner.socketFor`, over
|
|
633
|
+
* {@link SocketHost.handleFor}), and durable connection identity is the
|
|
634
|
+
* engine's own `connectionId` — a UUID minted at upgrade and stamped onto the
|
|
635
|
+
* socket attachment, which is what survives hibernation. `@lunora/shard-engine`
|
|
636
|
+
* and `@lunora/do` dispatch to `handleFor`, `getSockets` and `accept`, and to
|
|
637
|
+
* nothing else on this interface.
|
|
638
|
+
*
|
|
639
|
+
* `idFor` is the portable **identity oracle** instead: the conformance suite
|
|
640
|
+
* compares sockets through it (a host is allowed to hand back a different
|
|
641
|
+
* wrapper object for the same socket, so object identity is not a legal
|
|
642
|
+
* assertion), and it is how a host's own recycle plumbing addresses a socket
|
|
643
|
+
* by id. That is a real job and it is why this stays required — every host
|
|
644
|
+
* has to answer it for the TCK regardless — but implement it as a test and
|
|
645
|
+
* tooling contract, not as something on the frame path.
|
|
566
646
|
*
|
|
567
647
|
* **A socket this host never {@link SocketHost.accept}ed** (a foreign socket
|
|
568
648
|
* the runtime hands back — a whisper sender in another pool, a relay peer)
|
|
@@ -605,4 +685,4 @@ interface SocketHost {
|
|
|
605
685
|
*/
|
|
606
686
|
setTag?: (socket: SocketHandle, tag: string) => void;
|
|
607
687
|
}
|
|
608
|
-
export { DirectShardDirectory as D, ScheduleOptions as S, TwoStepShardDirectory as T, ScheduledJob as a, ScheduledJobStatus as b, SchedulerHost as c, ShardAlarms as d,
|
|
688
|
+
export { DirectShardDirectory as D, ScheduleOptions as S, TwoStepShardDirectory as T, ScheduledJob as a, ScheduledJobStatus as b, SchedulerHost as c, ShardAlarms as d, ShardDirectory as e, ShardHost as f, ShardJurisdiction as g, ShardKvListOptions as h, ShardKvStore as i, ShardRegionHint as j, ShardSqlCursor as k, ShardSqlExec as l, ShardStub as m, SocketHandle as n, SocketHost as o, SqlRow as p, resolveShard as r };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lunora/platform",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.50",
|
|
4
4
|
"description": "Provider-neutral host contracts for Lunora: shard/socket/directory/scheduler interfaces, binding projections, and the platform capability matrix",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cloudflare",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
const e={id:"cloudflare",name:"Cloudflare",features:{shardedState:{level:"native",note:"Durable Objects with SQLite"},globalTables:{level:"native",note:"D1 with Sessions API"},websocketHibernation:{level:"native",note:"DO WebSocket hibernation"},localSql:{level:"native",note:"state.storage.sql (SQLite)"},shardAlarms:{level:"native",note:"state.storage.setAlarm"},crossShardFanout:{level:"emulated",note:"Lunora query coordinator + relay tier over Durable Objects"},queues:{level:"native",note:"Cloudflare Queues"},workflows:{level:"native",note:"Cloudflare Workflows"},scheduler:{level:"emulated",note:"SchedulerDO (Lunora, on DO alarms) + declarative Cron Triggers; no runtime cron registration"},objectStorage:{level:"native",note:"R2"},keyValueStore:{level:"native",note:"Workers KV"},vectorStore:{level:"native",note:"Vectorize; query/upsert namespace scoping is native (remote filter), but getByIds/deleteByIds id-path tenant isolation is facade-enforced (client-side verification) since Vectorize's id operations take no namespace option"},ai:{level:"native",note:"Workers AI"},browser:{level:"native",note:"Browser Rendering"},containers:{level:"native",note:"Cloudflare Containers"},analytics:{level:"native",note:"Analytics Engine"},pipelines:{level:"native",note:"Cloudflare Pipelines"},mail:{level:"emulated",note:"Resend (third-party) via Cloudflare Queues"},secrets:{level:"native",note:"Secrets Store"},hyperdrive:{level:"native",note:"Cloudflare Hyperdrive"}}},t={id:"node",name:"Node",features:{shardedState:{level:"emulated",note:"One better-sqlite3 database per shard key, one process — no distributed placement or failover"},globalTables:{level:"unsupported",note:"No replicated SQL store (D1-equivalent) implemented"},websocketHibernation:{level:"emulated",note:"In-process socket registry; attachments/tags survive a simulated recycle, not a process restart, and nothing is ever actually evicted from memory"},localSql:{level:"native",note:"better-sqlite3 (synchronous, embedded)"},shardAlarms:{level:"unsupported",note:"In-process bookkeeping only — the armed timer clears state and never wakes anything; no dispatch, and nothing re-arms across a restart"},crossShardFanout:{level:"unsupported",note:"No query coordinator / relay tier implemented"},queues:{level:"unsupported",note:"No Cloudflare Queues equivalent implemented"},workflows:{level:"unsupported",note:"No Cloudflare Workflows equivalent implemented"},scheduler:{level:"unsupported",note:"Jobs are stored and timed but never dispatched — no delivery, no retries; also not durable across a process restart"},objectStorage:{level:"unsupported",note:"No R2/S3-equivalent binding implemented"},keyValueStore:{level:"emulated",note:"better-sqlite3 table behind the ShardKvStore API — not a dedicated KV product"},vectorStore:{level:"unsupported",note:"No Vectorize-equivalent binding implemented"},ai:{level:"unsupported",note:"No Workers AI-equivalent binding implemented"},browser:{level:"unsupported",note:"No headless-browser binding implemented"},containers:{level:"unsupported",note:"No container orchestration implemented"},analytics:{level:"unsupported",note:"No Analytics Engine-equivalent binding implemented"},pipelines:{level:"unsupported",note:"No Pipelines-equivalent binding implemented"},mail:{level:"unsupported",note:"@lunora/mail's queue-backed sends need a queues binding, which this target does not provide"},secrets:{level:"unsupported",note:"No Secrets Store-equivalent binding implemented (a real host would likely map this to env vars)"},hyperdrive:{level:"unsupported",note:"No connection-pooling binding implemented"}}};export{e as CLOUDFLARE_CAPABILITIES,t as NODE_CAPABILITIES};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{DatabaseSync as R}from"node:sqlite";let b=0,S=0;const j=()=>(b+=1,`socket-${b}`),C=()=>(S+=1,`job-${S}`),q=n=>n===void 0?null:n,E=n=>typeof n=="string"?new TextEncoder().encode(n).buffer:n instanceof ArrayBuffer?n:ArrayBuffer.isView(n)?n.buffer.slice(n.byteOffset,n.byteOffset+n.byteLength):new ArrayBuffer(0),O=()=>{const n=new R(":memory:"),s={alarmAt:null,alarmTimeout:null,pending:[],running:!1},l=new Map,f=new Map,u=new Map,x={exec:(e,...t)=>{const a=n.prepare(e),r=t.map(q),o=e.trim().toLowerCase().startsWith("select")?a.all(...r):(a.run(...r),[]);return{[Symbol.iterator]:()=>o[Symbol.iterator](),one:()=>{if(o.length!==1)throw new Error(`expected exactly one row, got ${String(o.length)}`);return o[0]},toArray:()=>[...o]}}},M={all:async(e,t)=>n.prepare(e).all(...t),run:async(e,t)=>{const a=n.prepare(e).run(...t);return{rowsAffected:Number(a.changes)}}},h=()=>{if(s.running||s.pending.length===0)return;const e=s.pending.shift();e!==void 0&&(s.running=!0,e.function_().then(e.resolve,e.reject).finally(()=>{s.running=!1,h()}))},F=e=>new Promise((t,a)=>{s.pending.push({function_:e,reject:r=>{a(r)},resolve:r=>{t(r)}}),h()});let g=Promise.resolve();const p=async e=>{n.exec("BEGIN");try{const t=await e();return n.exec("COMMIT"),t}catch(t){throw n.exec("ROLLBACK"),t}},D={alarms:{delete:()=>{s.alarmAt=null,s.alarmTimeout!==null&&(clearTimeout(s.alarmTimeout),s.alarmTimeout=null)},get:()=>s.alarmAt,set:e=>{const t=typeof e=="number"?e:e.getTime();s.alarmAt=t,s.alarmTimeout!==null&&clearTimeout(s.alarmTimeout);const a=Math.max(0,t-Date.now());s.alarmTimeout=setTimeout(()=>{s.alarmAt=null,s.alarmTimeout=null},a)}},asyncSql:M,runSerialized:F,sql:x,transaction:e=>{const t=g.then(()=>p(e),()=>p(e));return g=t.then(()=>{},()=>{}),t},waitUntil:()=>{}},c=new WeakMap,w=e=>{const t={bufferedAmount:e.bufferedAmount,close:(a,r)=>{e.closed=!0},deserializeAttachment:()=>e.attachment,send:a=>{e.received.push(typeof a=="string"?a:E(a))},serializeAttachment:a=>{e.attachment=a,f.set(e.id,a)}};return e.handle=t,c.set(t,e.id),t},P={accept:(e,t,a)=>{const r=j(),o={attachment:t,bufferedAmount:0,closed:!1,raw:e,handle:null,id:r,received:[],tags:new Set(a)};return l.set(r,o),u.set(r,new Set(a)),t!==void 0&&f.set(r,t),w(o)},getSockets:e=>{const t=[...l.values()];return(e===void 0?t:t.filter(a=>a.tags.has(e))).map(a=>a.handle)},handleFor:e=>[...l.values()].find(t=>t.raw===e)?.handle,idFor:e=>{const t=c.get(e);if(t===void 0)throw new Error("reference host: idFor called with a handle this host never issued");return t},removeTag:(e,t)=>{const a=l.get(c.get(e)??"");a!==void 0&&(t===void 0?a.tags.clear():a.tags.delete(t),u.set(c.get(e)??"",new Set(a.tags)))},setTag:(e,t)=>{const a=c.get(e)??"",r=l.get(a);r!==void 0&&(r.tags.add(t),u.set(a,new Set(r.tags)))}},y={get:e=>({fetch:async()=>new Response(String(e))}),getByName:e=>({fetch:async()=>new Response(e)}),idForName:e=>`shard:${e}`,jurisdiction:e=>y},d=new Map,k={delete:async e=>d.delete(e),get:async e=>d.get(e),list:async e=>{const t=e?.prefix??"",a=new Map;for(const[r,o]of d)r.startsWith(t)&&a.set(r,o);return a},put:async(e,t)=>{d.set(e,structuredClone(t))}},i=new Map,m=new Map,v=new Set,T=(e,t)=>({attempts:t.attempts,functionPath:t.functionPath,id:e,scheduledFor:t.scheduledFor});return{awaitAlarmFired:async e=>{await new Promise(t=>{setTimeout(t,Math.max(0,e-Date.now())+30)})},awaitJobDispatched:async e=>{const t=i.get(e);return t!==void 0&&await new Promise(a=>{setTimeout(a,Math.max(0,t.scheduledFor-Date.now())+30)}),v.has(e)},cleanup:()=>{n.close(),s.alarmTimeout!==null&&clearTimeout(s.alarmTimeout);for(const e of i.values())clearTimeout(e.timer)},directory:y,kv:k,readFrames:e=>(l.get(c.get(e)??"")?.received??[]).filter(t=>typeof t=="string"),restoreSocket:(e,t)=>{const a={attachment:t,bufferedAmount:0,closed:!1,handle:null,id:e,raw:void 0,received:[],tags:new Set(u.get(e))};return l.set(e,a),w(a)},scheduler:{cancel:async e=>{const t=i.get(e);return t===void 0?!1:(clearTimeout(t.timer),i.delete(e),!0)},cron:async()=>{},deadLetter:{list:async()=>[...m].map(([e,t])=>T(e,t)),requeue:async e=>{const t=m.get(e);return t===void 0?!1:(m.delete(e),i.set(e,{...t,attempts:0,timer:void 0}),!0)}},list:async()=>[...i].map(([e,t])=>T(e,t)),schedule:async(e,t,a)=>{const r=C();let o;a?.at===void 0?o=Date.now()+(a?.delayMs??0):o=typeof a.at=="number"?a.at:a.at.getTime();const B=Math.max(0,o-Date.now()),L=setTimeout(()=>{const A=i.get(r);A!==void 0&&(A.attempts+=1),v.add(r),i.delete(r)},B);return i.set(r,{args:t,attempts:0,functionPath:e,options:a??{},scheduledFor:o,timer:L}),{id:r,scheduledFor:o}}},simulateDeadLetter:async e=>{const t=i.get(e);return t===void 0?!1:(clearTimeout(t.timer),i.delete(e),m.set(e,{...t,attempts:(t.options.retry?.maxAttempts??5)+1,timer:void 0}),!0)},shard:D,simulateRecycle:()=>{l.clear()},socket:P}};export{O as createReferenceHost};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
const t=(e,o)=>e.getByName!==void 0?e.getByName(o):e.get(e.idForName(o));export{t as resolveShard};
|