@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
package/dist/index.d.mts
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-BsnGTydH.mjs";
|
|
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,15 +22,70 @@ 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;
|
|
28
35
|
tags?: string[];
|
|
29
36
|
}) => Promise<unknown>;
|
|
30
37
|
};
|
|
38
|
+
/**
|
|
39
|
+
* Loopback bindings to the Worker's own top-level exports — where Cloudflare
|
|
40
|
+
* exposes the workflows and agents declared in wrangler `exports`, keyed by
|
|
41
|
+
* class name (see `shared/workflow-binding.ts`).
|
|
42
|
+
*/
|
|
43
|
+
exports?: unknown;
|
|
31
44
|
passThroughOnException?: () => void;
|
|
32
45
|
waitUntil?: (promise: Promise<unknown>) => void;
|
|
33
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* The identity Cloudflare Access attaches to a Worker-protected request.
|
|
49
|
+
*
|
|
50
|
+
* Shape follows the Access application-token payload: `sub` is the stable per-user
|
|
51
|
+
* id, `email` the verified address, `common_name` the service-token name (machine
|
|
52
|
+
* callers, whose `sub` is empty), and `exp` the credential expiry in epoch
|
|
53
|
+
* **seconds**. Group membership is whatever the Access policy emits — a list of
|
|
54
|
+
* names, or of `{ id, name }` objects — hence `unknown`; normalize before use.
|
|
55
|
+
*
|
|
56
|
+
* Cloudflare may add further fields, so the index signature keeps them rather
|
|
57
|
+
* than dropping them: this is a view of a payload we do not own.
|
|
58
|
+
*/
|
|
59
|
+
interface AccessIdentityLike {
|
|
60
|
+
[claim: string]: unknown;
|
|
61
|
+
/** Service-token name. Present for non-interactive (machine) callers instead of `email`. */
|
|
62
|
+
common_name?: string;
|
|
63
|
+
/** Verified user email. Present for interactive (SSO) callers. */
|
|
64
|
+
email?: string;
|
|
65
|
+
/** Credential expiry, epoch **seconds**. */
|
|
66
|
+
exp?: number;
|
|
67
|
+
/** IdP group membership — names or `{ id, name }` objects, depending on the policy. */
|
|
68
|
+
groups?: unknown;
|
|
69
|
+
/** Display name from the identity provider, when it emits one. */
|
|
70
|
+
name?: string;
|
|
71
|
+
/** Stable per-user id, and what consumers key a user on. Empty for service tokens. */
|
|
72
|
+
sub?: string;
|
|
73
|
+
/** 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. */
|
|
74
|
+
user_uuid?: string;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* The `ctx.access` facade Cloudflare exposes on a Worker protected by Access.
|
|
78
|
+
*
|
|
79
|
+
* Reading the identity from here is preferable to verifying the
|
|
80
|
+
* `Cf-Access-Jwt-Assertion` header: the platform has already authenticated the
|
|
81
|
+
* caller, so there is no JWKS fetch, no audience check to get wrong, and nothing
|
|
82
|
+
* a request can forge — the field simply does not exist unless Access authorized
|
|
83
|
+
* the call. The header path remains the fallback for hostname-scoped Access
|
|
84
|
+
* applications, which do not populate this.
|
|
85
|
+
*/
|
|
86
|
+
interface AccessContextLike {
|
|
87
|
+
getIdentity: () => AccessIdentityLike | null | undefined | Promise<AccessIdentityLike | null | undefined>;
|
|
88
|
+
}
|
|
34
89
|
/**
|
|
35
90
|
* No-op `ExecutionContext` used when the host runtime didn't supply one (a
|
|
36
91
|
* non-Cloudflare preview, or a unit test), so the worker's `fetch` always
|
|
@@ -65,6 +120,35 @@ interface QueueMessageLike<Body = unknown> {
|
|
|
65
120
|
}) => void;
|
|
66
121
|
readonly timestamp: Date;
|
|
67
122
|
}
|
|
123
|
+
/** Options accepted by {@link HttpCacheLike.match} and {@link HttpCacheLike.delete}. */
|
|
124
|
+
interface HttpCacheQueryOptions {
|
|
125
|
+
/** Match a non-`GET` request against a stored `GET` entry. */
|
|
126
|
+
ignoreMethod?: boolean;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Minimal projection of one Web Cache API cache — the store a host puts in front
|
|
130
|
+
* of the app, reached on Cloudflare as `caches.default` (the colo cache).
|
|
131
|
+
*
|
|
132
|
+
* Only the three calls Lunora makes are declared, so a host that has a cache but
|
|
133
|
+
* not the whole `Cache` interface still satisfies it, and a unit test can pass a
|
|
134
|
+
* plain object double. This is a **host** primitive, not a binding: it is reached
|
|
135
|
+
* through a runtime global rather than `env`, and a target without one leaves it
|
|
136
|
+
* `undefined` rather than shipping a fake — see `httpCache` in
|
|
137
|
+
* `PlatformCapabilities`.
|
|
138
|
+
*
|
|
139
|
+
* The stored entry is keyed by the request, so a caller that needs `Vary`
|
|
140
|
+
* semantics must fold the varying header values into the key itself: Cloudflare's
|
|
141
|
+
* cache honours `Vary` for `Accept-Encoding` only, and a projection cannot make
|
|
142
|
+
* that portable.
|
|
143
|
+
*/
|
|
144
|
+
interface HttpCacheLike {
|
|
145
|
+
/** Evict the entry stored under `request`. Resolves `true` when something was removed. */
|
|
146
|
+
delete: (request: Request | string, options?: HttpCacheQueryOptions) => Promise<boolean>;
|
|
147
|
+
/** The stored response for `request`, or `undefined` on a miss. */
|
|
148
|
+
match: (request: Request | string, options?: HttpCacheQueryOptions) => Promise<Response | undefined>;
|
|
149
|
+
/** Store `response` under `request`. Rejects for a `206`, a `Vary: *`, or a `Set-Cookie`-bearing response. */
|
|
150
|
+
put: (request: Request | string, response: Response) => Promise<void>;
|
|
151
|
+
}
|
|
68
152
|
/** A single vector match. */
|
|
69
153
|
interface VectorMatchLike {
|
|
70
154
|
id: string;
|
|
@@ -85,6 +169,7 @@ interface AnalyticsEngineDataPointLike {
|
|
|
85
169
|
}
|
|
86
170
|
interface D1PreparedStatementLike {
|
|
87
171
|
all: <T = unknown>() => Promise<{
|
|
172
|
+
meta?: Record<string, unknown>;
|
|
88
173
|
results: T[];
|
|
89
174
|
success: boolean;
|
|
90
175
|
}>;
|
|
@@ -214,30 +299,53 @@ type KvNamespaceListResult<Metadata = unknown> = {
|
|
|
214
299
|
list_complete: true;
|
|
215
300
|
};
|
|
216
301
|
/**
|
|
217
|
-
* Minimal structural projection of `
|
|
302
|
+
* Minimal structural projection of `Vectorize` so unit tests can pass a
|
|
218
303
|
* plain-object double and the real Cloudflare binding satisfies the same shape.
|
|
219
304
|
* Mirrors the surface documented at
|
|
220
305
|
* https://developers.cloudflare.com/vectorize/reference/client-api/.
|
|
306
|
+
*
|
|
307
|
+
* **Method** syntax, not arrow properties: `ReadonlyArray` is the correct (wider)
|
|
308
|
+
* parameter type, but an arrow property makes parameters strictly contravariant
|
|
309
|
+
* under `strictFunctionTypes`, and Cloudflare declares `deleteByIds(ids: string[])`
|
|
310
|
+
* — so the real binding did not satisfy this interface at all. Methods are checked
|
|
311
|
+
* bivariantly, which admits both it and a `readonly`-clean double.
|
|
312
|
+
* `binding-assignability.test-d.ts` in `@lunora/bindings` pins that against the
|
|
313
|
+
* published types. `this: void` keeps the members usable as bare references, which
|
|
314
|
+
* is what every `expect(double.query)` assertion in those tests does.
|
|
221
315
|
*/
|
|
222
316
|
interface VectorizeIndexLike {
|
|
223
|
-
deleteByIds:
|
|
224
|
-
describe
|
|
225
|
-
getByIds:
|
|
226
|
-
insert:
|
|
227
|
-
query:
|
|
228
|
-
upsert:
|
|
317
|
+
deleteByIds(this: void, ids: ReadonlyArray<string>): Promise<VectorizeDeleteMutation>;
|
|
318
|
+
describe?(this: void): Promise<VectorizeIndexDetails>;
|
|
319
|
+
getByIds(this: void, ids: ReadonlyArray<string>): Promise<ReadonlyArray<VectorizeVector>>;
|
|
320
|
+
insert(this: void, vectors: ReadonlyArray<VectorizeVector>): Promise<VectorizeUpsertMutation>;
|
|
321
|
+
query(this: void, vector: VectorValues, options?: VectorizeQueryOptions): Promise<VectorizeMatches>;
|
|
322
|
+
upsert(this: void, vectors: ReadonlyArray<VectorizeVector>): Promise<VectorizeUpsertMutation>;
|
|
229
323
|
}
|
|
230
324
|
type VectorMetric = "cosine" | "euclidean" | "dot-product";
|
|
325
|
+
/**
|
|
326
|
+
* An embedding, as either a plain array or one of the typed arrays Vectorize
|
|
327
|
+
* accepts. The typed-array arms are not a convenience: Cloudflare's own
|
|
328
|
+
* `VectorizeVector.values` is `VectorFloatArray | number[]`, so a projection
|
|
329
|
+
* limited to `ReadonlyArray<number>` excluded every vector the real binding
|
|
330
|
+
* returns. Read one with `Array.from(...)` rather than an array method.
|
|
331
|
+
*/
|
|
332
|
+
type VectorValues = Float32Array | Float64Array | ReadonlyArray<number>;
|
|
231
333
|
interface VectorizeVector {
|
|
232
334
|
id: string;
|
|
233
335
|
metadata?: Record<string, unknown>;
|
|
234
336
|
namespace?: string;
|
|
235
|
-
values:
|
|
337
|
+
values: VectorValues;
|
|
236
338
|
}
|
|
339
|
+
/**
|
|
340
|
+
* Query options, kept a superset of Cloudflare's own `VectorizeQueryOptions` so
|
|
341
|
+
* the real binding satisfies {@link VectorizeIndexLike}. `returnMetadata` carries
|
|
342
|
+
* the legacy `boolean` arm for that reason - pass one of the three levels; the
|
|
343
|
+
* boolean is what Cloudflare still accepts, not what callers should write.
|
|
344
|
+
*/
|
|
237
345
|
interface VectorizeQueryOptions {
|
|
238
346
|
filter?: Record<string, unknown>;
|
|
239
347
|
namespace?: string;
|
|
240
|
-
returnMetadata?: "none" | "indexed" | "all";
|
|
348
|
+
returnMetadata?: "none" | "indexed" | "all" | boolean;
|
|
241
349
|
returnValues?: boolean;
|
|
242
350
|
topK?: number;
|
|
243
351
|
}
|
|
@@ -246,7 +354,7 @@ interface VectorizeMatch {
|
|
|
246
354
|
metadata?: Record<string, unknown>;
|
|
247
355
|
namespace?: string;
|
|
248
356
|
score: number;
|
|
249
|
-
values?:
|
|
357
|
+
values?: VectorValues;
|
|
250
358
|
}
|
|
251
359
|
interface VectorizeMatches {
|
|
252
360
|
count: number;
|
|
@@ -259,11 +367,24 @@ interface VectorizeDeleteMutation {
|
|
|
259
367
|
count?: number;
|
|
260
368
|
mutationId: string;
|
|
261
369
|
}
|
|
370
|
+
/**
|
|
371
|
+
* What `describe()` reports. Both spellings of the row count are optional because
|
|
372
|
+
* Cloudflare renamed it between API generations — the beta `VectorizeIndex`
|
|
373
|
+
* returns `vectorsCount`, the current `Vectorize` returns `vectorCount` — and a
|
|
374
|
+
* projection that required either one excluded a real binding. Read them as
|
|
375
|
+
* `vectorCount ?? vectorsCount`.
|
|
376
|
+
*
|
|
377
|
+
* `processedUpTo*` are typed loosely for the same reason: documented as ISO 8601
|
|
378
|
+
* strings, typed as `number` in `@cloudflare/workers-types`.
|
|
379
|
+
*/
|
|
262
380
|
interface VectorizeIndexDetails {
|
|
263
381
|
dimensions: number;
|
|
264
|
-
processedUpToDatetime?: string;
|
|
265
|
-
processedUpToMutation?: string;
|
|
266
|
-
|
|
382
|
+
processedUpToDatetime?: number | string;
|
|
383
|
+
processedUpToMutation?: number | string;
|
|
384
|
+
/** The current `Vectorize.describe()` spelling. */
|
|
385
|
+
vectorCount?: number;
|
|
386
|
+
/** The beta `VectorizeIndex.describe()` spelling. */
|
|
387
|
+
vectorsCount?: number;
|
|
267
388
|
}
|
|
268
389
|
/**
|
|
269
390
|
* A single-range read against R2: an `{ offset, length }` window (at least one
|
|
@@ -310,18 +431,22 @@ interface R2BucketLike {
|
|
|
310
431
|
list: (options?: {
|
|
311
432
|
cursor?: string;
|
|
312
433
|
delimiter?: string;
|
|
434
|
+
include?: ("customMetadata" | "httpMetadata")[];
|
|
313
435
|
limit?: number;
|
|
314
436
|
prefix?: string;
|
|
437
|
+
startAfter?: string;
|
|
315
438
|
}) => Promise<{
|
|
316
439
|
cursor?: string;
|
|
440
|
+
delimitedPrefixes?: string[];
|
|
317
441
|
objects: R2ObjectLike[];
|
|
318
442
|
truncated?: boolean;
|
|
319
443
|
}>;
|
|
320
|
-
put: (key: string, body: ReadableStream | ArrayBuffer | Blob | string | null, options?: {
|
|
444
|
+
put: (key: string, body: ReadableStream | ArrayBuffer | ArrayBufferView | Blob | string | null, options?: {
|
|
321
445
|
customMetadata?: Record<string, string>;
|
|
322
446
|
httpMetadata?: {
|
|
323
447
|
contentType?: string;
|
|
324
448
|
};
|
|
449
|
+
sha256?: ArrayBuffer | string;
|
|
325
450
|
}) => Promise<R2ObjectLike>;
|
|
326
451
|
/** Resume an in-progress multipart upload by id (R2 `resumeMultipartUpload`). Optional; see {@link Storage.resumeMultipartUpload}. */
|
|
327
452
|
resumeMultipartUpload?: (key: string, uploadId: string) => R2MultipartUploadLike;
|
|
@@ -417,15 +542,15 @@ interface MessageSendRequestLike<Body = unknown> {
|
|
|
417
542
|
delaySeconds?: number;
|
|
418
543
|
}
|
|
419
544
|
/**
|
|
420
|
-
* Minimal structural projection of workers-types' `Queue
|
|
545
|
+
* Minimal structural projection of workers-types' `Queue<Body>` (the producer
|
|
421
546
|
* binding). The real binding's `send`/`sendBatch` resolve to a metadata object;
|
|
422
|
-
* we widen the return to `Promise
|
|
547
|
+
* we widen the return to `Promise<unknown>` so a plain-object fake satisfies it.
|
|
423
548
|
*/
|
|
424
549
|
interface QueueBindingLike<Body = unknown> {
|
|
425
550
|
send: (message: Body, options?: QueueSendOptions) => Promise<unknown>;
|
|
426
551
|
sendBatch: (messages: Iterable<MessageSendRequestLike<Body>>, options?: QueueSendBatchOptions) => Promise<unknown>;
|
|
427
552
|
}
|
|
428
|
-
/** Structural mirror of workers-types' `Message
|
|
553
|
+
/** Structural mirror of workers-types' `Message<Body>` (one delivered message). */
|
|
429
554
|
interface MessageLike<Body = unknown> {
|
|
430
555
|
/** Acknowledge this message so it is not redelivered. */
|
|
431
556
|
ack: () => void;
|
|
@@ -436,7 +561,7 @@ interface MessageLike<Body = unknown> {
|
|
|
436
561
|
retry: (options?: QueueRetryOptions) => void;
|
|
437
562
|
readonly timestamp: Date;
|
|
438
563
|
}
|
|
439
|
-
/** Structural mirror of workers-types' `MessageBatch
|
|
564
|
+
/** Structural mirror of workers-types' `MessageBatch<Body>` handed to a consumer. */
|
|
440
565
|
interface MessageBatchLike<Body = unknown> {
|
|
441
566
|
/** Acknowledge every message in the batch. */
|
|
442
567
|
ackAll: () => void;
|
|
@@ -455,9 +580,105 @@ interface QueueRetryOptions {
|
|
|
455
580
|
* Lunora features a target platform supports natively, emulates, or cannot
|
|
456
581
|
* support at all.
|
|
457
582
|
*
|
|
458
|
-
*
|
|
459
|
-
*
|
|
460
|
-
*
|
|
583
|
+
* # Who reads it
|
|
584
|
+
*
|
|
585
|
+
* **`@lunora/codegen` is the only gating consumer.** `gateAgainstMatrix`
|
|
586
|
+
* (`packages/codegen/src/platform-target.ts`) intersects an app's detected
|
|
587
|
+
* feature usage with the target's matrix and diagnoses exactly two states:
|
|
588
|
+
* `unsupported` (`platform_unsupported_feature`) and a key missing from the
|
|
589
|
+
* matrix altogether (`platform_undeclared_feature`, the fail-closed arm).
|
|
590
|
+
* `native` and `emulated` are emitted identically, with no diagnostic between
|
|
591
|
+
* them — that distinction exists for honest parity reporting, not for codegen.
|
|
592
|
+
*
|
|
593
|
+
* The generated worker also reports its target's levels to `@lunora/studio`
|
|
594
|
+
* (the `platform` field of `__lunora_admin__:studioFeatures`), which marks a
|
|
595
|
+
* page whose feature the target rates `unsupported` as unavailable. Studio
|
|
596
|
+
* still imports nothing from here — the worker tells it. The per-feature table
|
|
597
|
+
* in `packages/platform-node/docs/index.mdx` is a hand-written copy held
|
|
598
|
+
* verbatim by `pnpm run lint:node-capabilities-docs`: change a rating or a note
|
|
599
|
+
* here first, then that table, or the check fails.
|
|
600
|
+
*
|
|
601
|
+
* # Gate-bearing keys
|
|
602
|
+
*
|
|
603
|
+
* A rating only gates something if `@lunora/codegen` reads it — either through a
|
|
604
|
+
* usage key mapped onto the feature (`CAPABILITY_ROWS` + `CAPABILITY_TO_FEATURE`,
|
|
605
|
+
* for an app-imported `ctx.*` module) or through a `PlatformSignals` entry (for
|
|
606
|
+
* something the app declares in its schema or a declaration file). The
|
|
607
|
+
* gate-bearing keys are:
|
|
608
|
+
*
|
|
609
|
+
* `agents`, `ai`, `aiSearch`, `analytics`, `artifacts`, `browser`, `commitOrderedTables`,
|
|
610
|
+
* `containerEgressPolicy`, `containerRuntimeScheduling`, `containerSandboxTools`, `containers`,
|
|
611
|
+
* `cronTriggers`, `crossShardFanout`,
|
|
612
|
+
* `durableStreams`, `globalTables`, `hyperdrive`, `images`, `keyValueStore`,
|
|
613
|
+
* `mail`, `objectStorage`, `pipelines`, `queues`, `relationGraph`,
|
|
614
|
+
* `scheduler`, `secrets`, `services`, `topics`, `vectorStore`, `workerLoaders`, `workflowRollback`,
|
|
615
|
+
* `workflowSchedules`, `workflows`.
|
|
616
|
+
*
|
|
617
|
+
* Every other key here — `authJurisdictionMove`, `edgeRequestMetadata`, `hostTraceFusion`, `httpCache`,
|
|
618
|
+
* `identityProxy`, `localSql`, `logArchive`, `memoryTables`,
|
|
619
|
+
* `objectStorageBackups`, `objectStorageCdcArchive`, `pointInTimeRecovery`,
|
|
620
|
+
* `serverReactors`,
|
|
621
|
+
* `shardAlarms`, `shardedState`, `shardPlacement`, `shardReadReplicas`,
|
|
622
|
+
* `websocketHibernation` — is
|
|
623
|
+
* **advisory**: rating one `unsupported` omits no surface and warns nobody. It
|
|
624
|
+
* still records parity honestly, which is its job; it is not a gate.
|
|
625
|
+
*
|
|
626
|
+
* # Advisory is not one thing — there are two reasons, and only one is final
|
|
627
|
+
*
|
|
628
|
+
* `authJurisdictionMove` is advisory by nature too: an app never declares it.
|
|
629
|
+
* Codegen wires the admin ops whenever DO-backed auth is pinned to a
|
|
630
|
+
* jurisdiction, so the thing a host would have to refuse is the jurisdiction.
|
|
631
|
+
*
|
|
632
|
+
* Most advisory keys are advisory *by nature*: the feature is engine-internal
|
|
633
|
+
* (`shardAlarms`, `shardedState`, `shardPlacement`, `shardReadReplicas`,
|
|
634
|
+
* `websocketHibernation`, `localSql`, `serverReactors`) or degrades honestly on
|
|
635
|
+
* its own (`httpCache` falls back to headers-only, `identityProxy` to header
|
|
636
|
+
* verification). There is nothing an app declares for codegen to notice, so
|
|
637
|
+
* there is nothing to gate. These stay ratings, permanently.
|
|
638
|
+
*
|
|
639
|
+
* The three telemetry keys — `edgeRequestMetadata`, `hostTraceFusion`,
|
|
640
|
+
* `logArchive` — are advisory by nature for a third reason worth naming, since
|
|
641
|
+
* it is not obvious: each is configured through a `createWorker` argument or an
|
|
642
|
+
* `ObservabilitySink` field (`trustInboundTraceContext`, `fuseCloudflareTraces`,
|
|
643
|
+
* `logArchive`), and codegen reads neither. There is no app-side DECLARATION to
|
|
644
|
+
* gate on, so promoting them would mean inventing one. Where that silence
|
|
645
|
+
* actually bites — `trustInboundTraceContext: "mtls"` collapsing to never-trust
|
|
646
|
+
* off Cloudflare — the runtime warns once instead, from the only tier that can
|
|
647
|
+
* observe it (`createDroppedTraceNotice` in `@lunora/runtime`).
|
|
648
|
+
*
|
|
649
|
+
* The rest of the telemetry pipeline — `ctx.log`, `ctx.trace`, `ctx.span`,
|
|
650
|
+
* `ctx.metrics`, traced `ctx.fetch`, and W3C trace propagation — deliberately
|
|
651
|
+
* has NO key: it is sink callbacks over the `fetch` global, needs no host
|
|
652
|
+
* primitive, and a key every target must rate `native` forever is paperwork, not
|
|
653
|
+
* a control.
|
|
654
|
+
*
|
|
655
|
+
* The rest are advisory only because nobody wired them, and they are the ones
|
|
656
|
+
* to watch: an app DOES declare the feature, codegen CAN see the declaration,
|
|
657
|
+
* and the rating is still consulted by nothing. Codegen already has the shape
|
|
658
|
+
* for exactly this — `PlatformSignals` in `platform-target.ts`, the second gate
|
|
659
|
+
* pass that diagnoses app-declared features with no `ctx.*` capability row
|
|
660
|
+
* (`agents`, `commitOrderedTables`, `containerEgressPolicy`,
|
|
661
|
+
* `containerRuntimeScheduling`, `containerSandboxTools`, `cronTriggers`,
|
|
662
|
+
* `crossShardFanout`, `durableStreams`, `globalTables`, `queues`,
|
|
663
|
+
* `relationGraph`, `secrets`, `services`, `topics`, `vectorStore`, `workflowRollback`,
|
|
664
|
+
* `workflowSchedules`).
|
|
665
|
+
* Promoting one is three lines there: a `PlatformSignals` field, plus its entry
|
|
666
|
+
* in that module's signal-key list and its human-readable label — and then
|
|
667
|
+
* setting the signal from the IR.
|
|
668
|
+
*
|
|
669
|
+
* `commitOrderedTables` was promoted that way: `TableIR.commitOrdered` sits in
|
|
670
|
+
* the same IR that feeds `globalTables`, and until it was read a host rating it
|
|
671
|
+
* `unsupported` emitted the full `.commitOrdered()` surface with no diagnostic
|
|
672
|
+
* and silently lost commit ordering — the one guarantee the feature is.
|
|
673
|
+
* `memoryTables`, `objectStorageBackups` and `objectStorageCdcArchive` remain
|
|
674
|
+
* weaker instances of the same shape, still unpromoted.
|
|
675
|
+
*
|
|
676
|
+
* **Adding a feature key is therefore half a change.** The other half is a row
|
|
677
|
+
* in `CAPABILITY_ROWS` and an entry in `CAPABILITY_TO_FEATURE` (for an
|
|
678
|
+
* app-imported `ctx.*` module), or a `PlatformSignals` entry (for something the
|
|
679
|
+
* app declares in its schema), or a deliberate decision that the key is advisory
|
|
680
|
+
* by nature — recorded here. Silence means the rating ships as documentation
|
|
681
|
+
* while the surface it describes is emitted anyway.
|
|
461
682
|
*/
|
|
462
683
|
/** Support level for a single feature on a target platform. */
|
|
463
684
|
type CapabilityLevel = "native" | "emulated" | "unsupported";
|
|
@@ -475,52 +696,476 @@ interface Capability {
|
|
|
475
696
|
interface PlatformCapabilities {
|
|
476
697
|
/** Feature-level capabilities. */
|
|
477
698
|
features: {
|
|
478
|
-
/**
|
|
699
|
+
/**
|
|
700
|
+
* Durable agents — a `defineAgent` export in `lunora/agents.ts`.
|
|
701
|
+
*
|
|
702
|
+
* Its own key rather than a facet of `workflows` or `ai`, because an
|
|
703
|
+
* agent needs BOTH and neither implies the other: the generated class
|
|
704
|
+
* compiles onto the host's workflow engine as a `ctx.exports.<Class>` workflow
|
|
705
|
+
* the emitted context resolves off `env`, and the loop it runs there
|
|
706
|
+
* calls model inference. A host that emulates workflows but has no
|
|
707
|
+
* inference (or no way to mount a generated class into its engine) can
|
|
708
|
+
* rate `workflows` honestly and still not run an agent.
|
|
709
|
+
*/
|
|
710
|
+
agents?: Capability;
|
|
711
|
+
/**
|
|
712
|
+
* AI inference — `ctx.ai` (Workers AI / Bedrock / OpenAI).
|
|
713
|
+
*
|
|
714
|
+
* Covers every model id `ctx.ai.model(...)` resolves, including
|
|
715
|
+
* `"<provider>/<model>"` catalog slugs and `dynamic/<route>` ids, which
|
|
716
|
+
* route through Cloudflare AI Gateway over the same `AI` binding rather
|
|
717
|
+
* than a binding of their own. A host rating this `native` must say how
|
|
718
|
+
* slugs resolve there; one without a gateway is honest to rate the
|
|
719
|
+
* Workers AI half and note the slug half missing. Usage accounting rides
|
|
720
|
+
* `ctx.trace` / `ctx.metrics`, which need no key.
|
|
721
|
+
*/
|
|
479
722
|
ai?: Capability;
|
|
723
|
+
/**
|
|
724
|
+
* Cloudflare AI Search (formerly AutoRAG) — `ctx.aiSearch`, the raw
|
|
725
|
+
* `ai_search_namespaces` binding passed through as-is, on ActionCtx only.
|
|
726
|
+
*
|
|
727
|
+
* Its own key rather than a facet of `ai` or `vectorStore`: it is a
|
|
728
|
+
* managed retrieval service (chunking, embedding, the hybrid index and
|
|
729
|
+
* reranking all live on the instance), not inference and not a vector
|
|
730
|
+
* index a host could stand in for. No Lunora host contract carries it —
|
|
731
|
+
* a second host would have to supply an `AiSearchNamespace` look-alike.
|
|
732
|
+
* Gate-bearing through `CAPABILITY_TO_FEATURE`.
|
|
733
|
+
*/
|
|
734
|
+
aiSearch?: Capability;
|
|
480
735
|
/** Analytics / observability sinks. */
|
|
481
736
|
analytics?: Capability;
|
|
737
|
+
/** Git-backed versioned repos via an Artifacts binding (`ctx.artifacts`). */
|
|
738
|
+
artifacts?: Capability;
|
|
739
|
+
/**
|
|
740
|
+
* Copying DO-backed auth from its un-pinned object into the
|
|
741
|
+
* jurisdiction-pinned one, and purging the un-pinned copy afterwards
|
|
742
|
+
* (the worker's `copyAuthToJurisdiction` / `purgeUnpinnedAuth` admin
|
|
743
|
+
* ops). Advisory by nature: it exists only once a schema pins auth with
|
|
744
|
+
* `.jurisdiction(…)`, which is itself the thing a host without
|
|
745
|
+
* jurisdictions cannot run.
|
|
746
|
+
*/
|
|
747
|
+
authJurisdictionMove?: Capability;
|
|
482
748
|
/** Browser rendering / headless browser. */
|
|
483
749
|
browser?: Capability;
|
|
484
|
-
/**
|
|
750
|
+
/**
|
|
751
|
+
* `.commitOrdered()` tables — the `_commitSeq` system field: a per-shard
|
|
752
|
+
* integer allocated once per mutation and strictly increasing in commit
|
|
753
|
+
* order.
|
|
754
|
+
*
|
|
755
|
+
* Listed as a capability rather than assumed, because the ordering
|
|
756
|
+
* guarantee is not the engine's to give. It rests on two things the HOST
|
|
757
|
+
* provides: an atomic write boundary the counter bump shares with the
|
|
758
|
+
* rows it stamps, and serialized execution so two mutations cannot
|
|
759
|
+
* interleave their allocations. A host that offers neither can still
|
|
760
|
+
* create the counter and hand out increasing numbers — they just would
|
|
761
|
+
* not order commits, which is the whole contract.
|
|
762
|
+
*
|
|
763
|
+
* Gate-bearing: `TableIR.commitOrdered` feeds the `PlatformSignals`
|
|
764
|
+
* pass off the same IR the `globalTables` signal reads, so a host
|
|
765
|
+
* rating this `unsupported` refuses the app rather than emitting the
|
|
766
|
+
* full `.commitOrdered()` surface and silently dropping the ordering
|
|
767
|
+
* guarantee — which is the only thing the feature is.
|
|
768
|
+
*/
|
|
769
|
+
commitOrderedTables?: Capability;
|
|
770
|
+
/**
|
|
771
|
+
* An outbound-traffic policy on a container — `defineContainer({
|
|
772
|
+
* allowedHosts | deniedHosts | interceptHttps })`, which
|
|
773
|
+
* `LunoraContainer` applies through `@cloudflare/containers`'
|
|
774
|
+
* outbound interception.
|
|
775
|
+
*
|
|
776
|
+
* Rated apart from `containers` because a host can run a container and
|
|
777
|
+
* still not police its egress; the policy is then not merely absent,
|
|
778
|
+
* the container refuses to start. Gate-bearing: codegen sets the
|
|
779
|
+
* `containerEgressPolicy` `PlatformSignals` flag when a
|
|
780
|
+
* `defineContainer` call carries one of those keys, so the gap is a
|
|
781
|
+
* build-time diagnostic rather than a container that fails on first use.
|
|
782
|
+
*/
|
|
783
|
+
containerEgressPolicy?: Capability;
|
|
784
|
+
/**
|
|
785
|
+
* The `durable_object` container scheduling policy —
|
|
786
|
+
* `defineContainer({ schedulingPolicy: "durable_object" })`: each
|
|
787
|
+
* instance picks its image and size at `start()`, and can save and
|
|
788
|
+
* restore its filesystem (`snapshot()` / `start({ snapshot })`).
|
|
789
|
+
*
|
|
790
|
+
* One key for both halves because snapshots only exist under this
|
|
791
|
+
* policy — there is no app declaration for a snapshot that codegen
|
|
792
|
+
* could gate apart from the policy. Rated apart from `containers`
|
|
793
|
+
* because a host can run a container from one configured image and
|
|
794
|
+
* still have no way to start a chosen image or restore a filesystem.
|
|
795
|
+
* Gate-bearing: codegen sets the `containerRuntimeScheduling`
|
|
796
|
+
* `PlatformSignals` flag off `ContainerIR.schedulingPolicy`.
|
|
797
|
+
*/
|
|
798
|
+
containerRuntimeScheduling?: Capability;
|
|
799
|
+
/**
|
|
800
|
+
* Container execution (Cloudflare Containers / Fargate), including
|
|
801
|
+
* `ctx.containers.<name>.exec`. Deliberately one rating rather than two:
|
|
802
|
+
* `exec` is a method on the accessor this key already gates, not a
|
|
803
|
+
* separate app-imported surface, so there is no usage signal codegen
|
|
804
|
+
* could gate it on independently and nothing that could act on a second
|
|
805
|
+
* rating. A host that can reach a container but cannot carry a command
|
|
806
|
+
* result back should say so in this note.
|
|
807
|
+
*/
|
|
485
808
|
containers?: Capability;
|
|
809
|
+
/**
|
|
810
|
+
* The Sandbox SDK helpers on a `defineContainer({ sandbox: true })`
|
|
811
|
+
* container: structured file operations (`Files`), directory backups to
|
|
812
|
+
* R2 (`DirectoryBackup`) and S3-compatible bucket mounts (`S3Mount`),
|
|
813
|
+
* from `@cloudflare/sandbox`. All three run the `sandbox-shim` helper
|
|
814
|
+
* through the native `ctx.container.exec()`, and the backup and mount
|
|
815
|
+
* helpers route the container's storage traffic through Worker
|
|
816
|
+
* entrypoints via `interceptOutboundHttp`. A host that runs a container
|
|
817
|
+
* but has neither cannot carry them. Gate-bearing: codegen sets the
|
|
818
|
+
* `containerSandboxTools` `PlatformSignals` flag off `ContainerIR.sandbox`.
|
|
819
|
+
*/
|
|
820
|
+
containerSandboxTools?: Capability;
|
|
821
|
+
/**
|
|
822
|
+
* DECLARED cron triggers — the `cronJobs()` registrations codegen lifts
|
|
823
|
+
* into `LUNORA_CRONS`, dispatched by whatever the host wakes on a
|
|
824
|
+
* schedule.
|
|
825
|
+
*
|
|
826
|
+
* Separate from {@link PlatformCapabilities.features.scheduler}, which
|
|
827
|
+
* rates the imperative surface (`ctx.scheduler.runAfter/runAt`, a job
|
|
828
|
+
* the app enqueues at runtime). The two are genuinely independent: a
|
|
829
|
+
* host can dispatch enqueued jobs perfectly and still walk nothing into
|
|
830
|
+
* its declared crons, in which case an app's `crons.daily(...)` never
|
|
831
|
+
* fires. One rating covering both is how that shipped as green.
|
|
832
|
+
*/
|
|
833
|
+
cronTriggers?: Capability;
|
|
486
834
|
/** Cross-shard fan-out queries. */
|
|
487
835
|
crossShardFanout?: Capability;
|
|
836
|
+
/**
|
|
837
|
+
* Durable streams: a `.stream()` run whose chunks are persisted and
|
|
838
|
+
* whose producer outlives the socket that opened it, so a reconnecting
|
|
839
|
+
* or second client resumes the same transcript.
|
|
840
|
+
*/
|
|
841
|
+
durableStreams?: Capability;
|
|
842
|
+
/**
|
|
843
|
+
* Platform-injected PER-REQUEST metadata the runtime reads off the
|
|
844
|
+
* request object itself rather than a header — Cloudflare's `request.cf`.
|
|
845
|
+
*
|
|
846
|
+
* Two telemetry surfaces stand on it, and both degrade SILENTLY without
|
|
847
|
+
* it, which is why it is rated rather than assumed: the
|
|
848
|
+
* `trustInboundTraceContext: "mtls"` trust signal reads
|
|
849
|
+
* `cf.tlsClientAuth.certVerified` (absent ⇒ no caller is ever trusted, so
|
|
850
|
+
* every inbound `traceparent` is dropped), and the OTLP resource detector
|
|
851
|
+
* reads the colo/country placement attributes (absent ⇒ the spans carry
|
|
852
|
+
* no placement resource).
|
|
853
|
+
*
|
|
854
|
+
* What makes it a capability and not a header check is unforgeability:
|
|
855
|
+
* the platform sets it, so a caller cannot write it. A host that merely
|
|
856
|
+
* stamps a header carries no such proof and should rate this
|
|
857
|
+
* `unsupported`.
|
|
858
|
+
*
|
|
859
|
+
* Advisory by nature: both consumers are `createWorker` options, which
|
|
860
|
+
* codegen never sees, so there is no app-side declaration to gate on. The
|
|
861
|
+
* runtime warns once instead when a dropped trace proves the signal is
|
|
862
|
+
* undeliverable — see `createDroppedTraceNotice` in `@lunora/runtime`.
|
|
863
|
+
*/
|
|
864
|
+
edgeRequestMetadata?: Capability;
|
|
488
865
|
/** Global (replicated) tables backed by a SQL store. */
|
|
489
866
|
globalTables?: Capability;
|
|
867
|
+
/**
|
|
868
|
+
* Merging Lunora's spans into the HOST's own trace tree, so its native
|
|
869
|
+
* tracing shows one nested tree instead of two unrelated ones — the
|
|
870
|
+
* sink's `fuseCloudflareTraces` opt-in, which reaches `cloudflare:workers`'
|
|
871
|
+
* `tracing.enterSpan` (plus, where present, `getActiveSpan` for the
|
|
872
|
+
* invocation root and a span's `setAttributes` / `recordException`).
|
|
873
|
+
*
|
|
874
|
+
* Rated because it is the one telemetry surface that reaches past
|
|
875
|
+
* `ShardHost` into a provider API. Everything else in the pipeline is
|
|
876
|
+
* engine-level (a sink callback), so it runs anywhere; this needs the
|
|
877
|
+
* host to HAVE a trace tree and to expose a way to enter a span in it.
|
|
878
|
+
*
|
|
879
|
+
* `unsupported` costs nothing: the feature is already capability-probed
|
|
880
|
+
* at runtime and no-ops where the import is unavailable. The rating is
|
|
881
|
+
* what makes that a stated fact rather than something a second host
|
|
882
|
+
* discovers.
|
|
883
|
+
*
|
|
884
|
+
* Advisory by nature: the flag lives on the sink object passed to
|
|
885
|
+
* `createWorker`/`createShardDO`, which codegen never sees.
|
|
886
|
+
*/
|
|
887
|
+
hostTraceFusion?: Capability;
|
|
888
|
+
/**
|
|
889
|
+
* A shared HTTP cache in front of the app that the runtime can READ AND
|
|
890
|
+
* WRITE — the Web Cache API (`caches.default` on Cloudflare), projected
|
|
891
|
+
* as `HttpCacheLike`.
|
|
892
|
+
*
|
|
893
|
+
* Rated separately from the app merely emitting `Cache-Control`, because
|
|
894
|
+
* only this half needs a host primitive. Emitting the header is portable
|
|
895
|
+
* by construction: any host that returns an HTTP response can do it, and
|
|
896
|
+
* browsers and downstream CDNs honour it wherever the app runs. What is
|
|
897
|
+
* not portable is a store the Worker itself can `match`/`put` against,
|
|
898
|
+
* which is why `@lunora/runtime`'s REST edge cache degrades to
|
|
899
|
+
* headers-only on a target rated `unsupported` rather than failing.
|
|
900
|
+
*/
|
|
901
|
+
httpCache?: Capability;
|
|
490
902
|
/** BYO database via connection pooling (Hyperdrive / RDS Proxy). */
|
|
491
903
|
hyperdrive?: Capability;
|
|
904
|
+
/**
|
|
905
|
+
* An identity-aware proxy in front of the app that authenticates the
|
|
906
|
+
* caller before the request reaches it, and hands the runtime a verified
|
|
907
|
+
* identity **out-of-band** — on the execution context rather than on the
|
|
908
|
+
* request (Cloudflare Access attached to a Worker; IAP; an ALB OIDC
|
|
909
|
+
* action).
|
|
910
|
+
*
|
|
911
|
+
* Rated separately from the header-stamping form of the same product
|
|
912
|
+
* because only this one needs a host primitive. An identity-aware proxy
|
|
913
|
+
* that merely adds a signed header is portable by construction: any host
|
|
914
|
+
* that receives an HTTP request can verify it, which is why
|
|
915
|
+
* `@lunora/cloudflare-access` still works on a target rated
|
|
916
|
+
* `unsupported` here (it falls back to the `Cf-Access-Jwt-Assertion`
|
|
917
|
+
* JWT). What is not portable is the identity arriving beside the
|
|
918
|
+
* request, which is why `ExecutionContextLike.access` is a projection a
|
|
919
|
+
* host either populates or does not.
|
|
920
|
+
*/
|
|
921
|
+
identityProxy?: Capability;
|
|
922
|
+
/** Image transforms (resize/format/optimize) via an Images binding. */
|
|
923
|
+
images?: Capability;
|
|
492
924
|
/** Key-value storage (KV / Redis / DynamoDB). */
|
|
493
925
|
keyValueStore?: Capability;
|
|
494
926
|
/** Local SQL execution inside a shard. */
|
|
495
927
|
localSql?: Capability;
|
|
928
|
+
/**
|
|
929
|
+
* Reading back the DURABLE `ctx.log` archive — the `logArchive` worker
|
|
930
|
+
* option behind the studio Logs panel's Archive feed and `lunora logs
|
|
931
|
+
* --durable`.
|
|
932
|
+
*
|
|
933
|
+
* Distinct from `pipelines` (which writes the records) because the read
|
|
934
|
+
* side needs two more things the write side does not: an Iceberg catalog
|
|
935
|
+
* over the object store, and an SQL engine that can query it
|
|
936
|
+
* (R2 Data Catalog + R2 SQL). A host that can ship log records to cold
|
|
937
|
+
* storage but cannot query them back should say `unsupported` here.
|
|
938
|
+
*
|
|
939
|
+
* Advisory by nature: the archive table is named by a `createWorker`
|
|
940
|
+
* option or the `LUNORA_LOG_ARCHIVE_TABLE` env var, neither of which
|
|
941
|
+
* codegen sees. Both fail closed at runtime with a single
|
|
942
|
+
* `LOG_ARCHIVE_NOT_CONFIGURED`, which the panel renders as an empty
|
|
943
|
+
* state, so an unsupported host degrades visibly rather than silently.
|
|
944
|
+
*/
|
|
945
|
+
logArchive?: Capability;
|
|
496
946
|
/** Email sending (Resend / SES / etc). */
|
|
497
947
|
mail?: Capability;
|
|
498
|
-
/**
|
|
948
|
+
/**
|
|
949
|
+
* `.memory()` tables — the ephemeral tier: rows cleared on every shard
|
|
950
|
+
* cold start, never written to the CDC changelog, refilled by
|
|
951
|
+
* `onShardInit`.
|
|
952
|
+
*
|
|
953
|
+
* The rating answers "does a memory table avoid durable storage on this
|
|
954
|
+
* host", NOT "does it work". The lifetime semantics are the engine's and
|
|
955
|
+
* hold everywhere; whether the rows actually stay out of the durable
|
|
956
|
+
* store depends on the host offering a second, memory-backed SQL handle,
|
|
957
|
+
* which is a per-target fact.
|
|
958
|
+
*/
|
|
959
|
+
memoryTables?: Capability;
|
|
960
|
+
/**
|
|
961
|
+
* Object storage (R2 / S3 / MinIO).
|
|
962
|
+
*
|
|
963
|
+
* `ctx.storage.deleteAfterCommit(key)` rides on this rating and gets no
|
|
964
|
+
* key of its own: it needs no host primitive beyond the bucket. The
|
|
965
|
+
* post-commit flush uses `ShardHost.waitUntil` where the host has one and
|
|
966
|
+
* is awaited inline where it does not, so a host that can serve
|
|
967
|
+
* `objectStorage` serves the deferral at the same level.
|
|
968
|
+
*/
|
|
499
969
|
objectStorage?: Capability;
|
|
970
|
+
/**
|
|
971
|
+
* Snapshot backups kept in object storage rather than on the machine
|
|
972
|
+
* that took them — `lunora backup create|list|restore --bucket`, and
|
|
973
|
+
* the platform's own `backupCron`. Distinct from
|
|
974
|
+
* `objectStorage` above because it needs three things a
|
|
975
|
+
* bucket alone does not imply: an admin-gated read of one object
|
|
976
|
+
* (`GET /_lunora/admin/storage/object`), a checksum-verified write, and
|
|
977
|
+
* a scheduler to run the unattended half.
|
|
978
|
+
*/
|
|
979
|
+
objectStorageBackups?: Capability;
|
|
980
|
+
/**
|
|
981
|
+
* The CDC changelog's cold tier: rows a retention sweep is about to
|
|
982
|
+
* destroy are written to an object-storage bucket first
|
|
983
|
+
* (`LUNORA_CDC_ARCHIVE`), and a consumer whose cursor has fallen below
|
|
984
|
+
* the retained window is served from there instead of being told to
|
|
985
|
+
* re-seed.
|
|
986
|
+
*
|
|
987
|
+
* Distinct from `objectStorage` because it needs the bucket to do one
|
|
988
|
+
* thing a plain byte store need not: resume a key-ordered listing from a
|
|
989
|
+
* position (`list({ startAfter })`). Without it the read-back re-lists
|
|
990
|
+
* the prefix from the front every time and stops finding the range it
|
|
991
|
+
* needs once enough segments precede the cursor — which fails as a
|
|
992
|
+
* refusal rather than a gap, but fails permanently and silently, so a
|
|
993
|
+
* host that cannot seek should say `unsupported` here rather than
|
|
994
|
+
* inherit `objectStorage`'s rating.
|
|
995
|
+
*/
|
|
996
|
+
objectStorageCdcArchive?: Capability;
|
|
500
997
|
/** Pipelines / streaming data. */
|
|
501
998
|
pipelines?: Capability;
|
|
999
|
+
/**
|
|
1000
|
+
* In-place point-in-time recovery of a shard — the `getPitrBookmark` /
|
|
1001
|
+
* `pitrRestore` admin ops and the studio page that drives them. They
|
|
1002
|
+
* need the host's own change log: a bookmark names a moment in it and a
|
|
1003
|
+
* restore rewinds the shard's database to one. Nothing in `ShardHost`
|
|
1004
|
+
* carries that, so a host without it answers `PITR_UNAVAILABLE`.
|
|
1005
|
+
*
|
|
1006
|
+
* Advisory for codegen by nature — an app declares nothing to gate on;
|
|
1007
|
+
* the admin ops are always wired. Studio reads it to mark the page
|
|
1008
|
+
* unavailable on a host that cannot serve it. The off-platform tier
|
|
1009
|
+
* (`lunora backup`) is {@link PlatformCapabilities.features.objectStorageBackups}.
|
|
1010
|
+
*/
|
|
1011
|
+
pointInTimeRecovery?: Capability;
|
|
502
1012
|
/** Queue-backed workpools. */
|
|
503
1013
|
queues?: Capability;
|
|
1014
|
+
/**
|
|
1015
|
+
* `ctx.db.related(...)` — breadth-first traversal of the foreign-key
|
|
1016
|
+
* graph the schema's `v.id("target")` columns describe, returning each
|
|
1017
|
+
* reached row with its depth, the edge names walked to reach it, and a
|
|
1018
|
+
* depth-decaying score.
|
|
1019
|
+
*
|
|
1020
|
+
* Rated on its own key rather than folded into `localSql`, because the
|
|
1021
|
+
* two answer different questions: `localSql` says a shard can run SQL,
|
|
1022
|
+
* while this says a host can serve the traversal's read SHAPE — an
|
|
1023
|
+
* id lookup per out-edge and a batched `WHERE fk IN (...)` per in-edge,
|
|
1024
|
+
* repeated per hop within one request. A host whose reads are remote
|
|
1025
|
+
* enough that a multi-hop expansion cannot finish inside a request
|
|
1026
|
+
* should say `unsupported` here even though every individual read works.
|
|
1027
|
+
*
|
|
1028
|
+
* Gate-bearing: codegen sets the `relationGraph` `PlatformSignals` flag
|
|
1029
|
+
* from the schema IR's `v.id` columns, so a host rating it
|
|
1030
|
+
* `unsupported` refuses the app rather than emitting a `related` that
|
|
1031
|
+
* throws (or worse, silently returns nothing) on the first hop.
|
|
1032
|
+
*/
|
|
1033
|
+
relationGraph?: Capability;
|
|
1034
|
+
/**
|
|
1035
|
+
* The origin a request reached the app on, forwarded to the shard with the
|
|
1036
|
+
* dispatch: `ctx.origin` in mutations and actions, and the base
|
|
1037
|
+
* `ctx.storage` signs URLs against when no `publicBaseUrl` is declared.
|
|
1038
|
+
*
|
|
1039
|
+
* The runtime reads it off `request.url`, never off a client header, so
|
|
1040
|
+
* the rating answers whether the HOST vouches for that URL's host. An app
|
|
1041
|
+
* signing URLs against it may hand them to other callers, and a host that
|
|
1042
|
+
* builds the URL from a caller-typed `Host` header lets that caller choose
|
|
1043
|
+
* where they point.
|
|
1044
|
+
*
|
|
1045
|
+
* Advisory by nature: every target populates it, so codegen has nothing
|
|
1046
|
+
* to omit. Where the host does not vouch for it, configure a base
|
|
1047
|
+
* (`publicBaseUrl`, or the storage registry item's
|
|
1048
|
+
* `STORAGE_PUBLIC_BASE_URL`), which always wins over the origin.
|
|
1049
|
+
*/
|
|
1050
|
+
requestOrigin?: Capability;
|
|
504
1051
|
/** Cron triggers / scheduled functions. */
|
|
505
1052
|
scheduler?: Capability;
|
|
506
1053
|
/** Secrets management. */
|
|
507
1054
|
secrets?: Capability;
|
|
1055
|
+
/**
|
|
1056
|
+
* `onQueryChange` reactors — server-side reactivity: a subscriber that is
|
|
1057
|
+
* not a socket, woken after a write flush when a watched read's result
|
|
1058
|
+
* changed.
|
|
1059
|
+
*
|
|
1060
|
+
* Host-dependent because the whole mechanism rests on the host being able
|
|
1061
|
+
* to run work AFTER a write commits, on the same shard, without a client
|
|
1062
|
+
* connection to hang it off — and on that work being serialized against
|
|
1063
|
+
* further writes so a reactor's own writes cascade deterministically
|
|
1064
|
+
* rather than interleaving.
|
|
1065
|
+
*/
|
|
1066
|
+
serverReactors?: Capability;
|
|
1067
|
+
/**
|
|
1068
|
+
* Service bindings to sibling Workers — `lunora.config.*` `services` →
|
|
1069
|
+
* `ctx.services.<name>` on actions (plan 457): a fetch `Fetcher`, or an
|
|
1070
|
+
* RPC stub for a `WorkerEntrypoint`. Gate-bearing: codegen sets the
|
|
1071
|
+
* `services` `PlatformSignals` flag on a declared service.
|
|
1072
|
+
*/
|
|
1073
|
+
services?: Capability;
|
|
508
1074
|
/** Alarms / scheduled wakeup inside a shard. */
|
|
509
1075
|
shardAlarms?: Capability;
|
|
510
1076
|
/** Durable Object-style sharded state. */
|
|
511
1077
|
shardedState?: Capability;
|
|
1078
|
+
/** Geographic placement of a shard (`ShardPlacement.locationHint`). */
|
|
1079
|
+
shardPlacement?: Capability;
|
|
1080
|
+
/** Region-local read replicas of a shard, for one-shot queries. */
|
|
1081
|
+
shardReadReplicas?: Capability;
|
|
1082
|
+
/**
|
|
1083
|
+
* Pub/Sub topics — `defineTopic` / `defineSubscription` → `ctx.topics`.
|
|
1084
|
+
* Each subscription deploys as its own queue and a publish sends to all
|
|
1085
|
+
* of them, so this is never better than `queues` on the same host.
|
|
1086
|
+
* Gate-bearing: codegen sets the `topics` `PlatformSignals` flag on a
|
|
1087
|
+
* `defineTopic` export.
|
|
1088
|
+
*/
|
|
1089
|
+
topics?: Capability;
|
|
512
1090
|
/** Vector database (Vectorize / pgvector / Pinecone). */
|
|
513
1091
|
vectorStore?: Capability;
|
|
514
1092
|
/** Hibernated WebSocket subscriptions. */
|
|
515
1093
|
websocketHibernation?: Capability;
|
|
1094
|
+
/**
|
|
1095
|
+
* Worker Loaders (Dynamic Workers): load code supplied at runtime into
|
|
1096
|
+
* its own isolate with an `env`, egress and CPU budget the loader
|
|
1097
|
+
* chooses. `@lunora/agent`'s `jsCodeTool` runs model-written scripts
|
|
1098
|
+
* through it. Gate-bearing: codegen sets the `workerLoaders`
|
|
1099
|
+
* `PlatformSignals` flag on a `jsCodeTool` import in `lunora/`.
|
|
1100
|
+
*/
|
|
1101
|
+
workerLoaders?: Capability;
|
|
1102
|
+
/**
|
|
1103
|
+
* Compensation for a failed workflow — `defineStep({ rollback })`,
|
|
1104
|
+
* which `@lunora/workflow` forwards to the host's `step.do` rollback
|
|
1105
|
+
* option.
|
|
1106
|
+
*
|
|
1107
|
+
* Rated apart from `workflows` because a host can run steps, sleeps and
|
|
1108
|
+
* events and still not implement rollback, and there the step does not
|
|
1109
|
+
* run without it — it fails. Gate-bearing: codegen sets the
|
|
1110
|
+
* `workflowRollback` `PlatformSignals` flag when a `defineStep` call
|
|
1111
|
+
* declares a `rollback`, so the gap is a build-time diagnostic rather
|
|
1112
|
+
* than a step that fails on first use.
|
|
1113
|
+
*/
|
|
1114
|
+
workflowRollback?: Capability;
|
|
516
1115
|
/** Durable workflows (step-based). */
|
|
517
1116
|
workflows?: Capability;
|
|
1117
|
+
/**
|
|
1118
|
+
* Cron-started workflow instances — `defineWorkflow({ schedules })`,
|
|
1119
|
+
* written to the wrangler `workflows[].schedules` list, where the host
|
|
1120
|
+
* itself creates an instance on each tick (no `scheduled()` handler).
|
|
1121
|
+
*
|
|
1122
|
+
* Rated apart from `workflows` and from `cronTriggers` because it is
|
|
1123
|
+
* neither: a host can run workflows it is asked to create, and dispatch
|
|
1124
|
+
* declared crons to `scheduled()`, and still never start a workflow off
|
|
1125
|
+
* its own schedule list. That host would build the app green and the
|
|
1126
|
+
* workflow would simply never run. Gate-bearing: codegen sets the
|
|
1127
|
+
* `workflowSchedules` `PlatformSignals` flag when a workflow declares
|
|
1128
|
+
* `schedules`.
|
|
1129
|
+
*/
|
|
1130
|
+
workflowSchedules?: Capability;
|
|
518
1131
|
};
|
|
519
1132
|
/** Platform identifier used in codegen and config (e.g. "cloudflare", "aws"). */
|
|
520
1133
|
id: string;
|
|
521
1134
|
/** Human-readable platform name (e.g. "Cloudflare", "AWS", "Rivet"). */
|
|
522
1135
|
name: string;
|
|
523
1136
|
}
|
|
1137
|
+
/**
|
|
1138
|
+
* The celld capability matrix — `@lunora/platform-celld`'s honest self-rating.
|
|
1139
|
+
*
|
|
1140
|
+
* celld (github.com/denoland/celld) is a self-hosted, distributed Durable
|
|
1141
|
+
* Objects daemon: each node embeds V8, executes Wrangler bundles, and
|
|
1142
|
+
* coordinates ownership through an S3-compatible (or GCS / Azure Blob) bucket
|
|
1143
|
+
* instead of a control plane. Because it implements the Workers/Durable Object
|
|
1144
|
+
* API itself, the Cloudflare host adapters ARE the celld host adapters — what
|
|
1145
|
+
* differs is which primitives exist, and that difference is exactly this
|
|
1146
|
+
* matrix.
|
|
1147
|
+
*
|
|
1148
|
+
* Ratings track celld **v0.6.0** and derive from its documented compatibility
|
|
1149
|
+
* surface (`docs/cloudflare-compat.md`, `docs/services/*.md`,
|
|
1150
|
+
* `docs/limitations.md` in the celld repo; v0.6.0 is celld's first beta). The host contracts
|
|
1151
|
+
* behind `shardedState`, `localSql`, `shardAlarms`, `commitOrderedTables` and
|
|
1152
|
+
* `websocketHibernation` are also exercised by the conformance TCK against a
|
|
1153
|
+
* live single-node celld (`@lunora/platform-celld`'s `celld` vitest project),
|
|
1154
|
+
* which also drives D1, KV, R2, Queues, Workflows and Cron Triggers through
|
|
1155
|
+
* Lunora's own adapters and runs a `LunoraContainer`, and a two-node fleet
|
|
1156
|
+
* test covers routing, crash takeover and rebalancing. celld's own rule is
|
|
1157
|
+
* that an unsupported configuration or API must fail at deploy or first use,
|
|
1158
|
+
* so "Partial" there means a listed set of gaps rather than silent
|
|
1159
|
+
* degradation; the gaps that bite Lunora are named per key below.
|
|
1160
|
+
*
|
|
1161
|
+
* `shardPlacement` and `shardReadReplicas` stay `unsupported`, and by celld's
|
|
1162
|
+
* own design rather than a gap: its Durable Object docs state it "makes no
|
|
1163
|
+
* placement, migration, or jurisdiction promise". Nodes carry no region, and
|
|
1164
|
+
* rebalancing moves hibernated cells toward each node's `CELLD_PLACEMENT_WEIGHT`
|
|
1165
|
+
* share, not toward a reader — so there is no location to hint at and no region
|
|
1166
|
+
* to place a read replica in. Revisit only if celld grows node regions.
|
|
1167
|
+
*/
|
|
1168
|
+
declare const CELLD_CAPABILITIES: PlatformCapabilities;
|
|
524
1169
|
/**
|
|
525
1170
|
* The Cloudflare capability matrix — the reference implementation.
|
|
526
1171
|
*
|
|
@@ -535,33 +1180,72 @@ declare const CLOUDFLARE_CAPABILITIES: PlatformCapabilities;
|
|
|
535
1180
|
* The Node capability matrix — `@lunora/platform-node`'s honest self-rating
|
|
536
1181
|
* (plan 234).
|
|
537
1182
|
*
|
|
538
|
-
* `@lunora/platform-node`
|
|
539
|
-
* `
|
|
540
|
-
* `better-sqlite3` and an in-process registry,
|
|
541
|
-
*
|
|
542
|
-
*
|
|
543
|
-
*
|
|
544
|
-
*
|
|
545
|
-
*
|
|
546
|
-
*
|
|
547
|
-
* rated `"unsupported"`
|
|
548
|
-
*
|
|
549
|
-
*
|
|
550
|
-
*
|
|
551
|
-
*
|
|
552
|
-
*
|
|
553
|
-
*
|
|
554
|
-
*
|
|
555
|
-
*
|
|
556
|
-
*
|
|
557
|
-
*
|
|
558
|
-
*
|
|
559
|
-
*
|
|
560
|
-
*
|
|
561
|
-
*
|
|
562
|
-
*
|
|
563
|
-
*
|
|
564
|
-
*
|
|
1183
|
+
* `@lunora/platform-node` implements every contract in this package
|
|
1184
|
+
* (`ShardHost`, `SocketHost`, `ShardDirectory`, `ShardKvStore`,
|
|
1185
|
+
* `SchedulerHost`) over `better-sqlite3` and an in-process registry, plus the
|
|
1186
|
+
* `.global()` table backend via `@lunora/sql-store`. It began as a spike to run
|
|
1187
|
+
* the conformance TCK against a second host; the durability gaps that spike
|
|
1188
|
+
* surfaced — alarms and scheduler jobs that were persisted but never re-armed,
|
|
1189
|
+
* socket attachments that lived only in memory — are closed, and each is now
|
|
1190
|
+
* pinned by a restart test rather than only by a simulated recycle.
|
|
1191
|
+
*
|
|
1192
|
+
* `scheduler` and `shardAlarms` were rated `"unsupported"` under plan 267, on
|
|
1193
|
+
* the grounds that the host stored and timed both while its timer body only
|
|
1194
|
+
* cleared bookkeeping — nothing dispatched the scheduled function or woke the
|
|
1195
|
+
* alarm. That rating was correct for the code it described, and the code is
|
|
1196
|
+
* what changed: both now dispatch (through `onDispatch` / `onAlarm`) and both
|
|
1197
|
+
* re-arm from their durable rows on construction, so `"emulated"` — built on
|
|
1198
|
+
* lower-level primitives and *working* — is now the honest reading.
|
|
1199
|
+
*
|
|
1200
|
+
* What remains genuinely absent is everything a single Node process cannot
|
|
1201
|
+
* distribute: placement across nodes, failover, and most Cloudflare-specific
|
|
1202
|
+
* product bindings (Vectorize, Workers AI, Containers, Browser Rendering,
|
|
1203
|
+
* Analytics Engine, Secrets Store, Hyperdrive). Workflows, object storage and
|
|
1204
|
+
* queues are the three that CAN be emulated locally — `defineWorkflow` handlers
|
|
1205
|
+
* compile onto the `@visulima/workflow` engine, R2 becomes a filesystem bucket,
|
|
1206
|
+
* and Queues becomes a durable table with the same batch/ack/retry/dead-letter
|
|
1207
|
+
* semantics — so those three are rated `"emulated"`; the rest of the Cloudflare
|
|
1208
|
+
* products most `ctx.*` surfaces are built on are rated `"unsupported"` here
|
|
1209
|
+
* rather than left undeclared — see `gateAgainstMatrix` in `@lunora/codegen`,
|
|
1210
|
+
* whose fail-closed gate (plan 229) treats an undeclared feature as unsupported
|
|
1211
|
+
* anyway, but under a different diagnostic name than an honest, explicit rating.
|
|
1212
|
+
*
|
|
1213
|
+
* Almost nothing here is rated `"native"`, and that is the matrix's own
|
|
1214
|
+
* definition doing its job rather than a hedge: `native` means the platform
|
|
1215
|
+
* itself provides the feature, and a bare Node process provides essentially
|
|
1216
|
+
* none of them — Lunora builds alarms out of `setTimeout` plus a durable row,
|
|
1217
|
+
* a KV store out of a SQL table, and `.global()` tables out of a second SQLite
|
|
1218
|
+
* file. `localSql` is the exception, because SQLite genuinely is the platform
|
|
1219
|
+
* primitive there. The ratings say who does the work; the notes say how well.
|
|
1220
|
+
* Both are argued in detail in `plans/234-node-host-findings.md`.
|
|
565
1221
|
*/
|
|
566
1222
|
declare const NODE_CAPABILITIES: PlatformCapabilities;
|
|
567
|
-
export { type AnalyticsEngineDataPoint, type AnalyticsEngineDataPointLike, type AnalyticsEngineDatasetLike, CLOUDFLARE_CAPABILITIES, type Capability, type CapabilityLevel, type D1DatabaseLike, type D1PreparedStatementLike, type D1SessionLike,
|
|
1223
|
+
export { type AnalyticsEngineDataPoint, type AnalyticsEngineDataPointLike, type AnalyticsEngineDatasetLike, CELLD_CAPABILITIES, CLOUDFLARE_CAPABILITIES, type Capability, type CapabilityLevel, type D1DatabaseLike, type D1PreparedStatementLike, type D1SessionLike,
|
|
1224
|
+
/**
|
|
1225
|
+
* `@lunora/platform` — provider-neutral host contracts for Lunora.
|
|
1226
|
+
*
|
|
1227
|
+
* This package defines the structural interfaces that separate the Lunora
|
|
1228
|
+
* engine from any specific host (Cloudflare Workers, AWS, Rivet, Node, etc.).
|
|
1229
|
+
* It contains **types and capability metadata only** — near-zero runtime code.
|
|
1230
|
+
*
|
|
1231
|
+
* The contracts fall into four groups:
|
|
1232
|
+
*
|
|
1233
|
+
* 1. **Shard host** (`ShardHost`) — single-writer execution, transactions,
|
|
1234
|
+
* local SQL, alarms, and background continuation per shard key.
|
|
1235
|
+
* 2. **Socket host** (`SocketHost`) — hibernated WebSocket subscriptions with
|
|
1236
|
+
* durable attachments and tagged fan-out.
|
|
1237
|
+
* 3. **Shard directory** (`ShardDirectory`) — deterministic placement and RPC
|
|
1238
|
+
* dispatch from shard keys to stubs.
|
|
1239
|
+
* 4. **Scheduler host** (`SchedulerHost`) — durable delayed jobs, cron, and
|
|
1240
|
+
* at-least-once dispatch.
|
|
1241
|
+
*
|
|
1242
|
+
* Plus canonical binding projections (`KVNamespaceLike`, `R2BucketLike`,
|
|
1243
|
+
* `QueueBindingLike`, `D1DatabaseLike`, `VectorizeIndexLike`, …) and the
|
|
1244
|
+
* `PlatformCapabilities` matrix that codegen uses to tailor emitted types per
|
|
1245
|
+
* target.
|
|
1246
|
+
*
|
|
1247
|
+
* This package is **zero-dependency** and safe on every runtime (browser,
|
|
1248
|
+
* workerd, Node). It is intended to be the leaf dependency every other
|
|
1249
|
+
* `@lunora/*` package can import without creating cycles.
|
|
1250
|
+
*/
|
|
1251
|
+
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 VectorValues, type VectorizeDeleteMutation, type VectorizeIndexDetails, type VectorizeIndexLike, type VectorizeMatch, type VectorizeMatches, type VectorizeQueryOptions, type VectorizeUpsertMutation, type VectorizeVector };
|