@lunora/platform 1.0.0-alpha.6 → 1.0.0-alpha.60

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.
@@ -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
- * `ShardDirectory` — the provider-neutral contract for resolving shard keys to
184
- * callable stubs. On Cloudflare this is backed by `DurableObjectNamespace`
185
- * (`idFromName` + `get` + `jurisdiction`). On another provider it may be an
186
- * actor registry, a consistent-hash router, or a local in-process map.
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 engine relies on two capabilities:
189
- * 1. **Deterministic placement** — a shard key always resolves to the same
190
- * logical shard (`idForName`).
191
- * 2. **RPC dispatch** — a resolved stub can receive a `fetch` request (or
192
- * equivalent RPC call) that the shard handles.
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
- * Placement hints (jurisdiction, region) are provider-mapped and may be
195
- * unsupported per the capability matrix.
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
  */
@@ -265,12 +304,24 @@ interface TwoStepShardDirectory {
265
304
  * closed when a jurisdiction is requested but the method is absent.
266
305
  */
267
306
  type ShardDirectory = DirectShardDirectory | TwoStepShardDirectory;
307
+ /**
308
+ * The instance-name prefix reserved for framework roles that share the shard
309
+ * directory when an app merges its Durable Object classes (plan 462): the
310
+ * scheduler lives at `__lunora_do__:scheduler:<name>`. No shard key may carry
311
+ * it, so {@link resolveShard} refuses one on every host.
312
+ */
313
+ declare const LUNORA_ROLE_PREFIX = "__lunora_do__:";
268
314
  /**
269
315
  * Resolve a shard key to a stub against either directory shape. Uses direct
270
316
  * name lookup when the provider has it, and falls back to the two-step
271
317
  * `idForName` + `get` dance otherwise.
318
+ *
319
+ * `locationHint` is forwarded to whichever branch runs. It is advisory in
320
+ * both: a provider with no placement concept ignores the extra argument, which
321
+ * is exactly what an implementation written against the pre-placement
322
+ * signature does.
272
323
  */
273
- declare const resolveShard: (directory: ShardDirectory, name: string) => ShardStub;
324
+ declare const resolveShard: (directory: ShardDirectory, name: string, locationHint?: ShardRegionHint) => ShardStub;
274
325
  /**
275
326
  * `ShardHost` — the provider-neutral contract for a single-writer, durable
276
327
  * shard execution slot. On Cloudflare this is backed by one Durable Object
@@ -301,22 +352,47 @@ type SqlRow = Record<string, unknown>;
301
352
  /**
302
353
  * The result of one statement: a cursor over its rows.
303
354
  *
304
- * Every member here is required, because the engine uses all three and a host
305
- * that omits one fails at runtime rather than at compile time. That is not
306
- * hypothetical — an earlier revision of this contract made `toArray` optional
307
- * and offered a `rowsAffected` nothing reads, which meant a host could satisfy
308
- * the type and still be unusable.
355
+ * The three members the ENGINE uses are required, because a host that omits one
356
+ * fails at runtime rather than at compile time. That is not hypothetical — an
357
+ * earlier revision of this contract made `toArray` optional and offered a
358
+ * `rowsAffected` nothing reads, which meant a host could satisfy the type and
359
+ * still be unusable.
309
360
  *
310
361
  * Iteration is part of the contract because read paths stream cursors directly
311
362
  * rather than buffering; `toArray` is the buffered form, and `one` is the
312
363
  * exactly-one-row form used by lookups and aggregates.
364
+ *
365
+ * `columnNames` / `raw` are the exception and are deliberately optional: no
366
+ * engine path needs them, one admin read-back reports a better result when they
367
+ * are there, and that surface degrades explicitly when they are not. Requiring
368
+ * them would make a host unusable over a detail it can live without.
313
369
  */
314
370
  interface ShardSqlCursor<Row = SqlRow> extends Iterable<Row> {
371
+ /**
372
+ * The result columns in SELECT order, as the statement declares them, before
373
+ * a row object collapses them into keys. Mirrors Cloudflare
374
+ * `SqlStorageCursor`'s field of the same name.
375
+ *
376
+ * Optional, and absent is a supported answer rather than a bug: a host
377
+ * supplies it when its driver has one (Cloudflare's cursor, better-sqlite3's
378
+ * `Statement#columns`, `node:sqlite`'s `columns()` from Node 22.14 on) and
379
+ * omits it otherwise. A reader with it can report a result faithfully;
380
+ * without it, a zero-row result has no column list at all and two same-named
381
+ * result columns (`SELECT u.id, o.id`) collapse to one.
382
+ */
383
+ readonly columnNames?: string[];
315
384
  /**
316
385
  * The single row this statement produced.
317
386
  * @throws when the result does not hold exactly one row.
318
387
  */
319
388
  one: () => Row;
389
+ /**
390
+ * The rows as positional value arrays aligned with `columnNames` rather
391
+ * than as objects — the only shape that survives two result columns sharing
392
+ * a name. Optional alongside `columnNames` (a host offers both or neither),
393
+ * and consuming it consumes the cursor: call it *or* `toArray`, never both.
394
+ */
395
+ raw?: () => IterableIterator<unknown[]>;
320
396
  /** Buffer every row. */
321
397
  toArray: () => Row[];
322
398
  }
@@ -340,17 +416,6 @@ interface ShardSqlExec {
340
416
  /** Execute a SQL statement with optional bound parameters. */
341
417
  exec: <Row = SqlRow>(query: string, ...bindings: ReadonlyArray<unknown>) => ShardSqlCursor<Row>;
342
418
  }
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
419
  /**
355
420
  * Alarm scheduling for a shard. Alarms are durable: they survive host
356
421
  * recycling and fire at the requested timestamp.
@@ -368,16 +433,21 @@ interface ShardAlarms {
368
433
  }
369
434
  /**
370
435
  * The core shard host contract. One instance per shard key.
436
+ *
437
+ * **At most one LIVE instance per shard key, at any moment — a requirement,
438
+ * not a convention.** `ShardDO` keeps correctness-bearing state in memory on
439
+ * that basis: its in-flight dispatch claim (`@lunora/do`'s `InFlightClaims`)
440
+ * declines a second concurrent delivery of the same idempotency id only because
441
+ * every delivery for the shard reaches the same instance, and treats a missing
442
+ * claim as "the holder is gone" only because a replaced instance takes its
443
+ * running handlers with it. A host that could run two instances of one shard
444
+ * side by side (a second Node process, a celld failover that overlaps the old
445
+ * cell) would let an action run twice concurrently. Cloudflare Durable Objects
446
+ * provide this natively; every other host must enforce it.
371
447
  */
372
448
  interface ShardHost {
373
449
  /** Durable alarm scheduling for the shard. */
374
450
  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
451
  /**
382
452
  * Run `fn` with exclusive ownership of the shard. Concurrent calls are
383
453
  * queued; no two closures run at once for the same shard key. On
@@ -560,9 +630,26 @@ interface SocketHost {
560
630
  * durable tag minted at accept (Cloudflare), a registry key, a `WeakMap`.
561
631
  *
562
632
  * Must answer consistently for the same socket within a wake AND across a
563
- * recycle, since the engine uses it to reassociate a rehydrated socket with
564
- * its subscription state. Callers outside the O(subscribers) loops are the
565
- * intended consumers; do not reach for this per socket per frame.
633
+ * recycle. Callers outside the O(subscribers) loops are the intended
634
+ * consumers; do not reach for this per socket per frame.
635
+ *
636
+ * **Who actually calls it, since the previous wording named the wrong
637
+ * caller.** It said "the engine uses it to reassociate a rehydrated socket
638
+ * with its subscription state"; the engine does no such thing. Per-socket
639
+ * state is keyed on the handle object itself (`ShardRunner.socketFor`, over
640
+ * {@link SocketHost.handleFor}), and durable connection identity is the
641
+ * engine's own `connectionId` — a UUID minted at upgrade and stamped onto the
642
+ * socket attachment, which is what survives hibernation. `@lunora/shard-engine`
643
+ * and `@lunora/do` dispatch to `handleFor`, `getSockets` and `accept`, and to
644
+ * nothing else on this interface.
645
+ *
646
+ * `idFor` is the portable **identity oracle** instead: the conformance suite
647
+ * compares sockets through it (a host is allowed to hand back a different
648
+ * wrapper object for the same socket, so object identity is not a legal
649
+ * assertion), and it is how a host's own recycle plumbing addresses a socket
650
+ * by id. That is a real job and it is why this stays required — every host
651
+ * has to answer it for the TCK regardless — but implement it as a test and
652
+ * tooling contract, not as something on the frame path.
566
653
  *
567
654
  * **A socket this host never {@link SocketHost.accept}ed** (a foreign socket
568
655
  * the runtime hands back — a whisper sender in another pool, a relay peer)
@@ -605,4 +692,4 @@ interface SocketHost {
605
692
  */
606
693
  setTag?: (socket: SocketHandle, tag: string) => void;
607
694
  }
608
- export { DirectShardDirectory as D, ScheduleOptions as S, TwoStepShardDirectory as T, ScheduledJob as a, ScheduledJobStatus as b, SchedulerHost as c, ShardAlarms as d, ShardAsyncSqlExec as e, ShardDirectory as f, ShardHost as g, ShardJurisdiction as h, ShardKvListOptions as i, ShardKvStore as j, ShardSqlCursor as k, ShardSqlExec as l, ShardStub as m, SocketHandle as n, SocketHost as o, SqlRow as p, resolveShard as r };
695
+ export { DirectShardDirectory as D, LUNORA_ROLE_PREFIX as L, 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.6",
3
+ "version": "1.0.0-alpha.60",
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};