@lunora/runtime 1.0.0-alpha.2 → 1.0.0-alpha.21
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/LICENSE.md +6 -0
- package/README.md +17 -0
- package/__assets__/package-og.svg +1 -1
- package/dist/index.d.mts +561 -80
- package/dist/index.d.ts +561 -80
- package/dist/index.mjs +9 -7
- package/dist/packem_shared/{DEFAULT_REGISTRY_CACHE_TTL_MS-BpCwo_mo.mjs → DEFAULT_REGISTRY_CACHE_TTL_MS-B3pA7aXp.mjs} +9 -5
- package/dist/packem_shared/LunoraError-Bpb9EFJ3.mjs +22 -0
- package/dist/packem_shared/NOOP_EXECUTION_CONTEXT-CCTu0Bf1.mjs +8 -0
- package/dist/packem_shared/applyJurisdiction-BkZtTkct.mjs +20 -0
- package/dist/packem_shared/composeIdentityResolvers-XGjO7V1J.mjs +55 -0
- package/dist/packem_shared/{composeWorker-BmfKl1ei.mjs → composeWorker-Dpw9d1s5.mjs} +707 -91
- package/dist/packem_shared/{createCrossShardRelationCapabilities-C0KOf7er.mjs → createCrossShardRelationCapabilities-CbcWjkAn.mjs} +4 -2
- package/dist/packem_shared/{createQueryCoordinator-DbxC7iUz.mjs → createQueryCoordinator-DNCJzOZE.mjs} +12 -3
- package/dist/packem_shared/{decorateResponse-DbISh_Wi.mjs → decorateResponse-CsZc49QC.mjs} +42 -9
- package/package.json +5 -2
- package/dist/packem_shared/LunoraError-CL0aOtpo.mjs +0 -46
- package/dist/packem_shared/resolveShard-DDkzWtrU.mjs +0 -9
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { RankDirection, RankPageRow, DatabaseWriterLike } from '@lunora/do';
|
|
2
2
|
export type { RankDirection as RankPageDirection, RankPageRowKey as RankPageKey, RankPageRow, ShardRankPageResult } from '@lunora/do';
|
|
3
3
|
import { WorkflowsRestClient } from '@lunora/workflow';
|
|
4
|
+
import { LunoraError as LunoraError$1, ErrorBody } from '@lunora/errors';
|
|
4
5
|
/**
|
|
5
6
|
* Turn-key incremental-sync source helpers for warehouse connectors
|
|
6
7
|
* (Fivetran custom functions, Airbyte incremental sources).
|
|
@@ -110,6 +111,44 @@ declare const toFivetranResponse: (page: ConnectorSyncPage, primaryKey?: Record<
|
|
|
110
111
|
* @param emittedAt epoch-ms stamped on each `RECORD` (default `Date.now()`).
|
|
111
112
|
*/
|
|
112
113
|
declare const toAirbyteMessages: (page: ConnectorSyncPage, emittedAt?: number) => AirbyteMessage[];
|
|
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
|
+
*/
|
|
136
|
+
interface ExecutionContextLike {
|
|
137
|
+
cache?: {
|
|
138
|
+
purge: (options: {
|
|
139
|
+
purgeEverything?: boolean;
|
|
140
|
+
tags?: string[];
|
|
141
|
+
}) => Promise<unknown>;
|
|
142
|
+
};
|
|
143
|
+
passThroughOnException?: () => void;
|
|
144
|
+
waitUntil?: (promise: Promise<unknown>) => void;
|
|
145
|
+
}
|
|
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
|
+
*/
|
|
151
|
+
declare const NOOP_EXECUTION_CONTEXT: ExecutionContextLike;
|
|
113
152
|
/** A timestamp as better-auth stores it: epoch-ms, an ISO string, or absent. */
|
|
114
153
|
type AuthTimestamp = null | number | string;
|
|
115
154
|
/**
|
|
@@ -164,6 +203,43 @@ interface AuthCapabilities {
|
|
|
164
203
|
passkey: boolean;
|
|
165
204
|
twoFactor: boolean;
|
|
166
205
|
}
|
|
206
|
+
/** One user-settable extra field for the create-user form, derived from the merged `user` table. */
|
|
207
|
+
interface AuthUserFieldSpec {
|
|
208
|
+
name: string;
|
|
209
|
+
plugin?: string;
|
|
210
|
+
required: boolean;
|
|
211
|
+
type: "boolean" | "date" | "number" | "string";
|
|
212
|
+
unique: boolean;
|
|
213
|
+
}
|
|
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
|
+
*/
|
|
220
|
+
interface AuthConfigInfo {
|
|
221
|
+
capabilities: AuthCapabilities;
|
|
222
|
+
emailAndPassword: boolean;
|
|
223
|
+
organization: {
|
|
224
|
+
enabled: boolean;
|
|
225
|
+
roles: boolean;
|
|
226
|
+
teams: boolean;
|
|
227
|
+
};
|
|
228
|
+
plugins: string[];
|
|
229
|
+
rateLimit: {
|
|
230
|
+
enabled: boolean;
|
|
231
|
+
max?: number;
|
|
232
|
+
window?: number;
|
|
233
|
+
};
|
|
234
|
+
session: {
|
|
235
|
+
cookieCache?: boolean;
|
|
236
|
+
expiresIn?: number;
|
|
237
|
+
freshAge?: number;
|
|
238
|
+
updateAge?: number;
|
|
239
|
+
};
|
|
240
|
+
socialProviders: string[];
|
|
241
|
+
userFields: AuthUserFieldSpec[];
|
|
242
|
+
}
|
|
167
243
|
/** Filtering / paging options forwarded to {@link AuthAdmin.listUsers} from the users endpoint's query string. */
|
|
168
244
|
interface ListAuthUsersOptions {
|
|
169
245
|
filterField?: string;
|
|
@@ -188,6 +264,15 @@ interface ListAuthUsersOptions {
|
|
|
188
264
|
* implementation is a trusted server-side operator, not an end-user API.
|
|
189
265
|
*/
|
|
190
266
|
interface AuthAdmin {
|
|
267
|
+
addMember?: (input: {
|
|
268
|
+
organizationId: string;
|
|
269
|
+
role?: string;
|
|
270
|
+
userId: string;
|
|
271
|
+
}) => Promise<Record<string, unknown>>;
|
|
272
|
+
addTeamMember?: (input: {
|
|
273
|
+
teamId: string;
|
|
274
|
+
userId: string;
|
|
275
|
+
}) => Promise<Record<string, unknown>>;
|
|
191
276
|
banUser?: (input: {
|
|
192
277
|
expiresInSeconds?: number;
|
|
193
278
|
reason?: string;
|
|
@@ -197,6 +282,23 @@ interface AuthAdmin {
|
|
|
197
282
|
invitationId: string;
|
|
198
283
|
}) => Promise<void>;
|
|
199
284
|
capabilities?: () => Promise<AuthCapabilities>;
|
|
285
|
+
config?: () => Promise<AuthConfigInfo>;
|
|
286
|
+
createOrganization?: (input: {
|
|
287
|
+
logo?: string;
|
|
288
|
+
metadata?: Record<string, unknown>;
|
|
289
|
+
name: string;
|
|
290
|
+
ownerId?: string;
|
|
291
|
+
slug?: string;
|
|
292
|
+
}) => Promise<Record<string, unknown>>;
|
|
293
|
+
createOrgRole?: (input: {
|
|
294
|
+
organizationId: string;
|
|
295
|
+
permission: Record<string, string[]>;
|
|
296
|
+
role: string;
|
|
297
|
+
}) => Promise<Record<string, unknown>>;
|
|
298
|
+
createTeam?: (input: {
|
|
299
|
+
name: string;
|
|
300
|
+
organizationId: string;
|
|
301
|
+
}) => Promise<Record<string, unknown>>;
|
|
200
302
|
createUser?: (input: {
|
|
201
303
|
data?: Record<string, unknown>;
|
|
202
304
|
email: string;
|
|
@@ -204,6 +306,12 @@ interface AuthAdmin {
|
|
|
204
306
|
password?: string;
|
|
205
307
|
role?: string | string[];
|
|
206
308
|
}) => Promise<AuthUser>;
|
|
309
|
+
deleteOrganization?: (input: {
|
|
310
|
+
organizationId: string;
|
|
311
|
+
}) => Promise<void>;
|
|
312
|
+
deleteOrgRole?: (input: {
|
|
313
|
+
roleId: string;
|
|
314
|
+
}) => Promise<void>;
|
|
207
315
|
deletePasskey?: (input: {
|
|
208
316
|
passkeyId: string;
|
|
209
317
|
}) => Promise<void>;
|
|
@@ -213,6 +321,12 @@ interface AuthAdmin {
|
|
|
213
321
|
impersonateUser?: (input: {
|
|
214
322
|
userId: string;
|
|
215
323
|
}) => Promise<AuthImpersonation>;
|
|
324
|
+
inviteMember?: (input: {
|
|
325
|
+
email: string;
|
|
326
|
+
inviterId?: string;
|
|
327
|
+
organizationId: string;
|
|
328
|
+
role?: string;
|
|
329
|
+
}) => Promise<Record<string, unknown>>;
|
|
216
330
|
listAccounts?: (input: {
|
|
217
331
|
userId: string;
|
|
218
332
|
}) => Promise<Record<string, unknown>[]>;
|
|
@@ -230,6 +344,11 @@ interface AuthAdmin {
|
|
|
230
344
|
limit?: number;
|
|
231
345
|
offset?: number;
|
|
232
346
|
}) => Promise<AuthPage<Record<string, unknown>>>;
|
|
347
|
+
listOrgRoles?: (options: {
|
|
348
|
+
limit?: number;
|
|
349
|
+
offset?: number;
|
|
350
|
+
organizationId: string;
|
|
351
|
+
}) => Promise<AuthPage<Record<string, unknown>>>;
|
|
233
352
|
listPasskeys?: (input: {
|
|
234
353
|
userId: string;
|
|
235
354
|
}) => Promise<Record<string, unknown>[]>;
|
|
@@ -238,10 +357,26 @@ interface AuthAdmin {
|
|
|
238
357
|
offset?: number;
|
|
239
358
|
userId?: string;
|
|
240
359
|
}) => Promise<AuthPage<AuthSession>>;
|
|
360
|
+
listTeamMembers?: (options: {
|
|
361
|
+
limit?: number;
|
|
362
|
+
offset?: number;
|
|
363
|
+
teamId: string;
|
|
364
|
+
}) => Promise<AuthPage<Record<string, unknown>>>;
|
|
365
|
+
listTeams?: (options: {
|
|
366
|
+
limit?: number;
|
|
367
|
+
offset?: number;
|
|
368
|
+
organizationId: string;
|
|
369
|
+
}) => Promise<AuthPage<Record<string, unknown>>>;
|
|
241
370
|
listUsers: (options: ListAuthUsersOptions) => Promise<AuthPage<AuthUser>>;
|
|
242
371
|
removeMember?: (input: {
|
|
243
372
|
memberId: string;
|
|
244
373
|
}) => Promise<void>;
|
|
374
|
+
removeTeam?: (input: {
|
|
375
|
+
teamId: string;
|
|
376
|
+
}) => Promise<void>;
|
|
377
|
+
removeTeamMember?: (input: {
|
|
378
|
+
teamMemberId: string;
|
|
379
|
+
}) => Promise<void>;
|
|
245
380
|
removeUser?: (input: {
|
|
246
381
|
userId: string;
|
|
247
382
|
}) => Promise<void>;
|
|
@@ -266,6 +401,25 @@ interface AuthAdmin {
|
|
|
266
401
|
accountId: string;
|
|
267
402
|
userId: string;
|
|
268
403
|
}) => Promise<void>;
|
|
404
|
+
updateMemberRole?: (input: {
|
|
405
|
+
memberId: string;
|
|
406
|
+
role: string | string[];
|
|
407
|
+
}) => Promise<Record<string, unknown>>;
|
|
408
|
+
updateOrganization?: (input: {
|
|
409
|
+
logo?: string;
|
|
410
|
+
metadata?: Record<string, unknown>;
|
|
411
|
+
name?: string;
|
|
412
|
+
organizationId: string;
|
|
413
|
+
slug?: string;
|
|
414
|
+
}) => Promise<Record<string, unknown>>;
|
|
415
|
+
updateOrgRole?: (input: {
|
|
416
|
+
permission: Record<string, string[]>;
|
|
417
|
+
roleId: string;
|
|
418
|
+
}) => Promise<Record<string, unknown>>;
|
|
419
|
+
updateTeam?: (input: {
|
|
420
|
+
name: string;
|
|
421
|
+
teamId: string;
|
|
422
|
+
}) => Promise<Record<string, unknown>>;
|
|
269
423
|
updateUser?: (input: {
|
|
270
424
|
data: Record<string, unknown>;
|
|
271
425
|
userId: string;
|
|
@@ -305,6 +459,182 @@ interface FunctionArgumentDescriptor {
|
|
|
305
459
|
* kind only — enough for a signature view without a deep recursive walk.
|
|
306
460
|
*/
|
|
307
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
|
+
*/
|
|
472
|
+
interface ResolvedIdentity {
|
|
473
|
+
/** Arbitrary additional claims. Must be JSON-serialisable. */
|
|
474
|
+
[key: string]: unknown;
|
|
475
|
+
/**
|
|
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
|
+
*/
|
|
480
|
+
exp?: number;
|
|
481
|
+
/**
|
|
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
|
+
*/
|
|
486
|
+
expiresAtMs?: number;
|
|
487
|
+
/** Stable user identifier (e.g. `"user_2k3..."` or `"u_42"`). */
|
|
488
|
+
userId: string;
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
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
|
+
*/
|
|
497
|
+
type IdentityResolver = (request: Request, env: unknown) => Promise<ResolvedIdentity | null> | ResolvedIdentity | null;
|
|
498
|
+
/** Error policy for {@link composeIdentityResolvers} when a participant resolver throws. */
|
|
499
|
+
type ComposeIdentityResolversErrorMode = "fail-closed" | "skip";
|
|
500
|
+
/** Options for {@link composeIdentityResolvers}. */
|
|
501
|
+
interface ComposeIdentityResolversOptions {
|
|
502
|
+
/**
|
|
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
|
+
*/
|
|
509
|
+
readonly onError?: ComposeIdentityResolversErrorMode;
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
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
|
+
*/
|
|
521
|
+
declare const composeIdentityResolvers: (resolvers: ReadonlyArray<IdentityResolver>, options?: ComposeIdentityResolversOptions) => IdentityResolver;
|
|
522
|
+
/**
|
|
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
|
+
*/
|
|
531
|
+
declare const routeIdentityResolvers: (routes: Record<string, IdentityResolver>) => IdentityResolver;
|
|
532
|
+
/** The result of validating a candidate identity against an {@link IdentityContractLike}. */
|
|
533
|
+
type IdentityValidation = {
|
|
534
|
+
ok: true;
|
|
535
|
+
} | {
|
|
536
|
+
error: string;
|
|
537
|
+
ok: false;
|
|
538
|
+
};
|
|
539
|
+
/**
|
|
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
|
+
*/
|
|
548
|
+
interface IdentityContractLike {
|
|
549
|
+
/** Reject policy applied when validation fails: downgrade to anonymous, or reject the request (401). */
|
|
550
|
+
readonly onInvalid: "anonymous" | "reject";
|
|
551
|
+
/** Validate resolver-returned claims against the declared contract. */
|
|
552
|
+
validate: (identity: Record<string, unknown>) => IdentityValidation;
|
|
553
|
+
}
|
|
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
|
+
/** One KV namespace as the studio's KV browser surfaces it. */
|
|
572
|
+
interface KvNamespaceSummary {
|
|
573
|
+
/** The wrangler/env binding name, e.g. `"MY_KV"`. */
|
|
574
|
+
binding: string;
|
|
575
|
+
}
|
|
576
|
+
/** One key entry as the KV admin browser surfaces it. */
|
|
577
|
+
interface KvKeyEntry {
|
|
578
|
+
/** Absolute expiration (Unix seconds), when set. */
|
|
579
|
+
expiration?: number;
|
|
580
|
+
/** Per-key metadata set at write time, or absent when none. */
|
|
581
|
+
metadata?: unknown;
|
|
582
|
+
/** The key name. */
|
|
583
|
+
name: string;
|
|
584
|
+
}
|
|
585
|
+
/** A paginated page of KV keys as the admin browser returns it. */
|
|
586
|
+
interface KvKeyListResult {
|
|
587
|
+
/** Opaque cursor for the next page; absent when the listing is complete. */
|
|
588
|
+
cursor?: string;
|
|
589
|
+
/** The keys on this page. */
|
|
590
|
+
keys: KvKeyEntry[];
|
|
591
|
+
/** True when this is the final page. */
|
|
592
|
+
listComplete: boolean;
|
|
593
|
+
}
|
|
594
|
+
/** A KV value together with its stored metadata. */
|
|
595
|
+
interface KvValueResult {
|
|
596
|
+
/** Per-key metadata, or `null` when none. */
|
|
597
|
+
metadata: unknown;
|
|
598
|
+
/** The stored value as a string, or `null` when the key is absent. */
|
|
599
|
+
value: null | string;
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
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
|
+
*/
|
|
606
|
+
interface KvIntrospector {
|
|
607
|
+
/** Delete a key from a namespace. No-op when the key is absent. */
|
|
608
|
+
deleteKey: (options: {
|
|
609
|
+
key: string;
|
|
610
|
+
namespace: string;
|
|
611
|
+
}) => Promise<void>;
|
|
612
|
+
/** Read a value (as text) and its metadata from a namespace key. */
|
|
613
|
+
getValue: (options: {
|
|
614
|
+
key: string;
|
|
615
|
+
namespace: string;
|
|
616
|
+
}) => Promise<KvValueResult>;
|
|
617
|
+
/** List keys in a namespace, optionally filtered by prefix and paginated. */
|
|
618
|
+
listKeys: (options: {
|
|
619
|
+
cursor?: string;
|
|
620
|
+
limit?: number;
|
|
621
|
+
namespace: string;
|
|
622
|
+
prefix?: string;
|
|
623
|
+
}) => Promise<KvKeyListResult>;
|
|
624
|
+
/** List the registered KV namespaces (binding names). */
|
|
625
|
+
listNamespaces: () => Promise<KvNamespaceSummary[]>;
|
|
626
|
+
/** Write a value (as text) with optional absolute expiration / relative TTL and metadata. */
|
|
627
|
+
putValue: (options: {
|
|
628
|
+
expiration?: number;
|
|
629
|
+
expirationTtl?: number;
|
|
630
|
+
key: string;
|
|
631
|
+
metadata?: unknown;
|
|
632
|
+
namespace: string;
|
|
633
|
+
value: string;
|
|
634
|
+
}) => Promise<void>;
|
|
635
|
+
}
|
|
636
|
+
/** The worker internals the KV routes reach through injection rather than closure. */
|
|
637
|
+
/**
|
|
308
638
|
* Observability hooks for the Lunora runtime.
|
|
309
639
|
*
|
|
310
640
|
* A user-supplied {@link ObservabilitySink} receives one event per dispatched
|
|
@@ -420,6 +750,14 @@ declare const emitRpcEvent: (sink: ObservabilitySink | undefined, event: Observa
|
|
|
420
750
|
*/
|
|
421
751
|
declare const emitLogEvent: (sink: ObservabilitySink | undefined, event: LogEvent, context?: ObservabilitySinkContext) => void;
|
|
422
752
|
/**
|
|
753
|
+
* Cloudflare Durable Object jurisdictions restrict where a DO runs and persists
|
|
754
|
+
* data, for data-residency / compliance regimes (GDPR, FedRAMP, US data
|
|
755
|
+
* residency). The set is open — Cloudflare adds values over time — so this is a
|
|
756
|
+
* widening union rather than a closed enum.
|
|
757
|
+
* @see https://developers.cloudflare.com/durable-objects/reference/data-location/
|
|
758
|
+
*/
|
|
759
|
+
type DurableObjectJurisdiction = "eu" | "fedramp" | "us";
|
|
760
|
+
/**
|
|
423
761
|
* Structural projection of the bits of `DurableObjectNamespace` the runtime
|
|
424
762
|
* needs. Real workers-types defines a much wider surface; this lets us pass
|
|
425
763
|
* unit-test doubles without coupling to `@cloudflare/workers-types`.
|
|
@@ -437,10 +775,29 @@ interface ShardNamespaceLike {
|
|
|
437
775
|
fetch: (request: Request) => Promise<Response>;
|
|
438
776
|
};
|
|
439
777
|
idFromName: (name: string) => unknown;
|
|
778
|
+
/**
|
|
779
|
+
* Derive a jurisdiction-restricted subnamespace. Every ID and stub created
|
|
780
|
+
* from the returned namespace is pinned to `jurisdiction`. Optional because
|
|
781
|
+
* older workers-types releases (and unit-test doubles) may not expose it;
|
|
782
|
+
* {@link applyJurisdiction} fails closed when a jurisdiction is requested
|
|
783
|
+
* but this method is absent.
|
|
784
|
+
*/
|
|
785
|
+
jurisdiction?: (jurisdiction: DurableObjectJurisdiction) => ShardNamespaceLike;
|
|
440
786
|
}
|
|
441
787
|
interface ResolvedShard {
|
|
442
788
|
fetch: (request: Request) => Promise<Response>;
|
|
443
789
|
}
|
|
790
|
+
/**
|
|
791
|
+
* Return a jurisdiction-restricted view of `namespace`, or `namespace`
|
|
792
|
+
* unchanged when no jurisdiction is configured.
|
|
793
|
+
*
|
|
794
|
+
* Fail-closed: if a jurisdiction is requested but the binding does not expose
|
|
795
|
+
* `.jurisdiction()` (an older workers-types, or a misconfigured test double),
|
|
796
|
+
* this throws rather than silently routing to the un-pinned global namespace —
|
|
797
|
+
* silently dropping a residency constraint would let data land outside the
|
|
798
|
+
* compliance boundary the caller asked for.
|
|
799
|
+
*/
|
|
800
|
+
declare const applyJurisdiction: (namespace: ShardNamespaceLike, jurisdiction?: DurableObjectJurisdiction) => ShardNamespaceLike;
|
|
444
801
|
/** Look up a shard stub by name, preferring `getByName` when present. */
|
|
445
802
|
declare const resolveShard: (namespace: ShardNamespaceLike, shardKey: string) => ResolvedShard;
|
|
446
803
|
/**
|
|
@@ -959,31 +1316,17 @@ interface ShardTrafficFanOutResult {
|
|
|
959
1316
|
shards: ReadonlyArray<ShardTrafficEntry>;
|
|
960
1317
|
}
|
|
961
1318
|
declare const createQueryCoordinator: (options: QueryCoordinatorOptions) => QueryCoordinator;
|
|
962
|
-
/**
|
|
963
|
-
* Secure-by-default HTTP edge for the Lunora worker.
|
|
964
|
-
*
|
|
965
|
-
* The worker's top-level `fetch` (see `./create-worker`) is the single choke
|
|
966
|
-
* point every response passes through — RPC, auth, admin, `httpRoute` handlers,
|
|
967
|
-
* and the SSR fallback alike. This module supplies what is applied there:
|
|
968
|
-
* `decorateResponse` adds baseline security headers plus, for allowed
|
|
969
|
-
* cross-origin requests, the matching `Access-Control-Allow-*` headers (never
|
|
970
|
-
* overwriting a header the inner handler set); `handleCorsPreflight` answers
|
|
971
|
-
* `OPTIONS` preflights for allowlisted origins; `enforceOrigin` is a CSRF guard
|
|
972
|
-
* that rejects state-changing, cookie-authenticated requests from untrusted
|
|
973
|
-
* origins.
|
|
974
|
-
*
|
|
975
|
-
* Every layer is on by default and individually disable-able through the
|
|
976
|
-
* `SecurityOptions` passed to `createWorker`. Resolution (`resolveSecurity`) is
|
|
977
|
-
* pure and platform-agnostic — it touches only the global `Request`/`Response`/
|
|
978
|
-
* `Headers`/`URL`, so it unit-tests under plain Node without workerd.
|
|
979
|
-
*/
|
|
980
1319
|
/** Per-header overrides for {@link SecurityHeadersOptions}. `false` omits the header. */
|
|
981
1320
|
interface SecurityHeadersOptions {
|
|
982
1321
|
/**
|
|
983
1322
|
* `Content-Security-Policy`. Omitted (`undefined`) applies a restrictive
|
|
984
|
-
* default to **non-HTML** responses
|
|
985
|
-
*
|
|
986
|
-
*
|
|
1323
|
+
* `default-src 'none'` policy to **non-HTML** responses, and a conservative
|
|
1324
|
+
* hardening policy to **HTML** responses (`base-uri 'none'; frame-ancestors
|
|
1325
|
+
* 'self'; object-src 'none'`) — this does NOT set `default-src`/`script-src`,
|
|
1326
|
+
* so it never blocks an SSR page's own scripts/styles/images/fetches, it only
|
|
1327
|
+
* locks down the `base` element, framing (mirroring the `SAMEORIGIN`
|
|
1328
|
+
* X-Frame-Options default), and legacy plugins. Pass a string to apply that
|
|
1329
|
+
* exact policy to every response (HTML included); `false` to never send one.
|
|
987
1330
|
*/
|
|
988
1331
|
csp?: string | false;
|
|
989
1332
|
/** `X-Frame-Options` (clickjacking). Defaults to `SAMEORIGIN`. `false` omits it. */
|
|
@@ -1032,7 +1375,7 @@ interface SecurityOptions {
|
|
|
1032
1375
|
interface ResolvedHeaders {
|
|
1033
1376
|
coop: string | undefined;
|
|
1034
1377
|
csp: {
|
|
1035
|
-
|
|
1378
|
+
htmlValue: string | undefined;
|
|
1036
1379
|
value: string;
|
|
1037
1380
|
} | undefined;
|
|
1038
1381
|
enabled: boolean;
|
|
@@ -1095,6 +1438,24 @@ declare const resolveSecurity: (security: SecurityOptions | undefined, env?: Rec
|
|
|
1095
1438
|
* @returns a `403` Response when the origin is untrusted, or `undefined` when the request is allowed.
|
|
1096
1439
|
*/
|
|
1097
1440
|
declare const enforceOrigin: (request: Request, resolved: ResolvedSecurity) => Response | undefined;
|
|
1441
|
+
/**
|
|
1442
|
+
* CSRF defense for the WebSocket upgrade (Cross-Site WebSocket Hijacking).
|
|
1443
|
+
*
|
|
1444
|
+
* A WS handshake is an HTTP `GET`, so {@link enforceOrigin}'s safe-method
|
|
1445
|
+
* exemption never fires for it — yet the browser auto-attaches the session
|
|
1446
|
+
* cookie to the handshake and WebSocket connections are NOT governed by
|
|
1447
|
+
* CORS/SOP. Without an explicit `Origin` check any page can open
|
|
1448
|
+
* `wss://app/_lunora/ws`, authenticate as the logged-in victim, and read the
|
|
1449
|
+
* victim's live queries + issue mutations as them. This guard closes that hole.
|
|
1450
|
+
*
|
|
1451
|
+
* Scoped to cookie-bearing upgrades — the only vector CSWSH can ride (a browser
|
|
1452
|
+
* attaches cookies but never a bearer token). Bearer/token/server-to-server
|
|
1453
|
+
* upgrades (no `Cookie`) are exempt. A browser ALWAYS sends `Origin` on a WS
|
|
1454
|
+
* handshake, so a missing/untrusted `Origin` on a cookie-bearing upgrade fails
|
|
1455
|
+
* closed (mirrors {@link enforceOrigin}).
|
|
1456
|
+
* @returns a `403` Response when the upgrade origin is untrusted, or `undefined` when it is allowed.
|
|
1457
|
+
*/
|
|
1458
|
+
|
|
1098
1459
|
/**
|
|
1099
1460
|
* Answer a CORS preflight (`OPTIONS` carrying `Access-Control-Request-Method`)
|
|
1100
1461
|
* for an allowlisted origin with a `204`. Returns `undefined` for non-preflight
|
|
@@ -1130,10 +1491,6 @@ interface RpcEnvelope {
|
|
|
1130
1491
|
functionPath: string;
|
|
1131
1492
|
shardKey?: string;
|
|
1132
1493
|
}
|
|
1133
|
-
interface ExecutionContextLike {
|
|
1134
|
-
passThroughOnException: () => void;
|
|
1135
|
-
waitUntil: (promise: Promise<unknown>) => void;
|
|
1136
|
-
}
|
|
1137
1494
|
type Route = (request: Request, env: unknown, context: ExecutionContextLike) => Promise<Response> | Response;
|
|
1138
1495
|
/**
|
|
1139
1496
|
* Context handed to HTTP-action handlers. Built per request by the worker; its
|
|
@@ -1149,6 +1506,12 @@ interface HttpActionContext {
|
|
|
1149
1506
|
getIdentity: () => Promise<Record<string, unknown> | null>;
|
|
1150
1507
|
userId: null | string;
|
|
1151
1508
|
};
|
|
1509
|
+
cache?: {
|
|
1510
|
+
purge: (options: {
|
|
1511
|
+
purgeEverything?: boolean;
|
|
1512
|
+
tags?: string[];
|
|
1513
|
+
}) => Promise<unknown>;
|
|
1514
|
+
};
|
|
1152
1515
|
fetch: typeof globalThis.fetch;
|
|
1153
1516
|
runAction: <R>(reference: unknown, args?: Record<string, unknown>) => Promise<R>;
|
|
1154
1517
|
runMutation: <R>(reference: unknown, args?: Record<string, unknown>) => Promise<R>;
|
|
@@ -1168,35 +1531,6 @@ interface HttpRouterLike {
|
|
|
1168
1531
|
fetch(request: Request, env?: unknown, context?: ExecutionContextLike): Promise<Response> | Response;
|
|
1169
1532
|
}
|
|
1170
1533
|
/**
|
|
1171
|
-
* Identity resolved from the inbound request by {@link WorkerOptions.resolveIdentity}.
|
|
1172
|
-
*
|
|
1173
|
-
* The `userId` field is special — it becomes `ctx.auth.userId` inside the
|
|
1174
|
-
* Durable Object. Any other keys (`email`, `name`, custom roles, etc.) are
|
|
1175
|
-
* forwarded verbatim as `ctx.auth.getIdentity()`'s return value.
|
|
1176
|
-
*
|
|
1177
|
-
* Return `null` to signal that the request is anonymous; the runtime will
|
|
1178
|
-
* skip both `x-lunora-userid` and `x-lunora-identity` headers, and
|
|
1179
|
-
* `ctx.auth.userId` will be `undefined` on the shard side.
|
|
1180
|
-
*/
|
|
1181
|
-
interface ResolvedIdentity {
|
|
1182
|
-
/** Arbitrary additional claims. Must be JSON-serialisable. */
|
|
1183
|
-
[key: string]: unknown;
|
|
1184
|
-
/**
|
|
1185
|
-
* JWT-standard expiry in epoch SECONDS. When present (and `expiresAtMs` is
|
|
1186
|
-
* absent), the runtime forwards it as the socket's credential expiry — the
|
|
1187
|
-
* DO drops the socket once it lapses. Used only on the WebSocket path.
|
|
1188
|
-
*/
|
|
1189
|
-
exp?: number;
|
|
1190
|
-
/**
|
|
1191
|
-
* Credential expiry in epoch MILLISECONDS. Preferred over `exp` when
|
|
1192
|
-
* both are present. Forwarded as the socket's expiry on the WebSocket path
|
|
1193
|
-
* so the DO drops the socket once it lapses; omit for non-expiring sessions.
|
|
1194
|
-
*/
|
|
1195
|
-
expiresAtMs?: number;
|
|
1196
|
-
/** Stable user identifier (e.g. `"user_2k3..."` or `"u_42"`). */
|
|
1197
|
-
userId: string;
|
|
1198
|
-
}
|
|
1199
|
-
/**
|
|
1200
1534
|
* Per-table sharding metadata the admin import endpoint needs to route rows.
|
|
1201
1535
|
* Structural so this package stays free of `@lunora/server`. The codegen-
|
|
1202
1536
|
* generated worker entry passes a thin projection of the user's schema.
|
|
@@ -1472,6 +1806,12 @@ interface ScheduledControllerLike {
|
|
|
1472
1806
|
*/
|
|
1473
1807
|
type CronHandler = (controller: ScheduledControllerLike, env: unknown, context: ExecutionContextLike) => Promise<void> | void;
|
|
1474
1808
|
/**
|
|
1809
|
+
* A Cloudflare Queues push-consumer handler — the worker's `queue()` entry
|
|
1810
|
+
* forwards each delivered `MessageBatch` (typed `unknown` here to keep the
|
|
1811
|
+
* runtime decoupled from `@lunora/queue`'s structural batch type).
|
|
1812
|
+
*/
|
|
1813
|
+
type QueueConsumerHandler = (batch: unknown, env: unknown, context: ExecutionContextLike) => Promise<void>;
|
|
1814
|
+
/**
|
|
1475
1815
|
* A single code-defined cron job, shaped like an entry of the generated
|
|
1476
1816
|
* `LUNORA_CRONS` map. `functionPath` is the `"namespace:fn"` to run, `args` its
|
|
1477
1817
|
* bound arguments, and `name` the human label from the `cronJobs()` builder.
|
|
@@ -1553,6 +1893,22 @@ interface BackupManifest {
|
|
|
1553
1893
|
tables?: string;
|
|
1554
1894
|
}
|
|
1555
1895
|
interface WorkerOptions {
|
|
1896
|
+
/**
|
|
1897
|
+
* An additional, async authorization gate for the `/_lunora/admin/*` plane
|
|
1898
|
+
* (the Studio's HTTP + WS endpoints), OR-ed with the static {@link WorkerOptions.adminToken}
|
|
1899
|
+
* bearer. When it resolves `true` for a request, that request is treated as
|
|
1900
|
+
* admin-authorized even without the bearer; when it resolves `false` (or is
|
|
1901
|
+
* unset) the bearer remains the only path. Evaluated once per admin request
|
|
1902
|
+
* and never on the RPC/WebSocket data hot path.
|
|
1903
|
+
*
|
|
1904
|
+
* The intended producer is `@lunora/cloudflare-access`'s `accessAdminGate(...)`,
|
|
1905
|
+
* which verifies the request's `Cf-Access-Jwt-Assertion` JWT and applies an
|
|
1906
|
+
* `isAdmin(claims)` predicate — so the Studio can sit behind Cloudflare Access
|
|
1907
|
+
* instead of (or alongside) a shared admin token. It takes only the request
|
|
1908
|
+
* (verification needs static team-domain/aud config + the remote JWKS, no env
|
|
1909
|
+
* binding), so it composes without threading async through every admin route.
|
|
1910
|
+
*/
|
|
1911
|
+
adminGate?: (request: Request) => boolean | Promise<boolean>;
|
|
1556
1912
|
/**
|
|
1557
1913
|
* Admin bearer token expected by the export/import endpoints. When unset,
|
|
1558
1914
|
* the endpoints respond with `ADMIN_FORBIDDEN` — the same posture the
|
|
@@ -1560,17 +1916,25 @@ interface WorkerOptions {
|
|
|
1560
1916
|
*/
|
|
1561
1917
|
adminToken?: string;
|
|
1562
1918
|
/**
|
|
1563
|
-
*
|
|
1564
|
-
*
|
|
1565
|
-
*
|
|
1566
|
-
*
|
|
1567
|
-
*
|
|
1568
|
-
*
|
|
1569
|
-
*
|
|
1570
|
-
*
|
|
1571
|
-
*
|
|
1572
|
-
*
|
|
1573
|
-
*
|
|
1919
|
+
* Opt into an authorization-open posture for sharded and fan-out access.
|
|
1920
|
+
*
|
|
1921
|
+
* By default (this flag unset/`false`) the runtime FAILS CLOSED per
|
|
1922
|
+
* operation: naming a non-default shard (a potential cross-tenant hop) is
|
|
1923
|
+
* rejected with a `403` (`FORBIDDEN_SHARD`) unless
|
|
1924
|
+
* {@link WorkerOptions.authorizeShard} is configured, and a fan-out
|
|
1925
|
+
* envelope is rejected (`FORBIDDEN_FANOUT`) unless
|
|
1926
|
+
* {@link WorkerOptions.authorizeFanOut} is. Set this to `true` to allow
|
|
1927
|
+
* such requests from any caller (including unauthenticated ones) —
|
|
1928
|
+
* appropriate only when every table is protected by per-row RLS. The
|
|
1929
|
+
* runtime then emits a single `console.warn` so the open posture stays
|
|
1930
|
+
* visible in logs. The flag is consulted per operation: it has no effect
|
|
1931
|
+
* on an operation whose own `authorize*` callback is configured (that
|
|
1932
|
+
* callback gates directly), but configuring only one of the two callbacks
|
|
1933
|
+
* does NOT cover the other operation.
|
|
1934
|
+
*
|
|
1935
|
+
* NOTE: this is a behaviour change from earlier alphas, where the same
|
|
1936
|
+
* situation was warn-once-then-allow. Apps that relied on client-chosen
|
|
1937
|
+
* shard keys without an `authorize*` callback must set this flag explicitly.
|
|
1574
1938
|
*/
|
|
1575
1939
|
allowUnauthenticatedShardAccess?: boolean;
|
|
1576
1940
|
/**
|
|
@@ -1737,11 +2101,51 @@ interface WorkerOptions {
|
|
|
1737
2101
|
*/
|
|
1738
2102
|
httpRouter?: HttpRouterLike;
|
|
1739
2103
|
/**
|
|
2104
|
+
* The declared identity claim contract (`defineIdentity(...)` from
|
|
2105
|
+
* `@lunora/server`), passed by the generated worker entry. When present, the
|
|
2106
|
+
* worker validates every `resolveIdentity` result against it at the trust
|
|
2107
|
+
* boundary (on the public data paths — RPC / WebSocket / HTTP-action /
|
|
2108
|
+
* server-query, never the admin path) *before* the claims become `ctx.auth`.
|
|
2109
|
+
* A resolver output that violates the contract is downgraded to anonymous or
|
|
2110
|
+
* rejected with a `401`, per the contract's `onInvalid`. Omitted → no
|
|
2111
|
+
* validation, and the identity stays the historical untyped claim bag.
|
|
2112
|
+
*/
|
|
2113
|
+
identity?: IdentityContractLike;
|
|
2114
|
+
/**
|
|
1740
2115
|
* Insert `.global()` rows for the admin import endpoint. When omitted,
|
|
1741
2116
|
* rows targeting global tables are reported as hard errors.
|
|
1742
2117
|
*/
|
|
1743
2118
|
importGlobals?: GlobalImportFunction;
|
|
1744
2119
|
/**
|
|
2120
|
+
* Restrict every Durable Object this worker reaches — shard DOs, the
|
|
2121
|
+
* scheduler DO, the fan-out coordinator, subscriptions — to a Cloudflare
|
|
2122
|
+
* data-residency jurisdiction (`"eu"`, `"us"`, `"fedramp"`). The runtime
|
|
2123
|
+
* derives a jurisdiction-pinned subnamespace from {@link WorkerOptions.shardDO}
|
|
2124
|
+
* and {@link WorkerOptions.schedulerDO} once, so all routing inherits it.
|
|
2125
|
+
*
|
|
2126
|
+
* Fail-closed: if the bound namespace does not expose `.jurisdiction()`
|
|
2127
|
+
* (an older `@cloudflare/workers-types`), the worker throws rather than
|
|
2128
|
+
* silently routing to the un-pinned global namespace. Omit it for the
|
|
2129
|
+
* default, un-pinned behaviour.
|
|
2130
|
+
*
|
|
2131
|
+
* ⚠️ Set once, before the first deploy — changing it strands data. A DO name
|
|
2132
|
+
* maps to a *different* ID per jurisdiction, so toggling this on an existing
|
|
2133
|
+
* deployment makes every shard/scheduler call resolve to a new, empty DO; the
|
|
2134
|
+
* prior data stays in the old jurisdiction and is unreachable (no in-place
|
|
2135
|
+
* migration). Usually set via the schema's `.jurisdiction(...)`, which codegen
|
|
2136
|
+
* threads here.
|
|
2137
|
+
* @see https://developers.cloudflare.com/durable-objects/reference/data-location/
|
|
2138
|
+
*/
|
|
2139
|
+
jurisdiction?: DurableObjectJurisdiction;
|
|
2140
|
+
/**
|
|
2141
|
+
* Introspector for Workers KV namespaces, backing the studio's KV browser
|
|
2142
|
+
* via `GET /_lunora/admin/kv/namespaces`, `GET /_lunora/admin/kv/keys`,
|
|
2143
|
+
* `GET|PUT|DELETE /_lunora/admin/kv/value`. Build it from the env's bound
|
|
2144
|
+
* KV namespaces with `createKvIntrospector` from `@lunora/bindings/kv`.
|
|
2145
|
+
* Omit it and those endpoints respond `KV_NOT_CONFIGURED`.
|
|
2146
|
+
*/
|
|
2147
|
+
kvIntrospector?: KvIntrospector;
|
|
2148
|
+
/**
|
|
1745
2149
|
* Optional telemetry sink. When supplied, the worker emits one
|
|
1746
2150
|
* `onRpc` event per dispatched RPC (single-shard forward or fan-out)
|
|
1747
2151
|
* with duration / ok / error / shardKey or fanOut metadata. Sink
|
|
@@ -1796,6 +2200,14 @@ interface WorkerOptions {
|
|
|
1796
2200
|
*/
|
|
1797
2201
|
queryCoordinator?: QueryCoordinator;
|
|
1798
2202
|
/**
|
|
2203
|
+
* Cloudflare Queues push-consumer handler — the worker's `queue(batch, …)`
|
|
2204
|
+
* entry forwards every delivered `MessageBatch` here. Built by codegen from
|
|
2205
|
+
* `lunora/queues.ts` (via `@lunora/queue`'s `dispatchQueueBatch`, which routes
|
|
2206
|
+
* by `batch.queue` to the matching `defineQueue` handler), so the runtime
|
|
2207
|
+
* stays decoupled from the queue package. Omitted when no push queues exist.
|
|
2208
|
+
*/
|
|
2209
|
+
queue?: QueueConsumerHandler;
|
|
2210
|
+
/**
|
|
1799
2211
|
* Resolve the calling identity from the inbound RPC request. Called once
|
|
1800
2212
|
* per RPC (and per fan-out) before the request is forwarded to the
|
|
1801
2213
|
* shard. The returned `userId` becomes `ctx.auth.userId` on the shard
|
|
@@ -1918,6 +2330,12 @@ interface RpcContext {
|
|
|
1918
2330
|
*/
|
|
1919
2331
|
interface LunoraWorker {
|
|
1920
2332
|
fetch: (request: Request, env: unknown, context: ExecutionContextLike) => Promise<Response>;
|
|
2333
|
+
/**
|
|
2334
|
+
* Cloudflare Queues consumer entry — present only when the app declares push
|
|
2335
|
+
* queues. Forwards each delivered `MessageBatch` to the configured
|
|
2336
|
+
* {@link WorkerOptions.queue} handler; a no-op when none is set.
|
|
2337
|
+
*/
|
|
2338
|
+
queue?: (batch: unknown, env: unknown, context: ExecutionContextLike) => Promise<void>;
|
|
1921
2339
|
scheduled: (controller: ScheduledControllerLike, env: unknown, context: ExecutionContextLike) => Promise<void>;
|
|
1922
2340
|
/**
|
|
1923
2341
|
* In-process query/mutation dispatch for SSR loaders co-located in this
|
|
@@ -2019,6 +2437,55 @@ type FrameworkWorkerOptionsInput = ((env: unknown) => FrameworkWorkerOptions) |
|
|
|
2019
2437
|
* @param optionsInput Lunora options minus `httpRouter`, or an `(env) => options` factory.
|
|
2020
2438
|
*/
|
|
2021
2439
|
declare const withFrameworkWorker: (host: FrameworkHostHandler, optionsInput: FrameworkWorkerOptionsInput) => LunoraWorker;
|
|
2440
|
+
/**
|
|
2441
|
+
* Options for {@link createLunoraHandler}. Either an `(env) => options` factory
|
|
2442
|
+
* (full control — for bindings that only exist at request time), or a partial
|
|
2443
|
+
* {@link FrameworkWorkerOptions} object whose `shardDO` defaults to the
|
|
2444
|
+
* conventional `env.SHARD` binding. Pass nothing for the common case.
|
|
2445
|
+
*/
|
|
2446
|
+
type LunoraHandlerOptions = ((env: unknown) => FrameworkWorkerOptions) | Partial<FrameworkWorkerOptions>;
|
|
2447
|
+
/**
|
|
2448
|
+
* Resolve per-request Lunora worker options. A factory is called with the
|
|
2449
|
+
* request `env`; a partial object has its `shardDO` defaulted to `env.SHARD` so
|
|
2450
|
+
* the common case needs no configuration. Throws a clear error when no shard
|
|
2451
|
+
* namespace can be found — a wiring mistake, not a runtime condition to swallow.
|
|
2452
|
+
*/
|
|
2453
|
+
declare const resolveLunoraOptions: (options: LunoraHandlerOptions, env: unknown) => FrameworkWorkerOptions;
|
|
2454
|
+
/**
|
|
2455
|
+
* Build a framework-neutral request handler for Lunora's realtime plane
|
|
2456
|
+
* (`/_lunora/rpc`, `/_lunora/ws`, `/_lunora/admin/*`). This is the **one shared
|
|
2457
|
+
* seam** every web-standard framework integration mounts — Hono, Nitro/h3,
|
|
2458
|
+
* Elysia, or any WinterCG host running on Cloudflare Workers — so each is a
|
|
2459
|
+
* 1–2 line bridge (`(request, env, ctx) => Response`) rather than a bespoke
|
|
2460
|
+
* adapter package.
|
|
2461
|
+
*
|
|
2462
|
+
* Mount it under `/_lunora/*` (or whatever path you reserve) inside your app's
|
|
2463
|
+
* router; everything else stays your framework's. The host supplies, per
|
|
2464
|
+
* request: a Web `Request`, the Cloudflare `env` (carrying the `SHARD` Durable
|
|
2465
|
+
* Object namespace), and — when available — the `ExecutionContext`. The
|
|
2466
|
+
* `101 Switching Protocols` WebSocket-upgrade `Response` (with its `webSocket`)
|
|
2467
|
+
* is returned verbatim, so the framework streams the socket through unchanged.
|
|
2468
|
+
*
|
|
2469
|
+
* ```ts
|
|
2470
|
+
* // Hono
|
|
2471
|
+
* const lunora = createLunoraHandler();
|
|
2472
|
+
* app.use("/_lunora/*", (c) => lunora(c.req.raw, c.env, c.executionCtx));
|
|
2473
|
+
*
|
|
2474
|
+
* // Nitro / h3
|
|
2475
|
+
* const lunora = createLunoraHandler();
|
|
2476
|
+
* export default defineEventHandler((event) => {
|
|
2477
|
+
* const { ctx, env } = event.context.cloudflare;
|
|
2478
|
+
* return lunora(toWebRequest(event), env, ctx);
|
|
2479
|
+
* });
|
|
2480
|
+
* ```
|
|
2481
|
+
*
|
|
2482
|
+
* `shardDO` defaults to `env.SHARD`; pass `options` (or an `(env) => options`
|
|
2483
|
+
* factory) to add `auth`, `crons`, a `security` posture, or a custom namespace.
|
|
2484
|
+
* A new worker is composed per request because the options (and the `SHARD`
|
|
2485
|
+
* binding they default from) are only known once `env` arrives.
|
|
2486
|
+
* @param options Partial worker options (default `shardDO: env.SHARD`), or an `(env) => options` factory.
|
|
2487
|
+
*/
|
|
2488
|
+
declare const createLunoraHandler: (options?: LunoraHandlerOptions) => ((request: Request, env: unknown, context?: ExecutionContextLike) => Promise<Response>);
|
|
2022
2489
|
/** Re-exported helper so callers can roundtrip envelopes in tests. */
|
|
2023
2490
|
declare const defineRpcEnvelope: (envelope: RpcEnvelope) => RpcEnvelope;
|
|
2024
2491
|
/**
|
|
@@ -2082,6 +2549,12 @@ interface DynamicShardRegistryOptions {
|
|
|
2082
2549
|
* only if you run multiple isolated registries in one environment.
|
|
2083
2550
|
*/
|
|
2084
2551
|
instanceName?: string;
|
|
2552
|
+
/**
|
|
2553
|
+
* Pin the registry DO to a Cloudflare data-residency jurisdiction. Pass the
|
|
2554
|
+
* same value as the worker's `jurisdiction` so the registry co-locates with
|
|
2555
|
+
* the shards it tracks. Omit for the un-pinned global namespace.
|
|
2556
|
+
*/
|
|
2557
|
+
jurisdiction?: DurableObjectJurisdiction;
|
|
2085
2558
|
/** DO namespace binding (`env.SHARD_REGISTRY`). */
|
|
2086
2559
|
namespace: ShardNamespaceLike;
|
|
2087
2560
|
}
|
|
@@ -2104,18 +2577,30 @@ interface DynamicShardRegistry extends ShardRegistry {
|
|
|
2104
2577
|
}
|
|
2105
2578
|
declare const createDynamicShardRegistry: (options: DynamicShardRegistryOptions) => DynamicShardRegistry;
|
|
2106
2579
|
interface LunoraErrorBody {
|
|
2107
|
-
error:
|
|
2108
|
-
code: string;
|
|
2109
|
-
message: string;
|
|
2110
|
-
};
|
|
2580
|
+
error: ErrorBody;
|
|
2111
2581
|
}
|
|
2112
2582
|
/**
|
|
2113
|
-
*
|
|
2114
|
-
*
|
|
2583
|
+
* Convert any thrown value into a JSON error response.
|
|
2584
|
+
*
|
|
2585
|
+
* Delegates the envelope + redaction to `@lunora/errors`' {@link toErrorBody} —
|
|
2586
|
+
* a non-internal `LunoraError` (from `@lunora/server`, this runtime, or the
|
|
2587
|
+
* `@lunora/do` data layer) is echoed with its `code`/`message`/`hint`/`docsUrl`;
|
|
2588
|
+
* an internal-coded error keeps its status but its message is redacted; anything
|
|
2589
|
+
* else becomes a generic `INTERNAL` 500. All of these share the unified shape
|
|
2590
|
+
* recognized by `isLunoraError`.
|
|
2115
2591
|
*/
|
|
2116
|
-
declare
|
|
2117
|
-
|
|
2118
|
-
|
|
2592
|
+
declare const toErrorResponse: (error: unknown) => Response;
|
|
2593
|
+
/**
|
|
2594
|
+
* Transport-level error for the worker entry. A thin ergonomic wrapper over the
|
|
2595
|
+
* shared `@lunora/errors` `LunoraError` that keeps the runtime's historical
|
|
2596
|
+
* `(message, { code, status })` signature — the runtime mints these with
|
|
2597
|
+
* dispatch-specific codes (`METHOD_NOT_ALLOWED`, `*_NOT_CONFIGURED`, …) and an
|
|
2598
|
+
* explicit status, so they don't need a central catalog entry. Because it is a
|
|
2599
|
+
* real `LunoraError`, it carries the unified wire shape and is recognized by
|
|
2600
|
+
* `isLunoraError` everywhere. Anything thrown that isn't a `LunoraError`
|
|
2601
|
+
* is mapped to a generic 500 with code `INTERNAL`.
|
|
2602
|
+
*/
|
|
2603
|
+
declare class LunoraError extends LunoraError$1 {
|
|
2119
2604
|
constructor(message: string, options?: {
|
|
2120
2605
|
cause?: unknown;
|
|
2121
2606
|
code?: string;
|
|
@@ -2123,10 +2608,6 @@ declare class LunoraError extends Error {
|
|
|
2123
2608
|
});
|
|
2124
2609
|
toResponse(): Response;
|
|
2125
2610
|
}
|
|
2126
|
-
/** Shape recognised by the runtime's structural error checks. */
|
|
2127
|
-
|
|
2128
|
-
/** Convert any thrown value into a JSON error response. */
|
|
2129
|
-
declare const toErrorResponse: (error: unknown) => Response;
|
|
2130
2611
|
/** Shared shape for sinks that can be limited to error events only. */
|
|
2131
2612
|
interface OnlyErrorsOption {
|
|
2132
2613
|
/** When true, only events with `ok === false` are forwarded. */
|
|
@@ -2256,4 +2737,4 @@ declare const analyticsEngineSink: (options: AnalyticsEngineSinkOptions) => Obse
|
|
|
2256
2737
|
*/
|
|
2257
2738
|
declare const combineSinks: (...sinks: ObservabilitySink[]) => ObservabilitySink;
|
|
2258
2739
|
declare const VERSION: string;
|
|
2259
|
-
export { type AdminTableResolver, type AirbyteMessage, type AnalyticsEngineDataPointLike, type AnalyticsEngineDatasetLike, type AnalyticsEngineSinkOptions, type AuthAdmin, type AuthCapabilities, type AuthImpersonation, type AuthIntrospector, type AuthPage, type AuthSession, type AuthUser, type BackupManifest, type BackupStore, 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 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 ImportFanOutRequest, type ImportFanOutResult, type ListAuthUsersOptions, type LogEvent, type LogLevel, LunoraError, type LunoraErrorBody, type LunoraWorker, type MergeStrategy, type MigrationFanOutRequest, type MigrationFanOutResult, type ObservabilityEvent, type ObservabilitySink, type ObservabilitySinkContext, 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, combineSinks, composeWorker, consoleSink, createCrossShardRelationCapabilities, createDynamicShardRegistry, createQueryCoordinator, createStaticShardRegistry, createWorker, decorateResponse, defineRpcEnvelope, emitLogEvent, emitRpcEvent, enforceOrigin, handleCorsPreflight, mergeStrategyForAggregate, resolveSecurity, resolveShard, sentrySink, toAirbyteMessages, toErrorResponse, toFivetranResponse, webhookSink, withFrameworkWorker };
|
|
2740
|
+
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 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, resolveLunoraOptions, resolveSecurity, resolveShard, routeIdentityResolvers, sentrySink, toAirbyteMessages, toErrorResponse, toFivetranResponse, webhookSink, withFrameworkWorker };
|