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