@lunora/runtime 1.0.0-alpha.28 → 1.0.0-alpha.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -3,30 +3,30 @@ export type { RankDirection as RankPageDirection, RankPageRowKey as RankPageKey,
3
3
  import { WorkflowsRestClient } from '@lunora/workflow';
4
4
  import { LunoraError as LunoraError$1, ErrorBody } from '@lunora/errors';
5
5
  /**
6
- * Turn-key incremental-sync source helpers for warehouse connectors
7
- * (Fivetran custom functions, Airbyte incremental sources).
8
- *
9
- * The runtime's admin `/_lunora/admin/connector/sync` endpoint returns a
10
- * {@link ConnectorSyncPage}: a flat list of change records since an opaque
11
- * cursor, a `nextCursor` to resume from, and a `hasMore` flag. These helpers
12
- * reshape that page into the response envelopes the two ecosystems expect, so a
13
- * connector wrapper stays a few lines.
14
- *
15
- * {@link toFivetranResponse} produces the `{ state, insert, update, delete,
16
- * hasMore, schema }` object a Fivetran connector function returns from its
17
- * handler. {@link toAirbyteMessages} produces an ordered array of Airbyte
18
- * protocol messages (a `RECORD` per row, a trailing `STATE` carrying the cursor),
19
- * the line-delimited stream an Airbyte incremental source emits.
20
- *
21
- * Both consume the SAME page, so a single endpoint feeds either ecosystem.
22
- */
23
- /**
24
- * One change record in a {@link ConnectorSyncPage}. Mirrors a row of the CDC log
25
- * the shard / D1 change feed produces: an `op` (insert / update / delete), the
26
- * owning `table`, and the document. `op` is normalised to the three warehouse
27
- * verbs; an unknown / absent op is treated as `"upsert"` (insert-or-update),
28
- * which is the safe default for change feeds that don't distinguish the two.
29
- */
6
+ * Turn-key incremental-sync source helpers for warehouse connectors
7
+ * (Fivetran custom functions, Airbyte incremental sources).
8
+ *
9
+ * The runtime's admin `/_lunora/admin/connector/sync` endpoint returns a
10
+ * {@link ConnectorSyncPage}: a flat list of change records since an opaque
11
+ * cursor, a `nextCursor` to resume from, and a `hasMore` flag. These helpers
12
+ * reshape that page into the response envelopes the two ecosystems expect, so a
13
+ * connector wrapper stays a few lines.
14
+ *
15
+ * {@link toFivetranResponse} produces the `{ state, insert, update, delete,
16
+ * hasMore, schema }` object a Fivetran connector function returns from its
17
+ * handler. {@link toAirbyteMessages} produces an ordered array of Airbyte
18
+ * protocol messages (a `RECORD` per row, a trailing `STATE` carrying the cursor),
19
+ * the line-delimited stream an Airbyte incremental source emits.
20
+ *
21
+ * Both consume the SAME page, so a single endpoint feeds either ecosystem.
22
+ */
23
+ /**
24
+ * One change record in a {@link ConnectorSyncPage}. Mirrors a row of the CDC log
25
+ * the shard / D1 change feed produces: an `op` (insert / update / delete), the
26
+ * owning `table`, and the document. `op` is normalised to the three warehouse
27
+ * verbs; an unknown / absent op is treated as `"upsert"` (insert-or-update),
28
+ * which is the safe default for change feeds that don't distinguish the two.
29
+ */
30
30
  interface ConnectorChange {
31
31
  /** The full document. For a delete, may carry only the primary key. */
32
32
  doc: Record<string, unknown>;
@@ -36,11 +36,11 @@ interface ConnectorChange {
36
36
  table: string;
37
37
  }
38
38
  /**
39
- * A page of changes the connector endpoint returns. `nextCursor` is an opaque
40
- * token the consumer stores and re-posts verbatim to resume; never parse it.
41
- * `hasMore` is `true` while the source has further pages past this one — keep
42
- * paging until it is `false` (caught up).
43
- */
39
+ * A page of changes the connector endpoint returns. `nextCursor` is an opaque
40
+ * token the consumer stores and re-posts verbatim to resume; never parse it.
41
+ * `hasMore` is `true` while the source has further pages past this one — keep
42
+ * paging until it is `false` (caught up).
43
+ */
44
44
  interface ConnectorSyncPage {
45
45
  changes: ReadonlyArray<ConnectorChange>;
46
46
  hasMore: boolean;
@@ -48,15 +48,15 @@ interface ConnectorSyncPage {
48
48
  nextCursor: string;
49
49
  }
50
50
  /**
51
- * Fivetran connector-function response envelope. A Fivetran custom function
52
- * returns this object: `state` is persisted by Fivetran and handed back on the
53
- * next sync (map it straight to {@link ConnectorSyncPage.nextCursor}), the
54
- * `insert` / `update` / `delete` maps bucket records per table, `hasMore` drives
55
- * Fivetran's "call me again immediately" loop, and `schema` declares each table's
56
- * primary key.
57
- *
58
- * See https://fivetran.com/docs/connectors/functions#responseformat.
59
- */
51
+ * Fivetran connector-function response envelope. A Fivetran custom function
52
+ * returns this object: `state` is persisted by Fivetran and handed back on the
53
+ * next sync (map it straight to {@link ConnectorSyncPage.nextCursor}), the
54
+ * `insert` / `update` / `delete` maps bucket records per table, `hasMore` drives
55
+ * Fivetran's "call me again immediately" loop, and `schema` declares each table's
56
+ * primary key.
57
+ *
58
+ * See https://fivetran.com/docs/connectors/functions#responseformat.
59
+ */
60
60
  interface FivetranResponse {
61
61
  delete: Record<string, Record<string, unknown>[]>;
62
62
  hasMore: boolean;
@@ -86,53 +86,53 @@ type AirbyteMessage = {
86
86
  type: "STATE";
87
87
  };
88
88
  /**
89
- * Format a {@link ConnectorSyncPage} as a Fivetran connector-function response.
90
- *
91
- * Inserts and upserts both land in `insert` (Fivetran upserts on primary key, so
92
- * an insert and an update of an existing row are wire-identical); explicit
93
- * updates land in `update`; deletes in `delete`. `state.cursor` carries the
94
- * opaque resume token Fivetran will echo back on the next invocation.
95
- * @param page the page returned by the connector sync endpoint.
96
- * @param primaryKey the primary-key column per table (default `"_id"`); pass a
97
- * map to override per table, used to fill the `schema` block.
98
- */
89
+ * Format a {@link ConnectorSyncPage} as a Fivetran connector-function response.
90
+ *
91
+ * Inserts and upserts both land in `insert` (Fivetran upserts on primary key, so
92
+ * an insert and an update of an existing row are wire-identical); explicit
93
+ * updates land in `update`; deletes in `delete`. `state.cursor` carries the
94
+ * opaque resume token Fivetran will echo back on the next invocation.
95
+ * @param page the page returned by the connector sync endpoint.
96
+ * @param primaryKey the primary-key column per table (default `"_id"`); pass a
97
+ * map to override per table, used to fill the `schema` block.
98
+ */
99
99
  declare const toFivetranResponse: (page: ConnectorSyncPage, primaryKey?: Record<string, string> | string) => FivetranResponse;
100
100
  /**
101
- * Format a {@link ConnectorSyncPage} as an ordered array of Airbyte protocol
102
- * messages: one `RECORD` per change (stream = table name), followed by a single
103
- * trailing `STATE` message carrying the opaque cursor. An Airbyte source serializes
104
- * these as line-delimited JSON to stdout.
105
- *
106
- * Airbyte's protocol has no native delete verb in `RECORD`; a delete is emitted
107
- * as a `RECORD` with a `_lunora_deleted: true` marker on the row so a downstream
108
- * normalization / dbt step can tombstone it. Callers needing true CDC deletes
109
- * should run Airbyte's CDC-deletion handling on that marker.
110
- * @param page the page returned by the connector sync endpoint.
111
- * @param emittedAt epoch-ms stamped on each `RECORD` (default `Date.now()`).
112
- */
101
+ * Format a {@link ConnectorSyncPage} as an ordered array of Airbyte protocol
102
+ * messages: one `RECORD` per change (stream = table name), followed by a single
103
+ * trailing `STATE` message carrying the opaque cursor. An Airbyte source serializes
104
+ * these as line-delimited JSON to stdout.
105
+ *
106
+ * Airbyte's protocol has no native delete verb in `RECORD`; a delete is emitted
107
+ * as a `RECORD` with a `_lunora_deleted: true` marker on the row so a downstream
108
+ * normalization / dbt step can tombstone it. Callers needing true CDC deletes
109
+ * should run Airbyte's CDC-deletion handling on that marker.
110
+ * @param page the page returned by the connector sync endpoint.
111
+ * @param emittedAt epoch-ms stamped on each `RECORD` (default `Date.now()`).
112
+ */
113
113
  declare const toAirbyteMessages: (page: ConnectorSyncPage, emittedAt?: number) => AirbyteMessage[];
114
114
  /**
115
- * The subset of the Cloudflare `ExecutionContext` the Lunora worker entry and
116
- * the framework mount seams rely on — `waitUntil` for fire-and-forget work that
117
- * must outlive the response, and `passThroughOnException` for the top-level
118
- * error posture.
119
- *
120
- * It is deliberately **not** a package. `@lunora/runtime` is the leaf server
121
- * runtime and `@lunora/nuxt` is a framework integration that intentionally does
122
- * not depend on `@lunora/runtime`'s worker types, yet both need this exact
123
- * shape: the runtime to build/forward the worker `fetch`, Nuxt to forward an
124
- * inbound request to the user's composed worker. Each imports this file by
125
- * relative path and the bundler (packem/rollup) inlines it: no runtime
126
- * dependency edge is created, the helper is duplicated only in emitted output,
127
- * never in source. One source of truth, zero deps. See AGENTS.md → "Top-level
128
- * `shared/` — bundler-inlined source".
129
- *
130
- * Both methods are **optional**: a real Cloudflare `ExecutionContext` always
131
- * supplies them, but a host that mounts Lunora as a sub-handler (Nitro/H3, a
132
- * non-Cloudflare preview, a unit test) may hand over a partial context or none
133
- * at all. Callers therefore invoke them defensively (`ctx.waitUntil?.(…)`) or
134
- * fall back to {@link NOOP_EXECUTION_CONTEXT}.
135
- */
115
+ * The subset of the Cloudflare `ExecutionContext` the Lunora worker entry and
116
+ * the framework mount seams rely on — `waitUntil` for fire-and-forget work that
117
+ * must outlive the response, and `passThroughOnException` for the top-level
118
+ * error posture.
119
+ *
120
+ * It is deliberately **not** a package. `@lunora/runtime` is the leaf server
121
+ * runtime and `@lunora/nuxt` is a framework integration that intentionally does
122
+ * not depend on `@lunora/runtime`'s worker types, yet both need this exact
123
+ * shape: the runtime to build/forward the worker `fetch`, Nuxt to forward an
124
+ * inbound request to the user's composed worker. Each imports this file by
125
+ * relative path and the bundler (packem/rollup) inlines it: no runtime
126
+ * dependency edge is created, the helper is duplicated only in emitted output,
127
+ * never in source. One source of truth, zero deps. See AGENTS.md → "Top-level
128
+ * `shared/` — bundler-inlined source".
129
+ *
130
+ * Both methods are **optional**: a real Cloudflare `ExecutionContext` always
131
+ * supplies them, but a host that mounts Lunora as a sub-handler (Nitro/H3, a
132
+ * non-Cloudflare preview, a unit test) may hand over a partial context or none
133
+ * at all. Callers therefore invoke them defensively (`ctx.waitUntil?.(…)`) or
134
+ * fall back to {@link NOOP_EXECUTION_CONTEXT}.
135
+ */
136
136
  interface ExecutionContextLike {
137
137
  cache?: {
138
138
  purge: (options: {
@@ -144,18 +144,18 @@ interface ExecutionContextLike {
144
144
  waitUntil?: (promise: Promise<unknown>) => void;
145
145
  }
146
146
  /**
147
- * No-op `ExecutionContext` used when the host runtime didn't supply one (a
148
- * non-Cloudflare preview, or a unit test), so the worker's `fetch` always
149
- * receives a valid third argument.
150
- */
147
+ * No-op `ExecutionContext` used when the host runtime didn't supply one (a
148
+ * non-Cloudflare preview, or a unit test), so the worker's `fetch` always
149
+ * receives a valid third argument.
150
+ */
151
151
  declare const NOOP_EXECUTION_CONTEXT: ExecutionContextLike;
152
152
  /** A timestamp as better-auth stores it: epoch-ms, an ISO string, or absent. */
153
153
  type AuthTimestamp = null | number | string;
154
154
  /**
155
- * One authenticated user, as the auth browser surfaces it. Mirrors better-auth's
156
- * `user` row plus the `admin()` plugin columns (`role`/`banned`/…); the index
157
- * signature additionally carries any app-defined `user.additionalFields`.
158
- */
155
+ * One authenticated user, as the auth browser surfaces it. Mirrors better-auth's
156
+ * `user` row plus the `admin()` plugin columns (`role`/`banned`/…); the index
157
+ * signature additionally carries any app-defined `user.additionalFields`.
158
+ */
159
159
  interface AuthUser {
160
160
  [key: string]: unknown;
161
161
  banExpires?: AuthTimestamp;
@@ -192,10 +192,10 @@ interface AuthImpersonation {
192
192
  user: AuthUser;
193
193
  }
194
194
  /**
195
- * Which admin surfaces the configured auth plane supports, derived from the
196
- * enabled better-auth plugins. The studio renders only the panels whose
197
- * capability is `true`.
198
- */
195
+ * Which admin surfaces the configured auth plane supports, derived from the
196
+ * enabled better-auth plugins. The studio renders only the panels whose
197
+ * capability is `true`.
198
+ */
199
199
  interface AuthCapabilities {
200
200
  accounts: boolean;
201
201
  admin: boolean;
@@ -212,11 +212,11 @@ interface AuthUserFieldSpec {
212
212
  unique: boolean;
213
213
  }
214
214
  /**
215
- * Rich, read-only description of the deployment's auth configuration — enabled
216
- * plugins, sign-in methods, user-settable fields, organization sub-features, and
217
- * session / rate-limit policy — for the studio's config panel and dynamic
218
- * create-user form. Never carries a secret.
219
- */
215
+ * Rich, read-only description of the deployment's auth configuration — enabled
216
+ * plugins, sign-in methods, user-settable fields, organization sub-features, and
217
+ * session / rate-limit policy — for the studio's config panel and dynamic
218
+ * create-user form. Never carries a secret.
219
+ */
220
220
  interface AuthConfigInfo {
221
221
  capabilities: AuthCapabilities;
222
222
  emailAndPassword: boolean;
@@ -252,17 +252,17 @@ interface ListAuthUsersOptions {
252
252
  sortDirection?: "asc" | "desc";
253
253
  }
254
254
  /**
255
- * The auth user-management plane backing the studio's auth dashboard. The host
256
- * wires this to better-auth (typically via `@lunora/auth`'s `createAuthAdmin`);
257
- * the runtime stays free of a hard dependency on `@lunora/auth`. The read
258
- * methods back the GET browse endpoints; the optional mutations back the
259
- * admin-gated POST endpoints — a host that only needs read-only browsing can
260
- * omit them (the POST routes then respond `AUTH_OP_NOT_SUPPORTED`). Omit the
261
- * whole option and every `/auth/*` endpoint responds `AUTH_NOT_CONFIGURED`.
262
- *
263
- * Every method here runs behind the worker's `LUNORA_ADMIN_TOKEN` gate — the
264
- * implementation is a trusted server-side operator, not an end-user API.
265
- */
255
+ * The auth user-management plane backing the studio's auth dashboard. The host
256
+ * wires this to better-auth (typically via `@lunora/auth`'s `createAuthAdmin`);
257
+ * the runtime stays free of a hard dependency on `@lunora/auth`. The read
258
+ * methods back the GET browse endpoints; the optional mutations back the
259
+ * admin-gated POST endpoints — a host that only needs read-only browsing can
260
+ * omit them (the POST routes then respond `AUTH_OP_NOT_SUPPORTED`). Omit the
261
+ * whole option and every `/auth/*` endpoint responds `AUTH_NOT_CONFIGURED`.
262
+ *
263
+ * Every method here runs behind the worker's `LUNORA_ADMIN_TOKEN` gate — the
264
+ * implementation is a trusted server-side operator, not an end-user API.
265
+ */
266
266
  interface AuthAdmin {
267
267
  addMember?: (input: {
268
268
  organizationId: string;
@@ -425,13 +425,12 @@ interface AuthAdmin {
425
425
  userId: string;
426
426
  }) => Promise<AuthUser>;
427
427
  }
428
- /** Closure-scoped worker helpers the auth routes borrow (so this module stays out of the worker's god-closure). */
429
428
  /**
430
- * A compact, transport-safe description of one function argument — the runtime
431
- * read of a `v.*` validator's reflection tags (`kind` + `_meta`). The runtime
432
- * deliberately avoids a hard dependency on `@lunora/values`, so this reads the
433
- * validator structurally rather than importing its types.
434
- */
429
+ * A compact, transport-safe description of one function argument — the runtime
430
+ * read of a `v.*` validator's reflection tags (`kind` + `_meta`). The runtime
431
+ * deliberately avoids a hard dependency on `@lunora/values`, so this reads the
432
+ * validator structurally rather than importing its types.
433
+ */
435
434
  interface FunctionArgumentDescriptor {
436
435
  /** Element validator kind for an `array` arg (one level), e.g. `string`. */
437
436
  element?: string;
@@ -445,82 +444,75 @@ interface FunctionArgumentDescriptor {
445
444
  table?: string;
446
445
  }
447
446
  /**
448
- * Describe one named argument from its validator. Unwraps a single `v.optional`
449
- * layer (marking the arg optional and reporting the inner kind), and surfaces
450
- * the two most useful per-kind details: an `id` arg's target table and an
451
- * `array` arg's element kind. Nested object/union shapes report their top-level
452
- * kind only enough for a signature view without a deep recursive walk.
453
- */
454
- /**
455
- * Identity resolved from the inbound request by `WorkerOptions.resolveIdentity`.
456
- *
457
- * The `userId` field is special — it becomes `ctx.auth.userId` inside the
458
- * Durable Object. Any other keys (`email`, `name`, custom roles, etc.) are
459
- * forwarded verbatim as `ctx.auth.getIdentity()`'s return value.
460
- *
461
- * Return `null` to signal that the request is anonymous; the runtime will
462
- * skip both `x-lunora-userid` and `x-lunora-identity` headers, and
463
- * `ctx.auth.userId` will be `undefined` on the shard side.
464
- */
447
+ * Identity resolved from the inbound request by `WorkerOptions.resolveIdentity`.
448
+ *
449
+ * The `userId` field is special it becomes `ctx.auth.userId` inside the
450
+ * Durable Object. Any other keys (`email`, `name`, custom roles, etc.) are
451
+ * forwarded verbatim as `ctx.auth.getIdentity()`'s return value.
452
+ *
453
+ * Return `null` to signal that the request is anonymous; the runtime will
454
+ * skip both `x-lunora-userid` and `x-lunora-identity` headers, and
455
+ * `ctx.auth.userId` will be `undefined` on the shard side.
456
+ */
465
457
  interface ResolvedIdentity {
466
458
  /** Arbitrary additional claims. Must be JSON-serialisable. */
467
459
  [key: string]: unknown;
468
460
  /**
469
- * JWT-standard expiry in epoch SECONDS. When present (and `expiresAtMs` is
470
- * absent), the runtime forwards it as the socket's credential expiry — the
471
- * DO drops the socket once it lapses. Used only on the WebSocket path.
472
- */
461
+ * JWT-standard expiry in epoch SECONDS. When present (and `expiresAtMs` is
462
+ * absent), the runtime forwards it as the socket's credential expiry — the
463
+ * DO drops the socket once it lapses. Used only on the WebSocket path.
464
+ */
473
465
  exp?: number;
474
466
  /**
475
- * Credential expiry in epoch MILLISECONDS. Preferred over `exp` when
476
- * both are present. Forwarded as the socket's expiry on the WebSocket path
477
- * so the DO drops the socket once it lapses; omit for non-expiring sessions.
478
- */
467
+ * Credential expiry in epoch MILLISECONDS. Preferred over `exp` when
468
+ * both are present. Forwarded as the socket's expiry on the WebSocket path
469
+ * so the DO drops the socket once it lapses; omit for non-expiring sessions.
470
+ */
479
471
  expiresAtMs?: number;
480
472
  /** Stable user identifier (e.g. `"user_2k3..."` or `"u_42"`). */
481
473
  userId: string;
482
474
  }
483
475
  /**
484
- * A verifier that turns an inbound request into a {@link ResolvedIdentity} (or
485
- * `null` for anonymous). Structurally identical to `WorkerOptions.resolveIdentity`,
486
- * so `.auth()`'s better-auth session resolver, a signed-preview-link verifier, a
487
- * per-tenant bearer check, an upstream-JWT reader, … are all just `IdentityResolver`s
488
- * — the identity layer is generic over every scheme, not coupled to any one.
489
- */
476
+ * A verifier that turns an inbound request into a {@link ResolvedIdentity} (or
477
+ * `null` for anonymous). Structurally identical to `WorkerOptions.resolveIdentity`,
478
+ * so `.auth()`'s better-auth session resolver, a signed-preview-link verifier, a
479
+ * per-tenant bearer check, an upstream-JWT reader, … are all just `IdentityResolver`s
480
+ * — the identity layer is generic over every scheme, not coupled to any one.
481
+ */
490
482
  type IdentityResolver = (request: Request, env: unknown) => Promise<ResolvedIdentity | null> | ResolvedIdentity | null;
491
483
  /** Error policy for {@link composeIdentityResolvers} when a participant resolver throws. */
492
484
  type ComposeIdentityResolversErrorMode = "fail-closed" | "skip";
493
485
  /** Options for {@link composeIdentityResolvers}. */
494
486
  interface ComposeIdentityResolversOptions {
495
487
  /**
496
- * What to do when a resolver throws. `"fail-closed"` (default, safe)
497
- * re-throws so a broken verifier fails the request rather than silently
498
- * falling through to a weaker one; `"skip"` swallows the error and tries the
499
- * next resolver (use only when a resolver's failure genuinely means "not my
500
- * scheme").
501
- */
488
+ * What to do when a resolver throws. `"fail-closed"` (default, safe)
489
+ * re-throws so a broken verifier fails the request rather than silently
490
+ * falling through to a weaker one; `"skip"` swallows the error and tries the
491
+ * next resolver (use only when a resolver's failure genuinely means "not my
492
+ * scheme").
493
+ */
502
494
  readonly onError?: ComposeIdentityResolversErrorMode;
503
495
  }
504
496
  /**
505
- * Compose several {@link IdentityResolver}s into one, first-match-wins: each is
506
- * tried in order and the first that returns a non-null identity short-circuits.
507
- * Generic over every scheme — the better-auth session resolver (obtained via the
508
- * builder's `derived.resolveIdentity` escape hatch) is just one entry in the list,
509
- * so composition never means losing it.
510
- *
511
- * A resolver that throws is handled per {@link ComposeIdentityResolversOptions.onError}
512
- * (default `"fail-closed"`: the error propagates).
513
- */
497
+ * Compose several {@link IdentityResolver}s into one, first-match-wins: each is
498
+ * tried in order and the first that returns a non-null identity short-circuits.
499
+ * Generic over every scheme — the better-auth session resolver (obtained via the
500
+ * builder's `derived.resolveIdentity` escape hatch) is just one entry in the list,
501
+ * so composition never means losing it.
502
+ *
503
+ * A resolver that throws is handled per {@link ComposeIdentityResolversOptions.onError}
504
+ * (default `"fail-closed"`: the error propagates).
505
+ */
514
506
  declare const composeIdentityResolvers: (resolvers: ReadonlyArray<IdentityResolver>, options?: ComposeIdentityResolversOptions) => IdentityResolver;
515
507
  /**
516
- * A thin, generic helper over {@link composeIdentityResolvers} for the per-route case:
517
- * pick a resolver by `new URL(request.url).pathname`. Keys are matched by longest
518
- * path prefix; `"*"` is the fallback. Still fully generic — a route→resolver map,
519
- * with no portal / preview / tenant concepts baked in (those live in the app's
520
- * own resolvers).
521
- * @example
522
- * routeIdentityResolvers({ "/admin": adminResolver, "/partner": partnerResolver, "*": sessionResolver })
523
- */
508
+ * A thin, generic helper over {@link composeIdentityResolvers} for the per-route case:
509
+ * pick a resolver by `new URL(request.url).pathname`. Keys are matched by longest
510
+ * path prefix; `"*"` is the fallback. Still fully generic — a route→resolver map,
511
+ * with no portal / preview / tenant concepts baked in (those live in the app's
512
+ * own resolvers).
513
+ * @example
514
+ * routeIdentityResolvers({ "/admin": adminResolver, "/partner": partnerResolver, "*": sessionResolver })
515
+ */
524
516
  declare const routeIdentityResolvers: (routes: Record<string, IdentityResolver>) => IdentityResolver;
525
517
  /** The result of validating a candidate identity against an {@link IdentityContractLike}. */
526
518
  type IdentityValidation = {
@@ -530,37 +522,20 @@ type IdentityValidation = {
530
522
  ok: false;
531
523
  };
532
524
  /**
533
- * Structural view of `@lunora/server`'s `IdentityContract` (from `defineIdentity`).
534
- * Kept structural so `@lunora/runtime` stays free of an `@lunora/server` dependency.
535
- * Keep the `onInvalid` union and `validate`/`IdentityValidation` shapes in sync with
536
- * `@lunora/server`'s `IdentityContract` — they are projected by hand, not imported.
537
- * The generated worker entry passes the app's `defineIdentity(...)` result here;
538
- * the worker validates every resolver's returned claims against it at the trust
539
- * boundary before they become `ctx.auth`.
540
- */
525
+ * Structural view of `@lunora/server`'s `IdentityContract` (from `defineIdentity`).
526
+ * Kept structural so `@lunora/runtime` stays free of an `@lunora/server` dependency.
527
+ * Keep the `onInvalid` union and `validate`/`IdentityValidation` shapes in sync with
528
+ * `@lunora/server`'s `IdentityContract` — they are projected by hand, not imported.
529
+ * The generated worker entry passes the app's `defineIdentity(...)` result here;
530
+ * the worker validates every resolver's returned claims against it at the trust
531
+ * boundary before they become `ctx.auth`.
532
+ */
541
533
  interface IdentityContractLike {
542
534
  /** Reject policy applied when validation fails: downgrade to anonymous, or reject the request (401). */
543
535
  readonly onInvalid: "anonymous" | "reject";
544
536
  /** Validate resolver-returned claims against the declared contract. */
545
537
  validate: (identity: Record<string, unknown>) => IdentityValidation;
546
538
  }
547
- /**
548
- * The trust-boundary identity gate. Given the worker's `resolveIdentity` and an
549
- * optional `defineIdentity(...)` contract, return a resolver that validates every
550
- * resolved identity against the declared claims BEFORE it becomes `ctx.auth`.
551
- *
552
- * Claims arrive from untrusted tokens; a forged / malformed set is either
553
- * downgraded to anonymous (`onInvalid: "anonymous"`, the safe default — the bad
554
- * identity never reaches a policy as valid) or rejected with a `401`
555
- * (`onInvalid: "reject"`), rather than flowing in as an unchecked cast. A valid
556
- * identity is returned unchanged, so undeclared claims are forwarded verbatim.
557
- *
558
- * When no contract is configured (or there is no `resolveIdentity`), the original
559
- * resolver is returned untouched — zero overhead and byte-identical behaviour.
560
- * Only the public data paths (RPC / WebSocket / HTTP-action / server-query) use
561
- * the wrapped resolver; the admin path keeps the raw one (admin is gated by the
562
- * bearer / Access, not the app's identity contract).
563
- */
564
539
  /** One KV namespace as the studio's KV browser surfaces it. */
565
540
  interface KvNamespaceSummary {
566
541
  /** The wrangler/env binding name, e.g. `"MY_KV"`. */
@@ -592,10 +567,10 @@ interface KvValueResult {
592
567
  value: null | string;
593
568
  }
594
569
  /**
595
- * The introspector the worker wires for the studio's KV browser. Build it from
596
- * the env's bound KV namespaces. Omit it and the `/_lunora/admin/kv/*`
597
- * endpoints respond `KV_NOT_CONFIGURED`.
598
- */
570
+ * The introspector the worker wires for the studio's KV browser. Build it from
571
+ * the env's bound KV namespaces. Omit it and the `/_lunora/admin/kv/*`
572
+ * endpoints respond `KV_NOT_CONFIGURED`.
573
+ */
599
574
  interface KvIntrospector {
600
575
  /** Delete a key from a namespace. No-op when the key is absent. */
601
576
  deleteKey: (options: {
@@ -626,44 +601,90 @@ interface KvIntrospector {
626
601
  value: string;
627
602
  }) => Promise<void>;
628
603
  }
629
- /** The worker internals the KV routes reach through injection rather than closure. */
630
- /**
631
- * Observability hooks for the Lunora runtime.
632
- *
633
- * A user-supplied {@link ObservabilitySink} receives one event per dispatched
634
- * RPC (single-shard forward or fan-out). The runtime is otherwise oblivious
635
- * to where the telemetry goesadapters that forward to Cloudflare Analytics
636
- * Engine, OTLP-over-HTTP, Sentry, or stdout all implement the same shape.
637
- *
638
- * Failure model: the sink callback is wrapped in a try/catch so a faulty
639
- * adapter never breaks user-facing RPC dispatch. Errors thrown from inside
640
- * the sink are swallowed (they would otherwise replace a useful user-visible
641
- * error with a telemetry-pipeline failure).
642
- */
643
- /**
644
- * Per-RPC dispatch event. Single-shard calls set `shardKey`; cross-shard
645
- * fan-outs set `fanOut` with the table being aggregated, shard count, and
646
- * per-shard failure count.
647
- */
604
+ /**
605
+ * Shared, bundler-inlined helpers for the structured `fields` a
606
+ * `ctx.log.<level>(message, fields)` / `ctx.log.with(fields)` call carries.
607
+ *
608
+ * Inlined (like {@link file://./otlp.ts}) so `@lunora/do`, `@lunora/runtime`,
609
+ * `@lunora/config`, and `@lunora/studio` which sit on different tiers with no
610
+ * acceptable runtime dependency edge between them share ONE implementation of
611
+ * field rendering/normalization instead of the byte-identical copies they would
612
+ * otherwise hand-mirror. Keep this genuinely zero-dependency (only built-ins) so
613
+ * inlining into each `dist` stays sound.
614
+ */
615
+ /** Structured, filterable key/value fields attached to a `ctx.log` line. */
616
+ type LogFields = Record<string, unknown>;
617
+ /**
618
+ * Severity of a `ctx.log.*` call. The five console method names (`log` is the
619
+ * default level, distinct from `info`) plus `trace`/`fatal`, so the logger spans
620
+ * the full OpenTelemetry severity ramp (`trace`→`fatal`).
621
+ */
622
+ type ContextLogLevel = "debug" | "error" | "fatal" | "info" | "log" | "trace" | "warn";
623
+ /**
624
+ * Per-event context handed to a sink alongside the event: lets a sink register
625
+ * background work (a telemetry POST, a durable pipeline send) with the request's
626
+ * `waitUntil` so it survives isolate teardown after the response returns. Absent
627
+ * `waitUntil` (no request context) means the sink falls back to fire-and-forget.
628
+ */
629
+ interface LogSinkContext {
630
+ /** Keep a background promise alive past the response (the request's `waitUntil`). */
631
+ waitUntil?: (promise: Promise<unknown>) => void;
632
+ }
633
+ /**
634
+ * One application log line emitted from a function handler via `ctx.log`.
635
+ * Produced per `ctx.log.*` call (unlike a per-dispatch RPC summary).
636
+ */
637
+ interface LogEvent {
638
+ /** Raw arguments passed to the `ctx.log.*` call, in order. */
639
+ args: unknown[];
640
+ /**
641
+ * Structured fields the caller attached (`ctx.log.info(message, fields)` or a
642
+ * bound `ctx.log.with(fields)` child), already normalized to a fresh bag of
643
+ * JSON-safe primitives (see `shared/log-fields.ts`). Absent for a plain
644
+ * console-style call.
645
+ */
646
+ fields?: LogFields;
647
+ /** Function path that emitted the line, e.g. `"messages:list"`. */
648
+ functionPath: string;
649
+ /** Severity the line was logged at. */
650
+ level: ContextLogLevel;
651
+ /** Display string — the message, or the console-style args rendered and space-joined. */
652
+ message: string;
653
+ /** Shard key for single-shard calls; absent for the unnamed root DO. */
654
+ shardKey?: string;
655
+ /** Span id of the RPC this line was emitted under (trace correlation), or absent. */
656
+ spanId?: string;
657
+ /** Trace id this line belongs to (from the inbound `traceparent`), or absent. */
658
+ traceId?: string;
659
+ /** Wall-clock millis when the line was emitted. */
660
+ ts: number;
661
+ /** Acting userId, or absent when anonymous. */
662
+ userId?: string;
663
+ }
664
+ /**
665
+ * Per-RPC dispatch event. Single-shard calls set `shardKey`; cross-shard
666
+ * fan-outs set `fanOut` with the table being aggregated, shard count, and
667
+ * per-shard failure count.
668
+ */
648
669
  interface ObservabilityEvent {
649
670
  /** Wall-clock duration of the dispatch, in milliseconds. */
650
671
  durationMs: number;
651
672
  /**
652
- * Populated on `ok === false`. `code`/`status` mirror the LunoraError
653
- * taxonomy; `message` is the human-readable string (may include user
654
- * input — sinks that ship to third parties should scrub it).
655
- */
673
+ * Populated on `ok === false`. `code`/`status` mirror the LunoraError
674
+ * taxonomy; `message` is the human-readable string (may include user
675
+ * input — sinks that ship to third parties should scrub it).
676
+ */
656
677
  error?: {
657
678
  code: string;
658
679
  message: string;
659
680
  status: number;
660
681
  };
661
682
  /**
662
- * Populated for fan-out dispatches.
663
- * `shards` is the total fan-out cardinality; `failed` counts shards that
664
- * timed out or returned an error (the same `errors[]` the response body
665
- * carries to the caller).
666
- */
683
+ * Populated for fan-out dispatches.
684
+ * `shards` is the total fan-out cardinality; `failed` counts shards that
685
+ * timed out or returned an error (the same `errors[]` the response body
686
+ * carries to the caller).
687
+ */
667
688
  fanOut?: {
668
689
  failed: number;
669
690
  shards: number;
@@ -676,63 +697,32 @@ interface ObservabilityEvent {
676
697
  /** Shard key for single-shard calls; absent for fan-outs. */
677
698
  shardKey?: string;
678
699
  /**
679
- * W3C trace context for this dispatch, generated once at dispatch entry (32-
680
- * and 16-hex). A sink (e.g. `otlpSink`) reuses these for the dispatch's span
681
- * instead of minting fresh ids, and the runtime propagates them to the shard
682
- * as a `traceparent` so a container the handler calls can stitch its spans
683
- * under the same trace. Absent on paths that don't originate a trace (a sink
684
- * falls back to random ids).
685
- */
700
+ * W3C trace context for this dispatch, generated once at dispatch entry (32-
701
+ * and 16-hex). A sink (e.g. `otlpSink`) reuses these for the dispatch's span
702
+ * instead of minting fresh ids, and the runtime propagates them to the shard
703
+ * as a `traceparent` so a container the handler calls can stitch its spans
704
+ * under the same trace. Absent on paths that don't originate a trace (a sink
705
+ * falls back to random ids).
706
+ */
686
707
  spanId?: string;
687
708
  traceId?: string;
688
709
  }
689
- /** Severity of a {@link LogEvent}, mirroring the usual console levels. */
690
- type LogLevel = "debug" | "error" | "info" | "log" | "warn";
691
- /**
692
- * One application log line emitted from a function handler via `ctx.log`.
693
- *
694
- * Unlike {@link ObservabilityEvent} (one summary per dispatch), a `LogEvent`
695
- * is produced for each `ctx.log.*` call, carrying the human-readable `message`
696
- * (the args joined for display) plus the structured `args` array for sinks that
697
- * want the raw values. `functionPath` attributes the line to the handler that
698
- * emitted it; `shardKey`/`userId` mirror the dispatch context.
699
- *
700
- * This is how `ctx.log` reaches a destination in production: wire a sink's
701
- * {@link ObservabilitySink.onLog} and route it wherever you ship logs. In dev
702
- * the runtime also emits these to `console` so the CLI / Vite plugin can format
703
- * them in the terminal.
704
- */
705
- interface LogEvent {
706
- /** Raw arguments passed to the `ctx.log.*` call, in order. */
707
- args: unknown[];
708
- /** Function path that emitted the line, e.g. `"messages:list"`. */
709
- functionPath: string;
710
- /** Severity the line was logged at. */
711
- level: LogLevel;
712
- /** Display string — the args rendered and space-joined. */
713
- message: string;
714
- /** Shard key for single-shard calls; absent for the unnamed root DO. */
715
- shardKey?: string;
716
- /** Wall-clock millis when the line was emitted. */
717
- ts: number;
718
- /** Acting userId, or absent when anonymous. */
719
- userId?: string;
720
- }
721
- /**
722
- * Per-event context handed to a sink alongside the event. Lets a sink register
723
- * background work (e.g. a telemetry POST) with the request's `ctx.waitUntil` so
724
- * it survives isolate teardown after the response returns. Absent (`undefined`
725
- * `waitUntil`) on paths with no request context (e.g. the in-process
726
- * `serverQuery` fast-path), where the sink falls back to fire-and-forget.
727
- */
728
- interface ObservabilitySinkContext {
729
- /** Keep a background promise alive past the response (the request's `ctx.waitUntil`). */
730
- waitUntil?: (promise: Promise<unknown>) => void;
731
- }
732
710
  /**
733
- * The hook contract. Methods are optional so a sink can opt into only the
734
- * events it cares about; the runtime no-ops the others.
735
- */
711
+ * The `ctx.log` observability contract lives in `shared/` (inlined into each
712
+ * `dist`) so the DO that builds the events and the runtime sink that consumes
713
+ * them agree by construction rather than by hand-mirrored duplication. Re-exported
714
+ * here under the runtime's historical names.
715
+ *
716
+ * `LogLevel` is the canonical `ctx.log` severity union (the five console tiers
717
+ * plus `trace`/`fatal`); `ObservabilitySinkContext` is the shared per-event sink
718
+ * context (a `waitUntil` to keep a background send alive past the response).
719
+ */
720
+ type LogLevel = ContextLogLevel;
721
+ type ObservabilitySinkContext = LogSinkContext;
722
+ /**
723
+ * The hook contract. Methods are optional so a sink can opt into only the
724
+ * events it cares about; the runtime no-ops the others.
725
+ */
736
726
  interface ObservabilitySink {
737
727
  /** Invoked once per `ctx.log.*` call from a function handler. */
738
728
  onLog?: (event: LogEvent, context?: ObservabilitySinkContext) => void;
@@ -740,107 +730,107 @@ interface ObservabilitySink {
740
730
  onRpc?: (event: ObservabilityEvent, context?: ObservabilitySinkContext) => void;
741
731
  }
742
732
  /**
743
- * Invoke `sink.onRpc` with the given event, swallowing any error the sink
744
- * throws. Use at the dispatch boundary; the runtime should never see a
745
- * sink-originating throw bubble up past this point. `context.waitUntil`, when
746
- * supplied, lets a network sink keep its send alive past the response.
747
- */
733
+ * Invoke `sink.onRpc` with the given event, swallowing any error the sink
734
+ * throws. Use at the dispatch boundary; the runtime should never see a
735
+ * sink-originating throw bubble up past this point. `context.waitUntil`, when
736
+ * supplied, lets a network sink keep its send alive past the response.
737
+ */
748
738
  declare const emitRpcEvent: (sink: ObservabilitySink | undefined, event: ObservabilityEvent, context?: ObservabilitySinkContext) => void;
749
739
  /**
750
- * Invoke `sink.onLog` with the given log event, swallowing any error the sink
751
- * throws. The same failure model as {@link emitRpcEvent}: a buggy log sink must
752
- * never break the handler that emitted the line.
753
- */
740
+ * Invoke `sink.onLog` with the given log event, swallowing any error the sink
741
+ * throws. The same failure model as {@link emitRpcEvent}: a buggy log sink must
742
+ * never break the handler that emitted the line.
743
+ */
754
744
  declare const emitLogEvent: (sink: ObservabilitySink | undefined, event: LogEvent, context?: ObservabilitySinkContext) => void;
755
745
  /**
756
- * Cloudflare Durable Object jurisdictions restrict where a DO runs and persists
757
- * data, for data-residency / compliance regimes (GDPR, FedRAMP, US data
758
- * residency). The set is open — Cloudflare adds values over time — so this is a
759
- * widening union rather than a closed enum.
760
- * @see https://developers.cloudflare.com/durable-objects/reference/data-location/
761
- */
746
+ * Cloudflare Durable Object jurisdictions restrict where a DO runs and persists
747
+ * data, for data-residency / compliance regimes (GDPR, FedRAMP, US data
748
+ * residency). The set is open — Cloudflare adds values over time — so this is a
749
+ * widening union rather than a closed enum.
750
+ * @see https://developers.cloudflare.com/durable-objects/reference/data-location/
751
+ */
762
752
  type DurableObjectJurisdiction = "eu" | "fedramp" | "us";
763
753
  /**
764
- * Structural projection of the bits of `DurableObjectNamespace` the runtime
765
- * needs. Real workers-types defines a much wider surface; this lets us pass
766
- * unit-test doubles without coupling to `@cloudflare/workers-types`.
767
- */
754
+ * Structural projection of the bits of `DurableObjectNamespace` the runtime
755
+ * needs. Real workers-types defines a much wider surface; this lets us pass
756
+ * unit-test doubles without coupling to `@cloudflare/workers-types`.
757
+ */
768
758
  interface ShardNamespaceLike {
769
759
  get: (id: unknown) => {
770
760
  fetch: (request: Request) => Promise<Response>;
771
761
  };
772
762
  /**
773
- * `getByName` is the friendlier API but isn't on every workers-types
774
- * release yet. We prefer it when available and fall back to
775
- * `idFromName` + `get` for compatibility.
776
- */
763
+ * `getByName` is the friendlier API but isn't on every workers-types
764
+ * release yet. We prefer it when available and fall back to
765
+ * `idFromName` + `get` for compatibility.
766
+ */
777
767
  getByName?: (name: string) => {
778
768
  fetch: (request: Request) => Promise<Response>;
779
769
  };
780
770
  idFromName: (name: string) => unknown;
781
771
  /**
782
- * Derive a jurisdiction-restricted subnamespace. Every ID and stub created
783
- * from the returned namespace is pinned to `jurisdiction`. Optional because
784
- * older workers-types releases (and unit-test doubles) may not expose it;
785
- * {@link applyJurisdiction} fails closed when a jurisdiction is requested
786
- * but this method is absent.
787
- */
772
+ * Derive a jurisdiction-restricted subnamespace. Every ID and stub created
773
+ * from the returned namespace is pinned to `jurisdiction`. Optional because
774
+ * older workers-types releases (and unit-test doubles) may not expose it;
775
+ * {@link applyJurisdiction} fails closed when a jurisdiction is requested
776
+ * but this method is absent.
777
+ */
788
778
  jurisdiction?: (jurisdiction: DurableObjectJurisdiction) => ShardNamespaceLike;
789
779
  }
790
780
  interface ResolvedShard {
791
781
  fetch: (request: Request) => Promise<Response>;
792
782
  }
793
783
  /**
794
- * Return a jurisdiction-restricted view of `namespace`, or `namespace`
795
- * unchanged when no jurisdiction is configured.
796
- *
797
- * Fail-closed: if a jurisdiction is requested but the binding does not expose
798
- * `.jurisdiction()` (an older workers-types, or a misconfigured test double),
799
- * this throws rather than silently routing to the un-pinned global namespace —
800
- * silently dropping a residency constraint would let data land outside the
801
- * compliance boundary the caller asked for.
802
- */
784
+ * Return a jurisdiction-restricted view of `namespace`, or `namespace`
785
+ * unchanged when no jurisdiction is configured.
786
+ *
787
+ * Fail-closed: if a jurisdiction is requested but the binding does not expose
788
+ * `.jurisdiction()` (an older workers-types, or a misconfigured test double),
789
+ * this throws rather than silently routing to the un-pinned global namespace —
790
+ * silently dropping a residency constraint would let data land outside the
791
+ * compliance boundary the caller asked for.
792
+ */
803
793
  declare const applyJurisdiction: (namespace: ShardNamespaceLike, jurisdiction?: DurableObjectJurisdiction) => ShardNamespaceLike;
804
794
  /** Look up a shard stub by name, preferring `getByName` when present. */
805
795
  declare const resolveShard: (namespace: ShardNamespaceLike, shardKey: string) => ResolvedShard;
806
796
  /**
807
- * Source of "which shard keys exist for a given table right now". Returning
808
- * an empty array is valid — the coordinator will respond with the merge
809
- * strategy's identity (empty array for `concat`, `0` for `sum`, etc.).
810
- */
797
+ * Source of "which shard keys exist for a given table right now". Returning
798
+ * an empty array is valid — the coordinator will respond with the merge
799
+ * strategy's identity (empty array for `concat`, `0` for `sum`, etc.).
800
+ */
811
801
  interface ShardRegistry {
812
802
  listShardKeys: (table: string) => Promise<ReadonlyArray<string>> | ReadonlyArray<string>;
813
803
  }
814
804
  /**
815
- * Static-map implementation. Useful for tests and for small deployments
816
- * where shard keys are known up front (e.g. a fixed set of channel IDs).
817
- */
805
+ * Static-map implementation. Useful for tests and for small deployments
806
+ * where shard keys are known up front (e.g. a fixed set of channel IDs).
807
+ */
818
808
  declare const createStaticShardRegistry: (table_to_keys: Readonly<Record<string, ReadonlyArray<string>>>) => ShardRegistry;
819
809
  /**
820
- * Wire-serializable merge strategy. `topK.by` is a field name on the row
821
- * (the runtime looks it up with a string key), not a closure.
822
- *
823
- * Aggregate-friendly variants for cross-shard `count` / `aggregate` /
824
- * `groupBy` fan-outs:
825
- *
826
- * - `sum` — `count(*)`, `aggregate({ op: "sum" })` (sums numeric per-shard payloads).
827
- * - `max` — `aggregate({ op: "max" })`.
828
- * - `min` — `aggregate({ op: "min" })`.
829
- * - `groupBy` — per-shard `GroupByEntry[]` payloads, reduced into one
830
- * entry per distinct key tuple. `op` controls how values combine across
831
- * shards: `sum` (default — works for `COUNT(*)` and `SUM`), `max`, `min`.
832
- *
833
- * `avg` is intentionally absent in v1 — a correct cross-shard average
834
- * requires shipping `(sum, count)` per shard, not the post-shard mean.
835
- * Use two separate fan-outs (`sum` + `count`) and divide in the caller.
836
- *
837
- * `rank` — cross-shard `rank()` over a partition that spans shards (e.g. a
838
- * global leaderboard `.shardBy("userId")` with `rankIndex(partitionBy: [])`).
839
- * Each shard's `__lunora_admin__:rankBefore` returns `{before, total}` (its
840
- * local rows strictly-before the explicit key, plus its local partition
841
- * total); the merge sums them into `{position: Σbefore + 1, total: Σtotal}` —
842
- * the 1-based global position and global partition size.
843
- */
810
+ * Wire-serializable merge strategy. `topK.by` is a field name on the row
811
+ * (the runtime looks it up with a string key), not a closure.
812
+ *
813
+ * Aggregate-friendly variants for cross-shard `count` / `aggregate` /
814
+ * `groupBy` fan-outs:
815
+ *
816
+ * - `sum` — `count(*)`, `aggregate({ op: "sum" })` (sums numeric per-shard payloads).
817
+ * - `max` — `aggregate({ op: "max" })`.
818
+ * - `min` — `aggregate({ op: "min" })`.
819
+ * - `groupBy` — per-shard `GroupByEntry[]` payloads, reduced into one
820
+ * entry per distinct key tuple. `op` controls how values combine across
821
+ * shards: `sum` (default — works for `COUNT(*)` and `SUM`), `max`, `min`.
822
+ *
823
+ * `avg` is intentionally absent in v1 — a correct cross-shard average
824
+ * requires shipping `(sum, count)` per shard, not the post-shard mean.
825
+ * Use two separate fan-outs (`sum` + `count`) and divide in the caller.
826
+ *
827
+ * `rank` — cross-shard `rank()` over a partition that spans shards (e.g. a
828
+ * global leaderboard `.shardBy("userId")` with `rankIndex(partitionBy: [])`).
829
+ * Each shard's `__lunora_admin__:rankBefore` returns `{before, total}` (its
830
+ * local rows strictly-before the explicit key, plus its local partition
831
+ * total); the merge sums them into `{position: Σbefore + 1, total: Σtotal}` —
832
+ * the 1-based global position and global partition size.
833
+ */
844
834
  type MergeStrategy = {
845
835
  kind: "concat";
846
836
  } | {
@@ -863,17 +853,17 @@ type MergeStrategy = {
863
853
  op?: "max" | "min" | "sum";
864
854
  };
865
855
  /**
866
- * Convenience: build the right wire-serializable {@link MergeStrategy} for a
867
- * given aggregate read. The reader doesn't know which op the caller chose, so
868
- * a fan-out wrapper passes the user's op + by-keys through this to derive the
869
- * merge.
870
- *
871
- * - `count` → `sum`.
872
- * - `aggregate({ op })` → `sum`/`max`/`min` (or throws for `avg`).
873
- * - `groupBy({ by, agg })` → `groupBy({ op })` (defaults to `sum` since
874
- * `groupBy`'s default reducer is `count`).
875
- * @returns the derived {@link MergeStrategy}.
876
- */
856
+ * Convenience: build the right wire-serializable {@link MergeStrategy} for a
857
+ * given aggregate read. The reader doesn't know which op the caller chose, so
858
+ * a fan-out wrapper passes the user's op + by-keys through this to derive the
859
+ * merge.
860
+ *
861
+ * - `count` → `sum`.
862
+ * - `aggregate({ op })` → `sum`/`max`/`min` (or throws for `avg`).
863
+ * - `groupBy({ by, agg })` → `groupBy({ op })` (defaults to `sum` since
864
+ * `groupBy`'s default reducer is `count`).
865
+ * @returns the derived {@link MergeStrategy}.
866
+ */
877
867
  declare const mergeStrategyForAggregate: (input: {
878
868
  agg?: {
879
869
  op?: "avg" | "count" | "max" | "min" | "sum";
@@ -891,11 +881,11 @@ interface FanOutSpec {
891
881
  table: string;
892
882
  }
893
883
  /**
894
- * Per-shard failure surfaced in the aggregate response's `errors` field. We
895
- * never throw out of `fanOut` — slow/failed shards are *data*, not an
896
- * exception, so callers can decide whether to retry or surface a partial
897
- * UI.
898
- */
884
+ * Per-shard failure surfaced in the aggregate response's `errors` field. We
885
+ * never throw out of `fanOut` — slow/failed shards are *data*, not an
886
+ * exception, so callers can decide whether to retry or surface a partial
887
+ * UI.
888
+ */
899
889
  interface ShardError {
900
890
  /** Human-readable; tests assert on `.includes("timeout")` and similar. */
901
891
  message: string;
@@ -914,15 +904,15 @@ interface FanOutResult<T = unknown> {
914
904
  }
915
905
  interface QueryCoordinatorOptions {
916
906
  /**
917
- * Maximum number of shard RPCs to issue in parallel. Defaults to 16 —
918
- * keeps the 30-second Worker CPU budget healthy when fanning out to
919
- * dozens of shards and avoids stampeding the DO namespace.
920
- */
907
+ * Maximum number of shard RPCs to issue in parallel. Defaults to 16 —
908
+ * keeps the 30-second Worker CPU budget healthy when fanning out to
909
+ * dozens of shards and avoids stampeding the DO namespace.
910
+ */
921
911
  maxConcurrency?: number;
922
912
  /**
923
- * Hard per-shard timeout in milliseconds. Defaults to 5000; a slow
924
- * shard surfaces in `errors[]` rather than stalling the aggregate.
925
- */
913
+ * Hard per-shard timeout in milliseconds. Defaults to 5000; a slow
914
+ * shard surfaces in `errors[]` rather than stalling the aggregate.
915
+ */
926
916
  perShardTimeoutMs?: number;
927
917
  /** Required — drives which shards to fan out to. */
928
918
  registry: ShardRegistry;
@@ -935,16 +925,16 @@ interface FanOutRequest {
935
925
  headers?: Record<string, string>;
936
926
  }
937
927
  /**
938
- * Cross-shard migration request. Unlike {@link FanOutRequest} there is no merge
939
- * strategy — per-shard payloads are `MigrationRunResult`-shaped objects, not
940
- * rows, so {@link QueryCoordinator.orchestrateMigration} rolls them up with the
941
- * fixed semantics documented on {@link MigrationFanOutResult}.
942
- *
943
- * `functionPath` is the admin RPC to invoke on each shard
944
- * (`__lunora_admin__:runMigration` or `:migrationStatus`); `headers` must carry
945
- * the `Authorization` bearer header the shard's admin gate requires (the
946
- * configured admin token), or every shard comes back as a 403 error.
947
- */
928
+ * Cross-shard migration request. Unlike {@link FanOutRequest} there is no merge
929
+ * strategy — per-shard payloads are `MigrationRunResult`-shaped objects, not
930
+ * rows, so {@link QueryCoordinator.orchestrateMigration} rolls them up with the
931
+ * fixed semantics documented on {@link MigrationFanOutResult}.
932
+ *
933
+ * `functionPath` is the admin RPC to invoke on each shard
934
+ * (`__lunora_admin__:runMigration` or `:migrationStatus`); `headers` must carry
935
+ * the `Authorization` bearer header the shard's admin gate requires (the
936
+ * configured admin token), or every shard comes back as a 403 error.
937
+ */
948
938
  interface MigrationFanOutRequest {
949
939
  args?: Record<string, unknown>;
950
940
  functionPath: string;
@@ -974,23 +964,23 @@ interface MigrationFanOutResult {
974
964
  /** Per-shard outcomes, in registry order. */
975
965
  shards: ReadonlyArray<ShardMigrationOutcome>;
976
966
  /**
977
- * Rolled-up status. `"failed"` if any shard's runner reported failure;
978
- * `"in_progress"` if any shard is incomplete or unreachable (the run stays
979
- * resumable); `"completed"` only when every shard finished cleanly.
980
- */
967
+ * Rolled-up status. `"failed"` if any shard's runner reported failure;
968
+ * `"in_progress"` if any shard is incomplete or unreachable (the run stays
969
+ * resumable); `"completed"` only when every shard finished cleanly.
970
+ */
981
971
  status: "completed" | "failed" | "in_progress";
982
972
  }
983
973
  /**
984
- * Cross-shard rank request. Like {@link MigrationFanOutRequest} there is no
985
- * caller-supplied merge — per-shard payloads are `{before, total}` objects, so
986
- * {@link QueryCoordinator.orchestrateRank} rolls them up with the fixed
987
- * `{position: Σbefore + 1, total: Σtotal}` semantics {@link mergeRank} defines.
988
- *
989
- * The key tuple (`partitionKey`/`sortValues`/`rowId`) is built off the row doc
990
- * via `@lunora/do`'s `rankKeyFromDoc(index, doc)` and forwarded verbatim to
991
- * each shard's `__lunora_admin__:rankBefore` admin RPC; `headers` must carry
992
- * the admin bearer the shard's admin gate requires.
993
- */
974
+ * Cross-shard rank request. Like {@link MigrationFanOutRequest} there is no
975
+ * caller-supplied merge — per-shard payloads are `{before, total}` objects, so
976
+ * {@link QueryCoordinator.orchestrateRank} rolls them up with the fixed
977
+ * `{position: Σbefore + 1, total: Σtotal}` semantics {@link mergeRank} defines.
978
+ *
979
+ * The key tuple (`partitionKey`/`sortValues`/`rowId`) is built off the row doc
980
+ * via `@lunora/do`'s `rankKeyFromDoc(index, doc)` and forwarded verbatim to
981
+ * each shard's `__lunora_admin__:rankBefore` admin RPC; `headers` must carry
982
+ * the admin bearer the shard's admin gate requires.
983
+ */
994
984
  interface RankFanOutRequest {
995
985
  headers?: Record<string, string>;
996
986
  /** Rank index name on `table`. */
@@ -1031,18 +1021,18 @@ interface ShardRankOutcome {
1031
1021
  shardKey: string;
1032
1022
  }
1033
1023
  /**
1034
- * Cross-shard ranked-pagination request. Like {@link RankFanOutRequest} there's
1035
- * no caller-supplied merge — the merge is the fixed k-way merge by the rank-key
1036
- * tuple. `take` is the global page size; `cursor` is the opaque composite cursor
1037
- * from the prior page's `continueCursor` (absent → first page). `partitionKey`,
1038
- * when set, pins a single partition (`encodePartitionKey(index.partitionBy, where)`),
1039
- * forwarded so each shard scopes its local slice to that partition.
1040
- *
1041
- * `directions` is the per-sort-key direction list (`index.sortBy[i].direction`)
1042
- * the coordinator's comparator needs to break ties the same way each shard's
1043
- * `ORDER BY` does. `partitionKey` and the `__id__` tiebreak are always ascending
1044
- * (matching the shard companion's btree), so only the sort columns vary.
1045
- */
1024
+ * Cross-shard ranked-pagination request. Like {@link RankFanOutRequest} there's
1025
+ * no caller-supplied merge — the merge is the fixed k-way merge by the rank-key
1026
+ * tuple. `take` is the global page size; `cursor` is the opaque composite cursor
1027
+ * from the prior page's `continueCursor` (absent → first page). `partitionKey`,
1028
+ * when set, pins a single partition (`encodePartitionKey(index.partitionBy, where)`),
1029
+ * forwarded so each shard scopes its local slice to that partition.
1030
+ *
1031
+ * `directions` is the per-sort-key direction list (`index.sortBy[i].direction`)
1032
+ * the coordinator's comparator needs to break ties the same way each shard's
1033
+ * `ORDER BY` does. `partitionKey` and the `__id__` tiebreak are always ascending
1034
+ * (matching the shard companion's btree), so only the sort columns vary.
1035
+ */
1046
1036
  interface RankPageFanOutRequest {
1047
1037
  /** Opaque composite cursor from the prior page's `continueCursor`. */
1048
1038
  cursor?: null | string;
@@ -1089,78 +1079,78 @@ interface RankPageFanOutResult {
1089
1079
  interface QueryCoordinator {
1090
1080
  fanOut: <T = unknown>(namespace: ShardNamespaceLike, request: FanOutRequest) => Promise<FanOutResult<T>>;
1091
1081
  /**
1092
- * Fan the `__lunora_admin__:applyCdc` admin RPC out by forwarding each
1093
- * pre-bucketed per-shard batch of CDC changes, rolling up the applied/failed
1094
- * counts. The replay half of point-in-time recovery.
1095
- */
1082
+ * Fan the `__lunora_admin__:applyCdc` admin RPC out by forwarding each
1083
+ * pre-bucketed per-shard batch of CDC changes, rolling up the applied/failed
1084
+ * counts. The replay half of point-in-time recovery.
1085
+ */
1096
1086
  orchestrateApplyCdc: (namespace: ShardNamespaceLike, request: ApplyCdcFanOutRequest) => Promise<ApplyCdcFanOutResult>;
1097
1087
  /**
1098
- * Fan the `__lunora_admin__:cdcSync` admin RPC out to every live shard,
1099
- * each resumed from its own cursor in `request.cursors` (shardKey → seq).
1100
- * Returns the per-shard change pages plus their new cursors so the caller
1101
- * can checkpoint each shard independently — the streaming-export feed.
1102
- */
1088
+ * Fan the `__lunora_admin__:cdcSync` admin RPC out to every live shard,
1089
+ * each resumed from its own cursor in `request.cursors` (shardKey → seq).
1090
+ * Returns the per-shard change pages plus their new cursors so the caller
1091
+ * can checkpoint each shard independently — the streaming-export feed.
1092
+ */
1103
1093
  orchestrateCdcSync: (namespace: ShardNamespaceLike, request: CdcSyncFanOutRequest) => Promise<CdcSyncFanOutResult>;
1104
1094
  /**
1105
- * Fan an export admin RPC out to every live shard, returning the
1106
- * per-shard `{rows}` payloads alongside any per-shard errors. Each shard
1107
- * returns a JSON envelope (not a streaming body) so this method is the
1108
- * collector — the worker assembles the NDJSON stream.
1109
- */
1095
+ * Fan an export admin RPC out to every live shard, returning the
1096
+ * per-shard `{rows}` payloads alongside any per-shard errors. Each shard
1097
+ * returns a JSON envelope (not a streaming body) so this method is the
1098
+ * collector — the worker assembles the NDJSON stream.
1099
+ */
1110
1100
  orchestrateExport: (namespace: ShardNamespaceLike, request: ExportFanOutRequest) => Promise<ExportFanOutResult>;
1111
1101
  /**
1112
- * Fan an import admin RPC out by routing each row to its owning shard. The
1113
- * shard registry resolves which shards exist; rows whose table has a
1114
- * `shardBy(field)` are bucketed using that field's value as the shard key,
1115
- * other tables fall back to the runtime's default `__root__` shard.
1116
- */
1102
+ * Fan an import admin RPC out by routing each row to its owning shard. The
1103
+ * shard registry resolves which shards exist; rows whose table has a
1104
+ * `shardBy(field)` are bucketed using that field's value as the shard key,
1105
+ * other tables fall back to the runtime's default `__root__` shard.
1106
+ */
1117
1107
  orchestrateImport: (namespace: ShardNamespaceLike, request: ImportFanOutRequest) => Promise<ImportFanOutResult>;
1118
1108
  /** Fan a migration admin RPC out to every live shard of a table and roll up the per-shard outcomes. */
1119
1109
  orchestrateMigration: (namespace: ShardNamespaceLike, request: MigrationFanOutRequest) => Promise<MigrationFanOutResult>;
1120
1110
  /**
1121
- * Fan the `__lunora_admin__:rankBefore` admin RPC out to every live shard of
1122
- * a table and roll up the per-shard `{before, total}` payloads into the
1123
- * global rank (`{position: Σbefore + 1, total: Σtotal}`). The cross-shard
1124
- * `rank()` path for a partition that spans shards.
1125
- */
1111
+ * Fan the `__lunora_admin__:rankBefore` admin RPC out to every live shard of
1112
+ * a table and roll up the per-shard `{before, total}` payloads into the
1113
+ * global rank (`{position: Σbefore + 1, total: Σtotal}`). The cross-shard
1114
+ * `rank()` path for a partition that spans shards.
1115
+ */
1126
1116
  orchestrateRank: (namespace: ShardNamespaceLike, request: RankFanOutRequest) => Promise<RankFanOutResult>;
1127
1117
  /**
1128
- * Page a ranked query across every live shard of a `.shardBy(...)` table.
1129
- * Fans `__lunora_admin__:rankPage` out to each shard, gathers each shard's
1130
- * local ranked slice (rows tagged with their rank-key tuple), and k-way
1131
- * merges them by that tuple into one globally-ranked page of `take` rows.
1132
- * The opaque `continueCursor` is a composite of per-shard cursors so the
1133
- * next page resumes each shard strictly-after the last row the global page
1134
- * consumed from it — pages never drop or duplicate a row at a shard
1135
- * boundary. The cross-shard `rankPage()` path (PLAN5 §7.1 / PLAN2 #3).
1136
- */
1118
+ * Page a ranked query across every live shard of a `.shardBy(...)` table.
1119
+ * Fans `__lunora_admin__:rankPage` out to each shard, gathers each shard's
1120
+ * local ranked slice (rows tagged with their rank-key tuple), and k-way
1121
+ * merges them by that tuple into one globally-ranked page of `take` rows.
1122
+ * The opaque `continueCursor` is a composite of per-shard cursors so the
1123
+ * next page resumes each shard strictly-after the last row the global page
1124
+ * consumed from it — pages never drop or duplicate a row at a shard
1125
+ * boundary. The cross-shard `rankPage()` path (PLAN5 §7.1 / PLAN2 #3).
1126
+ */
1137
1127
  orchestrateRankPage: (namespace: ShardNamespaceLike, request: RankPageFanOutRequest) => Promise<RankPageFanOutResult>;
1138
1128
  /**
1139
- * Fan the `__lunora_admin__:getMetrics` admin RPC out to every live shard of
1140
- * a table and collect each shard's lifetime `requests` total into a per-shard
1141
- * `{ shardKey, requests }` distribution. The feed the studio's `hot_shard`
1142
- * advisor lint needs: a single shard's snapshot can't reveal cross-shard
1143
- * skew, so this fans the cheap metrics read out and returns the whole shard
1144
- * set's request volumes (a failed shard surfaces as `requests: 0`).
1145
- */
1129
+ * Fan the `__lunora_admin__:getMetrics` admin RPC out to every live shard of
1130
+ * a table and collect each shard's lifetime `requests` total into a per-shard
1131
+ * `{ shardKey, requests }` distribution. The feed the studio's `hot_shard`
1132
+ * advisor lint needs: a single shard's snapshot can't reveal cross-shard
1133
+ * skew, so this fans the cheap metrics read out and returns the whole shard
1134
+ * set's request volumes (a failed shard surfaces as `requests: 0`).
1135
+ */
1146
1136
  orchestrateShardTraffic: (namespace: ShardNamespaceLike, request: ShardTrafficFanOutRequest) => Promise<ShardTrafficFanOutResult>;
1147
1137
  readonly registry: ShardRegistry;
1148
1138
  }
1149
1139
  /**
1150
- * Cross-shard export request. `tables` is the union of every table the caller
1151
- * wants exported (shard-local **or** global); `headers` carries the admin
1152
- * bearer the per-shard gate expects. Shard registries are queried for the
1153
- * complete set of live shards across all listed shard-local tables.
1154
- */
1140
+ * Cross-shard export request. `tables` is the union of every table the caller
1141
+ * wants exported (shard-local **or** global); `headers` carries the admin
1142
+ * bearer the per-shard gate expects. Shard registries are queried for the
1143
+ * complete set of live shards across all listed shard-local tables.
1144
+ */
1155
1145
  interface ExportFanOutRequest {
1156
1146
  args?: Record<string, unknown>;
1157
1147
  headers?: Record<string, string>;
1158
1148
  /**
1159
- * Tables driving the fan-out. Shards are derived from the union of each
1160
- * table's live shard keys — so an export of `["users","messages"]` reaches
1161
- * every shard that holds either table. Globals are skipped here; the
1162
- * worker reads them from D1 directly.
1163
- */
1149
+ * Tables driving the fan-out. Shards are derived from the union of each
1150
+ * table's live shard keys — so an export of `["users","messages"]` reaches
1151
+ * every shard that holds either table. Globals are skipped here; the
1152
+ * worker reads them from D1 directly.
1153
+ */
1164
1154
  tables: ReadonlyArray<string>;
1165
1155
  }
1166
1156
  /** Per-shard export outcome. */
@@ -1182,11 +1172,11 @@ interface ExportFanOutResult {
1182
1172
  shards: ReadonlyArray<ShardExportOutcome>;
1183
1173
  }
1184
1174
  /**
1185
- * Cross-shard change-data-capture request. `tables` drives shard discovery (the
1186
- * union of their live shard keys, like export); `cursors` maps each shard key
1187
- * to the `seq` it was last read through (absent → from the beginning). `limit`
1188
- * caps each shard's page.
1189
- */
1175
+ * Cross-shard change-data-capture request. `tables` drives shard discovery (the
1176
+ * union of their live shard keys, like export); `cursors` maps each shard key
1177
+ * to the `seq` it was last read through (absent → from the beginning). `limit`
1178
+ * caps each shard's page.
1179
+ */
1190
1180
  interface CdcSyncFanOutRequest {
1191
1181
  cursors?: Record<string, number>;
1192
1182
  headers?: Record<string, string>;
@@ -1210,16 +1200,16 @@ interface CdcSyncFanOutResult {
1210
1200
  shards: ReadonlyArray<ShardCdcOutcome>;
1211
1201
  }
1212
1202
  /**
1213
- * Cross-shard import request. Rows have already been bucketed by the runtime
1214
- * into one batch per shard key — the coordinator's job is to forward each
1215
- * batch and roll up the per-shard insert counts + errors.
1216
- */
1203
+ * Cross-shard import request. Rows have already been bucketed by the runtime
1204
+ * into one batch per shard key — the coordinator's job is to forward each
1205
+ * batch and roll up the per-shard insert counts + errors.
1206
+ */
1217
1207
  interface ImportFanOutRequest {
1218
1208
  /**
1219
- * Per-shard batches keyed by shard key. Each entry will be POSTed as the
1220
- * `rows` arg of `__lunora_admin__:importShard`. The shard's
1221
- * starting-line-number for error attribution is carried in `startLine`.
1222
- */
1209
+ * Per-shard batches keyed by shard key. Each entry will be POSTed as the
1210
+ * `rows` arg of `__lunora_admin__:importShard`. The shard's
1211
+ * starting-line-number for error attribution is carried in `startLine`.
1212
+ */
1223
1213
  batches: ReadonlyArray<{
1224
1214
  rows: ReadonlyArray<{
1225
1215
  doc: Record<string, unknown>;
@@ -1264,10 +1254,10 @@ interface ImportFanOutResult {
1264
1254
  shards: ReadonlyArray<ShardImportOutcome>;
1265
1255
  }
1266
1256
  /**
1267
- * Cross-shard CDC replay request (point-in-time recovery). Changes are
1268
- * pre-bucketed by the runtime into one batch per shard key — the coordinator
1269
- * forwards each batch to `__lunora_admin__:applyCdc` and rolls up the counts.
1270
- */
1257
+ * Cross-shard CDC replay request (point-in-time recovery). Changes are
1258
+ * pre-bucketed by the runtime into one batch per shard key — the coordinator
1259
+ * forwards each batch to `__lunora_admin__:applyCdc` and rolls up the counts.
1260
+ */
1271
1261
  interface ApplyCdcFanOutRequest {
1272
1262
  batches: ReadonlyArray<{
1273
1263
  changes: ReadonlyArray<Record<string, unknown>>;
@@ -1282,17 +1272,17 @@ interface ApplyCdcFanOutResult {
1282
1272
  ok: number;
1283
1273
  }
1284
1274
  /**
1285
- * Cross-shard traffic request. Like {@link MigrationFanOutRequest} there is no
1286
- * caller-supplied merge — each shard's `__lunora_admin__:getMetrics` payload
1287
- * carries its own lifetime `requests` total, and {@link rollUpShardTraffic}
1288
- * collects them into one `{ shardKey, requests }` entry per shard. `headers`
1289
- * must carry the admin bearer the per-shard `getMetrics` gate requires.
1290
- *
1291
- * `table` drives shard discovery: the registry's live shard keys for the table
1292
- * are the shards fanned out to. This is the feed the studio's `hot_shard`
1293
- * runtime advisor consumes to compute cross-shard skew — a single shard's
1294
- * snapshot can't, so the panel fans this out on demand.
1295
- */
1275
+ * Cross-shard traffic request. Like {@link MigrationFanOutRequest} there is no
1276
+ * caller-supplied merge — each shard's `__lunora_admin__:getMetrics` payload
1277
+ * carries its own lifetime `requests` total, and {@link rollUpShardTraffic}
1278
+ * collects them into one `{ shardKey, requests }` entry per shard. `headers`
1279
+ * must carry the admin bearer the per-shard `getMetrics` gate requires.
1280
+ *
1281
+ * `table` drives shard discovery: the registry's live shard keys for the table
1282
+ * are the shards fanned out to. This is the feed the studio's `hot_shard`
1283
+ * runtime advisor consumes to compute cross-shard skew — a single shard's
1284
+ * snapshot can't, so the panel fans this out on demand.
1285
+ */
1296
1286
  interface ShardTrafficFanOutRequest {
1297
1287
  headers?: Record<string, string>;
1298
1288
  /** Table whose live shard keys the traffic fan-out runs across. */
@@ -1311,26 +1301,26 @@ interface ShardTrafficFanOutResult {
1311
1301
  /** Shards that returned a 2xx `getMetrics` snapshot. */
1312
1302
  ok: number;
1313
1303
  /**
1314
- * Per-shard request totals, in registry order. Shaped to plug straight into
1315
- * the advisor's `LintContext.shardTraffic` so the `hot_shard` lint can
1316
- * compute the cross-shard share. A failed shard still appears (with
1317
- * `requests: 0`) so callers see the full shard set.
1318
- */
1304
+ * Per-shard request totals, in registry order. Shaped to plug straight into
1305
+ * the advisor's `LintContext.shardTraffic` so the `hot_shard` lint can
1306
+ * compute the cross-shard share. A failed shard still appears (with
1307
+ * `requests: 0`) so callers see the full shard set.
1308
+ */
1319
1309
  shards: ReadonlyArray<ShardTrafficEntry>;
1320
1310
  }
1321
1311
  declare const createQueryCoordinator: (options: QueryCoordinatorOptions) => QueryCoordinator;
1322
1312
  /** Per-header overrides for {@link SecurityHeadersOptions}. `false` omits the header. */
1323
1313
  interface SecurityHeadersOptions {
1324
1314
  /**
1325
- * `Content-Security-Policy`. Omitted (`undefined`) applies a restrictive
1326
- * `default-src 'none'` policy to **non-HTML** responses, and a conservative
1327
- * hardening policy to **HTML** responses (`base-uri 'none'; frame-ancestors
1328
- * 'self'; object-src 'none'`) — this does NOT set `default-src`/`script-src`,
1329
- * so it never blocks an SSR page's own scripts/styles/images/fetches, it only
1330
- * locks down the `base` element, framing (mirroring the `SAMEORIGIN`
1331
- * X-Frame-Options default), and legacy plugins. Pass a string to apply that
1332
- * exact policy to every response (HTML included); `false` to never send one.
1333
- */
1315
+ * `Content-Security-Policy`. Omitted (`undefined`) applies a restrictive
1316
+ * `default-src 'none'` policy to **non-HTML** responses, and a conservative
1317
+ * hardening policy to **HTML** responses (`base-uri 'none'; frame-ancestors
1318
+ * 'self'; object-src 'none'`) — this does NOT set `default-src`/`script-src`,
1319
+ * so it never blocks an SSR page's own scripts/styles/images/fetches, it only
1320
+ * locks down the `base` element, framing (mirroring the `SAMEORIGIN`
1321
+ * X-Frame-Options default), and legacy plugins. Pass a string to apply that
1322
+ * exact policy to every response (HTML included); `false` to never send one.
1323
+ */
1334
1324
  csp?: string | false;
1335
1325
  /** `X-Frame-Options` (clickjacking). Defaults to `SAMEORIGIN`. `false` omits it. */
1336
1326
  frameOptions?: "DENY" | "SAMEORIGIN" | false;
@@ -1364,9 +1354,9 @@ interface CsrfOptions {
1364
1354
  trustedOrigins?: string[];
1365
1355
  }
1366
1356
  /**
1367
- * The `security` option on `createWorker`. Every field is optional and defaults
1368
- * to a secure posture; set a field to `false` to opt out of that layer.
1369
- */
1357
+ * The `security` option on `createWorker`. Every field is optional and defaults
1358
+ * to a secure posture; set a field to `false` to opt out of that layer.
1359
+ */
1370
1360
  interface SecurityOptions {
1371
1361
  /** CORS. Defaults to **deny cross-origin**; supply an allowlist to permit specific origins. `false` disables CORS handling. */
1372
1362
  cors?: CorsOptions | false;
@@ -1394,13 +1384,13 @@ interface ResolvedCors {
1394
1384
  enabled: boolean;
1395
1385
  isAllowed: (origin: string) => boolean;
1396
1386
  /**
1397
- * Like {@link ResolvedCors.isAllowed} but NEVER satisfied by a wildcard `*`
1398
- * allowlist — an origin counts only when matched by an explicit, non-wildcard
1399
- * rule (a named origin in the list, or a custom predicate the developer
1400
- * wrote). Used by the CSRF guard: a wildcard CORS allowlist means "any origin
1401
- * may read my non-credentialed responses", which must NOT be conflated with
1402
- * "I trust any origin to make authenticated state changes".
1403
- */
1387
+ * Like {@link ResolvedCors.isAllowed} but NEVER satisfied by a wildcard `*`
1388
+ * allowlist — an origin counts only when matched by an explicit, non-wildcard
1389
+ * rule (a named origin in the list, or a custom predicate the developer
1390
+ * wrote). Used by the CSRF guard: a wildcard CORS allowlist means "any origin
1391
+ * may read my non-credentialed responses", which must NOT be conflated with
1392
+ * "I trust any origin to make authenticated state changes".
1393
+ */
1404
1394
  isExplicitlyAllowed: (origin: string) => boolean;
1405
1395
  maxAge: number;
1406
1396
  }
@@ -1415,79 +1405,61 @@ interface ResolvedSecurity {
1415
1405
  headers: ResolvedHeaders;
1416
1406
  }
1417
1407
  /**
1418
- * Normalize the public {@link SecurityOptions} into the resolved form the
1419
- * request path applies. Pure — throws only on an invalid combination (wildcard
1420
- * CORS + credentials) so the misconfiguration surfaces at worker construction
1421
- * rather than silently shipping an unenforceable policy.
1422
- *
1423
- * `env` supplies the deployment-level security vars: `LUNORA_SECURITY_HEADERS` /
1424
- * `LUNORA_SECURITY_CSRF` opt out of those layers (set either to `off`/`false`/`0`),
1425
- * and `LUNORA_ALLOWED_ORIGINS` / `LUNORA_CORS_ALLOW_CREDENTIALS` configure CORS
1426
- * when it isn't set in code. **Code config wins** — an explicit `security.*` in
1427
- * {@link SecurityOptions} overrides the matching env knob — so the env var only
1428
- * relaxes or fills the secure default, and the DO security audit (which reads the
1429
- * same vars) and the running worker stay in agreement.
1430
- */
1408
+ * Normalize the public {@link SecurityOptions} into the resolved form the
1409
+ * request path applies. Pure — throws only on an invalid combination (wildcard
1410
+ * CORS + credentials) so the misconfiguration surfaces at worker construction
1411
+ * rather than silently shipping an unenforceable policy.
1412
+ *
1413
+ * `env` supplies the deployment-level security vars: `LUNORA_SECURITY_HEADERS` /
1414
+ * `LUNORA_SECURITY_CSRF` opt out of those layers (set either to `off`/`false`/`0`),
1415
+ * and `LUNORA_ALLOWED_ORIGINS` / `LUNORA_CORS_ALLOW_CREDENTIALS` configure CORS
1416
+ * when it isn't set in code. **Code config wins** — an explicit `security.*` in
1417
+ * {@link SecurityOptions} overrides the matching env knob — so the env var only
1418
+ * relaxes or fills the secure default, and the DO security audit (which reads the
1419
+ * same vars) and the running worker stay in agreement.
1420
+ */
1431
1421
  declare const resolveSecurity: (security: SecurityOptions | undefined, env?: Record<string, unknown>) => ResolvedSecurity;
1432
1422
  /**
1433
- * CSRF defense: reject an unsafe (state-changing), **cookie-authenticated**
1434
- * request whose `Origin`/`Referer` is neither same-origin nor allowlisted.
1435
- *
1436
- * Scoped deliberately to cookie-bearing browser requests — the only vector a
1437
- * cross-site forgery can ride, since a browser auto-attaches cookies but never a
1438
- * bearer token or custom header. Bearer/server-to-server traffic (no `Cookie`)
1439
- * is exempt, as are safe methods. Returns a `403` `Response` to short-circuit,
1440
- * or `undefined` when the request may proceed.
1441
- * @returns a `403` Response when the origin is untrusted, or `undefined` when the request is allowed.
1442
- */
1423
+ * CSRF defense: reject an unsafe (state-changing), **cookie-authenticated**
1424
+ * request whose `Origin`/`Referer` is neither same-origin nor allowlisted.
1425
+ *
1426
+ * Scoped deliberately to cookie-bearing browser requests — the only vector a
1427
+ * cross-site forgery can ride, since a browser auto-attaches cookies but never a
1428
+ * bearer token or custom header. Bearer/server-to-server traffic (no `Cookie`)
1429
+ * is exempt, as are safe methods. Returns a `403` `Response` to short-circuit,
1430
+ * or `undefined` when the request may proceed.
1431
+ * @returns a `403` Response when the origin is untrusted, or `undefined` when the request is allowed.
1432
+ */
1443
1433
  declare const enforceOrigin: (request: Request, resolved: ResolvedSecurity) => Response | undefined;
1444
1434
  /**
1445
- * CSRF defense for the WebSocket upgrade (Cross-Site WebSocket Hijacking).
1446
- *
1447
- * A WS handshake is an HTTP `GET`, so {@link enforceOrigin}'s safe-method
1448
- * exemption never fires for it — yet the browser auto-attaches the session
1449
- * cookie to the handshake and WebSocket connections are NOT governed by
1450
- * CORS/SOP. Without an explicit `Origin` check any page can open
1451
- * `wss://app/_lunora/ws`, authenticate as the logged-in victim, and read the
1452
- * victim's live queries + issue mutations as them. This guard closes that hole.
1453
- *
1454
- * Scoped to cookie-bearing upgrades — the only vector CSWSH can ride (a browser
1455
- * attaches cookies but never a bearer token). Bearer/token/server-to-server
1456
- * upgrades (no `Cookie`) are exempt. A browser ALWAYS sends `Origin` on a WS
1457
- * handshake, so a missing/untrusted `Origin` on a cookie-bearing upgrade fails
1458
- * closed (mirrors {@link enforceOrigin}).
1459
- * @returns a `403` Response when the upgrade origin is untrusted, or `undefined` when it is allowed.
1460
- */
1461
-
1462
- /**
1463
- * Answer a CORS preflight (`OPTIONS` carrying `Access-Control-Request-Method`)
1464
- * for an allowlisted origin with a `204`. Returns `undefined` for non-preflight
1465
- * requests, a disabled CORS layer, or a disallowed origin — letting the request
1466
- * fall through to normal routing.
1467
- * @returns a `204` Response for valid preflights, or `undefined` to fall through.
1468
- */
1435
+ * Answer a CORS preflight (`OPTIONS` carrying `Access-Control-Request-Method`)
1436
+ * for an allowlisted origin with a `204`. Returns `undefined` for non-preflight
1437
+ * requests, a disabled CORS layer, or a disallowed origin letting the request
1438
+ * fall through to normal routing.
1439
+ * @returns a `204` Response for valid preflights, or `undefined` to fall through.
1440
+ */
1469
1441
  declare const handleCorsPreflight: (request: Request, resolved: ResolvedSecurity) => Response | undefined;
1470
1442
  /**
1471
- * Apply baseline security headers and (for allowed cross-origin requests) CORS
1472
- * headers to an outgoing response, without overwriting anything the inner
1473
- * handler already set.
1474
- *
1475
- * WebSocket upgrade responses (`status 101` / a `webSocket` field) are returned
1476
- * untouched: re-wrapping them in a new `Response` would drop the socket and the
1477
- * hibernation handshake.
1478
- */
1443
+ * Apply baseline security headers and (for allowed cross-origin requests) CORS
1444
+ * headers to an outgoing response, without overwriting anything the inner
1445
+ * handler already set.
1446
+ *
1447
+ * WebSocket upgrade responses (`status 101` / a `webSocket` field) are returned
1448
+ * untouched: re-wrapping them in a new `Response` would drop the socket and the
1449
+ * hibernation handshake.
1450
+ */
1479
1451
  declare const decorateResponse: (response: Response, request: Request, resolved: ResolvedSecurity) => Response;
1480
1452
  /**
1481
- * Wire-format RPC envelope. Posted to `POST /_lunora/rpc`.
1482
- *
1483
- * `functionPath` is the `&lt;file>:&lt;function>` identifier emitted by codegen,
1484
- * e.g. `"messages:list"`. `shardKey` is optional — when omitted the runtime
1485
- * routes to {@link WorkerOptions.defaultShardKey} (default `"__root__"`).
1486
- *
1487
- * `fanOut` opts the envelope into cross-shard routing via the
1488
- * {@link WorkerOptions.queryCoordinator}; mutually exclusive with
1489
- * `shardKey` (specifying both is a 400 — fan-out *is* the shard choice).
1490
- */
1453
+ * Wire-format RPC envelope. Posted to `POST /_lunora/rpc`.
1454
+ *
1455
+ * `functionPath` is the `&lt;file>:&lt;function>` identifier emitted by codegen,
1456
+ * e.g. `"messages:list"`. `shardKey` is optional — when omitted the runtime
1457
+ * routes to {@link WorkerOptions.defaultShardKey} (default `"__root__"`).
1458
+ *
1459
+ * `fanOut` opts the envelope into cross-shard routing via the
1460
+ * {@link WorkerOptions.queryCoordinator}; mutually exclusive with
1461
+ * `shardKey` (specifying both is a 400 — fan-out *is* the shard choice).
1462
+ */
1491
1463
  interface RpcEnvelope {
1492
1464
  args?: Record<string, unknown>;
1493
1465
  fanOut?: FanOutSpec;
@@ -1496,14 +1468,14 @@ interface RpcEnvelope {
1496
1468
  }
1497
1469
  type Route = (request: Request, env: unknown, context: ExecutionContextLike) => Promise<Response> | Response;
1498
1470
  /**
1499
- * Context handed to HTTP-action handlers. Built per request by the worker; its
1500
- * `run*` methods forward an RPC envelope to the shard, so handlers reach
1501
- * queries/mutations/actions without a direct DB binding.
1502
- *
1503
- * `reference` is typed `unknown` so this structural contract stays free of a
1504
- * `@lunora/server` dependency while remaining assignable from the fully-typed
1505
- * `HttpActionCtx` on the server side (`{ __lunoraRef }` is read at runtime).
1506
- */
1471
+ * Context handed to HTTP-action handlers. Built per request by the worker; its
1472
+ * `run*` methods forward an RPC envelope to the shard, so handlers reach
1473
+ * queries/mutations/actions without a direct DB binding.
1474
+ *
1475
+ * `reference` is typed `unknown` so this structural contract stays free of a
1476
+ * `@lunora/server` dependency while remaining assignable from the fully-typed
1477
+ * `HttpActionCtx` on the server side (`{ __lunoraRef }` is read at runtime).
1478
+ */
1507
1479
  interface HttpActionContext {
1508
1480
  auth: {
1509
1481
  getIdentity: () => Promise<Record<string, unknown> | null>;
@@ -1524,20 +1496,20 @@ interface HttpActionLike {
1524
1496
  handler: (context: HttpActionContext, request: Request) => Promise<Response> | Response;
1525
1497
  }
1526
1498
  /**
1527
- * Structural view of `@lunora/server`'s `httpRouter()`. The worker dispatches by
1528
- * calling `fetch` — the same shape as a hono app's `app.fetch` — so the runtime
1529
- * stays free of a hard dependency on the server package (and on hono). The
1530
- * per-request {@link HttpActionContext} is injected on the `__lunoraCtx` env
1531
- * binding; the router lifts it into the handler's context.
1532
- */
1499
+ * Structural view of `@lunora/server`'s `httpRouter()`. The worker dispatches by
1500
+ * calling `fetch` — the same shape as a hono app's `app.fetch` — so the runtime
1501
+ * stays free of a hard dependency on the server package (and on hono). The
1502
+ * per-request {@link HttpActionContext} is injected on the `__lunoraCtx` env
1503
+ * binding; the router lifts it into the handler's context.
1504
+ */
1533
1505
  interface HttpRouterLike {
1534
1506
  fetch(request: Request, env?: unknown, context?: ExecutionContextLike): Promise<Response> | Response;
1535
1507
  }
1536
1508
  /**
1537
- * Per-table sharding metadata the admin import endpoint needs to route rows.
1538
- * Structural so this package stays free of `@lunora/server`. The codegen-
1539
- * generated worker entry passes a thin projection of the user's schema.
1540
- */
1509
+ * Per-table sharding metadata the admin import endpoint needs to route rows.
1510
+ * Structural so this package stays free of `@lunora/server`. The codegen-
1511
+ * generated worker entry passes a thin projection of the user's schema.
1512
+ */
1541
1513
  interface ShardingInfo {
1542
1514
  /** `global` when the table lives in D1; `shardBy` when keyed by a field; `root` (or absent) otherwise. */
1543
1515
  mode: {
@@ -1546,15 +1518,15 @@ interface ShardingInfo {
1546
1518
  };
1547
1519
  }
1548
1520
  /**
1549
- * Lookup the runtime uses to bucket an import row to its owning shard. Returns
1550
- * `undefined` for unknown tables — the row is reported as a hard error.
1551
- */
1521
+ * Lookup the runtime uses to bucket an import row to its owning shard. Returns
1522
+ * `undefined` for unknown tables — the row is reported as a hard error.
1523
+ */
1552
1524
  type AdminTableResolver = (table: string) => ShardingInfo | undefined;
1553
1525
  /**
1554
- * Streamed bulk export of `.global()` tables, materialised as an async iterable
1555
- * of `{table, doc}` rows. The runtime concatenates this stream after the
1556
- * shard-local stream so the receiver sees a single NDJSON body.
1557
- */
1526
+ * Streamed bulk export of `.global()` tables, materialised as an async iterable
1527
+ * of `{table, doc}` rows. The runtime concatenates this stream after the
1528
+ * shard-local stream so the receiver sees a single NDJSON body.
1529
+ */
1558
1530
  type GlobalExportFunction = (request: {
1559
1531
  tables: ReadonlyArray<string>;
1560
1532
  }) => AsyncIterable<{
@@ -1562,10 +1534,10 @@ type GlobalExportFunction = (request: {
1562
1534
  table: string;
1563
1535
  }>;
1564
1536
  /**
1565
- * Read a page of the `.global()` (D1) change-data-capture log past `sinceSeq`
1566
- * for the admin sync endpoint. Wire it to `@lunora/d1`'s `readD1CdcChanges`.
1567
- * When omitted, the sync endpoint returns only shard-local changes.
1568
- */
1537
+ * Read a page of the `.global()` (D1) change-data-capture log past `sinceSeq`
1538
+ * for the admin sync endpoint. Wire it to `@lunora/d1`'s `readD1CdcChanges`.
1539
+ * When omitted, the sync endpoint returns only shard-local changes.
1540
+ */
1569
1541
  type GlobalCdcSyncFunction = (request: {
1570
1542
  limit?: number;
1571
1543
  sinceSeq: number;
@@ -1574,24 +1546,24 @@ type GlobalCdcSyncFunction = (request: {
1574
1546
  cursor: number;
1575
1547
  }>;
1576
1548
  /**
1577
- * Replay a batch of `.global()` (D1) CDC changes for the admin apply endpoint
1578
- * (point-in-time recovery). Wire it to `applyCdcChanges` on a D1 writer;
1579
- * returns the number applied. When omitted, the apply endpoint replays only
1580
- * shard-local changes.
1581
- */
1549
+ * Replay a batch of `.global()` (D1) CDC changes for the admin apply endpoint
1550
+ * (point-in-time recovery). Wire it to `applyCdcChanges` on a D1 writer;
1551
+ * returns the number applied. When omitted, the apply endpoint replays only
1552
+ * shard-local changes.
1553
+ */
1582
1554
  type GlobalCdcApplyFunction = (request: {
1583
1555
  changes: ReadonlyArray<Record<string, unknown>>;
1584
1556
  }) => Promise<number>;
1585
1557
  /**
1586
- * Bulk import of `.global()` rows. Returns insert counts + errors merged across
1587
- * tables.
1588
- *
1589
- * Each row carries its true physical source `line` so error attribution stays
1590
- * accurate even when global rows are interspersed with shard rows or blank lines
1591
- * in the NDJSON (a single `startLine` can't describe non-contiguous rows). The
1592
- * `startLine` field is the line of the FIRST global row, retained only as a
1593
- * backward-compatible fallback for importers that haven't adopted per-row lines.
1594
- */
1558
+ * Bulk import of `.global()` rows. Returns insert counts + errors merged across
1559
+ * tables.
1560
+ *
1561
+ * Each row carries its true physical source `line` so error attribution stays
1562
+ * accurate even when global rows are interspersed with shard rows or blank lines
1563
+ * in the NDJSON (a single `startLine` can't describe non-contiguous rows). The
1564
+ * `startLine` field is the line of the FIRST global row, retained only as a
1565
+ * backward-compatible fallback for importers that haven't adopted per-row lines.
1566
+ */
1595
1567
  type GlobalImportFunction = (request: {
1596
1568
  rows: ReadonlyArray<{
1597
1569
  doc: Record<string, unknown>;
@@ -1620,11 +1592,11 @@ interface StorageObject {
1620
1592
  size: number;
1621
1593
  }
1622
1594
  /**
1623
- * One registered function, as the discovery endpoint surfaces it. Structurally
1624
- * a subset of codegen's `RegisteredLunoraFunction` — only `kind` and
1625
- * `visibility` matter here, so the generated `LUNORA_FUNCTIONS` map satisfies
1626
- * the {@link FunctionRegistryLike} value shape.
1627
- */
1595
+ * One registered function, as the discovery endpoint surfaces it. Structurally
1596
+ * a subset of codegen's `RegisteredLunoraFunction` — only `kind` and
1597
+ * `visibility` matter here, so the generated `LUNORA_FUNCTIONS` map satisfies
1598
+ * the {@link FunctionRegistryLike} value shape.
1599
+ */
1628
1600
  interface FunctionDescriptor {
1629
1601
  /** The function's declared argument schema, derived from its `v.*` validators. */
1630
1602
  args: FunctionArgumentDescriptor[];
@@ -1639,56 +1611,56 @@ interface FunctionRegistryEntry {
1639
1611
  /** The function's `v.*` args validator map; read structurally for the signature view. */
1640
1612
  args?: unknown;
1641
1613
  /**
1642
- * The generated registry carries `"stream"` alongside query/mutation/action;
1643
- * the discovery endpoint surfaces the latter three only (a `stream` function
1644
- * isn't runnable from the function runner), but accepting the kind here lets
1645
- * callers pass the generated `LUNORA_FUNCTIONS` map without a cast.
1646
- */
1614
+ * The generated registry carries `"stream"` alongside query/mutation/action;
1615
+ * the discovery endpoint surfaces the latter three only (a `stream` function
1616
+ * isn't runnable from the function runner), but accepting the kind here lets
1617
+ * callers pass the generated `LUNORA_FUNCTIONS` map without a cast.
1618
+ */
1647
1619
  kind: "action" | "mutation" | "query" | "stream";
1648
1620
  visibility?: "internal" | "public";
1649
1621
  /**
1650
- * x402 payment tag set by the `.x402({ price })` builder modifier. Present
1651
- * only on paid public procedures; the origin worker answers an unpaid RPC
1652
- * for such a function with a real `402` challenge (via the injected
1653
- * {@link WorkerOptions.x402Charge} gate) before dispatching, then verifies +
1654
- * settles at the origin boundary so the shard never sees payment state.
1655
- * Rides along on the registered function object's identity — codegen casts
1656
- * the real `fn` into `LUNORA_FUNCTIONS`, so reading it needs no change to the
1657
- * generated shape (same as `fn.rls`).
1658
- */
1622
+ * x402 payment tag set by the `.x402({ price })` builder modifier. Present
1623
+ * only on paid public procedures; the origin worker answers an unpaid RPC
1624
+ * for such a function with a real `402` challenge (via the injected
1625
+ * {@link WorkerOptions.x402Charge} gate) before dispatching, then verifies +
1626
+ * settles at the origin boundary so the shard never sees payment state.
1627
+ * Rides along on the registered function object's identity — codegen casts
1628
+ * the real `fn` into `LUNORA_FUNCTIONS`, so reading it needs no change to the
1629
+ * generated shape (same as `fn.rls`).
1630
+ */
1659
1631
  x402?: {
1660
1632
  readonly price: number | string;
1661
1633
  };
1662
1634
  }
1663
1635
  /**
1664
- * The generated `LUNORA_FUNCTIONS` dispatch table, narrowed to what the
1665
- * discovery endpoint reads. Pass the map straight from `_generated/functions.ts`.
1666
- */
1636
+ * The generated `LUNORA_FUNCTIONS` dispatch table, narrowed to what the
1637
+ * discovery endpoint reads. Pass the map straight from `_generated/functions.ts`.
1638
+ */
1667
1639
  type FunctionRegistryLike = Record<string, FunctionRegistryEntry>;
1668
1640
  /**
1669
- * Injected x402 charge gate — the seam that paywalls a `.x402({ price })`-tagged
1670
- * procedure at the origin worker without the runtime importing `@lunora/x402`
1671
- * (which would pull viem/solana into every worker bundle). Build it with
1672
- * `createProcedureChargeGate(config)` from `@lunora/x402/charge` and pass it as
1673
- * {@link WorkerOptions.x402Charge}.
1674
- *
1675
- * Given the inbound `request`, the paid procedure's `spec` (its `functionPath` —
1676
- * used as the x402 challenge `resource` — and USD `price`), and a `dispatch`
1677
- * that runs the real shard forward, it returns a real `402` + `PAYMENT-REQUIRED`
1678
- * challenge when the request is unpaid, or the dispatched response (with
1679
- * `X-PAYMENT-RESPONSE` attached) once the client's `X-PAYMENT` is verified and
1680
- * settled. `dispatch` runs only after payment is verified — an unpaid or
1681
- * invalid request never reaches the shard.
1682
- */
1641
+ * Injected x402 charge gate — the seam that paywalls a `.x402({ price })`-tagged
1642
+ * procedure at the origin worker without the runtime importing `@lunora/x402`
1643
+ * (which would pull viem/solana into every worker bundle). Build it with
1644
+ * `createProcedureChargeGate(config)` from `@lunora/x402/charge` and pass it as
1645
+ * {@link WorkerOptions.x402Charge}.
1646
+ *
1647
+ * Given the inbound `request`, the paid procedure's `spec` (its `functionPath` —
1648
+ * used as the x402 challenge `resource` — and USD `price`), and a `dispatch`
1649
+ * that runs the real shard forward, it returns a real `402` + `PAYMENT-REQUIRED`
1650
+ * challenge when the request is unpaid, or the dispatched response (with
1651
+ * `X-PAYMENT-RESPONSE` attached) once the client's `X-PAYMENT` is verified and
1652
+ * settled. `dispatch` runs only after payment is verified — an unpaid or
1653
+ * invalid request never reaches the shard.
1654
+ */
1683
1655
  type X402ChargeGate = (request: Request, spec: {
1684
1656
  functionPath: string;
1685
1657
  price: number | string;
1686
1658
  }, dispatch: () => Promise<Response>) => Promise<Response>;
1687
1659
  /**
1688
- * Lists objects in the storage bucket for the admin file browser. Structurally
1689
- * compatible with `@lunora/storage`'s `Storage["list"]` — the runtime stays free
1690
- * of a hard dependency on the storage package.
1691
- */
1660
+ * Lists objects in the storage bucket for the admin file browser. Structurally
1661
+ * compatible with `@lunora/storage`'s `Storage["list"]` — the runtime stays free
1662
+ * of a hard dependency on the storage package.
1663
+ */
1692
1664
  type StorageListFunction = (prefix?: string, options?: {
1693
1665
  bucket?: string;
1694
1666
  cursor?: string;
@@ -1698,20 +1670,20 @@ type StorageListFunction = (prefix?: string, options?: {
1698
1670
  objects: StorageObject[];
1699
1671
  }>;
1700
1672
  /**
1701
- * Deletes one object from a storage bucket for the admin file browser.
1702
- * Structurally compatible with `@lunora/storage`'s `Storage["delete"]`, so
1703
- * passing `createStorage(...).delete` satisfies it. The optional `bucket` selects
1704
- * a named bucket for a multi-bucket deployment (ignored by single-bucket hosts).
1705
- */
1673
+ * Deletes one object from a storage bucket for the admin file browser.
1674
+ * Structurally compatible with `@lunora/storage`'s `Storage["delete"]`, so
1675
+ * passing `createStorage(...).delete` satisfies it. The optional `bucket` selects
1676
+ * a named bucket for a multi-bucket deployment (ignored by single-bucket hosts).
1677
+ */
1706
1678
  type StorageDeleteFunction = (key: string, options?: {
1707
1679
  bucket?: string;
1708
1680
  }) => Promise<void> | void;
1709
1681
  /**
1710
- * Uploads one object to a storage bucket for the admin file browser. Mirrors
1711
- * `@lunora/storage`'s `Storage["upload"]` (only the bits the admin endpoint
1712
- * needs): the key, the raw bytes, an optional content-type, and an optional
1713
- * target `bucket` for multi-bucket deployments.
1714
- */
1682
+ * Uploads one object to a storage bucket for the admin file browser. Mirrors
1683
+ * `@lunora/storage`'s `Storage["upload"]` (only the bits the admin endpoint
1684
+ * needs): the key, the raw bytes, an optional content-type, and an optional
1685
+ * target `bucket` for multi-bucket deployments.
1686
+ */
1715
1687
  type StorageUploadFunction = (key: string, body: ArrayBuffer, options?: {
1716
1688
  bucket?: string;
1717
1689
  contentType?: string;
@@ -1723,11 +1695,11 @@ type StorageUploadFunction = (key: string, body: ArrayBuffer, options?: {
1723
1695
  key: string;
1724
1696
  };
1725
1697
  /**
1726
- * Mints a (signed or public) URL for one object so the admin file browser can
1727
- * offer a "copy URL" action. The optional `expiresInSeconds` lets the caller pick
1728
- * a share-link lifetime (the host clamps it); `bucket` selects a named bucket.
1729
- * Structurally compatible with `@lunora/storage`'s `Storage["getSignedUrl"]`.
1730
- */
1698
+ * Mints a (signed or public) URL for one object so the admin file browser can
1699
+ * offer a "copy URL" action. The optional `expiresInSeconds` lets the caller pick
1700
+ * a share-link lifetime (the host clamps it); `bucket` selects a named bucket.
1701
+ * Structurally compatible with `@lunora/storage`'s `Storage["getSignedUrl"]`.
1702
+ */
1731
1703
  type StorageSignedUrlFunction = (key: string, options?: {
1732
1704
  bucket?: string;
1733
1705
  expiresInSeconds?: number;
@@ -1759,11 +1731,11 @@ interface GlobalFacetResult {
1759
1731
  }[];
1760
1732
  }
1761
1733
  /**
1762
- * Introspect `.global()` (D1-backed) tables for the data browser. Structurally
1763
- * compatible with `@lunora/d1`'s `listGlobalTables` / `readGlobalTablePage` /
1764
- * `facetGlobalColumn` (curried with the D1 exec + schema) — the runtime stays
1765
- * free of a hard dependency on the D1 package.
1766
- */
1734
+ * Introspect `.global()` (D1-backed) tables for the data browser. Structurally
1735
+ * compatible with `@lunora/d1`'s `listGlobalTables` / `readGlobalTablePage` /
1736
+ * `facetGlobalColumn` (curried with the D1 exec + schema) — the runtime stays
1737
+ * free of a hard dependency on the D1 package.
1738
+ */
1767
1739
  interface GlobalIntrospector {
1768
1740
  facetColumn: (options: {
1769
1741
  column: string;
@@ -1780,12 +1752,12 @@ interface GlobalIntrospector {
1780
1752
  }) => Promise<GlobalTablePage>;
1781
1753
  }
1782
1754
  /**
1783
- * One vector index as the studio's vector browser lists it: the static schema
1784
- * metadata (name/table/field/dimensions/metric/metadata) merged with the live
1785
- * Vectorize `describe()` stats (`vectorsCount`, processing watermark) when the
1786
- * binding is reachable. The live fields are optional so a never-bound index
1787
- * still lists with its declared shape.
1788
- */
1755
+ * One vector index as the studio's vector browser lists it: the static schema
1756
+ * metadata (name/table/field/dimensions/metric/metadata) merged with the live
1757
+ * Vectorize `describe()` stats (`vectorsCount`, processing watermark) when the
1758
+ * binding is reachable. The live fields are optional so a never-bound index
1759
+ * still lists with its declared shape.
1760
+ */
1789
1761
  interface VectorIndexSummary {
1790
1762
  dimensions?: number;
1791
1763
  field?: string;
@@ -1805,13 +1777,13 @@ interface VectorQueryMatch {
1805
1777
  score: number;
1806
1778
  }
1807
1779
  /**
1808
- * Introspect Vectorize indexes for the studio's vector browser. Built in the
1809
- * worker entry from the generated `LUNORA_VECTOR_INDEXES` registry (Vectorize
1810
- * cannot enumerate indexes at runtime) paired with the env bindings + the
1811
- * schema's per-index embedders. `queryIndex` is optional: an index with no
1812
- * embedder (a `select`-derived Shape B index, or a deployment that withholds the
1813
- * embedder) lists but cannot be similarity-queried from the studio.
1814
- */
1780
+ * Introspect Vectorize indexes for the studio's vector browser. Built in the
1781
+ * worker entry from the generated `LUNORA_VECTOR_INDEXES` registry (Vectorize
1782
+ * cannot enumerate indexes at runtime) paired with the env bindings + the
1783
+ * schema's per-index embedders. `queryIndex` is optional: an index with no
1784
+ * embedder (a `select`-derived Shape B index, or a deployment that withholds the
1785
+ * embedder) lists but cannot be similarity-queried from the studio.
1786
+ */
1815
1787
  interface VectorIntrospector {
1816
1788
  listIndexes: () => Promise<VectorIndexSummary[]>;
1817
1789
  queryIndex?: (options: {
@@ -1823,57 +1795,57 @@ interface VectorIntrospector {
1823
1795
  }>;
1824
1796
  }
1825
1797
  /**
1826
- * Cron controller handed to the worker's `scheduled()` entry by the Workers
1827
- * runtime. `cron` is the exact trigger expression that fired (matched against
1828
- * {@link WorkerOptions.crons} keys and {@link WorkerOptions.backupCron});
1829
- * `scheduledTime` is the firing time in epoch-ms, used as the backup id so the
1830
- * snapshot is named after the moment it represents rather than wall-clock skew.
1831
- */
1798
+ * Cron controller handed to the worker's `scheduled()` entry by the Workers
1799
+ * runtime. `cron` is the exact trigger expression that fired (matched against
1800
+ * {@link WorkerOptions.crons} keys and {@link WorkerOptions.backupCron});
1801
+ * `scheduledTime` is the firing time in epoch-ms, used as the backup id so the
1802
+ * snapshot is named after the moment it represents rather than wall-clock skew.
1803
+ */
1832
1804
  interface ScheduledControllerLike {
1833
1805
  cron: string;
1834
1806
  noRetry?: () => void;
1835
1807
  scheduledTime: number;
1836
1808
  }
1837
1809
  /**
1838
- * A cron-trigger handler registered on {@link WorkerOptions.crons}. The worker's
1839
- * `scheduled()` entry invokes the handler whose map key equals the firing
1840
- * trigger's `cron` expression. Runs server-side with no end-user identity.
1841
- */
1810
+ * A cron-trigger handler registered on {@link WorkerOptions.crons}. The worker's
1811
+ * `scheduled()` entry invokes the handler whose map key equals the firing
1812
+ * trigger's `cron` expression. Runs server-side with no end-user identity.
1813
+ */
1842
1814
  type CronHandler = (controller: ScheduledControllerLike, env: unknown, context: ExecutionContextLike) => Promise<void> | void;
1843
1815
  /**
1844
- * A Cloudflare Queues push-consumer handler — the worker's `queue()` entry
1845
- * forwards each delivered `MessageBatch` (typed `unknown` here to keep the
1846
- * runtime decoupled from `@lunora/queue`'s structural batch type).
1847
- */
1816
+ * A Cloudflare Queues push-consumer handler — the worker's `queue()` entry
1817
+ * forwards each delivered `MessageBatch` (typed `unknown` here to keep the
1818
+ * runtime decoupled from `@lunora/queue`'s structural batch type).
1819
+ */
1848
1820
  type QueueConsumerHandler = (batch: unknown, env: unknown, context: ExecutionContextLike) => Promise<void>;
1849
1821
  /**
1850
- * A single code-defined cron job, shaped like an entry of the generated
1851
- * `LUNORA_CRONS` map. `functionPath` is the `"namespace:fn"` to run, `args` its
1852
- * bound arguments, and `name` the human label from the `cronJobs()` builder.
1853
- * Pass the whole `LUNORA_CRONS` map as {@link WorkerOptions.cronJobs}; the worker
1854
- * dispatches each job on its firing trigger via the same authorized shard path
1855
- * as the scheduler.
1856
- */
1822
+ * A single code-defined cron job, shaped like an entry of the generated
1823
+ * `LUNORA_CRONS` map. `functionPath` is the `"namespace:fn"` to run, `args` its
1824
+ * bound arguments, and `name` the human label from the `cronJobs()` builder.
1825
+ * Pass the whole `LUNORA_CRONS` map as {@link WorkerOptions.cronJobs}; the worker
1826
+ * dispatches each job on its firing trigger via the same authorized shard path
1827
+ * as the scheduler.
1828
+ */
1857
1829
  interface CronJobDispatch {
1858
1830
  args?: Record<string, unknown>;
1859
1831
  functionPath?: string;
1860
1832
  name: string;
1861
1833
  shardKey?: string;
1862
1834
  /**
1863
- * Set when the job targets a durable workflow instead of a function: the
1864
- * `WORKFLOW_*` binding name on `env`. On a firing trigger the worker starts a
1865
- * NEW workflow instance (the {@link CronJobDispatch.args} become its
1866
- * `params`) rather than dispatching {@link CronJobDispatch.functionPath} to a
1867
- * shard. Mutually exclusive with `functionPath`.
1868
- */
1835
+ * Set when the job targets a durable workflow instead of a function: the
1836
+ * `WORKFLOW_*` binding name on `env`. On a firing trigger the worker starts a
1837
+ * NEW workflow instance (the {@link CronJobDispatch.args} become its
1838
+ * `params`) rather than dispatching {@link CronJobDispatch.functionPath} to a
1839
+ * shard. Mutually exclusive with `functionPath`.
1840
+ */
1869
1841
  workflow?: string;
1870
1842
  }
1871
1843
  /**
1872
- * One scheduled cron invocation as the discovery endpoint surfaces it: a
1873
- * {@link CronJobDispatch} flattened together with the `cron` expression that
1874
- * fires it. Cloudflare exposes no runtime cron introspection, so the injected
1875
- * `cronJobs` map is the only source of truth; the studio renders these read-only.
1876
- */
1844
+ * One scheduled cron invocation as the discovery endpoint surfaces it: a
1845
+ * {@link CronJobDispatch} flattened together with the `cron` expression that
1846
+ * fires it. Cloudflare exposes no runtime cron introspection, so the injected
1847
+ * `cronJobs` map is the only source of truth; the studio renders these read-only.
1848
+ */
1877
1849
  interface CronJobInfo {
1878
1850
  args?: Record<string, unknown>;
1879
1851
  /** The compiled cron expression, e.g. `"0 9 * * *"`. */
@@ -1885,12 +1857,12 @@ interface CronJobInfo {
1885
1857
  workflow?: string;
1886
1858
  }
1887
1859
  /**
1888
- * R2-like sink for scheduled backups. Structurally a subset of `@lunora/storage`'s
1889
- * `R2BucketLike` (and of the raw R2 binding), so passing `env.BACKUPS` straight
1890
- * through satisfies it. `put` writes the NDJSON snapshot and its manifest
1891
- * sidecar; `list`/`delete` drive retention pruning when
1892
- * {@link WorkerOptions.backupRetain} is set.
1893
- */
1860
+ * R2-like sink for scheduled backups. Structurally a subset of `@lunora/storage`'s
1861
+ * `R2BucketLike` (and of the raw R2 binding), so passing `env.BACKUPS` straight
1862
+ * through satisfies it. `put` writes the NDJSON snapshot and its manifest
1863
+ * sidecar; `list`/`delete` drive retention pruning when
1864
+ * {@link WorkerOptions.backupRetain} is set.
1865
+ */
1894
1866
  interface BackupStore {
1895
1867
  delete: (key: string) => Promise<unknown>;
1896
1868
  list: (options?: {
@@ -1912,11 +1884,11 @@ interface BackupStore {
1912
1884
  }) => Promise<unknown>;
1913
1885
  }
1914
1886
  /**
1915
- * Manifest sidecar written next to each scheduled backup's NDJSON object (at
1916
- * `&lt;file>.manifest.json`). Mirrors the manifest entry the CLI records for local
1917
- * backups so both backup planes describe a snapshot the same way;
1918
- * `cron`/`scheduledTime` additionally record which trigger produced it.
1919
- */
1887
+ * Manifest sidecar written next to each scheduled backup's NDJSON object (at
1888
+ * `&lt;file>.manifest.json`). Mirrors the manifest entry the CLI records for local
1889
+ * backups so both backup planes describe a snapshot the same way;
1890
+ * `cron`/`scheduledTime` additionally record which trigger produced it.
1891
+ */
1920
1892
  interface BackupManifest {
1921
1893
  bytes: number;
1922
1894
  createdAt: string;
@@ -1929,455 +1901,455 @@ interface BackupManifest {
1929
1901
  }
1930
1902
  interface WorkerOptions {
1931
1903
  /**
1932
- * An additional, async authorization gate for the `/_lunora/admin/*` plane
1933
- * (the Studio's HTTP + WS endpoints), OR-ed with the static {@link WorkerOptions.adminToken}
1934
- * bearer. When it resolves `true` for a request, that request is treated as
1935
- * admin-authorized even without the bearer; when it resolves `false` (or is
1936
- * unset) the bearer remains the only path. Evaluated once per admin request
1937
- * and never on the RPC/WebSocket data hot path.
1938
- *
1939
- * The intended producer is `@lunora/cloudflare-access`'s `accessAdminGate(...)`,
1940
- * which verifies the request's `Cf-Access-Jwt-Assertion` JWT and applies an
1941
- * `isAdmin(claims)` predicate — so the Studio can sit behind Cloudflare Access
1942
- * instead of (or alongside) a shared admin token. It takes only the request
1943
- * (verification needs static team-domain/aud config + the remote JWKS, no env
1944
- * binding), so it composes without threading async through every admin route.
1945
- */
1904
+ * An additional, async authorization gate for the `/_lunora/admin/*` plane
1905
+ * (the Studio's HTTP + WS endpoints), OR-ed with the static {@link WorkerOptions.adminToken}
1906
+ * bearer. When it resolves `true` for a request, that request is treated as
1907
+ * admin-authorized even without the bearer; when it resolves `false` (or is
1908
+ * unset) the bearer remains the only path. Evaluated once per admin request
1909
+ * and never on the RPC/WebSocket data hot path.
1910
+ *
1911
+ * The intended producer is `@lunora/cloudflare-access`'s `accessAdminGate(...)`,
1912
+ * which verifies the request's `Cf-Access-Jwt-Assertion` JWT and applies an
1913
+ * `isAdmin(claims)` predicate — so the Studio can sit behind Cloudflare Access
1914
+ * instead of (or alongside) a shared admin token. It takes only the request
1915
+ * (verification needs static team-domain/aud config + the remote JWKS, no env
1916
+ * binding), so it composes without threading async through every admin route.
1917
+ */
1946
1918
  adminGate?: (request: Request) => boolean | Promise<boolean>;
1947
1919
  /**
1948
- * Admin bearer token expected by the export/import endpoints. When unset,
1949
- * the endpoints respond with `ADMIN_FORBIDDEN` — the same posture the
1950
- * per-shard admin gate uses.
1951
- */
1920
+ * Admin bearer token expected by the export/import endpoints. When unset,
1921
+ * the endpoints respond with `ADMIN_FORBIDDEN` — the same posture the
1922
+ * per-shard admin gate uses.
1923
+ */
1952
1924
  adminToken?: string;
1953
1925
  /**
1954
- * Opt into an authorization-open posture for sharded and fan-out access.
1955
- *
1956
- * By default (this flag unset/`false`) the runtime FAILS CLOSED per
1957
- * operation: naming a non-default shard (a potential cross-tenant hop) is
1958
- * rejected with a `403` (`FORBIDDEN_SHARD`) unless
1959
- * {@link WorkerOptions.authorizeShard} is configured, and a fan-out
1960
- * envelope is rejected (`FORBIDDEN_FANOUT`) unless
1961
- * {@link WorkerOptions.authorizeFanOut} is. Set this to `true` to allow
1962
- * such requests from any caller (including unauthenticated ones) —
1963
- * appropriate only when every table is protected by per-row RLS. The
1964
- * runtime then emits a single `console.warn` so the open posture stays
1965
- * visible in logs. The flag is consulted per operation: it has no effect
1966
- * on an operation whose own `authorize*` callback is configured (that
1967
- * callback gates directly), but configuring only one of the two callbacks
1968
- * does NOT cover the other operation.
1969
- *
1970
- * NOTE: this is a behaviour change from earlier alphas, where the same
1971
- * situation was warn-once-then-allow. Apps that relied on client-chosen
1972
- * shard keys without an `authorize*` callback must set this flag explicitly.
1973
- */
1926
+ * Opt into an authorization-open posture for sharded and fan-out access.
1927
+ *
1928
+ * By default (this flag unset/`false`) the runtime FAILS CLOSED per
1929
+ * operation: naming a non-default shard (a potential cross-tenant hop) is
1930
+ * rejected with a `403` (`FORBIDDEN_SHARD`) unless
1931
+ * {@link WorkerOptions.authorizeShard} is configured, and a fan-out
1932
+ * envelope is rejected (`FORBIDDEN_FANOUT`) unless
1933
+ * {@link WorkerOptions.authorizeFanOut} is. Set this to `true` to allow
1934
+ * such requests from any caller (including unauthenticated ones) —
1935
+ * appropriate only when every table is protected by per-row RLS. The
1936
+ * runtime then emits a single `console.warn` so the open posture stays
1937
+ * visible in logs. The flag is consulted per operation: it has no effect
1938
+ * on an operation whose own `authorize*` callback is configured (that
1939
+ * callback gates directly), but configuring only one of the two callbacks
1940
+ * does NOT cover the other operation.
1941
+ *
1942
+ * NOTE: this is a behaviour change from earlier alphas, where the same
1943
+ * situation was warn-once-then-allow. Apps that relied on client-chosen
1944
+ * shard keys without an `authorize*` callback must set this flag explicitly.
1945
+ */
1974
1946
  allowUnauthenticatedShardAccess?: boolean;
1975
1947
  /**
1976
- * Replay `.global()` (D1) CDC changes for the admin apply endpoint
1977
- * (point-in-time recovery). When omitted, apply covers only shard-local tables.
1978
- */
1948
+ * Replay `.global()` (D1) CDC changes for the admin apply endpoint
1949
+ * (point-in-time recovery). When omitted, apply covers only shard-local tables.
1950
+ */
1979
1951
  applyGlobals?: GlobalCdcApplyFunction;
1980
1952
  /**
1981
- * The auth user-management plane backing the studio's users dashboard:
1982
- * browse via `GET /_lunora/admin/auth/users` + `/sessions`, and (when the
1983
- * implementation provides the optional mutations) create/ban/role/revoke/
1984
- * delete/impersonate via the matching admin-gated `POST /_lunora/admin/auth/*`
1985
- * routes. Wire it with `@lunora/auth`'s `createAuthAdmin(auth)`. Omit it and
1986
- * every `/auth/*` endpoint responds `AUTH_NOT_CONFIGURED`.
1987
- */
1953
+ * The auth user-management plane backing the studio's users dashboard:
1954
+ * browse via `GET /_lunora/admin/auth/users` + `/sessions`, and (when the
1955
+ * implementation provides the optional mutations) create/ban/role/revoke/
1956
+ * delete/impersonate via the matching admin-gated `POST /_lunora/admin/auth/*`
1957
+ * routes. Wire it with `@lunora/auth`'s `createAuthAdmin(auth)`. Omit it and
1958
+ * every `/auth/*` endpoint responds `AUTH_NOT_CONFIGURED`.
1959
+ */
1988
1960
  authAdmin?: AuthAdmin;
1989
1961
  /**
1990
- * Base path the auth routes are mounted under (default `/api/auth`). Used
1991
- * to classify which inbound paths are auth ATTEMPTS for the app-level
1992
- * auth-failure SLO signal (PLAN3 §2.3) — see {@link WorkerOptions.authHandler}.
1993
- * Only meaningful alongside `authHandler`.
1994
- */
1962
+ * Base path the auth routes are mounted under (default `/api/auth`). Used
1963
+ * to classify which inbound paths are auth ATTEMPTS for the app-level
1964
+ * auth-failure SLO signal (PLAN3 §2.3) — see {@link WorkerOptions.authHandler}.
1965
+ * Only meaningful alongside `authHandler`.
1966
+ */
1995
1967
  authBasePath?: string;
1996
1968
  /**
1997
- * Optional prebound `@lunora/auth` handler (`handleAuthRequest(auth, …)`
1998
- * with its `auth` argument already bound) the worker dispatches BEFORE its
1999
- * own routing — auth runs as a top-level `/api/auth/*` route, not through
2000
- * lunora functions. It returns a `Response` for an auth route and
2001
- * `undefined` to let the request fall through to the worker.
2002
- *
2003
- * Wiring it here (rather than in the host entry) lets the runtime instrument
2004
- * it for the app-level auth-failure SLO (PLAN3 §2.3): after the handler
2005
- * answers a genuine auth ATTEMPT route (sign-in / sign-up / callback under
2006
- * {@link WorkerOptions.authBasePath}), the worker fires a fire-and-forget
2007
- * `recordAuthEvent` against the root shard via `ctx.waitUntil` — classifying
2008
- * the outcome by status (`≥ 400` ⇒ `fail`). The recording never blocks or
2009
- * fails the auth response, and is skipped silently when no admin token or
2010
- * shard namespace is configured (the SLO signal is simply absent).
2011
- *
2012
- * Omit it and the host keeps calling `handleAuthRequest` itself; the SLO
2013
- * signal is then absent but auth behaves identically.
2014
- */
1969
+ * Optional prebound `@lunora/auth` handler (`handleAuthRequest(auth, …)`
1970
+ * with its `auth` argument already bound) the worker dispatches BEFORE its
1971
+ * own routing — auth runs as a top-level `/api/auth/*` route, not through
1972
+ * lunora functions. It returns a `Response` for an auth route and
1973
+ * `undefined` to let the request fall through to the worker.
1974
+ *
1975
+ * Wiring it here (rather than in the host entry) lets the runtime instrument
1976
+ * it for the app-level auth-failure SLO (PLAN3 §2.3): after the handler
1977
+ * answers a genuine auth ATTEMPT route (sign-in / sign-up / callback under
1978
+ * {@link WorkerOptions.authBasePath}), the worker fires a fire-and-forget
1979
+ * `recordAuthEvent` against the root shard via `ctx.waitUntil` — classifying
1980
+ * the outcome by status (`≥ 400` ⇒ `fail`). The recording never blocks or
1981
+ * fails the auth response, and is skipped silently when no admin token or
1982
+ * shard namespace is configured (the SLO signal is simply absent).
1983
+ *
1984
+ * Omit it and the host keeps calling `handleAuthRequest` itself; the SLO
1985
+ * signal is then absent but auth behaves identically.
1986
+ */
2015
1987
  authHandler?: (request: Request) => Promise<Response | undefined>;
2016
1988
  /**
2017
- * Optional table-level authorization callback for fan-out RPC envelopes.
2018
- * Called after `resolveIdentity` and before `coordinator.fanOut` walks
2019
- * the registry. Returning `false` rejects the request with 403
2020
- * `FORBIDDEN_FANOUT`. When unset, fan-out is denied by default
2021
- * whenever {@link WorkerOptions.authorizeShard} is configured — fan-out is a
2022
- * privileged operation (it dispatches the caller's function across
2023
- * every live shard for the table) and a per-shard gate is not
2024
- * sufficient to authorize it. Apps that need client-driven fan-out
2025
- * must opt in explicitly via this callback.
2026
- */
1989
+ * Optional table-level authorization callback for fan-out RPC envelopes.
1990
+ * Called after `resolveIdentity` and before `coordinator.fanOut` walks
1991
+ * the registry. Returning `false` rejects the request with 403
1992
+ * `FORBIDDEN_FANOUT`. When unset, fan-out is denied by default
1993
+ * whenever {@link WorkerOptions.authorizeShard} is configured — fan-out is a
1994
+ * privileged operation (it dispatches the caller's function across
1995
+ * every live shard for the table) and a per-shard gate is not
1996
+ * sufficient to authorize it. Apps that need client-driven fan-out
1997
+ * must opt in explicitly via this callback.
1998
+ */
2027
1999
  authorizeFanOut?: (identity: ResolvedIdentity | null, table: string, functionPath: string) => boolean | Promise<boolean>;
2028
2000
  /**
2029
- * Optional per-shard authorization callback. Called from both the RPC
2030
- * dispatch path and the WebSocket upgrade path after `resolveIdentity`
2031
- * has produced an identity but before the request is forwarded to the
2032
- * named shard. Returning `false` (or a promise resolving to `false`)
2033
- * causes the runtime to reject the request with a 403
2034
- * `FORBIDDEN_SHARD` error. When unset, the runtime allows the
2035
- * request — preserving the historical "any client may name any
2036
- * shard" posture.
2037
- *
2038
- * Note: this callback does NOT gate fan-out envelopes — fan-out
2039
- * targets every live shard for a table and must be authorized at the
2040
- * table level via {@link WorkerOptions.authorizeFanOut}. Configuring this callback
2041
- * without `authorizeFanOut` causes fan-out envelopes to be denied by
2042
- * default.
2043
- */
2001
+ * Optional per-shard authorization callback. Called from both the RPC
2002
+ * dispatch path and the WebSocket upgrade path after `resolveIdentity`
2003
+ * has produced an identity but before the request is forwarded to the
2004
+ * named shard. Returning `false` (or a promise resolving to `false`)
2005
+ * causes the runtime to reject the request with a 403
2006
+ * `FORBIDDEN_SHARD` error. When unset, the runtime allows the
2007
+ * request — preserving the historical "any client may name any
2008
+ * shard" posture.
2009
+ *
2010
+ * Note: this callback does NOT gate fan-out envelopes — fan-out
2011
+ * targets every live shard for a table and must be authorized at the
2012
+ * table level via {@link WorkerOptions.authorizeFanOut}. Configuring this callback
2013
+ * without `authorizeFanOut` causes fan-out envelopes to be denied by
2014
+ * default.
2015
+ */
2044
2016
  authorizeShard?: (identity: ResolvedIdentity | null, shardKey: string) => boolean | Promise<boolean>;
2045
2017
  /**
2046
- * Cron expression that triggers the built-in backup. When set alongside
2047
- * {@link WorkerOptions.backupStore} and {@link WorkerOptions.adminToken}, the
2048
- * worker's `scheduled()` entry runs a full export and writes an NDJSON
2049
- * snapshot + manifest sidecar to the backup store whenever a cron trigger
2050
- * with this exact expression fires. Must match an entry in the worker's
2051
- * wrangler `triggers.crons` (and the string is compared verbatim). Omit it
2052
- * and no automatic backup runs.
2053
- */
2018
+ * Cron expression that triggers the built-in backup. When set alongside
2019
+ * {@link WorkerOptions.backupStore} and {@link WorkerOptions.adminToken}, the
2020
+ * worker's `scheduled()` entry runs a full export and writes an NDJSON
2021
+ * snapshot + manifest sidecar to the backup store whenever a cron trigger
2022
+ * with this exact expression fires. Must match an entry in the worker's
2023
+ * wrangler `triggers.crons` (and the string is compared verbatim). Omit it
2024
+ * and no automatic backup runs.
2025
+ */
2054
2026
  backupCron?: string;
2055
2027
  /**
2056
- * Key prefix the scheduled backup writes under (default `"backups/"`). The
2057
- * NDJSON object lands at `&lt;prefix>lunora-backup-&lt;id>.ndjson` and its manifest
2058
- * at the same key plus `.manifest.json`.
2059
- */
2028
+ * Key prefix the scheduled backup writes under (default `"backups/"`). The
2029
+ * NDJSON object lands at `&lt;prefix>lunora-backup-&lt;id>.ndjson` and its manifest
2030
+ * at the same key plus `.manifest.json`.
2031
+ */
2060
2032
  backupPrefix?: string;
2061
2033
  /**
2062
- * Retention bound for scheduled backups: keep only the newest N snapshots
2063
- * under {@link WorkerOptions.backupPrefix}, pruning older NDJSON objects and
2064
- * their manifests after each run. Omit (or `0`) to keep every backup.
2065
- */
2034
+ * Retention bound for scheduled backups: keep only the newest N snapshots
2035
+ * under {@link WorkerOptions.backupPrefix}, pruning older NDJSON objects and
2036
+ * their manifests after each run. Omit (or `0`) to keep every backup.
2037
+ */
2066
2038
  backupRetain?: number;
2067
2039
  /**
2068
- * R2-like store the scheduled backup writes snapshots to. Pass the bound R2
2069
- * bucket (`env.BACKUPS`) directly — its shape satisfies {@link BackupStore}.
2070
- * Without it (or without {@link WorkerOptions.backupCron}) no automatic
2071
- * backup runs.
2072
- */
2040
+ * R2-like store the scheduled backup writes snapshots to. Pass the bound R2
2041
+ * bucket (`env.BACKUPS`) directly — its shape satisfies {@link BackupStore}.
2042
+ * Without it (or without {@link WorkerOptions.backupCron}) no automatic
2043
+ * backup runs.
2044
+ */
2073
2045
  backupStore?: BackupStore;
2074
2046
  /**
2075
- * Table allowlist for the scheduled backup. Omit to back up every table
2076
- * (shard-local + `.global()`). Mirrors the export endpoint's `tables`.
2077
- */
2047
+ * Table allowlist for the scheduled backup. Omit to back up every table
2048
+ * (shard-local + `.global()`). Mirrors the export endpoint's `tables`.
2049
+ */
2078
2050
  backupTables?: ReadonlyArray<string>;
2079
2051
  /**
2080
- * Code-defined cron jobs keyed by cron expression — pass the generated
2081
- * `LUNORA_CRONS` map directly. On a firing trigger the worker runs every job
2082
- * listed under the matching expression by dispatching its `functionPath`/`args`
2083
- * to the shard, server-side, through the same authorization as the scheduler.
2084
- * Runs alongside any {@link WorkerOptions.crons} handler and the backup.
2085
- */
2052
+ * Code-defined cron jobs keyed by cron expression — pass the generated
2053
+ * `LUNORA_CRONS` map directly. On a firing trigger the worker runs every job
2054
+ * listed under the matching expression by dispatching its `functionPath`/`args`
2055
+ * to the shard, server-side, through the same authorization as the scheduler.
2056
+ * Runs alongside any {@link WorkerOptions.crons} handler and the backup.
2057
+ */
2086
2058
  cronJobs?: Record<string, ReadonlyArray<CronJobDispatch>>;
2087
2059
  /**
2088
- * Cron-trigger handlers keyed by their exact cron expression. The worker's
2089
- * `scheduled()` entry dispatches the handler whose key equals the firing
2090
- * trigger's `cron`. Independent of the built-in backup — a handler keyed on
2091
- * the same expression as {@link WorkerOptions.backupCron} runs alongside it.
2092
- */
2060
+ * Cron-trigger handlers keyed by their exact cron expression. The worker's
2061
+ * `scheduled()` entry dispatches the handler whose key equals the firing
2062
+ * trigger's `cron`. Independent of the built-in backup — a handler keyed on
2063
+ * the same expression as {@link WorkerOptions.backupCron} runs alongside it.
2064
+ */
2093
2065
  crons?: Record<string, CronHandler>;
2094
2066
  /**
2095
- * D1 binding for `.global()` tables. Currently unused by the routing
2096
- * layer; downstream packages will read it from `env.DB` directly.
2097
- */
2067
+ * D1 binding for `.global()` tables. Currently unused by the routing
2068
+ * layer; downstream packages will read it from `env.DB` directly.
2069
+ */
2098
2070
  d1?: unknown;
2099
2071
  /** Default shard key used when an envelope omits one. */
2100
2072
  defaultShardKey?: string;
2101
2073
  /**
2102
- * Stream `.global()` rows for the admin export endpoint. When omitted,
2103
- * the export endpoint covers only shard-local tables.
2104
- */
2074
+ * Stream `.global()` rows for the admin export endpoint. When omitted,
2075
+ * the export endpoint covers only shard-local tables.
2076
+ */
2105
2077
  exportGlobals?: GlobalExportFunction;
2106
2078
  /**
2107
- * The generated `LUNORA_FUNCTIONS` map (from `_generated/functions.ts`). When
2108
- * set, the worker exposes the admin-gated `GET /_lunora/admin/functions`
2109
- * endpoint the studio uses to auto-discover queries/mutations/actions
2110
- * (internal functions are filtered out). Omit it and the endpoint responds
2111
- * `FUNCTIONS_NOT_CONFIGURED`.
2112
- */
2079
+ * The generated `LUNORA_FUNCTIONS` map (from `_generated/functions.ts`). When
2080
+ * set, the worker exposes the admin-gated `GET /_lunora/admin/functions`
2081
+ * endpoint the studio uses to auto-discover queries/mutations/actions
2082
+ * (internal functions are filtered out). Omit it and the endpoint responds
2083
+ * `FUNCTIONS_NOT_CONFIGURED`.
2084
+ */
2113
2085
  functions?: FunctionRegistryLike;
2114
2086
  /**
2115
- * Read-only introspector for `.global()` (D1) tables, backing the data
2116
- * browser's global mode via `GET /_lunora/admin/global/tables` and
2117
- * `/_lunora/admin/global/table`. Build it from `@lunora/d1`'s
2118
- * `listGlobalTables` / `readGlobalTablePage`. Omit it and those endpoints
2119
- * respond `GLOBALS_NOT_CONFIGURED`.
2120
- */
2087
+ * Read-only introspector for `.global()` (D1) tables, backing the data
2088
+ * browser's global mode via `GET /_lunora/admin/global/tables` and
2089
+ * `/_lunora/admin/global/table`. Build it from `@lunora/d1`'s
2090
+ * `listGlobalTables` / `readGlobalTablePage`. Omit it and those endpoints
2091
+ * respond `GLOBALS_NOT_CONFIGURED`.
2092
+ */
2121
2093
  globalIntrospector?: GlobalIntrospector;
2122
2094
  /**
2123
- * Router for HTTP actions (`httpRouter()` from `@lunora/server`, a hono app).
2124
- * Consulted for requests that miss the explicit {@link WorkerOptions.routes}
2125
- * map and the internal `/_lunora/*` endpoints. The runtime builds the action
2126
- * context, injects it on the `__lunoraCtx` env binding, and dispatches via
2127
- * `httpRouter.fetch`; matched handlers reach the data layer through
2128
- * `ctx.run*`, which forward to the shard. An unmatched request returns hono's
2129
- * own 404 (a path-match with the wrong verb is a 404, not a 405).
2130
- */
2095
+ * Router for HTTP actions (`httpRouter()` from `@lunora/server`, a hono app).
2096
+ * Consulted for requests that miss the explicit {@link WorkerOptions.routes}
2097
+ * map and the internal `/_lunora/*` endpoints. The runtime builds the action
2098
+ * context, injects it on the `__lunoraCtx` env binding, and dispatches via
2099
+ * `httpRouter.fetch`; matched handlers reach the data layer through
2100
+ * `ctx.run*`, which forward to the shard. An unmatched request returns hono's
2101
+ * own 404 (a path-match with the wrong verb is a 404, not a 405).
2102
+ */
2131
2103
  httpRouter?: HttpRouterLike;
2132
2104
  /**
2133
- * The declared identity claim contract (`defineIdentity(...)` from
2134
- * `@lunora/server`), passed by the generated worker entry. When present, the
2135
- * worker validates every `resolveIdentity` result against it at the trust
2136
- * boundary (on the public data paths — RPC / WebSocket / HTTP-action /
2137
- * server-query, never the admin path) *before* the claims become `ctx.auth`.
2138
- * A resolver output that violates the contract is downgraded to anonymous or
2139
- * rejected with a `401`, per the contract's `onInvalid`. Omitted → no
2140
- * validation, and the identity stays the historical untyped claim bag.
2141
- */
2105
+ * The declared identity claim contract (`defineIdentity(...)` from
2106
+ * `@lunora/server`), passed by the generated worker entry. When present, the
2107
+ * worker validates every `resolveIdentity` result against it at the trust
2108
+ * boundary (on the public data paths — RPC / WebSocket / HTTP-action /
2109
+ * server-query, never the admin path) *before* the claims become `ctx.auth`.
2110
+ * A resolver output that violates the contract is downgraded to anonymous or
2111
+ * rejected with a `401`, per the contract's `onInvalid`. Omitted → no
2112
+ * validation, and the identity stays the historical untyped claim bag.
2113
+ */
2142
2114
  identity?: IdentityContractLike;
2143
2115
  /**
2144
- * Insert `.global()` rows for the admin import endpoint. When omitted,
2145
- * rows targeting global tables are reported as hard errors.
2146
- */
2116
+ * Insert `.global()` rows for the admin import endpoint. When omitted,
2117
+ * rows targeting global tables are reported as hard errors.
2118
+ */
2147
2119
  importGlobals?: GlobalImportFunction;
2148
2120
  /**
2149
- * Restrict every Durable Object this worker reaches — shard DOs, the
2150
- * scheduler DO, the fan-out coordinator, subscriptions — to a Cloudflare
2151
- * data-residency jurisdiction (`"eu"`, `"us"`, `"fedramp"`). The runtime
2152
- * derives a jurisdiction-pinned subnamespace from {@link WorkerOptions.shardDO}
2153
- * and {@link WorkerOptions.schedulerDO} once, so all routing inherits it.
2154
- *
2155
- * Fail-closed: if the bound namespace does not expose `.jurisdiction()`
2156
- * (an older `@cloudflare/workers-types`), the worker throws rather than
2157
- * silently routing to the un-pinned global namespace. Omit it for the
2158
- * default, un-pinned behaviour.
2159
- *
2160
- * ⚠️ Set once, before the first deploy — changing it strands data. A DO name
2161
- * maps to a *different* ID per jurisdiction, so toggling this on an existing
2162
- * deployment makes every shard/scheduler call resolve to a new, empty DO; the
2163
- * prior data stays in the old jurisdiction and is unreachable (no in-place
2164
- * migration). Usually set via the schema's `.jurisdiction(...)`, which codegen
2165
- * threads here.
2166
- * @see https://developers.cloudflare.com/durable-objects/reference/data-location/
2167
- */
2121
+ * Restrict every Durable Object this worker reaches — shard DOs, the
2122
+ * scheduler DO, the fan-out coordinator, subscriptions — to a Cloudflare
2123
+ * data-residency jurisdiction (`"eu"`, `"us"`, `"fedramp"`). The runtime
2124
+ * derives a jurisdiction-pinned subnamespace from {@link WorkerOptions.shardDO}
2125
+ * and {@link WorkerOptions.schedulerDO} once, so all routing inherits it.
2126
+ *
2127
+ * Fail-closed: if the bound namespace does not expose `.jurisdiction()`
2128
+ * (an older `@cloudflare/workers-types`), the worker throws rather than
2129
+ * silently routing to the un-pinned global namespace. Omit it for the
2130
+ * default, un-pinned behaviour.
2131
+ *
2132
+ * ⚠️ Set once, before the first deploy — changing it strands data. A DO name
2133
+ * maps to a *different* ID per jurisdiction, so toggling this on an existing
2134
+ * deployment makes every shard/scheduler call resolve to a new, empty DO; the
2135
+ * prior data stays in the old jurisdiction and is unreachable (no in-place
2136
+ * migration). Usually set via the schema's `.jurisdiction(...)`, which codegen
2137
+ * threads here.
2138
+ * @see https://developers.cloudflare.com/durable-objects/reference/data-location/
2139
+ */
2168
2140
  jurisdiction?: DurableObjectJurisdiction;
2169
2141
  /**
2170
- * Introspector for Workers KV namespaces, backing the studio's KV browser
2171
- * via `GET /_lunora/admin/kv/namespaces`, `GET /_lunora/admin/kv/keys`,
2172
- * `GET|PUT|DELETE /_lunora/admin/kv/value`. Build it from the env's bound
2173
- * KV namespaces with `createKvIntrospector` from `@lunora/bindings/kv`.
2174
- * Omit it and those endpoints respond `KV_NOT_CONFIGURED`.
2175
- */
2142
+ * Introspector for Workers KV namespaces, backing the studio's KV browser
2143
+ * via `GET /_lunora/admin/kv/namespaces`, `GET /_lunora/admin/kv/keys`,
2144
+ * `GET|PUT|DELETE /_lunora/admin/kv/value`. Build it from the env's bound
2145
+ * KV namespaces with `createKvIntrospector` from `@lunora/bindings/kv`.
2146
+ * Omit it and those endpoints respond `KV_NOT_CONFIGURED`.
2147
+ */
2176
2148
  kvIntrospector?: KvIntrospector;
2177
2149
  /**
2178
- * Optional telemetry sink. When supplied, the worker emits one
2179
- * `onRpc` event per dispatched RPC (single-shard forward or fan-out)
2180
- * with duration / ok / error / shardKey or fanOut metadata. Sink
2181
- * throws are swallowed so a faulty adapter cannot break user-facing
2182
- * dispatch. See {@link ObservabilitySink}.
2183
- */
2150
+ * Optional telemetry sink. When supplied, the worker emits one
2151
+ * `onRpc` event per dispatched RPC (single-shard forward or fan-out)
2152
+ * with duration / ok / error / shardKey or fanOut metadata. Sink
2153
+ * throws are swallowed so a faulty adapter cannot break user-facing
2154
+ * dispatch. See {@link ObservabilitySink}.
2155
+ */
2184
2156
  observability?: ObservabilitySink;
2185
2157
  /**
2186
- * The generated OpenAPI 3.1 document. Import it from the codegen-emitted
2187
- * module and pass it through:
2188
- * `import { openApiSpec } from "./lunora/_generated/openapi"`. A Worker can't
2189
- * read the `_generated/openapi.json` file at runtime, so codegen also emits
2190
- * `openapi.ts` (the same document inlined as `export const openApiSpec`) for
2191
- * exactly this wiring — it regenerates on every `lunora/` change so the spec
2192
- * stays live.
2193
- *
2194
- * When set, the worker exposes the admin-gated `GET /_lunora/admin/openapi`
2195
- * endpoint the studio's API-reference view renders. The runtime does
2196
- * NOT assemble or validate the spec — it serves what the host injects verbatim.
2197
- * Omit it and the endpoint returns an empty-but-valid OpenAPI 3.1 document
2198
- * (no paths), so the studio shows a "not configured" state rather than erroring.
2199
- */
2158
+ * The generated OpenAPI 3.1 document. Import it from the codegen-emitted
2159
+ * module and pass it through:
2160
+ * `import { openApiSpec } from "./lunora/_generated/openapi"`. A Worker can't
2161
+ * read the `_generated/openapi.json` file at runtime, so codegen also emits
2162
+ * `openapi.ts` (the same document inlined as `export const openApiSpec`) for
2163
+ * exactly this wiring — it regenerates on every `lunora/` change so the spec
2164
+ * stays live.
2165
+ *
2166
+ * When set, the worker exposes the admin-gated `GET /_lunora/admin/openapi`
2167
+ * endpoint the studio's API-reference view renders. The runtime does
2168
+ * NOT assemble or validate the spec — it serves what the host injects verbatim.
2169
+ * Omit it and the endpoint returns an empty-but-valid OpenAPI 3.1 document
2170
+ * (no paths), so the studio shows a "not configured" state rather than erroring.
2171
+ */
2200
2172
  openApiSpec?: unknown;
2201
2173
  /**
2202
- * The generated OpenRPC 1.x document. Import it from the codegen-emitted
2203
- * module and pass it through:
2204
- * `import { openRpcSpec } from "./lunora/_generated/openrpc"` (only emitted
2205
- * when the project opts into `apiSpec: "openrpc"` or `"both"`). Like
2206
- * `openApiSpec`, codegen inlines the document into `openrpc.ts` because a
2207
- * Worker can't read the `.json` at runtime; both regenerate together.
2208
- *
2209
- * When set, the worker exposes the admin-gated `GET /_lunora/admin/openrpc`
2210
- * endpoint the studio's API-reference view can render. OpenRPC is the
2211
- * RPC-native spec (a `methods` array over the JSON-RPC-shaped
2212
- * `POST /_lunora/rpc` transport); it covers only the RPC functions, not
2213
- * `httpRouter()` REST routes. The runtime does NOT assemble or validate the
2214
- * spec — it serves what the host injects verbatim. Omit it and the endpoint
2215
- * returns an empty-but-valid OpenRPC 1.x document (no methods), so the studio
2216
- * shows a "not configured" state rather than erroring.
2217
- */
2174
+ * The generated OpenRPC 1.x document. Import it from the codegen-emitted
2175
+ * module and pass it through:
2176
+ * `import { openRpcSpec } from "./lunora/_generated/openrpc"` (only emitted
2177
+ * when the project opts into `apiSpec: "openrpc"` or `"both"`). Like
2178
+ * `openApiSpec`, codegen inlines the document into `openrpc.ts` because a
2179
+ * Worker can't read the `.json` at runtime; both regenerate together.
2180
+ *
2181
+ * When set, the worker exposes the admin-gated `GET /_lunora/admin/openrpc`
2182
+ * endpoint the studio's API-reference view can render. OpenRPC is the
2183
+ * RPC-native spec (a `methods` array over the JSON-RPC-shaped
2184
+ * `POST /_lunora/rpc` transport); it covers only the RPC functions, not
2185
+ * `httpRouter()` REST routes. The runtime does NOT assemble or validate the
2186
+ * spec — it serves what the host injects verbatim. Omit it and the endpoint
2187
+ * returns an empty-but-valid OpenRPC 1.x document (no methods), so the studio
2188
+ * shows a "not configured" state rather than erroring.
2189
+ */
2218
2190
  openRpcSpec?: unknown;
2219
2191
  /**
2220
- * When true, the runtime calls `ctx.passThroughOnException()` at the top
2221
- * of the fetch handler. Forwards uncaught exceptions to the origin
2222
- * instead of returning a synthetic 500.
2223
- */
2192
+ * When true, the runtime calls `ctx.passThroughOnException()` at the top
2193
+ * of the fetch handler. Forwards uncaught exceptions to the origin
2194
+ * instead of returning a synthetic 500.
2195
+ */
2224
2196
  passThroughOnException?: boolean;
2225
2197
  /**
2226
- * Coordinator for cross-shard RPCs. When absent, envelopes with
2227
- * `fanOut` set are rejected with a 400. Construct via
2228
- * `createQueryCoordinator({ registry })`.
2229
- */
2198
+ * Coordinator for cross-shard RPCs. When absent, envelopes with
2199
+ * `fanOut` set are rejected with a 400. Construct via
2200
+ * `createQueryCoordinator({ registry })`.
2201
+ */
2230
2202
  queryCoordinator?: QueryCoordinator;
2231
2203
  /**
2232
- * Cloudflare Queues push-consumer handler — the worker's `queue(batch, …)`
2233
- * entry forwards every delivered `MessageBatch` here. Built by codegen from
2234
- * `lunora/queues.ts` (via `@lunora/queue`'s `dispatchQueueBatch`, which routes
2235
- * by `batch.queue` to the matching `defineQueue` handler), so the runtime
2236
- * stays decoupled from the queue package. Omitted when no push queues exist.
2237
- */
2204
+ * Cloudflare Queues push-consumer handler — the worker's `queue(batch, …)`
2205
+ * entry forwards every delivered `MessageBatch` here. Built by codegen from
2206
+ * `lunora/queues.ts` (via `@lunora/queue`'s `dispatchQueueBatch`, which routes
2207
+ * by `batch.queue` to the matching `defineQueue` handler), so the runtime
2208
+ * stays decoupled from the queue package. Omitted when no push queues exist.
2209
+ */
2238
2210
  queue?: QueueConsumerHandler;
2239
2211
  /**
2240
- * Enforce the ephemeral WS admin token: when `true`,
2241
- * the worker's WS admin gate rejects the raw master admin token in the
2242
- * `?token=` query parameter — only a short-lived sub-token minted by
2243
- * `POST /_lunora/admin/ws-token` (or the master token in the
2244
- * `Authorization` HEADER, which never leaks via URLs) authorizes. Off by
2245
- * default (the master token in `?token=` keeps working); also settable per
2246
- * deployment via `env.LUNORA_REQUIRE_EPHEMERAL_WS_TOKEN`
2247
- * (`1`/`true`/`on`/`yes`/`enabled`), which the shard/relay Durable Objects
2248
- * honor for their own upgrade gate too. Flipping it on is the step that
2249
- * actually closes the URL/log leak — do so once every studio the
2250
- * deployment uses mints ephemeral tokens.
2251
- */
2212
+ * Enforce the ephemeral WS admin token: when `true`,
2213
+ * the worker's WS admin gate rejects the raw master admin token in the
2214
+ * `?token=` query parameter — only a short-lived sub-token minted by
2215
+ * `POST /_lunora/admin/ws-token` (or the master token in the
2216
+ * `Authorization` HEADER, which never leaks via URLs) authorizes. Off by
2217
+ * default (the master token in `?token=` keeps working); also settable per
2218
+ * deployment via `env.LUNORA_REQUIRE_EPHEMERAL_WS_TOKEN`
2219
+ * (`1`/`true`/`on`/`yes`/`enabled`), which the shard/relay Durable Objects
2220
+ * honor for their own upgrade gate too. Flipping it on is the step that
2221
+ * actually closes the URL/log leak — do so once every studio the
2222
+ * deployment uses mints ephemeral tokens.
2223
+ */
2252
2224
  requireEphemeralWsToken?: boolean;
2253
2225
  /**
2254
- * Resolve the calling identity from the inbound RPC request. Called once
2255
- * per RPC (and per fan-out) before the request is forwarded to the
2256
- * shard. The returned `userId` becomes `ctx.auth.userId` on the shard
2257
- * side; remaining keys (`email`, role flags, etc.) are JSON-encoded and
2258
- * forwarded as `x-lunora-identity` so `ctx.auth.getIdentity()` can
2259
- * return them. Returning `null` (or omitting this option) means
2260
- * anonymous — no identity headers are injected.
2261
- */
2226
+ * Resolve the calling identity from the inbound RPC request. Called once
2227
+ * per RPC (and per fan-out) before the request is forwarded to the
2228
+ * shard. The returned `userId` becomes `ctx.auth.userId` on the shard
2229
+ * side; remaining keys (`email`, role flags, etc.) are JSON-encoded and
2230
+ * forwarded as `x-lunora-identity` so `ctx.auth.getIdentity()` can
2231
+ * return them. Returning `null` (or omitting this option) means
2232
+ * anonymous — no identity headers are injected.
2233
+ */
2262
2234
  resolveIdentity?: (request: Request, env: unknown) => Promise<ResolvedIdentity | null> | ResolvedIdentity | null;
2263
2235
  /**
2264
- * Resolve a table's sharding metadata. Required by the import endpoint to
2265
- * bucket rows; when omitted, every row routes to the default shard.
2266
- */
2236
+ * Resolve a table's sharding metadata. Required by the import endpoint to
2237
+ * bucket rows; when omitted, every row routes to the default shard.
2238
+ */
2267
2239
  resolveTableSharding?: AdminTableResolver;
2268
2240
  /**
2269
- * Map of routes for custom HTTP handlers (auth callbacks etc.). Keys can
2270
- * be either `"METHOD path"` (e.g. `"GET /healthz"`) or just `"path"`
2271
- * (e.g. `"/healthz"`) — the runtime will match the more specific form
2272
- * first.
2273
- */
2241
+ * Map of routes for custom HTTP handlers (auth callbacks etc.). Keys can
2242
+ * be either `"METHOD path"` (e.g. `"GET /healthz"`) or just `"path"`
2243
+ * (e.g. `"/healthz"`) — the runtime will match the more specific form
2244
+ * first.
2245
+ */
2274
2246
  routes?: Record<string, Route>;
2275
2247
  /**
2276
- * Namespace binding for the `SchedulerDO` (typically `env.SCHEDULER`). When
2277
- * set, the worker exposes the admin-gated `/_lunora/admin/scheduled`
2278
- * endpoints used by the studio to list and cancel `runAfter` / `runAt`
2279
- * jobs. Omit it and those endpoints respond `SCHEDULER_NOT_CONFIGURED`.
2280
- */
2248
+ * Namespace binding for the `SchedulerDO` (typically `env.SCHEDULER`). When
2249
+ * set, the worker exposes the admin-gated `/_lunora/admin/scheduled`
2250
+ * endpoints used by the studio to list and cancel `runAfter` / `runAt`
2251
+ * jobs. Omit it and those endpoints respond `SCHEDULER_NOT_CONFIGURED`.
2252
+ */
2281
2253
  schedulerDO?: ShardNamespaceLike;
2282
2254
  /**
2283
- * Named `SchedulerDO` instance the admin endpoints target. Must match the
2284
- * `instanceName` passed to `createScheduler` (both default to `default`).
2285
- */
2255
+ * Named `SchedulerDO` instance the admin endpoints target. Must match the
2256
+ * `instanceName` passed to `createScheduler` (both default to `default`).
2257
+ */
2286
2258
  schedulerInstanceName?: string;
2287
2259
  /**
2288
- * Secure-by-default HTTP edge applied to every response the worker emits
2289
- * (RPC, auth, admin, `httpRoute` handlers, SSR fallback): baseline security
2290
- * headers, deny-by-default CORS, and a CSRF/origin guard. Every layer is on
2291
- * by default and individually opt-out — see {@link SecurityOptions}. Omit it
2292
- * to take the hardened defaults; set a field to `false` to relax that layer
2293
- * (e.g. `security: { cors: { allowedOrigins: ["https://app.example.com"] } }`).
2294
- */
2260
+ * Secure-by-default HTTP edge applied to every response the worker emits
2261
+ * (RPC, auth, admin, `httpRoute` handlers, SSR fallback): baseline security
2262
+ * headers, deny-by-default CORS, and a CSRF/origin guard. Every layer is on
2263
+ * by default and individually opt-out — see {@link SecurityOptions}. Omit it
2264
+ * to take the hardened defaults; set a field to `false` to relax that layer
2265
+ * (e.g. `security: { cors: { allowedOrigins: ["https://app.example.com"] } }`).
2266
+ */
2295
2267
  security?: SecurityOptions;
2296
2268
  /** Namespace binding for the shard Durable Object (typically `env.SHARD`). */
2297
2269
  shardDO: ShardNamespaceLike;
2298
2270
  /**
2299
- * Names of the storage buckets the studio's file browser offers in its bucket
2300
- * picker, backing `GET /_lunora/admin/storage/buckets`. Supply the keys of a
2301
- * multi-bucket `createBucketStorage({...})` so the operator can switch buckets;
2302
- * the selected name is forwarded to the storage ops as `options.bucket`. Omit
2303
- * it (single-bucket deployments) and the picker is hidden — the ops target the
2304
- * default bucket.
2305
- */
2271
+ * Names of the storage buckets the studio's file browser offers in its bucket
2272
+ * picker, backing `GET /_lunora/admin/storage/buckets`. Supply the keys of a
2273
+ * multi-bucket `createBucketStorage({...})` so the operator can switch buckets;
2274
+ * the selected name is forwarded to the storage ops as `options.bucket`. Omit
2275
+ * it (single-bucket deployments) and the picker is hidden — the ops target the
2276
+ * default bucket.
2277
+ */
2306
2278
  storageBuckets?: string[];
2307
2279
  /**
2308
- * Deletes one object, backing the admin-gated `DELETE /_lunora/admin/storage`
2309
- * endpoint the studio's file browser calls. Passing
2310
- * `createStorage(...).delete` satisfies it. Omit it and the endpoint responds
2311
- * `STORAGE_DELETE_NOT_CONFIGURED` — the studio surfaces a clear inline error.
2312
- */
2280
+ * Deletes one object, backing the admin-gated `DELETE /_lunora/admin/storage`
2281
+ * endpoint the studio's file browser calls. Passing
2282
+ * `createStorage(...).delete` satisfies it. Omit it and the endpoint responds
2283
+ * `STORAGE_DELETE_NOT_CONFIGURED` — the studio surfaces a clear inline error.
2284
+ */
2313
2285
  storageDelete?: StorageDeleteFunction;
2314
2286
  /**
2315
- * Storage lister backing the admin-gated `GET /_lunora/admin/storage`
2316
- * endpoint the studio's file browser calls. The structural shape matches
2317
- * `@lunora/storage`'s `Storage["list"]`, so passing `createStorage(...).list`
2318
- * (or the raw R2 bucket's `list`) satisfies it. Omit it and the endpoint
2319
- * responds `STORAGE_NOT_CONFIGURED`.
2320
- */
2287
+ * Storage lister backing the admin-gated `GET /_lunora/admin/storage`
2288
+ * endpoint the studio's file browser calls. The structural shape matches
2289
+ * `@lunora/storage`'s `Storage["list"]`, so passing `createStorage(...).list`
2290
+ * (or the raw R2 bucket's `list`) satisfies it. Omit it and the endpoint
2291
+ * responds `STORAGE_NOT_CONFIGURED`.
2292
+ */
2321
2293
  storageList?: StorageListFunction;
2322
2294
  /**
2323
- * Mints a (signed or public) URL for one object, backing the admin-gated
2324
- * `GET /_lunora/admin/storage/url` endpoint the studio's "copy URL" action
2325
- * calls. Passing `createStorage(...).getSignedUrl` (or `.getUrl`) satisfies
2326
- * it. Omit it and the endpoint responds `STORAGE_URL_NOT_CONFIGURED` — the
2327
- * studio surfaces a clear inline error.
2328
- */
2295
+ * Mints a (signed or public) URL for one object, backing the admin-gated
2296
+ * `GET /_lunora/admin/storage/url` endpoint the studio's "copy URL" action
2297
+ * calls. Passing `createStorage(...).getSignedUrl` (or `.getUrl`) satisfies
2298
+ * it. Omit it and the endpoint responds `STORAGE_URL_NOT_CONFIGURED` — the
2299
+ * studio surfaces a clear inline error.
2300
+ */
2329
2301
  storageSignedUrl?: StorageSignedUrlFunction;
2330
2302
  /**
2331
- * Uploads one object, backing the admin-gated `PUT /_lunora/admin/storage`
2332
- * endpoint the studio's file browser calls. Passing `createStorage(...).upload`
2333
- * satisfies it. Omit it and the endpoint responds
2334
- * `STORAGE_UPLOAD_NOT_CONFIGURED` — the studio surfaces a clear inline error.
2335
- */
2303
+ * Uploads one object, backing the admin-gated `PUT /_lunora/admin/storage`
2304
+ * endpoint the studio's file browser calls. Passing `createStorage(...).upload`
2305
+ * satisfies it. Omit it and the endpoint responds
2306
+ * `STORAGE_UPLOAD_NOT_CONFIGURED` — the studio surfaces a clear inline error.
2307
+ */
2336
2308
  storageUpload?: StorageUploadFunction;
2337
2309
  /**
2338
- * Page the `.global()` (D1) change-data-capture log for the admin sync
2339
- * endpoint. When omitted, the sync feed covers only shard-local tables.
2340
- */
2310
+ * Page the `.global()` (D1) change-data-capture log for the admin sync
2311
+ * endpoint. When omitted, the sync feed covers only shard-local tables.
2312
+ */
2341
2313
  syncGlobals?: GlobalCdcSyncFunction;
2342
2314
  /**
2343
- * Read-only introspector for Vectorize indexes, backing the studio's vector
2344
- * browser via `GET /_lunora/admin/vector/indexes` and
2345
- * `POST /_lunora/admin/vector/query`. Build it from the generated
2346
- * `LUNORA_VECTOR_INDEXES` registry plus the env Vectorize bindings (and the
2347
- * schema's embedders, to enable similarity queries). Omit it and those
2348
- * endpoints respond `VECTORS_NOT_CONFIGURED`.
2349
- */
2315
+ * Read-only introspector for Vectorize indexes, backing the studio's vector
2316
+ * browser via `GET /_lunora/admin/vector/indexes` and
2317
+ * `POST /_lunora/admin/vector/query`. Build it from the generated
2318
+ * `LUNORA_VECTOR_INDEXES` registry plus the env Vectorize bindings (and the
2319
+ * schema's embedders, to enable similarity queries). Omit it and those
2320
+ * endpoints respond `VECTORS_NOT_CONFIGURED`.
2321
+ */
2350
2322
  vectorIntrospector?: VectorIntrospector;
2351
2323
  /**
2352
- * Voice-session Durable Object namespaces, keyed by the agent's
2353
- * `lunora/agents.ts` export name (e.g. `{ support: env.VOICE_SUPPORT }`).
2354
- * Codegen wires this for every voice-enabled agent. When set, the worker
2355
- * exposes `/_lunora/voice/&lt;agentExportName>` — a WebSocket upgrade that
2356
- * resolves the caller's identity, forwards it on the server-minted
2357
- * `x-lunora-userid` / `x-lunora-identity` headers, and hands the socket to
2358
- * the agent's `VoiceSessionDO`. Omit it (voice-free apps) and the route does
2359
- * not exist.
2360
- */
2324
+ * Voice-session Durable Object namespaces, keyed by the agent's
2325
+ * `lunora/agents.ts` export name (e.g. `{ support: env.VOICE_SUPPORT }`).
2326
+ * Codegen wires this for every voice-enabled agent. When set, the worker
2327
+ * exposes `/_lunora/voice/&lt;agentExportName>` — a WebSocket upgrade that
2328
+ * resolves the caller's identity, forwards it on the server-minted
2329
+ * `x-lunora-userid` / `x-lunora-identity` headers, and hands the socket to
2330
+ * the agent's `VoiceSessionDO`. Omit it (voice-free apps) and the route does
2331
+ * not exist.
2332
+ */
2361
2333
  voiceAgents?: Record<string, ShardNamespaceLike>;
2362
2334
  /**
2363
- * Resolver for the Cloudflare Workflows REST client, built from the
2364
- * deployment `env` (its `CLOUDFLARE_ACCOUNT_ID` / `CLOUDFLARE_API_TOKEN`).
2365
- * Set by the codegen-emitted worker entry (which depends on
2366
- * `@lunora/workflow`); when omitted, the `/_lunora/admin/workflows*` proxy
2367
- * reports "not configured" and the studio shows the credentials empty state.
2368
- */
2335
+ * Resolver for the Cloudflare Workflows REST client, built from the
2336
+ * deployment `env` (its `CLOUDFLARE_ACCOUNT_ID` / `CLOUDFLARE_API_TOKEN`).
2337
+ * Set by the codegen-emitted worker entry (which depends on
2338
+ * `@lunora/workflow`); when omitted, the `/_lunora/admin/workflows*` proxy
2339
+ * reports "not configured" and the studio shows the credentials empty state.
2340
+ */
2369
2341
  workflowsClient?: (env: unknown) => undefined | WorkflowsRestClient;
2370
2342
  /**
2371
- * Injected x402 charge gate for paid (`.x402({ price })`) procedures. Build
2372
- * it with `createProcedureChargeGate(config)` from `@lunora/x402/charge` and
2373
- * pass it here; the runtime stays free of a hard `@lunora/x402` dependency
2374
- * (and its viem/solana deps).
2375
- *
2376
- * **Required whenever any registered function is `.x402()`-tagged.** The
2377
- * origin worker refuses to dispatch a paid procedure with a config error
2378
- * (`500`) when this is absent, rather than serving it free — the paywall is
2379
- * fail-closed by construction. See {@link X402ChargeGate}.
2380
- */
2343
+ * Injected x402 charge gate for paid (`.x402({ price })`) procedures. Build
2344
+ * it with `createProcedureChargeGate(config)` from `@lunora/x402/charge` and
2345
+ * pass it here; the runtime stays free of a hard `@lunora/x402` dependency
2346
+ * (and its viem/solana deps).
2347
+ *
2348
+ * **Required whenever any registered function is `.x402()`-tagged.** The
2349
+ * origin worker refuses to dispatch a paid procedure with a config error
2350
+ * (`500`) when this is absent, rather than serving it free — the paywall is
2351
+ * fail-closed by construction. See {@link X402ChargeGate}.
2352
+ */
2381
2353
  x402Charge?: X402ChargeGate;
2382
2354
  }
2383
2355
  interface RpcContext {
@@ -2387,201 +2359,195 @@ interface RpcContext {
2387
2359
  shardKey: string;
2388
2360
  }
2389
2361
  /**
2390
- * Ask the owner how many relays to spread new connections across for `shardKey`
2391
- * (plan 075 Phase 2), cached per isolate so a promoted shard doesn't add a
2392
- * round-trip to every WS upgrade. Fails closed to `0` (owner-served) on any error,
2393
- * so a relay-probe hiccup can never break a connection.
2394
- */
2395
- /**
2396
- * The composed Lunora worker. `fetch` / `scheduled` are the standard Cloudflare
2397
- * module-worker entrypoints (so the object can be re-exported directly as
2398
- * `export default createWorker(...)`). `serverQuery` is the in-process fast-path
2399
- * (PLAN4 §2.2) an SSR loader running inside the same worker calls to reach a
2400
- * Lunora query without a self-`fetch` to `/_lunora/rpc`, with identity / RLS /
2401
- * auth semantics identical to the HTTP path.
2402
- */
2362
+ * The composed Lunora worker. `fetch` / `scheduled` are the standard Cloudflare
2363
+ * module-worker entrypoints (so the object can be re-exported directly as
2364
+ * `export default createWorker(...)`). `serverQuery` is the in-process fast-path
2365
+ * (PLAN4 §2.2) an SSR loader running inside the same worker calls to reach a
2366
+ * Lunora query without a self-`fetch` to `/_lunora/rpc`, with identity / RLS /
2367
+ * auth semantics identical to the HTTP path.
2368
+ */
2403
2369
  interface LunoraWorker {
2404
2370
  fetch: (request: Request, env: unknown, context: ExecutionContextLike) => Promise<Response>;
2405
2371
  /**
2406
- * Cloudflare Queues consumer entry — present only when the app declares push
2407
- * queues. Forwards each delivered `MessageBatch` to the configured
2408
- * {@link WorkerOptions.queue} handler; a no-op when none is set.
2409
- */
2372
+ * Cloudflare Queues consumer entry — present only when the app declares push
2373
+ * queues. Forwards each delivered `MessageBatch` to the configured
2374
+ * {@link WorkerOptions.queue} handler; a no-op when none is set.
2375
+ */
2410
2376
  queue?: (batch: unknown, env: unknown, context: ExecutionContextLike) => Promise<void>;
2411
2377
  scheduled: (controller: ScheduledControllerLike, env: unknown, context: ExecutionContextLike) => Promise<void>;
2412
2378
  /**
2413
- * In-process query/mutation dispatch for SSR loaders co-located in this
2414
- * worker. Resolves identity off `request` (cookies / bearer / bookmark) and
2415
- * runs the per-shard authorization gate exactly like `POST /_lunora/rpc`,
2416
- * then dispatches to the owning shard — no network self-fetch. Returns the
2417
- * raw shard {@link Response}, byte-identical to the HTTP path's, so callers
2418
- * can `.json()` it (`{ result }` / `{ error }`) or forward it verbatim. Like
2419
- * the worker's `fetch`, it never throws on a request fault: a denied auth
2420
- * gate, a bad reference, or a downstream error comes back as the SAME JSON
2421
- * error `Response` (`toErrorResponse`) the HTTP path returns.
2422
- * @param request The inbound SSR request — its `cookie` / `authorization`
2423
- * / `x-d1-bookmark` headers drive identity, exactly as the
2424
- * HTTP RPC path reads them.
2425
- * @param env The worker `env`, forwarded to `resolveIdentity`.
2426
- * @param reference A generated function reference (`api.foo.bar`); its
2427
- * `__lunoraRef` is the `"namespace:fn"` dispatched.
2428
- * @param args The function arguments.
2429
- * @param options Call options mirroring the RPC envelope.
2430
- * @param options.shardKey Routes to a specific shard (omitted → the worker's
2431
- * `defaultShardKey`).
2432
- */
2379
+ * In-process query/mutation dispatch for SSR loaders co-located in this
2380
+ * worker. Resolves identity off `request` (cookies / bearer / bookmark) and
2381
+ * runs the per-shard authorization gate exactly like `POST /_lunora/rpc`,
2382
+ * then dispatches to the owning shard — no network self-fetch. Returns the
2383
+ * raw shard {@link Response}, byte-identical to the HTTP path's, so callers
2384
+ * can `.json()` it (`{ result }` / `{ error }`) or forward it verbatim. Like
2385
+ * the worker's `fetch`, it never throws on a request fault: a denied auth
2386
+ * gate, a bad reference, or a downstream error comes back as the SAME JSON
2387
+ * error `Response` (`toErrorResponse`) the HTTP path returns.
2388
+ * @param request The inbound SSR request — its `cookie` / `authorization`
2389
+ * / `x-d1-bookmark` headers drive identity, exactly as the
2390
+ * HTTP RPC path reads them.
2391
+ * @param env The worker `env`, forwarded to `resolveIdentity`.
2392
+ * @param reference A generated function reference (`api.foo.bar`); its
2393
+ * `__lunoraRef` is the `"namespace:fn"` dispatched.
2394
+ * @param args The function arguments.
2395
+ * @param options Call options mirroring the RPC envelope.
2396
+ * @param options.shardKey Routes to a specific shard (omitted → the worker's
2397
+ * `defaultShardKey`).
2398
+ */
2433
2399
  serverQuery: (request: Request, env: unknown, reference: unknown, args?: Record<string, unknown>, options?: {
2434
2400
  shardKey?: string;
2435
2401
  }) => Promise<Response>;
2436
2402
  }
2437
2403
  /**
2438
- * Build a Cloudflare Worker entry. Returns an object with `fetch` so it can
2439
- * be re-exported directly as `export default createWorker(...)`.
2440
- */
2404
+ * Build a Cloudflare Worker entry. Returns an object with `fetch` so it can
2405
+ * be re-exported directly as `export default createWorker(...)`.
2406
+ */
2441
2407
  declare const createWorker: (options: WorkerOptions) => LunoraWorker;
2442
2408
  /**
2443
- * Compose a meta-framework SSR handler and Lunora into a single Cloudflare
2444
- * Worker (PLAN4 §1, §2.2). Thin sugar over {@link createWorker} — a
2445
- * near-pass-through whose value is naming and a documented, framework-neutral
2446
- * entrypoint, so a template reads cleanly:
2447
- *
2448
- * ```ts
2449
- * import { composeWorker } from "@lunora/runtime";
2450
- *
2451
- * export default composeWorker({
2452
- * httpRouter: ssrHandler, // TanStack Start / React Router / SolidStart / …
2453
- * shardDO: env.SHARD,
2454
- * auth,
2455
- * });
2456
- * ```
2457
- *
2458
- * `httpRouter` is *any* meta-framework SSR handler — structurally an
2459
- * {@link HttpRouterLike} (`{ fetch(request, env?, ctx?) }`). It is the
2460
- * lowest-priority matcher: the worker dispatches auth (`/api/auth/*`), explicit
2461
- * {@link WorkerOptions.routes}, and the reserved realtime endpoints
2462
- * (`/_lunora/rpc`, `/_lunora/ws`, `/_lunora/admin/*`) first, then falls through
2463
- * to `httpRouter.fetch` for everything else. An SSR render that throws is
2464
- * contained at that seam and surfaced as a plain 500 — it can never take down
2465
- * the realtime plane (see `dispatchHttpRoute`). The two flows share one worker
2466
- * but never collide.
2467
- *
2468
- * The signature is identical to {@link createWorker}; pass exactly the same
2469
- * options. Prefer this name in framework templates to make the composition
2470
- * intent explicit.
2471
- */
2409
+ * Compose a meta-framework SSR handler and Lunora into a single Cloudflare
2410
+ * Worker (PLAN4 §1, §2.2). Thin sugar over {@link createWorker} — a
2411
+ * near-pass-through whose value is naming and a documented, framework-neutral
2412
+ * entrypoint, so a template reads cleanly:
2413
+ *
2414
+ * ```ts
2415
+ * import { composeWorker } from "@lunora/runtime";
2416
+ *
2417
+ * export default composeWorker({
2418
+ * httpRouter: ssrHandler, // TanStack Start / React Router / SolidStart / …
2419
+ * shardDO: env.SHARD,
2420
+ * auth,
2421
+ * });
2422
+ * ```
2423
+ *
2424
+ * `httpRouter` is *any* meta-framework SSR handler — structurally an
2425
+ * {@link HttpRouterLike} (`{ fetch(request, env?, ctx?) }`). It is the
2426
+ * lowest-priority matcher: the worker dispatches auth (`/api/auth/*`), explicit
2427
+ * {@link WorkerOptions.routes}, and the reserved realtime endpoints
2428
+ * (`/_lunora/rpc`, `/_lunora/ws`, `/_lunora/admin/*`) first, then falls through
2429
+ * to `httpRouter.fetch` for everything else. An SSR render that throws is
2430
+ * contained at that seam and surfaced as a plain 500 — it can never take down
2431
+ * the realtime plane (see `dispatchHttpRoute`). The two flows share one worker
2432
+ * but never collide.
2433
+ *
2434
+ * The signature is identical to {@link createWorker}; pass exactly the same
2435
+ * options. Prefer this name in framework templates to make the composition
2436
+ * intent explicit.
2437
+ */
2472
2438
  declare const composeWorker: (options: WorkerOptions) => LunoraWorker;
2473
2439
  /**
2474
- * A meta-framework's emitted Cloudflare handler: either a bare `fetch` function
2475
- * or a `{ fetch }` module object (optionally carrying its own `scheduled`). Every
2476
- * class-B adapter output (`@sveltejs/adapter-cloudflare`, Nitro's
2477
- * `cloudflare-module`, `@astrojs/cloudflare`) is structurally one of these.
2478
- */
2440
+ * A meta-framework's emitted Cloudflare handler: either a bare `fetch` function
2441
+ * or a `{ fetch }` module object (optionally carrying its own `scheduled`). Every
2442
+ * class-B adapter output (`@sveltejs/adapter-cloudflare`, Nitro's
2443
+ * `cloudflare-module`, `@astrojs/cloudflare`) is structurally one of these.
2444
+ */
2479
2445
  type FrameworkHostHandler = ((request: Request, env?: unknown, context?: ExecutionContextLike) => Promise<Response> | Response) | (HttpRouterLike & {
2480
2446
  scheduled?: (controller: ScheduledControllerLike, env: unknown, context: ExecutionContextLike) => Promise<void> | void;
2481
2447
  });
2482
2448
  /** Lunora worker options for {@link withFrameworkWorker} — everything except `httpRouter` (supplied from the framework host). */
2483
2449
  type FrameworkWorkerOptions = Omit<WorkerOptions, "httpRouter">;
2484
2450
  /**
2485
- * Either fixed {@link FrameworkWorkerOptions}, or a factory deriving them from the
2486
- * per-request `env` — for bindings (like `env.SHARD` → `shardDO`) that only exist
2487
- * at request time.
2488
- */
2451
+ * Either fixed {@link FrameworkWorkerOptions}, or a factory deriving them from the
2452
+ * per-request `env` — for bindings (like `env.SHARD` → `shardDO`) that only exist
2453
+ * at request time.
2454
+ */
2489
2455
  type FrameworkWorkerOptionsInput = ((env: unknown) => FrameworkWorkerOptions) | FrameworkWorkerOptions;
2490
2456
  /**
2491
- * Compose a meta-framework's Cloudflare Worker handler with Lunora's realtime
2492
- * plane into one `{ fetch, scheduled }` Worker — the **single, shared** class-B
2493
- * (own-CF-adapter, hook-injection) composer behind `@lunora/svelte/worker`,
2494
- * `@lunora/vue/worker`, and `@lunora/astro`'s `withLunora` (PLAN4 §3). It wraps
2495
- * the framework handler as {@link composeWorker}'s `httpRouter`, so the reserved
2496
- * realtime endpoints (`/_lunora/rpc`, `/_lunora/ws`, `/_lunora/admin/*`) plus
2497
- * auth/explicit `routes` go to Lunora and **everything else** delegates to the
2498
- * framework. A framework render that throws is contained at the seam and
2499
- * surfaced as a plain 500 — it can never take down the realtime plane.
2500
- *
2501
- * Owns the three behaviors the adapters otherwise each re-implemented (and
2502
- * diverged on): (1) the host may be a bare `fetch` fn or a `{ fetch }` object;
2503
- * (2) options may be a fixed object or an `(env) => options` factory, rebuilt per
2504
- * request so per-request bindings wire in; (3) **`scheduled` preservation** — when
2505
- * Lunora configures no cron surface, the framework host's own `scheduled` (if any)
2506
- * is preserved rather than silently dropped; otherwise Lunora owns it (crons /
2507
- * backup).
2508
- * @param host The framework's emitted Cloudflare handler.
2509
- * @param optionsInput Lunora options minus `httpRouter`, or an `(env) => options` factory.
2510
- */
2457
+ * Compose a meta-framework's Cloudflare Worker handler with Lunora's realtime
2458
+ * plane into one `{ fetch, scheduled }` Worker — the **single, shared** class-B
2459
+ * (own-CF-adapter, hook-injection) composer behind `@lunora/svelte/worker`,
2460
+ * `@lunora/vue/worker`, and `@lunora/astro`'s `withLunora` (PLAN4 §3). It wraps
2461
+ * the framework handler as {@link composeWorker}'s `httpRouter`, so the reserved
2462
+ * realtime endpoints (`/_lunora/rpc`, `/_lunora/ws`, `/_lunora/admin/*`) plus
2463
+ * auth/explicit `routes` go to Lunora and **everything else** delegates to the
2464
+ * framework. A framework render that throws is contained at the seam and
2465
+ * surfaced as a plain 500 — it can never take down the realtime plane.
2466
+ *
2467
+ * Owns the three behaviors the adapters otherwise each re-implemented (and
2468
+ * diverged on): (1) the host may be a bare `fetch` fn or a `{ fetch }` object;
2469
+ * (2) options may be a fixed object or an `(env) => options` factory, rebuilt per
2470
+ * request so per-request bindings wire in; (3) **`scheduled` preservation** — when
2471
+ * Lunora configures no cron surface, the framework host's own `scheduled` (if any)
2472
+ * is preserved rather than silently dropped; otherwise Lunora owns it (crons /
2473
+ * backup).
2474
+ * @param host The framework's emitted Cloudflare handler.
2475
+ * @param optionsInput Lunora options minus `httpRouter`, or an `(env) => options` factory.
2476
+ */
2511
2477
  declare const withFrameworkWorker: (host: FrameworkHostHandler, optionsInput: FrameworkWorkerOptionsInput) => LunoraWorker;
2512
2478
  /**
2513
- * Options for {@link createLunoraHandler}. Either an `(env) => options` factory
2514
- * (full control — for bindings that only exist at request time), or a partial
2515
- * {@link FrameworkWorkerOptions} object whose `shardDO` defaults to the
2516
- * conventional `env.SHARD` binding. Pass nothing for the common case.
2517
- */
2479
+ * Options for {@link createLunoraHandler}. Either an `(env) => options` factory
2480
+ * (full control — for bindings that only exist at request time), or a partial
2481
+ * {@link FrameworkWorkerOptions} object whose `shardDO` defaults to the
2482
+ * conventional `env.SHARD` binding. Pass nothing for the common case.
2483
+ */
2518
2484
  type LunoraHandlerOptions = ((env: unknown) => FrameworkWorkerOptions) | Partial<FrameworkWorkerOptions>;
2519
2485
  /**
2520
- * Resolve per-request Lunora worker options. A factory is called with the
2521
- * request `env`; a partial object has its `shardDO` defaulted to `env.SHARD` so
2522
- * the common case needs no configuration. Throws a clear error when no shard
2523
- * namespace can be found — a wiring mistake, not a runtime condition to swallow.
2524
- */
2486
+ * Resolve per-request Lunora worker options. A factory is called with the
2487
+ * request `env`; a partial object has its `shardDO` defaulted to `env.SHARD` so
2488
+ * the common case needs no configuration. Throws a clear error when no shard
2489
+ * namespace can be found — a wiring mistake, not a runtime condition to swallow.
2490
+ */
2525
2491
  declare const resolveLunoraOptions: (options: LunoraHandlerOptions, env: unknown) => FrameworkWorkerOptions;
2526
2492
  /**
2527
- * Build a framework-neutral request handler for Lunora's realtime plane
2528
- * (`/_lunora/rpc`, `/_lunora/ws`, `/_lunora/admin/*`). This is the **one shared
2529
- * seam** every web-standard framework integration mounts — Hono, Nitro/h3,
2530
- * Elysia, or any WinterCG host running on Cloudflare Workers — so each is a
2531
- * 1–2 line bridge (`(request, env, ctx) => Response`) rather than a bespoke
2532
- * adapter package.
2533
- *
2534
- * Mount it under `/_lunora/*` (or whatever path you reserve) inside your app's
2535
- * router; everything else stays your framework's. The host supplies, per
2536
- * request: a Web `Request`, the Cloudflare `env` (carrying the `SHARD` Durable
2537
- * Object namespace), and — when available — the `ExecutionContext`. The
2538
- * `101 Switching Protocols` WebSocket-upgrade `Response` (with its `webSocket`)
2539
- * is returned verbatim, so the framework streams the socket through unchanged.
2540
- *
2541
- * ```ts
2542
- * // Hono
2543
- * const lunora = createLunoraHandler();
2544
- * app.use("/_lunora/*", (c) => lunora(c.req.raw, c.env, c.executionCtx));
2545
- *
2546
- * // Nitro / h3
2547
- * const lunora = createLunoraHandler();
2548
- * export default defineEventHandler((event) => {
2549
- * const { ctx, env } = event.context.cloudflare;
2550
- * return lunora(toWebRequest(event), env, ctx);
2551
- * });
2552
- * ```
2553
- *
2554
- * `shardDO` defaults to `env.SHARD`; pass `options` (or an `(env) => options`
2555
- * factory) to add `auth`, `crons`, a `security` posture, or a custom namespace.
2556
- * A new worker is composed per request because the options (and the `SHARD`
2557
- * binding they default from) are only known once `env` arrives.
2558
- * @param options Partial worker options (default `shardDO: env.SHARD`), or an `(env) => options` factory.
2559
- */
2493
+ * Build a framework-neutral request handler for Lunora's realtime plane
2494
+ * (`/_lunora/rpc`, `/_lunora/ws`, `/_lunora/admin/*`). This is the **one shared
2495
+ * seam** every web-standard framework integration mounts — Hono, Nitro/h3,
2496
+ * Elysia, or any WinterCG host running on Cloudflare Workers — so each is a
2497
+ * 1–2 line bridge (`(request, env, ctx) => Response`) rather than a bespoke
2498
+ * adapter package.
2499
+ *
2500
+ * Mount it under `/_lunora/*` (or whatever path you reserve) inside your app's
2501
+ * router; everything else stays your framework's. The host supplies, per
2502
+ * request: a Web `Request`, the Cloudflare `env` (carrying the `SHARD` Durable
2503
+ * Object namespace), and — when available — the `ExecutionContext`. The
2504
+ * `101 Switching Protocols` WebSocket-upgrade `Response` (with its `webSocket`)
2505
+ * is returned verbatim, so the framework streams the socket through unchanged.
2506
+ *
2507
+ * ```ts
2508
+ * // Hono
2509
+ * const lunora = createLunoraHandler();
2510
+ * app.use("/_lunora/*", (c) => lunora(c.req.raw, c.env, c.executionCtx));
2511
+ *
2512
+ * // Nitro / h3
2513
+ * const lunora = createLunoraHandler();
2514
+ * export default defineEventHandler((event) => {
2515
+ * const { ctx, env } = event.context.cloudflare;
2516
+ * return lunora(toWebRequest(event), env, ctx);
2517
+ * });
2518
+ * ```
2519
+ *
2520
+ * `shardDO` defaults to `env.SHARD`; pass `options` (or an `(env) => options`
2521
+ * factory) to add `auth`, `crons`, a `security` posture, or a custom namespace.
2522
+ * A new worker is composed per request because the options (and the `SHARD`
2523
+ * binding they default from) are only known once `env` arrives.
2524
+ * @param options Partial worker options (default `shardDO: env.SHARD`), or an `(env) => options` factory.
2525
+ */
2560
2526
  declare const createLunoraHandler: (options?: LunoraHandlerOptions) => ((request: Request, env: unknown, context?: ExecutionContextLike) => Promise<Response>);
2561
2527
  /** Re-exported helper so callers can roundtrip envelopes in tests. */
2562
2528
  declare const defineRpcEnvelope: (envelope: RpcEnvelope) => RpcEnvelope;
2563
2529
  /**
2564
- * Reader / counter capabilities, typed against the SAME canonical
2565
- * `DatabaseWriterLike` the `@lunora/d1` ctx-db derives its `crossShardReader` /
2566
- * `crossShardCounter` options from (`DatabaseWriterLike["findMany"]` /
2567
- * `["count"]`) — so the pair drops straight into `createD1CtxDb` with no cast and
2568
- * no structural drift. The import is type-only: `@lunora/runtime` keeps no hard
2569
- * (value) dependency on `@lunora/do`.
2570
- */
2530
+ * Reader / counter capabilities, typed against the SAME canonical
2531
+ * `DatabaseWriterLike` the `@lunora/d1` ctx-db derives its `crossShardReader` /
2532
+ * `crossShardCounter` options from (`DatabaseWriterLike["findMany"]` /
2533
+ * `["count"]`) — so the pair drops straight into `createD1CtxDb` with no cast and
2534
+ * no structural drift. The import is type-only: `@lunora/runtime` keeps no hard
2535
+ * (value) dependency on `@lunora/do`.
2536
+ */
2571
2537
  type CrossShardCounter = DatabaseWriterLike["count"];
2572
2538
  type CrossShardReader = DatabaseWriterLike["findMany"];
2573
2539
  interface CrossShardRelationOptions {
2574
2540
  /**
2575
- * `fetch` used for the worker subrequest. Defaults to `globalThis.fetch`.
2576
- * Injectable so the in-DO loopback (or a test) can supply its own.
2577
- */
2541
+ * `fetch` used for the worker subrequest. Defaults to `globalThis.fetch`.
2542
+ * Injectable so the in-DO loopback (or a test) can supply its own.
2543
+ */
2578
2544
  fetch?: typeof globalThis.fetch;
2579
2545
  /** Forwarded identity claims (the `x-lunora-identity` envelope), when present. */
2580
2546
  identity?: Record<string, unknown>;
2581
2547
  /**
2582
- * Origin the worker is reachable at (`LUNORA_WORKER_ORIGIN`). The DO issues a
2583
- * loopback subrequest to `${origin}/_lunora/rpc`.
2584
- */
2548
+ * Origin the worker is reachable at (`LUNORA_WORKER_ORIGIN`). The DO issues a
2549
+ * loopback subrequest to `${origin}/_lunora/rpc`.
2550
+ */
2585
2551
  origin: string;
2586
2552
  /** Forwarded user id (the `x-lunora-userid` header), when authenticated. */
2587
2553
  userId?: string;
@@ -2591,58 +2557,58 @@ interface CrossShardRelationCapabilities {
2591
2557
  crossShardReader: CrossShardReader;
2592
2558
  }
2593
2559
  /**
2594
- * Build the `crossShardReader` / `crossShardCounter` pair for a single request,
2595
- * wired to fan reverse-relation reads out across every shard via the worker's
2596
- * coordinator. Pass the result straight into `createD1CtxDb`.
2597
- */
2560
+ * Build the `crossShardReader` / `crossShardCounter` pair for a single request,
2561
+ * wired to fan reverse-relation reads out across every shard via the worker's
2562
+ * coordinator. Pass the result straight into `createD1CtxDb`.
2563
+ */
2598
2564
  declare const createCrossShardRelationCapabilities: (options: CrossShardRelationOptions) => CrossShardRelationCapabilities;
2599
2565
  /**
2600
- * Conventional DO instance name. Kept in sync with `SHARD_REGISTRY_DO_NAME`
2601
- * in `@lunora/do` (not imported to avoid the runtime → do dependency edge —
2602
- * `@lunora/runtime` MUST stay free of a hard `@lunora/do` dep).
2603
- */
2566
+ * Conventional DO instance name. Kept in sync with `SHARD_REGISTRY_DO_NAME`
2567
+ * in `@lunora/do` (not imported to avoid the runtime → do dependency edge —
2568
+ * `@lunora/runtime` MUST stay free of a hard `@lunora/do` dep).
2569
+ */
2604
2570
  declare const SHARD_REGISTRY_DO_NAME: string;
2605
2571
  /**
2606
- * Default per-table cache TTL in milliseconds. 30s is a balance between
2607
- * read amplification (a wide fan-out costs N registry round-trips at
2608
- * minimum every 30s) and registration latency (newly registered shards
2609
- * take up to 30s to participate in fan-outs).
2610
- */
2572
+ * Default per-table cache TTL in milliseconds. 30s is a balance between
2573
+ * read amplification (a wide fan-out costs N registry round-trips at
2574
+ * minimum every 30s) and registration latency (newly registered shards
2575
+ * take up to 30s to participate in fan-outs).
2576
+ */
2611
2577
  declare const DEFAULT_REGISTRY_CACHE_TTL_MS: number;
2612
2578
  interface DynamicShardRegistryOptions {
2613
2579
  /**
2614
- * Override the in-process per-table cache TTL. Set to `0` to disable
2615
- * caching (every `listShardKeys` call hits the DO — useful only for
2616
- * tests).
2617
- */
2580
+ * Override the in-process per-table cache TTL. Set to `0` to disable
2581
+ * caching (every `listShardKeys` call hits the DO — useful only for
2582
+ * tests).
2583
+ */
2618
2584
  cacheTtlMs?: number;
2619
2585
  /**
2620
- * DO instance name. Defaults to {@link SHARD_REGISTRY_DO_NAME}. Override
2621
- * only if you run multiple isolated registries in one environment.
2622
- */
2586
+ * DO instance name. Defaults to {@link SHARD_REGISTRY_DO_NAME}. Override
2587
+ * only if you run multiple isolated registries in one environment.
2588
+ */
2623
2589
  instanceName?: string;
2624
2590
  /**
2625
- * Pin the registry DO to a Cloudflare data-residency jurisdiction. Pass the
2626
- * same value as the worker's `jurisdiction` so the registry co-locates with
2627
- * the shards it tracks. Omit for the un-pinned global namespace.
2628
- */
2591
+ * Pin the registry DO to a Cloudflare data-residency jurisdiction. Pass the
2592
+ * same value as the worker's `jurisdiction` so the registry co-locates with
2593
+ * the shards it tracks. Omit for the un-pinned global namespace.
2594
+ */
2629
2595
  jurisdiction?: DurableObjectJurisdiction;
2630
2596
  /** DO namespace binding (`env.SHARD_REGISTRY`). */
2631
2597
  namespace: ShardNamespaceLike;
2632
2598
  }
2633
2599
  /**
2634
- * Extension of {@link ShardRegistry} with the mutator surface a worker
2635
- * needs to register / unregister shard keys.
2636
- */
2600
+ * Extension of {@link ShardRegistry} with the mutator surface a worker
2601
+ * needs to register / unregister shard keys.
2602
+ */
2637
2603
  interface DynamicShardRegistry extends ShardRegistry {
2638
2604
  /** Drop the local cache. Pass a table to invalidate one entry; omit for everything. */
2639
2605
  invalidate: (table?: string) => void;
2640
2606
  /** Register a shard key as live for `table`. Idempotent. */
2641
2607
  register: (table: string, shardKey: string) => Promise<void>;
2642
2608
  /**
2643
- * Read the full `table → shardKeys` map. Useful for admin / debug UIs;
2644
- * not on the fan-out hot path.
2645
- */
2609
+ * Read the full `table → shardKeys` map. Useful for admin / debug UIs;
2610
+ * not on the fan-out hot path.
2611
+ */
2646
2612
  snapshot: () => Promise<Record<string, ReadonlyArray<string>>>;
2647
2613
  /** Remove a shard key from `table`'s live set. Idempotent. */
2648
2614
  unregister: (table: string, shardKey: string) => Promise<void>;
@@ -2652,26 +2618,26 @@ interface LunoraErrorBody {
2652
2618
  error: ErrorBody;
2653
2619
  }
2654
2620
  /**
2655
- * Convert any thrown value into a JSON error response.
2656
- *
2657
- * Delegates the envelope + redaction to `@lunora/errors`' {@link toErrorBody} —
2658
- * a non-internal `LunoraError` (from `@lunora/server`, this runtime, or the
2659
- * `@lunora/do` data layer) is echoed with its `code`/`message`/`hint`/`docsUrl`;
2660
- * an internal-coded error keeps its status but its message is redacted; anything
2661
- * else becomes a generic `INTERNAL` 500. All of these share the unified shape
2662
- * recognized by `isLunoraError`.
2663
- */
2621
+ * Convert any thrown value into a JSON error response.
2622
+ *
2623
+ * Delegates the envelope + redaction to `@lunora/errors`' {@link toErrorBody} —
2624
+ * a non-internal `LunoraError` (from `@lunora/server`, this runtime, or the
2625
+ * `@lunora/do` data layer) is echoed with its `code`/`message`/`hint`/`docsUrl`;
2626
+ * an internal-coded error keeps its status but its message is redacted; anything
2627
+ * else becomes a generic `INTERNAL` 500. All of these share the unified shape
2628
+ * recognized by `isLunoraError`.
2629
+ */
2664
2630
  declare const toErrorResponse: (error: unknown) => Response;
2665
2631
  /**
2666
- * Transport-level error for the worker entry. A thin ergonomic wrapper over the
2667
- * shared `@lunora/errors` `LunoraError` that keeps the runtime's historical
2668
- * `(message, { code, status })` signature — the runtime mints these with
2669
- * dispatch-specific codes (`METHOD_NOT_ALLOWED`, `*_NOT_CONFIGURED`, …) and an
2670
- * explicit status, so they don't need a central catalog entry. Because it is a
2671
- * real `LunoraError`, it carries the unified wire shape and is recognized by
2672
- * `isLunoraError` everywhere. Anything thrown that isn't a `LunoraError`
2673
- * is mapped to a generic 500 with code `INTERNAL`.
2674
- */
2632
+ * Transport-level error for the worker entry. A thin ergonomic wrapper over the
2633
+ * shared `@lunora/errors` `LunoraError` that keeps the runtime's historical
2634
+ * `(message, { code, status })` signature — the runtime mints these with
2635
+ * dispatch-specific codes (`METHOD_NOT_ALLOWED`, `*_NOT_CONFIGURED`, …) and an
2636
+ * explicit status, so they don't need a central catalog entry. Because it is a
2637
+ * real `LunoraError`, it carries the unified wire shape and is recognized by
2638
+ * `isLunoraError` everywhere. Anything thrown that isn't a `LunoraError`
2639
+ * is mapped to a generic 500 with code `INTERNAL`.
2640
+ */
2675
2641
  declare class LunoraError extends LunoraError$1 {
2676
2642
  constructor(message: string, options?: {
2677
2643
  cause?: unknown;
@@ -2686,72 +2652,88 @@ interface OnlyErrorsOption {
2686
2652
  onlyErrors?: boolean;
2687
2653
  }
2688
2654
  /**
2689
- * A sink that logs each event via `console`.
2690
- *
2691
- * Useful as a zero-config default during development, or wired behind
2692
- * {@link combineSinks} alongside a network sink. Successful events are logged
2693
- * with `console.log`; error events (`ok === false`) with `console.error`.
2694
- * @param options Sink options; set `onlyErrors` to log error events only.
2695
- */
2655
+ * A sink that logs each event via `console`.
2656
+ *
2657
+ * Useful as a zero-config default during development, or wired behind
2658
+ * {@link combineSinks} alongside a network sink. Successful events are logged
2659
+ * with `console.log`; error events (`ok === false`) with `console.error`.
2660
+ * @param options Sink options; set `onlyErrors` to log error events only.
2661
+ */
2696
2662
  declare const consoleSink: (options?: OnlyErrorsOption) => ObservabilitySink;
2697
2663
  /** Options for {@link webhookSink}. */
2698
2664
  interface WebhookSinkOptions extends OnlyErrorsOption {
2699
2665
  /**
2700
- * Extra headers merged onto the POST. `Content-Type: application/json` is
2701
- * set by default and may be overridden here (e.g. to add an
2702
- * `Authorization` / API-key header for Axiom, Datadog, etc.).
2703
- */
2666
+ * Extra headers merged onto the POST. `Content-Type: application/json` is
2667
+ * set by default and may be overridden here (e.g. to add an
2668
+ * `Authorization` / API-key header for Axiom, Datadog, etc.).
2669
+ */
2704
2670
  headers?: Record<string, string>;
2705
2671
  /**
2706
- * Optional redaction hook applied to each event immediately before it is
2707
- * serialized and shipped. Use it to scrub or drop PII (e.g. strip
2708
- * `error.message`) before it leaves the worker. Return the (possibly
2709
- * modified) event to send, or `null`/`undefined` to drop the event
2710
- * entirely. A throwing `transform` drops the event (fail-closed) so a buggy
2711
- * redactor can never leak the un-scrubbed payload.
2712
- */
2672
+ * Optional redaction hook applied to each event immediately before it is
2673
+ * serialized and shipped. Use it to scrub or drop PII (e.g. strip
2674
+ * `error.message`) before it leaves the worker. Return the (possibly
2675
+ * modified) event to send, or `null`/`undefined` to drop the event
2676
+ * entirely. A throwing `transform` drops the event (fail-closed) so a buggy
2677
+ * redactor can never leak the un-scrubbed payload.
2678
+ */
2713
2679
  transform?: (event: ObservabilityEvent) => null | ObservabilityEvent | undefined;
2680
+ /**
2681
+ * Optional redaction hook for `ctx.log` events (the {@link transform}
2682
+ * counterpart for log lines). Same fail-closed contract: return the event to
2683
+ * ship it, `null`/`undefined` to drop it, and a throw drops it. When unset,
2684
+ * log events are shipped as-is (message + structured fields — which may carry
2685
+ * user input; see the privacy note).
2686
+ */
2687
+ transformLog?: (event: LogEvent) => LogEvent | null | undefined;
2714
2688
  /** The ingestion endpoint to POST each event to. */
2715
2689
  url: string;
2716
2690
  }
2717
2691
  /**
2718
- * A fire-and-forget sink that POSTs each event as JSON to an HTTP endpoint.
2719
- *
2720
- * This covers Axiom, Datadog, and any generic webhook/log-ingestion service —
2721
- * point `url` at the ingestion endpoint and supply auth via `headers`. Each
2722
- * event is sent as its own `fetch`. When the runtime supplies a per-event
2723
- * `context.waitUntil` (the request's `ctx.waitUntil`), the send is registered
2724
- * with it so it survives isolate teardown after the response returns; otherwise
2725
- * it degrades to fire-and-forget. Either way its rejection is swallowed so a
2726
- * flaky endpoint never surfaces to the caller.
2727
- *
2728
- * Privacy: the full event is serialized, including `error.message`, which may
2729
- * contain user input. See the module-level note. Pass a `transform` callback to
2730
- * scrub or drop fields before they leave the worker.
2731
- * @param options Sink options: `url` is the POST target, `headers` are merged
2732
- * request headers (e.g. an API key), `onlyErrors` ships error events only, and
2733
- * `transform` redacts/drops each event before send.
2734
- */
2692
+ * A fire-and-forget sink that POSTs each event as JSON to an HTTP endpoint.
2693
+ *
2694
+ * This covers Axiom, Datadog, and any generic webhook/log-ingestion service —
2695
+ * point `url` at the ingestion endpoint and supply auth via `headers`. Each
2696
+ * event is sent as its own `fetch`. When the runtime supplies a per-event
2697
+ * `context.waitUntil` (the request's `ctx.waitUntil`), the send is registered
2698
+ * with it so it survives isolate teardown after the response returns; otherwise
2699
+ * it degrades to fire-and-forget. Either way its rejection is swallowed so a
2700
+ * flaky endpoint never surfaces to the caller.
2701
+ *
2702
+ * Privacy: the full event is serialized, including `error.message`, which may
2703
+ * contain user input. See the module-level note. Pass a `transform` callback to
2704
+ * scrub or drop fields before they leave the worker.
2705
+ * @param options Sink options: `url` is the POST target, `headers` are merged
2706
+ * request headers (e.g. an API key), `onlyErrors` ships error events only, and
2707
+ * `transform` redacts/drops each event before send.
2708
+ */
2735
2709
  declare const webhookSink: (options: WebhookSinkOptions) => ObservabilitySink;
2736
2710
  /** Options for {@link sentrySink}. */
2737
2711
  interface SentrySinkOptions extends OnlyErrorsOption {
2738
2712
  /**
2739
- * User-supplied capture callback. Wire this to your Sentry client, e.g.
2740
- * `(event) => Sentry.captureMessage(...)` or `captureException`. Kept as an
2741
- * injected callback so the runtime takes no dependency on `@sentry/*`.
2742
- */
2713
+ * User-supplied capture callback. Wire this to your Sentry client, e.g.
2714
+ * `(event) => Sentry.captureMessage(...)` or `captureException`. Kept as an
2715
+ * injected callback so the runtime takes no dependency on `@sentry/*`.
2716
+ */
2743
2717
  capture: (event: ObservabilityEvent) => void;
2744
- }
2745
- /**
2746
- * A thin adapter that forwards events to an injected `capture` callback.
2747
- *
2748
- * Intentionally does NOT bundle `@sentry/*`: the user wires their own Sentry
2749
- * client (`captureException` / `captureMessage`) into `capture`, giving Sentry
2750
- * parity without a hard dependency. The callback is invoked inside a try/catch
2751
- * so a throwing client can't break dispatch.
2752
- * @param options Sink options: `capture` is invoked per forwarded event;
2753
- * `onlyErrors` defaults to true (error events only) — pass `false` for all.
2754
- */
2718
+ /**
2719
+ * Optional callback for `ctx.log` events. Wire it to Sentry's structured
2720
+ * logging or a breadcrumb, e.g. `(e) => Sentry.logger[e.level]?.(e.message,
2721
+ * e.fields)`. Omit it to leave `ctx.log` lines out of Sentry entirely
2722
+ * (capturing every log line would usually flood the project). Invoked inside
2723
+ * a try/catch so a throwing client can't break the handler.
2724
+ */
2725
+ captureLog?: (event: LogEvent) => void;
2726
+ }
2727
+ /**
2728
+ * A thin adapter that forwards events to an injected `capture` callback.
2729
+ *
2730
+ * Intentionally does NOT bundle `@sentry/*`: the user wires their own Sentry
2731
+ * client (`captureException` / `captureMessage`) into `capture`, giving Sentry
2732
+ * parity without a hard dependency. The callback is invoked inside a try/catch
2733
+ * so a throwing client can't break dispatch.
2734
+ * @param options Sink options: `capture` is invoked per forwarded event;
2735
+ * `onlyErrors` defaults to true (error events only) — pass `false` for all.
2736
+ */
2755
2737
  declare const sentrySink: (options: SentrySinkOptions) => ObservabilitySink;
2756
2738
  /** One Analytics Engine data point — the structural subset {@link analyticsEngineSink} writes. */
2757
2739
  interface AnalyticsEngineDataPointLike {
@@ -2763,11 +2745,11 @@ interface AnalyticsEngineDataPointLike {
2763
2745
  indexes?: (null | string)[];
2764
2746
  }
2765
2747
  /**
2766
- * The Cloudflare Analytics Engine dataset binding surface this sink needs — the
2767
- * `env` binding declared in `wrangler.jsonc` under `analytics_engine_datasets`.
2768
- * Typed structurally so the runtime takes no dependency on
2769
- * `@cloudflare/workers-types`.
2770
- */
2748
+ * The Cloudflare Analytics Engine dataset binding surface this sink needs — the
2749
+ * `env` binding declared in `wrangler.jsonc` under `analytics_engine_datasets`.
2750
+ * Typed structurally so the runtime takes no dependency on
2751
+ * `@cloudflare/workers-types`.
2752
+ */
2771
2753
  interface AnalyticsEngineDatasetLike {
2772
2754
  writeDataPoint: (point: AnalyticsEngineDataPointLike) => void;
2773
2755
  }
@@ -2777,90 +2759,126 @@ interface AnalyticsEngineSinkOptions extends OnlyErrorsOption {
2777
2759
  dataset: AnalyticsEngineDatasetLike;
2778
2760
  }
2779
2761
  /**
2780
- * A sink that writes each event to a Cloudflare Analytics Engine dataset.
2781
- *
2782
- * Analytics Engine is the platform's unbounded-cardinality, sampled time-series
2783
- * store — the natural backing for high-volume RPC observability metrics, queried
2784
- * later over SQL. Prefer it over rolling your own counters table for anything
2785
- * that doesn't need to be exact. Each event maps to one data point.
2786
- *
2787
- * indexes: `[functionPath]` — the sampling key, so Analytics Engine samples per
2788
- * function rather than globally.
2789
- *
2790
- * blobs (string dimensions): `[functionPath, ok-or-error, shardKey, error.code,
2791
- * fanOut.table]` — group/filter dimensions; absent fields are the empty string.
2792
- *
2793
- * doubles (numeric metrics): `[durationMs, errorCount, fanOut.shards,
2794
- * fanOut.failed]` where errorCount is 0 or 1 — so `SUM(double2)` is the error
2795
- * count and `AVG(double1)` the latency.
2796
- *
2797
- * `writeDataPoint` is fire-and-forget on the platform; the call is still wrapped
2798
- * in a try/catch so a missing/throwing binding can never break dispatch.
2799
- * @param options Sink options: `dataset` is the AE binding; `onlyErrors` writes
2800
- * only error events (defaults to all events).
2801
- */
2762
+ * A sink that writes each event to a Cloudflare Analytics Engine dataset.
2763
+ *
2764
+ * Analytics Engine is the platform's unbounded-cardinality, sampled time-series
2765
+ * store — the natural backing for high-volume RPC observability metrics, queried
2766
+ * later over SQL. Prefer it over rolling your own counters table for anything
2767
+ * that doesn't need to be exact. Each event maps to one data point.
2768
+ *
2769
+ * indexes: `[functionPath]` — the sampling key, so Analytics Engine samples per
2770
+ * function rather than globally.
2771
+ *
2772
+ * blobs (string dimensions): `[functionPath, ok-or-error, shardKey, error.code,
2773
+ * fanOut.table]` — group/filter dimensions; absent fields are the empty string.
2774
+ *
2775
+ * doubles (numeric metrics): `[durationMs, errorCount, fanOut.shards,
2776
+ * fanOut.failed]` where errorCount is 0 or 1 — so `SUM(double2)` is the error
2777
+ * count and `AVG(double1)` the latency.
2778
+ *
2779
+ * `writeDataPoint` is fire-and-forget on the platform; the call is still wrapped
2780
+ * in a try/catch so a missing/throwing binding can never break dispatch.
2781
+ * @param options Sink options: `dataset` is the AE binding; `onlyErrors` writes
2782
+ * only error events (defaults to all events).
2783
+ */
2802
2784
  declare const analyticsEngineSink: (options: AnalyticsEngineSinkOptions) => ObservabilitySink;
2785
+ /**
2786
+ * The Cloudflare Pipeline binding surface {@link pipelineLogSink} needs — the
2787
+ * `env` binding declared in `wrangler.jsonc` under `pipelines`. Typed
2788
+ * structurally (mirrors `@lunora/bindings/pipelines`' `PipelineBindingLike`) so
2789
+ * the runtime takes no dependency on `@lunora/bindings` or `@cloudflare/workers-types`.
2790
+ */
2791
+ interface PipelineLike {
2792
+ /** Durably ingest a batch of records (buffered to R2, read back later with R2 SQL). */
2793
+ send: (records: Record<string, unknown>[]) => Promise<void>;
2794
+ }
2795
+ /** Options for {@link pipelineLogSink}. */
2796
+ interface PipelineLogSinkOptions {
2797
+ /** The Cloudflare Pipeline binding each log record is durably sent to. */
2798
+ pipeline: PipelineLike;
2799
+ }
2800
+ /**
2801
+ * A sink that durably persists each `ctx.log` line to a Cloudflare Pipeline
2802
+ * (→ R2), so an app has a queryable log store WITHOUT the Cloud — read the
2803
+ * archived records back with R2 SQL. This is the durable counterpart to the
2804
+ * network {@link otlpSink}: where OTLP streams to a collector, this lands the
2805
+ * structured record (message, level, function path, fields, trace ids, shard,
2806
+ * user, timestamp) in object storage under the app's own account.
2807
+ *
2808
+ * Only `onLog` is implemented — RPC-span metrics belong in
2809
+ * {@link analyticsEngineSink}. `Pipeline.send` is durable/fire-and-forget on the
2810
+ * platform; the call is registered with the request's `context.waitUntil` when
2811
+ * present (the DO threads its `state.waitUntil`) so the send survives isolate
2812
+ * teardown, and every rejection is swallowed so a flaky pipeline never surfaces
2813
+ * to the caller.
2814
+ *
2815
+ * Privacy: the persisted record carries `message` + structured `fields` (not the
2816
+ * raw positional args). They may include user input — the R2 bucket is your own,
2817
+ * but treat it as a log store and gate PII upstream if that is a concern.
2818
+ * @param options Sink options: `pipeline` is the Cloudflare Pipeline binding.
2819
+ */
2820
+ declare const pipelineLogSink: (options: PipelineLogSinkOptions) => ObservabilitySink;
2803
2821
  /** Options for {@link otlpSink}. */
2804
2822
  interface OtlpSinkOptions extends OnlyErrorsOption {
2805
2823
  /**
2806
- * The OTLP-over-HTTP collector base endpoint (e.g.
2807
- * `https://collector.example.com`). Following the OTel base-endpoint
2808
- * convention, the sink POSTs spans to `${endpoint}/v1/traces` and log
2809
- * records to `${endpoint}/v1/logs`; a trailing slash is tolerated.
2810
- */
2824
+ * The OTLP-over-HTTP collector base endpoint (e.g.
2825
+ * `https://collector.example.com`). Following the OTel base-endpoint
2826
+ * convention, the sink POSTs spans to `${endpoint}/v1/traces` and log
2827
+ * records to `${endpoint}/v1/logs`; a trailing slash is tolerated.
2828
+ */
2811
2829
  endpoint: string;
2812
2830
  /**
2813
- * Extra headers merged onto every OTLP POST — typically an `Authorization`
2814
- * bearer plus the `x-lunora-deployment` / `x-lunora-org` correlation headers
2815
- * the platform injects at deploy. `Content-Type: application/json` is set by
2816
- * default and may be overridden here.
2817
- */
2831
+ * Extra headers merged onto every OTLP POST — typically an `Authorization`
2832
+ * bearer plus the `x-lunora-deployment` / `x-lunora-org` correlation headers
2833
+ * the platform injects at deploy. `Content-Type: application/json` is set by
2834
+ * default and may be overridden here.
2835
+ */
2818
2836
  headers?: Record<string, string>;
2819
2837
  /**
2820
- * Value of the `service.name` resource attribute on every exported span and
2821
- * log — the logical service the telemetry belongs to. Defaults to `lunora`.
2822
- */
2838
+ * Value of the `service.name` resource attribute on every exported span and
2839
+ * log — the logical service the telemetry belongs to. Defaults to `lunora`.
2840
+ */
2823
2841
  serviceName?: string;
2824
2842
  /**
2825
- * Convenience bearer token: when set, an `Authorization: Bearer` header
2826
- * carrying it is added to every POST (overriding any authorization in
2827
- * `headers`). Mirrors the container exporter so the platform can inject the
2828
- * same `LUNORA_OTLP_TOKEN` into both. Leave unset for an unauthenticated collector.
2829
- */
2843
+ * Convenience bearer token: when set, an `Authorization: Bearer` header
2844
+ * carrying it is added to every POST (overriding any authorization in
2845
+ * `headers`). Mirrors the container exporter so the platform can inject the
2846
+ * same `LUNORA_OTLP_TOKEN` into both. Leave unset for an unauthenticated collector.
2847
+ */
2830
2848
  token?: string;
2831
2849
  }
2832
2850
  /**
2833
- * A fire-and-forget sink that exports telemetry over OTLP-over-HTTP (JSON).
2834
- *
2835
- * This is the single, standard wire contract both the worker and (via the
2836
- * container exporter helper) container processes use, so telemetry from either
2837
- * side lands in the same collector. Each RPC dispatch becomes one OTLP **span**
2838
- * (`${endpoint}/v1/traces`) named after its `functionPath`, with start/end
2839
- * derived from `durationMs` and status OK/ERROR; each `ctx.log.*` line becomes
2840
- * one OTLP **log record** (`${endpoint}/v1/logs`). Trace/span ids are random
2841
- * per span — real trace correlation (worker→container `traceparent`) is a later
2842
- * phase.
2843
- *
2844
- * Like {@link webhookSink}, each export is its own `fetch`, registered with the
2845
- * request's `context.waitUntil` when present so it survives isolate teardown,
2846
- * and every rejection is swallowed so a flaky collector never surfaces to the
2847
- * caller.
2848
- *
2849
- * Privacy: spans carry `error.type`/`error.message` and logs carry the rendered
2850
- * `message`, which may include user input. Point `endpoint` only at a collector
2851
- * you trust, and gate PII upstream if that is a concern.
2852
- * @param options Sink options: `endpoint` is the collector base URL, `headers`
2853
- * are merged onto every POST (auth + correlation), `serviceName` sets the
2854
- * resource `service.name`, and `onlyErrors` exports error spans only.
2855
- */
2851
+ * A fire-and-forget sink that exports telemetry over OTLP-over-HTTP (JSON).
2852
+ *
2853
+ * This is the single, standard wire contract both the worker and (via the
2854
+ * container exporter helper) container processes use, so telemetry from either
2855
+ * side lands in the same collector. Each RPC dispatch becomes one OTLP **span**
2856
+ * (`${endpoint}/v1/traces`) named after its `functionPath`, with start/end
2857
+ * derived from `durationMs` and status OK/ERROR; each `ctx.log.*` line becomes
2858
+ * one OTLP **log record** (`${endpoint}/v1/logs`). Trace/span ids are random
2859
+ * per span — real trace correlation (worker→container `traceparent`) is a later
2860
+ * phase.
2861
+ *
2862
+ * Like {@link webhookSink}, each export is its own `fetch`, registered with the
2863
+ * request's `context.waitUntil` when present so it survives isolate teardown,
2864
+ * and every rejection is swallowed so a flaky collector never surfaces to the
2865
+ * caller.
2866
+ *
2867
+ * Privacy: spans carry `error.type`/`error.message` and logs carry the rendered
2868
+ * `message`, which may include user input. Point `endpoint` only at a collector
2869
+ * you trust, and gate PII upstream if that is a concern.
2870
+ * @param options Sink options: `endpoint` is the collector base URL, `headers`
2871
+ * are merged onto every POST (auth + correlation), `serviceName` sets the
2872
+ * resource `service.name`, and `onlyErrors` exports error spans only.
2873
+ */
2856
2874
  declare const otlpSink: (options: OtlpSinkOptions) => ObservabilitySink;
2857
2875
  /**
2858
- * Combine several sinks into one that fans each event out to all of them.
2859
- *
2860
- * Each child sink is invoked in order; a throw from one does not prevent the
2861
- * others from running (each call is individually guarded).
2862
- * @param sinks The sinks to fan out to.
2863
- */
2876
+ * Combine several sinks into one that fans each event out to all of them.
2877
+ *
2878
+ * Each child sink is invoked in order; a throw from one does not prevent the
2879
+ * others from running (each call is individually guarded).
2880
+ * @param sinks The sinks to fan out to.
2881
+ */
2864
2882
  declare const combineSinks: (...sinks: ObservabilitySink[]) => ObservabilitySink;
2865
2883
  declare const VERSION: string;
2866
- export { type AdminTableResolver, type AirbyteMessage, type AnalyticsEngineDataPointLike, type AnalyticsEngineDatasetLike, type AnalyticsEngineSinkOptions, type AuthAdmin, type AuthCapabilities, type AuthConfigInfo, type AuthImpersonation, type AuthPage, type AuthSession, type AuthUser, type AuthUserFieldSpec, type BackupManifest, type BackupStore, type ComposeIdentityResolversErrorMode, type ComposeIdentityResolversOptions, type ConnectorChange, type ConnectorSyncPage, type CorsOptions, type CronHandler, type CronJobDispatch, type CronJobInfo, type CrossShardCounter, type CrossShardReader, type CrossShardRelationCapabilities, type CrossShardRelationOptions, type CsrfOptions, DEFAULT_REGISTRY_CACHE_TTL_MS, type DurableObjectJurisdiction, type DynamicShardRegistry, type DynamicShardRegistryOptions, type ExecutionContextLike, type ExportFanOutRequest, type ExportFanOutResult, type FanOutRequest, type FanOutResult, type FanOutSpec, type FivetranResponse, type FrameworkHostHandler, type FrameworkWorkerOptions, type FrameworkWorkerOptionsInput, type FunctionDescriptor, type FunctionRegistryEntry, type FunctionRegistryLike, type GlobalExportFunction as GlobalExportFn, type GlobalImportFunction as GlobalImportFn, type GlobalIntrospector, type GlobalTableInfo as GlobalTableInfoMeta, type GlobalTablePage as GlobalTablePageMeta, type HttpActionContext, type HttpActionLike, type HttpRouterLike, type IdentityContractLike, type IdentityResolver, type IdentityValidation, type ImportFanOutRequest, type ImportFanOutResult, type KvIntrospector, type KvKeyEntry, type KvKeyListResult, type KvNamespaceSummary, type KvValueResult, type ListAuthUsersOptions, type LogEvent, type LogLevel, LunoraError, type LunoraErrorBody, type LunoraHandlerOptions, type LunoraWorker, type MergeStrategy, type MigrationFanOutRequest, type MigrationFanOutResult, NOOP_EXECUTION_CONTEXT, type ObservabilityEvent, type ObservabilitySink, type ObservabilitySinkContext, type OtlpSinkOptions, type QueryCoordinator, type QueryCoordinatorOptions, type RankFanOutRequest, type RankFanOutResult, type RankPageFanOutRequest, type RankPageFanOutResult, type ResolvedSecurity, type ResolvedShard, type Route, type RpcContext, type RpcEnvelope, SHARD_REGISTRY_DO_NAME, type ScheduledControllerLike, type SecurityHeadersOptions, type SecurityOptions, type SentrySinkOptions, type ShardError, type ShardExportOutcome, type ShardImportOutcome, type ShardMigrationOutcome, type ShardNamespaceLike, type ShardRankOutcome, type ShardRankPageOutcome, type ShardRegistry, type ShardTrafficEntry, type ShardTrafficFanOutRequest, type ShardTrafficFanOutResult, type ShardingInfo, type StorageListFunction as StorageListFn, type StorageObject, VERSION, type VectorIndexSummary, type VectorIntrospector, type VectorQueryMatch, type WebhookSinkOptions, type WorkerOptions, analyticsEngineSink, applyJurisdiction, combineSinks, composeIdentityResolvers, composeWorker, consoleSink, createCrossShardRelationCapabilities, createDynamicShardRegistry, createLunoraHandler, createQueryCoordinator, createStaticShardRegistry, createWorker, decorateResponse, defineRpcEnvelope, emitLogEvent, emitRpcEvent, enforceOrigin, handleCorsPreflight, mergeStrategyForAggregate, otlpSink, resolveLunoraOptions, resolveSecurity, resolveShard, routeIdentityResolvers, sentrySink, toAirbyteMessages, toErrorResponse, toFivetranResponse, webhookSink, withFrameworkWorker };
2884
+ export { type AdminTableResolver, type AirbyteMessage, type AnalyticsEngineDataPointLike, type AnalyticsEngineDatasetLike, type AnalyticsEngineSinkOptions, type AuthAdmin, type AuthCapabilities, type AuthConfigInfo, type AuthImpersonation, type AuthPage, type AuthSession, type AuthUser, type AuthUserFieldSpec, type BackupManifest, type BackupStore, type ComposeIdentityResolversErrorMode, type ComposeIdentityResolversOptions, type ConnectorChange, type ConnectorSyncPage, type CorsOptions, type CronHandler, type CronJobDispatch, type CronJobInfo, type CrossShardCounter, type CrossShardReader, type CrossShardRelationCapabilities, type CrossShardRelationOptions, type CsrfOptions, DEFAULT_REGISTRY_CACHE_TTL_MS, type DurableObjectJurisdiction, type DynamicShardRegistry, type DynamicShardRegistryOptions, type ExecutionContextLike, type ExportFanOutRequest, type ExportFanOutResult, type FanOutRequest, type FanOutResult, type FanOutSpec, type FivetranResponse, type FrameworkHostHandler, type FrameworkWorkerOptions, type FrameworkWorkerOptionsInput, type FunctionDescriptor, type FunctionRegistryEntry, type FunctionRegistryLike, type GlobalExportFunction as GlobalExportFn, type GlobalImportFunction as GlobalImportFn, type GlobalIntrospector, type GlobalTableInfo as GlobalTableInfoMeta, type GlobalTablePage as GlobalTablePageMeta, type HttpActionContext, type HttpActionLike, type HttpRouterLike, type IdentityContractLike, type IdentityResolver, type IdentityValidation, type ImportFanOutRequest, type ImportFanOutResult, type KvIntrospector, type KvKeyEntry, type KvKeyListResult, type KvNamespaceSummary, type KvValueResult, type ListAuthUsersOptions, type LogEvent, type LogFields, type LogLevel, LunoraError, type LunoraErrorBody, type LunoraHandlerOptions, type LunoraWorker, type MergeStrategy, type MigrationFanOutRequest, type MigrationFanOutResult, NOOP_EXECUTION_CONTEXT, type ObservabilityEvent, type ObservabilitySink, type ObservabilitySinkContext, type OtlpSinkOptions, type PipelineLike, type PipelineLogSinkOptions, type QueryCoordinator, type QueryCoordinatorOptions, type RankFanOutRequest, type RankFanOutResult, type RankPageFanOutRequest, type RankPageFanOutResult, type ResolvedSecurity, type ResolvedShard, type Route, type RpcContext, type RpcEnvelope, SHARD_REGISTRY_DO_NAME, type ScheduledControllerLike, type SecurityHeadersOptions, type SecurityOptions, type SentrySinkOptions, type ShardError, type ShardExportOutcome, type ShardImportOutcome, type ShardMigrationOutcome, type ShardNamespaceLike, type ShardRankOutcome, type ShardRankPageOutcome, type ShardRegistry, type ShardTrafficEntry, type ShardTrafficFanOutRequest, type ShardTrafficFanOutResult, type ShardingInfo, type StorageListFunction as StorageListFn, type StorageObject, VERSION, type VectorIndexSummary, type VectorIntrospector, type VectorQueryMatch, type WebhookSinkOptions, type WorkerOptions, analyticsEngineSink, applyJurisdiction, combineSinks, composeIdentityResolvers, composeWorker, consoleSink, createCrossShardRelationCapabilities, createDynamicShardRegistry, createLunoraHandler, createQueryCoordinator, createStaticShardRegistry, createWorker, decorateResponse, defineRpcEnvelope, emitLogEvent, emitRpcEvent, enforceOrigin, handleCorsPreflight, mergeStrategyForAggregate, otlpSink, pipelineLogSink, resolveLunoraOptions, resolveSecurity, resolveShard, routeIdentityResolvers, sentrySink, toAirbyteMessages, toErrorResponse, toFivetranResponse, webhookSink, withFrameworkWorker };