@lunora/platform 1.0.0-alpha.2 → 1.0.0-alpha.20
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 +26 -18
- package/dist/conformance/suite.d.ts +26 -18
- package/dist/conformance/suite.mjs +1 -1
- package/dist/index.d.mts +279 -31
- package/dist/index.d.ts +279 -31
- package/dist/index.mjs +1 -1
- package/dist/packem_shared/CLOUDFLARE_CAPABILITIES-DcCyL_87.mjs +1 -0
- package/dist/packem_shared/createReferenceHost-Cvx-Socp.mjs +1 -0
- package/dist/packem_shared/resolveShard-BzKOUEO4.mjs +1 -0
- package/dist/packem_shared/{socket-host.d-Cq5uVbiH.d.ts → socket-host.d-dVPE86WP.d.mts} +80 -35
- package/dist/packem_shared/{socket-host.d-Cq5uVbiH.d.mts → socket-host.d-dVPE86WP.d.ts} +80 -35
- package/package.json +1 -1
- package/dist/packem_shared/CLOUDFLARE_CAPABILITIES-CfXSyHOn.mjs +0 -1
- package/dist/packem_shared/createReferenceHost-GO8Hp4ft.mjs +0 -1
- package/dist/packem_shared/resolveShard-uGhAKuTB.mjs +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { type D as DirectShardDirectory, type S as ScheduleOptions, type a as ScheduledJob, type b as ScheduledJobStatus, type c as SchedulerHost, type d as ShardAlarms, type e as
|
|
1
|
+
export { type D as DirectShardDirectory, type S as ScheduleOptions, type a as ScheduledJob, type b as ScheduledJobStatus, type c as SchedulerHost, type d as ShardAlarms, type e as ShardDirectory, type f as ShardHost, type g as ShardJurisdiction, type h as ShardKvListOptions, type i as ShardKvStore, type j as ShardRegionHint, type k as ShardSqlCursor, type l as ShardSqlExec, type m as ShardStub, type n as SocketHandle, type o as SocketHost, type p as SqlRow, type T as TwoStepShardDirectory, r as resolveShard } from "./packem_shared/socket-host.d-dVPE86WP.js";
|
|
2
2
|
/**
|
|
3
3
|
* The subset of the Cloudflare `ExecutionContext` the Lunora worker entry and
|
|
4
4
|
* the framework mount seams rely on — `waitUntil` for fire-and-forget work that
|
|
@@ -22,6 +22,13 @@ export { type D as DirectShardDirectory, type S as ScheduleOptions, type a as Sc
|
|
|
22
22
|
* fall back to {@link NOOP_EXECUTION_CONTEXT}.
|
|
23
23
|
*/
|
|
24
24
|
interface ExecutionContextLike {
|
|
25
|
+
/**
|
|
26
|
+
* Present only when Cloudflare Access authenticated the request against a
|
|
27
|
+
* policy attached to the **Worker** (rather than to a hostname). `undefined`
|
|
28
|
+
* on every unauthenticated request, so its presence is itself the "Access
|
|
29
|
+
* authorized this caller" signal — see {@link AccessContextLike}.
|
|
30
|
+
*/
|
|
31
|
+
access?: AccessContextLike;
|
|
25
32
|
cache?: {
|
|
26
33
|
purge: (options: {
|
|
27
34
|
purgeEverything?: boolean;
|
|
@@ -31,6 +38,48 @@ interface ExecutionContextLike {
|
|
|
31
38
|
passThroughOnException?: () => void;
|
|
32
39
|
waitUntil?: (promise: Promise<unknown>) => void;
|
|
33
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* The identity Cloudflare Access attaches to a Worker-protected request.
|
|
43
|
+
*
|
|
44
|
+
* Shape follows the Access application-token payload: `sub` is the stable per-user
|
|
45
|
+
* id, `email` the verified address, `common_name` the service-token name (machine
|
|
46
|
+
* callers, whose `sub` is empty), and `exp` the credential expiry in epoch
|
|
47
|
+
* **seconds**. Group membership is whatever the Access policy emits — a list of
|
|
48
|
+
* names, or of `{ id, name }` objects — hence `unknown`; normalize before use.
|
|
49
|
+
*
|
|
50
|
+
* Cloudflare may add further fields, so the index signature keeps them rather
|
|
51
|
+
* than dropping them: this is a view of a payload we do not own.
|
|
52
|
+
*/
|
|
53
|
+
interface AccessIdentityLike {
|
|
54
|
+
[claim: string]: unknown;
|
|
55
|
+
/** Service-token name. Present for non-interactive (machine) callers instead of `email`. */
|
|
56
|
+
common_name?: string;
|
|
57
|
+
/** Verified user email. Present for interactive (SSO) callers. */
|
|
58
|
+
email?: string;
|
|
59
|
+
/** Credential expiry, epoch **seconds**. */
|
|
60
|
+
exp?: number;
|
|
61
|
+
/** IdP group membership — names or `{ id, name }` objects, depending on the policy. */
|
|
62
|
+
groups?: unknown;
|
|
63
|
+
/** Display name from the identity provider, when it emits one. */
|
|
64
|
+
name?: string;
|
|
65
|
+
/** Stable per-user id, and what consumers key a user on. Empty for service tokens. */
|
|
66
|
+
sub?: string;
|
|
67
|
+
/** Cloudflare's per-user UUID. Carried through, but deliberately not used as an id — only this path emits it, so keying on it would not match the JWT path. */
|
|
68
|
+
user_uuid?: string;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* The `ctx.access` facade Cloudflare exposes on a Worker protected by Access.
|
|
72
|
+
*
|
|
73
|
+
* Reading the identity from here is preferable to verifying the
|
|
74
|
+
* `Cf-Access-Jwt-Assertion` header: the platform has already authenticated the
|
|
75
|
+
* caller, so there is no JWKS fetch, no audience check to get wrong, and nothing
|
|
76
|
+
* a request can forge — the field simply does not exist unless Access authorized
|
|
77
|
+
* the call. The header path remains the fallback for hostname-scoped Access
|
|
78
|
+
* applications, which do not populate this.
|
|
79
|
+
*/
|
|
80
|
+
interface AccessContextLike {
|
|
81
|
+
getIdentity: () => AccessIdentityLike | null | undefined | Promise<AccessIdentityLike | null | undefined>;
|
|
82
|
+
}
|
|
34
83
|
/**
|
|
35
84
|
* No-op `ExecutionContext` used when the host runtime didn't supply one (a
|
|
36
85
|
* non-Cloudflare preview, or a unit test), so the worker's `fetch` always
|
|
@@ -65,6 +114,35 @@ interface QueueMessageLike<Body = unknown> {
|
|
|
65
114
|
}) => void;
|
|
66
115
|
readonly timestamp: Date;
|
|
67
116
|
}
|
|
117
|
+
/** Options accepted by {@link HttpCacheLike.match} and {@link HttpCacheLike.delete}. */
|
|
118
|
+
interface HttpCacheQueryOptions {
|
|
119
|
+
/** Match a non-`GET` request against a stored `GET` entry. */
|
|
120
|
+
ignoreMethod?: boolean;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Minimal projection of one Web Cache API cache — the store a host puts in front
|
|
124
|
+
* of the app, reached on Cloudflare as `caches.default` (the colo cache).
|
|
125
|
+
*
|
|
126
|
+
* Only the three calls Lunora makes are declared, so a host that has a cache but
|
|
127
|
+
* not the whole `Cache` interface still satisfies it, and a unit test can pass a
|
|
128
|
+
* plain object double. This is a **host** primitive, not a binding: it is reached
|
|
129
|
+
* through a runtime global rather than `env`, and a target without one leaves it
|
|
130
|
+
* `undefined` rather than shipping a fake — see `httpCache` in
|
|
131
|
+
* `PlatformCapabilities`.
|
|
132
|
+
*
|
|
133
|
+
* The stored entry is keyed by the request, so a caller that needs `Vary`
|
|
134
|
+
* semantics must fold the varying header values into the key itself: Cloudflare's
|
|
135
|
+
* cache honours `Vary` for `Accept-Encoding` only, and a projection cannot make
|
|
136
|
+
* that portable.
|
|
137
|
+
*/
|
|
138
|
+
interface HttpCacheLike {
|
|
139
|
+
/** Evict the entry stored under `request`. Resolves `true` when something was removed. */
|
|
140
|
+
delete: (request: Request | string, options?: HttpCacheQueryOptions) => Promise<boolean>;
|
|
141
|
+
/** The stored response for `request`, or `undefined` on a miss. */
|
|
142
|
+
match: (request: Request | string, options?: HttpCacheQueryOptions) => Promise<Response | undefined>;
|
|
143
|
+
/** Store `response` under `request`. Rejects for a `206`, a `Vary: *`, or a `Set-Cookie`-bearing response. */
|
|
144
|
+
put: (request: Request | string, response: Response) => Promise<void>;
|
|
145
|
+
}
|
|
68
146
|
/** A single vector match. */
|
|
69
147
|
interface VectorMatchLike {
|
|
70
148
|
id: string;
|
|
@@ -312,16 +390,18 @@ interface R2BucketLike {
|
|
|
312
390
|
delimiter?: string;
|
|
313
391
|
limit?: number;
|
|
314
392
|
prefix?: string;
|
|
393
|
+
startAfter?: string;
|
|
315
394
|
}) => Promise<{
|
|
316
395
|
cursor?: string;
|
|
317
396
|
objects: R2ObjectLike[];
|
|
318
397
|
truncated?: boolean;
|
|
319
398
|
}>;
|
|
320
|
-
put: (key: string, body: ReadableStream | ArrayBuffer | Blob | string | null, options?: {
|
|
399
|
+
put: (key: string, body: ReadableStream | ArrayBuffer | ArrayBufferView | Blob | string | null, options?: {
|
|
321
400
|
customMetadata?: Record<string, string>;
|
|
322
401
|
httpMetadata?: {
|
|
323
402
|
contentType?: string;
|
|
324
403
|
};
|
|
404
|
+
sha256?: ArrayBuffer | string;
|
|
325
405
|
}) => Promise<R2ObjectLike>;
|
|
326
406
|
/** Resume an in-progress multipart upload by id (R2 `resumeMultipartUpload`). Optional; see {@link Storage.resumeMultipartUpload}. */
|
|
327
407
|
resumeMultipartUpload?: (key: string, uploadId: string) => R2MultipartUploadLike;
|
|
@@ -417,15 +497,15 @@ interface MessageSendRequestLike<Body = unknown> {
|
|
|
417
497
|
delaySeconds?: number;
|
|
418
498
|
}
|
|
419
499
|
/**
|
|
420
|
-
* Minimal structural projection of workers-types' `Queue
|
|
500
|
+
* Minimal structural projection of workers-types' `Queue<Body>` (the producer
|
|
421
501
|
* binding). The real binding's `send`/`sendBatch` resolve to a metadata object;
|
|
422
|
-
* we widen the return to `Promise
|
|
502
|
+
* we widen the return to `Promise<unknown>` so a plain-object fake satisfies it.
|
|
423
503
|
*/
|
|
424
504
|
interface QueueBindingLike<Body = unknown> {
|
|
425
505
|
send: (message: Body, options?: QueueSendOptions) => Promise<unknown>;
|
|
426
506
|
sendBatch: (messages: Iterable<MessageSendRequestLike<Body>>, options?: QueueSendBatchOptions) => Promise<unknown>;
|
|
427
507
|
}
|
|
428
|
-
/** Structural mirror of workers-types' `Message
|
|
508
|
+
/** Structural mirror of workers-types' `Message<Body>` (one delivered message). */
|
|
429
509
|
interface MessageLike<Body = unknown> {
|
|
430
510
|
/** Acknowledge this message so it is not redelivered. */
|
|
431
511
|
ack: () => void;
|
|
@@ -436,7 +516,7 @@ interface MessageLike<Body = unknown> {
|
|
|
436
516
|
retry: (options?: QueueRetryOptions) => void;
|
|
437
517
|
readonly timestamp: Date;
|
|
438
518
|
}
|
|
439
|
-
/** Structural mirror of workers-types' `MessageBatch
|
|
519
|
+
/** Structural mirror of workers-types' `MessageBatch<Body>` handed to a consumer. */
|
|
440
520
|
interface MessageBatchLike<Body = unknown> {
|
|
441
521
|
/** Acknowledge every message in the batch. */
|
|
442
522
|
ackAll: () => void;
|
|
@@ -481,22 +561,131 @@ interface PlatformCapabilities {
|
|
|
481
561
|
analytics?: Capability;
|
|
482
562
|
/** Browser rendering / headless browser. */
|
|
483
563
|
browser?: Capability;
|
|
484
|
-
/**
|
|
564
|
+
/**
|
|
565
|
+
* `.commitOrdered()` tables — the `_commitSeq` system field: a per-shard
|
|
566
|
+
* integer allocated once per mutation and strictly increasing in commit
|
|
567
|
+
* order.
|
|
568
|
+
*
|
|
569
|
+
* Listed as a capability rather than assumed, because the ordering
|
|
570
|
+
* guarantee is not the engine's to give. It rests on two things the HOST
|
|
571
|
+
* provides: an atomic write boundary the counter bump shares with the
|
|
572
|
+
* rows it stamps, and serialized execution so two mutations cannot
|
|
573
|
+
* interleave their allocations. A host that offers neither can still
|
|
574
|
+
* create the counter and hand out increasing numbers — they just would
|
|
575
|
+
* not order commits, which is the whole contract.
|
|
576
|
+
*/
|
|
577
|
+
commitOrderedTables?: Capability;
|
|
578
|
+
/**
|
|
579
|
+
* Container execution (Cloudflare Containers / Fargate), including
|
|
580
|
+
* `ctx.containers.<name>.exec`. Deliberately one rating rather than two:
|
|
581
|
+
* `exec` is a method on the accessor this key already gates, not a
|
|
582
|
+
* separate app-imported surface, so there is no usage signal codegen
|
|
583
|
+
* could gate it on independently and nothing that could act on a second
|
|
584
|
+
* rating. A host that can reach a container but cannot carry a command
|
|
585
|
+
* result back should say so in this note.
|
|
586
|
+
*/
|
|
485
587
|
containers?: Capability;
|
|
486
588
|
/** Cross-shard fan-out queries. */
|
|
487
589
|
crossShardFanout?: Capability;
|
|
590
|
+
/**
|
|
591
|
+
* Durable streams: a `.stream()` run whose chunks are persisted and
|
|
592
|
+
* whose producer outlives the socket that opened it, so a reconnecting
|
|
593
|
+
* or second client resumes the same transcript.
|
|
594
|
+
*/
|
|
595
|
+
durableStreams?: Capability;
|
|
488
596
|
/** Global (replicated) tables backed by a SQL store. */
|
|
489
597
|
globalTables?: Capability;
|
|
598
|
+
/**
|
|
599
|
+
* A shared HTTP cache in front of the app that the runtime can READ AND
|
|
600
|
+
* WRITE — the Web Cache API (`caches.default` on Cloudflare), projected
|
|
601
|
+
* as `HttpCacheLike`.
|
|
602
|
+
*
|
|
603
|
+
* Rated separately from the app merely emitting `Cache-Control`, because
|
|
604
|
+
* only this half needs a host primitive. Emitting the header is portable
|
|
605
|
+
* by construction: any host that returns an HTTP response can do it, and
|
|
606
|
+
* browsers and downstream CDNs honour it wherever the app runs. What is
|
|
607
|
+
* not portable is a store the Worker itself can `match`/`put` against,
|
|
608
|
+
* which is why `@lunora/runtime`'s REST edge cache degrades to
|
|
609
|
+
* headers-only on a target rated `unsupported` rather than failing.
|
|
610
|
+
*/
|
|
611
|
+
httpCache?: Capability;
|
|
490
612
|
/** BYO database via connection pooling (Hyperdrive / RDS Proxy). */
|
|
491
613
|
hyperdrive?: Capability;
|
|
614
|
+
/**
|
|
615
|
+
* An identity-aware proxy in front of the app that authenticates the
|
|
616
|
+
* caller before the request reaches it, and hands the runtime a verified
|
|
617
|
+
* identity **out-of-band** — on the execution context rather than on the
|
|
618
|
+
* request (Cloudflare Access attached to a Worker; IAP; an ALB OIDC
|
|
619
|
+
* action).
|
|
620
|
+
*
|
|
621
|
+
* Rated separately from the header-stamping form of the same product
|
|
622
|
+
* because only this one needs a host primitive. An identity-aware proxy
|
|
623
|
+
* that merely adds a signed header is portable by construction: any host
|
|
624
|
+
* that receives an HTTP request can verify it, which is why
|
|
625
|
+
* `@lunora/cloudflare-access` still works on a target rated
|
|
626
|
+
* `unsupported` here (it falls back to the `Cf-Access-Jwt-Assertion`
|
|
627
|
+
* JWT). What is not portable is the identity arriving beside the
|
|
628
|
+
* request, which is why `ExecutionContextLike.access` is a projection a
|
|
629
|
+
* host either populates or does not.
|
|
630
|
+
*/
|
|
631
|
+
identityProxy?: Capability;
|
|
632
|
+
/** Image transforms (resize/format/optimize) via an Images binding. */
|
|
633
|
+
images?: Capability;
|
|
492
634
|
/** Key-value storage (KV / Redis / DynamoDB). */
|
|
493
635
|
keyValueStore?: Capability;
|
|
494
636
|
/** Local SQL execution inside a shard. */
|
|
495
637
|
localSql?: Capability;
|
|
496
638
|
/** Email sending (Resend / SES / etc). */
|
|
497
639
|
mail?: Capability;
|
|
498
|
-
/**
|
|
640
|
+
/**
|
|
641
|
+
* `.memory()` tables — the ephemeral tier: rows cleared on every shard
|
|
642
|
+
* cold start, never written to the CDC changelog, refilled by
|
|
643
|
+
* `onShardInit`.
|
|
644
|
+
*
|
|
645
|
+
* The rating answers "does a memory table avoid durable storage on this
|
|
646
|
+
* host", NOT "does it work". The lifetime semantics are the engine's and
|
|
647
|
+
* hold everywhere; whether the rows actually stay out of the durable
|
|
648
|
+
* store depends on the host offering a second, memory-backed SQL handle,
|
|
649
|
+
* which is a per-target fact.
|
|
650
|
+
*/
|
|
651
|
+
memoryTables?: Capability;
|
|
652
|
+
/**
|
|
653
|
+
* Object storage (R2 / S3 / MinIO).
|
|
654
|
+
*
|
|
655
|
+
* `ctx.storage.deleteAfterCommit(key)` rides on this rating and gets no
|
|
656
|
+
* key of its own: it needs no host primitive beyond the bucket. The
|
|
657
|
+
* post-commit flush uses `ShardHost.waitUntil` where the host has one and
|
|
658
|
+
* is awaited inline where it does not, so a host that can serve
|
|
659
|
+
* `objectStorage` serves the deferral at the same level.
|
|
660
|
+
*/
|
|
499
661
|
objectStorage?: Capability;
|
|
662
|
+
/**
|
|
663
|
+
* Snapshot backups kept in object storage rather than on the machine
|
|
664
|
+
* that took them — `lunora backup create|list|restore --bucket`, and
|
|
665
|
+
* the platform's own `backupCron`. Distinct from
|
|
666
|
+
* `objectStorage` above because it needs three things a
|
|
667
|
+
* bucket alone does not imply: an admin-gated read of one object
|
|
668
|
+
* (`GET /_lunora/admin/storage/object`), a checksum-verified write, and
|
|
669
|
+
* a scheduler to run the unattended half.
|
|
670
|
+
*/
|
|
671
|
+
objectStorageBackups?: Capability;
|
|
672
|
+
/**
|
|
673
|
+
* The CDC changelog's cold tier: rows a retention sweep is about to
|
|
674
|
+
* destroy are written to an object-storage bucket first
|
|
675
|
+
* (`LUNORA_CDC_ARCHIVE`), and a consumer whose cursor has fallen below
|
|
676
|
+
* the retained window is served from there instead of being told to
|
|
677
|
+
* re-seed.
|
|
678
|
+
*
|
|
679
|
+
* Distinct from `objectStorage` because it needs the bucket to do one
|
|
680
|
+
* thing a plain byte store need not: resume a key-ordered listing from a
|
|
681
|
+
* position (`list({ startAfter })`). Without it the read-back re-lists
|
|
682
|
+
* the prefix from the front every time and stops finding the range it
|
|
683
|
+
* needs once enough segments precede the cursor — which fails as a
|
|
684
|
+
* refusal rather than a gap, but fails permanently and silently, so a
|
|
685
|
+
* host that cannot seek should say `unsupported` here rather than
|
|
686
|
+
* inherit `objectStorage`'s rating.
|
|
687
|
+
*/
|
|
688
|
+
objectStorageCdcArchive?: Capability;
|
|
500
689
|
/** Pipelines / streaming data. */
|
|
501
690
|
pipelines?: Capability;
|
|
502
691
|
/** Queue-backed workpools. */
|
|
@@ -505,10 +694,26 @@ interface PlatformCapabilities {
|
|
|
505
694
|
scheduler?: Capability;
|
|
506
695
|
/** Secrets management. */
|
|
507
696
|
secrets?: Capability;
|
|
697
|
+
/**
|
|
698
|
+
* `onQueryChange` reactors — server-side reactivity: a subscriber that is
|
|
699
|
+
* not a socket, woken after a write flush when a watched read's result
|
|
700
|
+
* changed.
|
|
701
|
+
*
|
|
702
|
+
* Host-dependent because the whole mechanism rests on the host being able
|
|
703
|
+
* to run work AFTER a write commits, on the same shard, without a client
|
|
704
|
+
* connection to hang it off — and on that work being serialized against
|
|
705
|
+
* further writes so a reactor's own writes cascade deterministically
|
|
706
|
+
* rather than interleaving.
|
|
707
|
+
*/
|
|
708
|
+
serverReactors?: Capability;
|
|
508
709
|
/** Alarms / scheduled wakeup inside a shard. */
|
|
509
710
|
shardAlarms?: Capability;
|
|
510
711
|
/** Durable Object-style sharded state. */
|
|
511
712
|
shardedState?: Capability;
|
|
713
|
+
/** Geographic placement of a shard (`ShardPlacement.locationHint`). */
|
|
714
|
+
shardPlacement?: Capability;
|
|
715
|
+
/** Region-local read replicas of a shard, for one-shot queries. */
|
|
716
|
+
shardReadReplicas?: Capability;
|
|
512
717
|
/** Vector database (Vectorize / pgvector / Pinecone). */
|
|
513
718
|
vectorStore?: Capability;
|
|
514
719
|
/** Hibernated WebSocket subscriptions. */
|
|
@@ -535,29 +740,72 @@ declare const CLOUDFLARE_CAPABILITIES: PlatformCapabilities;
|
|
|
535
740
|
* The Node capability matrix — `@lunora/platform-node`'s honest self-rating
|
|
536
741
|
* (plan 234).
|
|
537
742
|
*
|
|
538
|
-
* `@lunora/platform-node`
|
|
539
|
-
* `
|
|
540
|
-
* `better-sqlite3` and an in-process registry,
|
|
541
|
-
*
|
|
542
|
-
*
|
|
543
|
-
*
|
|
544
|
-
*
|
|
545
|
-
*
|
|
546
|
-
* Hyperdrive) most `ctx.*` surfaces are built on. Every one of those is
|
|
547
|
-
* rated `"unsupported"` here rather than left undeclared — see
|
|
548
|
-
* `gateAgainstMatrix` in `@lunora/codegen`, whose fail-closed gate (plan
|
|
549
|
-
* 229) treats an undeclared feature as unsupported anyway, but under a
|
|
550
|
-
* different diagnostic name than an honest, explicit rating.
|
|
743
|
+
* `@lunora/platform-node` implements every contract in this package
|
|
744
|
+
* (`ShardHost`, `SocketHost`, `ShardDirectory`, `ShardKvStore`,
|
|
745
|
+
* `SchedulerHost`) over `better-sqlite3` and an in-process registry, plus the
|
|
746
|
+
* `.global()` table backend via `@lunora/sql-store`. It began as a spike to run
|
|
747
|
+
* the conformance TCK against a second host; the durability gaps that spike
|
|
748
|
+
* surfaced — alarms and scheduler jobs that were persisted but never re-armed,
|
|
749
|
+
* socket attachments that lived only in memory — are closed, and each is now
|
|
750
|
+
* pinned by a restart test rather than only by a simulated recycle.
|
|
551
751
|
*
|
|
552
|
-
*
|
|
553
|
-
*
|
|
554
|
-
*
|
|
555
|
-
*
|
|
556
|
-
*
|
|
557
|
-
*
|
|
558
|
-
*
|
|
559
|
-
*
|
|
560
|
-
*
|
|
752
|
+
* `scheduler` and `shardAlarms` were rated `"unsupported"` under plan 267, on
|
|
753
|
+
* the grounds that the host stored and timed both while its timer body only
|
|
754
|
+
* cleared bookkeeping — nothing dispatched the scheduled function or woke the
|
|
755
|
+
* alarm. That rating was correct for the code it described, and the code is
|
|
756
|
+
* what changed: both now dispatch (through `onDispatch` / `onAlarm`) and both
|
|
757
|
+
* re-arm from their durable rows on construction, so `"emulated"` — built on
|
|
758
|
+
* lower-level primitives and *working* — is now the honest reading.
|
|
759
|
+
*
|
|
760
|
+
* What remains genuinely absent is everything a single Node process cannot
|
|
761
|
+
* distribute: placement across nodes, failover, and most Cloudflare-specific
|
|
762
|
+
* product bindings (Vectorize, Workers AI, Containers, Browser Rendering,
|
|
763
|
+
* Analytics Engine, Secrets Store, Hyperdrive). Workflows, object storage and
|
|
764
|
+
* queues are the three that CAN be emulated locally — `defineWorkflow` handlers
|
|
765
|
+
* compile onto the `@visulima/workflow` engine, R2 becomes a filesystem bucket,
|
|
766
|
+
* and Queues becomes a durable table with the same batch/ack/retry/dead-letter
|
|
767
|
+
* semantics — so those three are rated `"emulated"`; the rest of the Cloudflare
|
|
768
|
+
* products most `ctx.*` surfaces are built on are rated `"unsupported"` here
|
|
769
|
+
* rather than left undeclared — see `gateAgainstMatrix` in `@lunora/codegen`,
|
|
770
|
+
* whose fail-closed gate (plan 229) treats an undeclared feature as unsupported
|
|
771
|
+
* anyway, but under a different diagnostic name than an honest, explicit rating.
|
|
772
|
+
*
|
|
773
|
+
* Almost nothing here is rated `"native"`, and that is the matrix's own
|
|
774
|
+
* definition doing its job rather than a hedge: `native` means the platform
|
|
775
|
+
* itself provides the feature, and a bare Node process provides essentially
|
|
776
|
+
* none of them — Lunora builds alarms out of `setTimeout` plus a durable row,
|
|
777
|
+
* a KV store out of a SQL table, and `.global()` tables out of a second SQLite
|
|
778
|
+
* file. `localSql` is the exception, because SQLite genuinely is the platform
|
|
779
|
+
* primitive there. The ratings say who does the work; the notes say how well.
|
|
780
|
+
* Both are argued in detail in `plans/234-node-host-findings.md`.
|
|
561
781
|
*/
|
|
562
782
|
declare const NODE_CAPABILITIES: PlatformCapabilities;
|
|
563
|
-
export { type AnalyticsEngineDataPoint, type AnalyticsEngineDataPointLike, type AnalyticsEngineDatasetLike, CLOUDFLARE_CAPABILITIES, type Capability, type CapabilityLevel, type D1DatabaseLike, type D1PreparedStatementLike, type D1SessionLike,
|
|
783
|
+
export { type AnalyticsEngineDataPoint, type AnalyticsEngineDataPointLike, type AnalyticsEngineDatasetLike, CLOUDFLARE_CAPABILITIES, type Capability, type CapabilityLevel, type D1DatabaseLike, type D1PreparedStatementLike, type D1SessionLike,
|
|
784
|
+
/**
|
|
785
|
+
* `@lunora/platform` — provider-neutral host contracts for Lunora.
|
|
786
|
+
*
|
|
787
|
+
* This package defines the structural interfaces that separate the Lunora
|
|
788
|
+
* engine from any specific host (Cloudflare Workers, AWS, Rivet, Node, etc.).
|
|
789
|
+
* It contains **types and capability metadata only** — near-zero runtime code.
|
|
790
|
+
*
|
|
791
|
+
* The contracts fall into four groups:
|
|
792
|
+
*
|
|
793
|
+
* 1. **Shard host** (`ShardHost`) — single-writer execution, transactions,
|
|
794
|
+
* local SQL, alarms, and background continuation per shard key.
|
|
795
|
+
* 2. **Socket host** (`SocketHost`) — hibernated WebSocket subscriptions with
|
|
796
|
+
* durable attachments and tagged fan-out.
|
|
797
|
+
* 3. **Shard directory** (`ShardDirectory`) — deterministic placement and RPC
|
|
798
|
+
* dispatch from shard keys to stubs.
|
|
799
|
+
* 4. **Scheduler host** (`SchedulerHost`) — durable delayed jobs, cron, and
|
|
800
|
+
* at-least-once dispatch.
|
|
801
|
+
*
|
|
802
|
+
* Plus canonical binding projections (`KVNamespaceLike`, `R2BucketLike`,
|
|
803
|
+
* `QueueBindingLike`, `D1DatabaseLike`, `VectorizeIndexLike`, …) and the
|
|
804
|
+
* `PlatformCapabilities` matrix that codegen uses to tailor emitted types per
|
|
805
|
+
* target.
|
|
806
|
+
*
|
|
807
|
+
* This package is **zero-dependency** and safe on every runtime (browser,
|
|
808
|
+
* workerd, Node). It is intended to be the leaf dependency every other
|
|
809
|
+
* `@lunora/*` package can import without creating cycles.
|
|
810
|
+
*/
|
|
811
|
+
type ExecutionContextLike, type HttpCacheLike, type HttpCacheQueryOptions, type KVNamespaceLike, type KvGetOptions, type KvListKey, type KvNamespaceListResult, type KvNamespacePutOptions, type KvValue, type KvValueType, type MessageBatchLike, type MessageLike, type MessageSendRequestLike, NODE_CAPABILITIES, NOOP_EXECUTION_CONTEXT, type PlatformCapabilities, type QueueBindingLike, type QueueContentType, type QueueMessageLike, type QueueRetryOptions, type QueueSendBatchOptions, type QueueSendOptions, type QueueSendOptionsLike, type QueueSendRequestLike, type R2BucketLike, type R2MultipartUploadLike, type R2ObjectBodyLike, type R2ObjectLike, type R2RangeLike, type R2UploadedPartLike, type VectorMatchLike, type VectorMetric, type VectorRecordLike, type VectorizeDeleteMutation, type VectorizeIndexDetails, type VectorizeIndexLike, type VectorizeMatch, type VectorizeMatches, type VectorizeQueryOptions, type VectorizeUpsertMutation, type VectorizeVector };
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{NOOP_EXECUTION_CONTEXT as E}from"./packem_shared/NOOP_EXECUTION_CONTEXT-YmXqH-jH.mjs";import{CLOUDFLARE_CAPABILITIES as O,NODE_CAPABILITIES as e}from"./packem_shared/CLOUDFLARE_CAPABILITIES-
|
|
1
|
+
import{NOOP_EXECUTION_CONTEXT as E}from"./packem_shared/NOOP_EXECUTION_CONTEXT-YmXqH-jH.mjs";import{CLOUDFLARE_CAPABILITIES as O,NODE_CAPABILITIES as e}from"./packem_shared/CLOUDFLARE_CAPABILITIES-DcCyL_87.mjs";import{resolveShard as C}from"./packem_shared/resolveShard-BzKOUEO4.mjs";export{O as CLOUDFLARE_CAPABILITIES,e as NODE_CAPABILITIES,E as NOOP_EXECUTION_CONTEXT,C as resolveShard};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e={id:"cloudflare",name:"Cloudflare",features:{shardedState:{level:"native",note:"Durable Objects with SQLite"},globalTables:{level:"native",note:"D1 with Sessions API. D1 has a documented, expected baseline error rate — Cloudflare's own team calls a handful of transient errors every few hours 'not unexpected' on a healthy database — so read-only statements are retried automatically; writes are not, because every one of those errors is ambiguous about whether the statement applied and D1 has no interactive transactions to resolve it"},websocketHibernation:{level:"native",note:"DO WebSocket hibernation"},durableStreams:{level:"emulated",note:"Lunora persists each chunk to the shard's SQLite under a monotonic seq and keeps the producer alive past the socket via waitUntil; the platform has no streaming primitive of its own, and a run whose DO is evicted mid-flight ends as STREAM_INTERRUPTED rather than resuming"},commitOrderedTables:{level:"native",note:"`state.storage.transaction` makes the `__commit_seq` bump atomic with the rows it stamps, and a Durable Object executes one event at a time — so the allocation order IS the commit order, with no lock of ours in the path"},localSql:{level:"native",note:"state.storage.sql (SQLite)"},serverReactors:{level:"emulated",note:"The wake-up is Lunora's, not the platform's: reactors ride the existing post-write refresh drain, which already exists to push subscription frames. Cloudflare supplies the two properties that make it correct — one event at a time per Durable Object, and `waitUntil` to keep the drain alive past the response — but has no notion of a server-side subscription of its own"},memoryTables:{level:"emulated",note:"The lifetime is real — an eviction drops the DO's heap and the framework clears every `.memory()` table on reconstruction, so the rows behave exactly like heap state, and their writes stay out of the CDC changelog. The STORAGE is not: workerd exposes one SQL handle and no memory-backed database, so a memory row is still written to the DO's SQLite and then deleted. `.memory()` buys the semantics, not the write"},shardAlarms:{level:"native",note:"state.storage.setAlarm"},shardPlacement:{level:"native",note:"DurableObjectNamespace.get/getByName locationHint — best-effort, and honoured only by the resolution that creates the object"},shardReadReplicas:{level:"emulated",note:"Lunora follows the shard's CDC changelog into a replica DO placed in the reader's region; the platform replicates for durability, not for reads, so the follow loop is ours"},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"},objectStorageBackups:{level:"emulated",note:"`lunora backup create|list|restore --bucket` writes NDJSON snapshots + a manifest sidecar per snapshot through the admin storage routes (checksum-verified upload, admin-gated object read), and `backupCron`/`backupStore` runs the same layout unattended on a Cron Trigger. Both are bounded by what a single request body / a Worker isolate can hold, not by R2. `emulated` because every part of that is Lunora's — R2 supplies a bucket, and Cloudflare has no backup product being consumed here; the snapshot format, the manifest, the checksum gate and the retention report are all ours"},objectStorageCdcArchive:{level:"emulated",note:"R2 supplies the bucket and the `startAfter` listing the segment keys are indexed on; everything above that is Lunora's — the segment format, the archive-before-trim ordering the sweep defers behind `waitUntil`, and the de-overlapping read-back. The platform has no notion of a changelog to tier, so this is not a product being consumed"},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"},images:{level:"native",note:"Cloudflare Images binding"},containers:{level:"native",note:"Cloudflare Containers; ctx.containers.<name>.exec rides the same binding over the /__lunora/exec contract, which the container image serves"},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"},httpCache:{level:"native",note:"The colo cache via caches.default. Worker-generated responses are NOT stored by it automatically — the runtime has to caches.default.put() them — and it honours Vary for Accept-Encoding only, so a varying response has to fold those header values into the cache key itself. A 206, a Vary: *, or a Set-Cookie-bearing response is refused by put()"},identityProxy:{level:"native",note:"Cloudflare Access. A policy attached to the Worker covers its custom domains, routes, workers.dev and preview URLs at once, and the authenticated identity arrives on the execution context as ctx.access — no header to verify, and nothing a request can forge to manufacture one. A hostname-scoped Access application instead stamps the Cf-Access-Jwt-Assertion header, which needs no host support at all"}}},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:"emulated",note:"The @lunora/sql-store core on its own SQLite file via the reference sqliteDialect — full store semantics, but one node with no replication"},websocketHibernation:{level:"emulated",note:"Socket registry with attachments/tags persisted to SQLite, so subscription state survives a process restart; nothing is ever actually evicted from memory, so this is durability without hibernation's memory saving"},durableStreams:{level:"unsupported",note:"The transcript store is host-neutral (@lunora/shard-engine), but the attach/produce state machine lives in @lunora/do and nothing in this host mounts it — a durable stream declared here would silently behave as an ephemeral one"},commitOrderedTables:{level:"emulated",note:"The sequence orders commits correctly, but the serialization it depends on is Lunora's per-shard write gate rather than a platform property — one process, one better-sqlite3 handle per shard key. Correct here; not something the host guarantees the way a Durable Object does"},localSql:{level:"native",note:"better-sqlite3 (synchronous, embedded)"},serverReactors:{level:"emulated",note:"Same engine-level implementation as Cloudflare; the per-shard serialization it depends on is the host's own write gate rather than a platform guarantee"},memoryTables:{level:"emulated",note:"Same shape as Cloudflare and for a different reason: better-sqlite3 CAN open `:memory:`, but a shard's memory tables share the one handle its durable tables use, so they are cleared rather than never written. A host process also outlives far more than a Durable Object does, so cold starts — and therefore `onShardInit` — are much rarer here than in production on Cloudflare; do not use this target to judge how often a memory table is actually empty"},shardAlarms:{level:"emulated",note:"setTimeout over a durable row, dispatched to onAlarm and re-armed on construction, so an alarm survives a restart and one whose time elapsed while the process was down fires late rather than never"},shardPlacement:{level:"unsupported",note:"One process — every shard lives where the process does, so a location hint has nowhere to place it"},shardReadReplicas:{level:"unsupported",note:"One process and one region: a replica here would be a second copy of a database already on the same disk"},crossShardFanout:{level:"emulated",note:"@lunora/runtime's query coordinator over the in-process shard registry; listShardKeys is seeded from the shard files on disk, and answers every shard rather than only those holding the table (a correct superset, at the cost of visiting shards with nothing to say)"},queues:{level:"emulated",note:`createNodeQueueHost (@lunora/platform-node) — a QueueBindingLike producer per declared queue over a durable _lunora_queue_messages table, and a batched consumer feeding the same dispatchQueueBatch the Cloudflare host uses. delaySeconds (capped at 12h), all four content types, maxBatchSize/maxBatchTimeout assembly, per-message ack/retry with workerd's implicit-ack-on-return and retry-on-throw, maxRetries into a declared deadLetterQueue (or parked in place, never dropped), and a visibility window so a crash mid-handler redelivers. Delivery is driven by poll(); there is no timer, because this host has no dev server to own one. mode: "pull" queues are written but not consumed — nothing here serves the HTTP pull endpoint`},workflows:{level:"emulated",note:"createNodeWorkflowHost (@lunora/platform-node) compiles defineWorkflow handlers onto the @visulima/workflow engine (createRuntime): step/sleep/waitForEvent are durable + replay-safe, status maps to complete/errored/waiting/terminated, create({ id }) is honoured through a durable alias row (so ctx.spawn resolves and a retried create is one run), and runs survive a restart when backed by createNodeWorkflowStore (a SQLite WorkflowStore; the store is required, so no caller silently gets in-process-only state). Gaps: no pause/restart; terminate is not a barrier, so an activation already in flight overwrites the tombstone; ctx.run dispatches to an endpoint no Node HTTP server serves; ctx.parallel's synchronous join cannot interleave within one trigger activation"},scheduler:{level:"emulated",note:"SQLite job table dispatched to onDispatch and re-armed on construction, with retry backoff and a dead-letter queue; the only host implementing runtime cron registration (SchedulerHost.cron), which Cloudflare cannot offer"},objectStorageBackups:{level:"emulated",note:"The commands work unchanged, but the bucket underneath is createNodeR2Bucket — a directory on the same machine the CLI runs on, so a bucket-backed backup here is not the separate failure domain it is on Cloudflare. The scheduled half additionally needs this host's scheduler, which exists but is not a shipping target"},objectStorageCdcArchive:{level:"emulated",note:"createNodeR2Bucket implements the `startAfter` seek the segment index needs, so the read-back behaves as it does on R2. Same caveat as the backups above: the bucket is a directory on the machine running the host, so archiving the changelog here moves it off SQLite but not off the disk that would take the shard with it"},objectStorage:{level:"emulated",note:"createNodeR2Bucket (@lunora/platform-node) — an R2BucketLike over the local filesystem (fs/promises, head/list/range). One file per object with the metadata in a trailer, so the single rename that publishes the bytes publishes their checksum and content-type with them, and a get reads body and metadata through one handle rather than reopening the path. put streams into the staged file and .body streams the requested range; .arrayBuffer()/.text() still allocate the range they return. The body is single-use, as R2's is. Keys fold the way the host filesystem folds them, so `A` and `a` are one object on a case-insensitive volume where real R2 keeps two. No multipart uploads, no presigned URLs"},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"},images:{level:"unsupported",note:"No Images-equivalent binding implemented"},containers:{level:"unsupported",note:"No container orchestration implemented, so there is nothing for ctx.containers.<name>.exec to run a command in either"},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"},httpCache:{level:"unsupported",note:"Nothing sits in front of this host to cache its responses, and Node exposes no Web Cache API global — the runtime's REST edge cache finds no HttpCacheLike here and degrades to emitting Cache-Control alone, which browsers and any CDN in front still honour"},identityProxy:{level:"unsupported",note:"Nothing sits in front of this host to authenticate callers, so it never populates the execution context's access identity. @lunora/cloudflare-access still works here through its Cf-Access-Jwt-Assertion fallback, which is a plain header check and needs no host support"}}};export{e as CLOUDFLARE_CAPABILITIES,t as NODE_CAPABILITIES};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{DatabaseSync as I}from"node:sqlite";let k=0,j=0;const O=()=>(k+=1,`socket-${k}`),R=()=>(j+=1,`job-${j}`),$=a=>a===void 0?null:a,W=a=>typeof a=="string"?new TextEncoder().encode(a).buffer:a instanceof ArrayBuffer?a:ArrayBuffer.isView(a)?a.buffer.slice(a.byteOffset,a.byteOffset+a.byteLength):new ArrayBuffer(0),H=()=>{const a=new I(":memory:");let m=!1;const u=e=>{if(m)throw new Error(`platform closed: cannot ${e}`)},s={alarmAt:null,alarmTimeout:null,pending:[],running:!1},i=new Map,w=new Map,f=new Map,F={exec:(e,...t)=>{const n=a.prepare(e),r=t.map($),o=e.trim().toLowerCase(),l=o.startsWith("select")||o.startsWith("pragma")?n.all(...r):(n.run(...r),[]);return{[Symbol.iterator]:()=>l[Symbol.iterator](),one:()=>{if(l.length!==1)throw new Error(`expected exactly one row, got ${String(l.length)}`);return l[0]},toArray:()=>[...l]}}},p=()=>{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,p()}))},D=e=>new Promise((t,n)=>{s.pending.push({function_:e,reject:r=>{n(r)},resolve:r=>{t(r)}}),p()});let y=Promise.resolve();const b=async e=>{a.exec("BEGIN");try{const t=await e();return a.exec("COMMIT"),t}catch(t){try{a.exec("ROLLBACK")}catch{}throw t}},B=e=>{const t=y.then(()=>b(e),()=>b(e));return y=t.then(()=>{},()=>{}),t},C=e=>{const t=typeof e=="number"?e:e.getTime();s.alarmAt=t,s.alarmTimeout!==null&&clearTimeout(s.alarmTimeout);const n=Math.max(0,t-Date.now());s.alarmTimeout=setTimeout(()=>{s.alarmAt=null,s.alarmTimeout=null},n)},J={alarms:{delete:()=>{u("delete an alarm"),s.alarmAt=null,s.alarmTimeout!==null&&(clearTimeout(s.alarmTimeout),s.alarmTimeout=null)},get:()=>s.alarmAt,set:e=>{u("set an alarm"),C(e)}},runSerialized:D,sql:F,transaction:B,waitUntil:()=>{}},d=new WeakMap,v=e=>{const t={bufferedAmount:e.bufferedAmount,close:(n,r)=>{e.closed=!0},deserializeAttachment:()=>e.attachment,send:n=>{e.received.push(typeof n=="string"?n:W(n))},serializeAttachment:n=>{e.attachment=n,w.set(e.id,n)}};return e.handle=t,d.set(t,e.id),t},L={accept:(e,t,n)=>{u("accept a socket");const r=O(),o={attachment:t,bufferedAmount:0,closed:!1,raw:e,handle:null,id:r,received:[],tags:new Set(n)};return i.set(r,o),f.set(r,new Set(n)),t!==void 0&&w.set(r,t),v(o)},getSockets:e=>{const t=[...i.values()];return(e===void 0?t:t.filter(r=>r.tags.has(e))).map(r=>r.handle)},handleFor:e=>[...i.values()].find(t=>t.raw===e)?.handle,idFor:e=>{const t=d.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)=>{u("remove a socket tag");const n=d.get(e)??"",r=i.get(n);r!==void 0&&(t===void 0?r.tags.clear():r.tags.delete(t),f.set(n,new Set(r.tags)))},setTag:(e,t)=>{u("set a socket tag");const n=d.get(e)??"",r=i.get(n);r!==void 0&&(r.tags.add(t),f.set(n,new Set(r.tags)))}},T={get:e=>({fetch:async()=>new Response(String(e))}),getByName:e=>({fetch:async()=>new Response(e)}),idForName:e=>`shard:${e}`,jurisdiction:e=>T},h=new Map,P={delete:async e=>h.delete(e),get:async e=>h.get(e),list:async e=>{const t=e?.prefix??"",n=new Map;for(const[r,o]of h)r.startsWith(t)&&n.set(r,o);return n},put:async(e,t)=>{h.set(e,structuredClone(t))}},c=new Map,g=new Map,S=new Set,A=(e,t)=>({attempts:t.attempts,functionPath:t.functionPath,id:e,scheduledFor:t.scheduledFor}),z={cancel:async e=>{const t=c.get(e);return t===void 0?!1:(clearTimeout(t.timer),c.delete(e),!0)},cron:async()=>{},deadLetter:{list:async()=>[...g].map(([e,t])=>A(e,t)),requeue:async e=>{const t=g.get(e);return t===void 0?!1:(g.delete(e),c.set(e,{...t,attempts:0,timer:void 0}),!0)}},list:async()=>[...c].map(([e,t])=>A(e,t)),schedule:async(e,t,n)=>{u("schedule a job");const r=R();let o;n?.at===void 0?o=Date.now()+(n?.delayMs??0):o=typeof n.at=="number"?n.at:n.at.getTime();const l=Math.max(0,o-Date.now()),E=setTimeout(()=>{const M=c.get(r);M!==void 0&&(M.attempts+=1),S.add(r),c.delete(r)},l);return c.set(r,{args:t,attempts:0,functionPath:e,options:n??{},scheduledFor:o,timer:E}),{id:r,scheduledFor:o}}},x=()=>{if(!m){m=!0,a.close(),s.alarmTimeout!==null&&clearTimeout(s.alarmTimeout);for(const e of c.values())clearTimeout(e.timer)}};return{awaitAlarmFired:async e=>{await new Promise(t=>{setTimeout(t,Math.max(0,e-Date.now())+30)})},awaitJobDispatched:async e=>{const t=c.get(e);return t!==void 0&&await new Promise(n=>{setTimeout(n,Math.max(0,t.scheduledFor-Date.now())+30)}),S.has(e)},cleanup:x,directory:T,disposeTerminally:x,kv:P,readFrames:e=>(i.get(d.get(e)??"")?.received??[]).filter(t=>typeof t=="string"),restoreSocket:(e,t)=>{const n={attachment:t,bufferedAmount:0,closed:!1,handle:null,id:e,raw:void 0,received:[],tags:new Set(f.get(e))};return i.set(e,n),v(n)},scheduler:z,simulateDeadLetter:async e=>{const t=c.get(e);return t===void 0?!1:(clearTimeout(t.timer),c.delete(e),g.set(e,{...t,attempts:(t.options.retry?.maxAttempts??5)+1,timer:void 0}),!0)},shard:J,simulateRecycle:()=>{i.clear()},socket:L}};export{H as createReferenceHost};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const a=(e,g,N)=>e.getByName!==void 0?e.getByName(g,N):e.get(e.idForName(g),N);export{a as resolveShard};
|