@lunora/runtime 1.0.0-alpha.11 → 1.0.0-alpha.13
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 +122 -30
- package/dist/index.d.ts +122 -30
- package/dist/index.mjs +3 -2
- package/dist/packem_shared/composeIdentityResolvers-YjvUKisc.mjs +55 -0
- package/dist/packem_shared/{composeWorker-BNYeYQqL.mjs → composeWorker-BOB2YZ6v.mjs} +7 -4
- package/dist/packem_shared/{createQueryCoordinator-Cbds9cUI.mjs → createQueryCoordinator-ZeZYUPNu.mjs} +10 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -337,6 +337,116 @@ interface FunctionArgumentDescriptor {
|
|
|
337
337
|
* kind only — enough for a signature view without a deep recursive walk.
|
|
338
338
|
*/
|
|
339
339
|
/**
|
|
340
|
+
* Identity resolved from the inbound request by `WorkerOptions.resolveIdentity`.
|
|
341
|
+
*
|
|
342
|
+
* The `userId` field is special — it becomes `ctx.auth.userId` inside the
|
|
343
|
+
* Durable Object. Any other keys (`email`, `name`, custom roles, etc.) are
|
|
344
|
+
* forwarded verbatim as `ctx.auth.getIdentity()`'s return value.
|
|
345
|
+
*
|
|
346
|
+
* Return `null` to signal that the request is anonymous; the runtime will
|
|
347
|
+
* skip both `x-lunora-userid` and `x-lunora-identity` headers, and
|
|
348
|
+
* `ctx.auth.userId` will be `undefined` on the shard side.
|
|
349
|
+
*/
|
|
350
|
+
interface ResolvedIdentity {
|
|
351
|
+
/** Arbitrary additional claims. Must be JSON-serialisable. */
|
|
352
|
+
[key: string]: unknown;
|
|
353
|
+
/**
|
|
354
|
+
* JWT-standard expiry in epoch SECONDS. When present (and `expiresAtMs` is
|
|
355
|
+
* absent), the runtime forwards it as the socket's credential expiry — the
|
|
356
|
+
* DO drops the socket once it lapses. Used only on the WebSocket path.
|
|
357
|
+
*/
|
|
358
|
+
exp?: number;
|
|
359
|
+
/**
|
|
360
|
+
* Credential expiry in epoch MILLISECONDS. Preferred over `exp` when
|
|
361
|
+
* both are present. Forwarded as the socket's expiry on the WebSocket path
|
|
362
|
+
* so the DO drops the socket once it lapses; omit for non-expiring sessions.
|
|
363
|
+
*/
|
|
364
|
+
expiresAtMs?: number;
|
|
365
|
+
/** Stable user identifier (e.g. `"user_2k3..."` or `"u_42"`). */
|
|
366
|
+
userId: string;
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* A verifier that turns an inbound request into a {@link ResolvedIdentity} (or
|
|
370
|
+
* `null` for anonymous). Structurally identical to `WorkerOptions.resolveIdentity`,
|
|
371
|
+
* so `.auth()`'s better-auth session resolver, a signed-preview-link verifier, a
|
|
372
|
+
* per-tenant bearer check, an upstream-JWT reader, … are all just `IdentityResolver`s
|
|
373
|
+
* — the identity layer is generic over every scheme, not coupled to any one.
|
|
374
|
+
*/
|
|
375
|
+
type IdentityResolver = (request: Request, env: unknown) => Promise<ResolvedIdentity | null> | ResolvedIdentity | null;
|
|
376
|
+
/** Error policy for {@link composeIdentityResolvers} when a participant resolver throws. */
|
|
377
|
+
type ComposeIdentityResolversErrorMode = "fail-closed" | "skip";
|
|
378
|
+
/** Options for {@link composeIdentityResolvers}. */
|
|
379
|
+
interface ComposeIdentityResolversOptions {
|
|
380
|
+
/**
|
|
381
|
+
* What to do when a resolver throws. `"fail-closed"` (default, safe)
|
|
382
|
+
* re-throws so a broken verifier fails the request rather than silently
|
|
383
|
+
* falling through to a weaker one; `"skip"` swallows the error and tries the
|
|
384
|
+
* next resolver (use only when a resolver's failure genuinely means "not my
|
|
385
|
+
* scheme").
|
|
386
|
+
*/
|
|
387
|
+
readonly onError?: ComposeIdentityResolversErrorMode;
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Compose several {@link IdentityResolver}s into one, first-match-wins: each is
|
|
391
|
+
* tried in order and the first that returns a non-null identity short-circuits.
|
|
392
|
+
* Generic over every scheme — the better-auth session resolver (obtained via the
|
|
393
|
+
* builder's `derived.resolveIdentity` escape hatch) is just one entry in the list,
|
|
394
|
+
* so composition never means losing it.
|
|
395
|
+
*
|
|
396
|
+
* A resolver that throws is handled per {@link ComposeIdentityResolversOptions.onError}
|
|
397
|
+
* (default `"fail-closed"`: the error propagates).
|
|
398
|
+
*/
|
|
399
|
+
declare const composeIdentityResolvers: (resolvers: ReadonlyArray<IdentityResolver>, options?: ComposeIdentityResolversOptions) => IdentityResolver;
|
|
400
|
+
/**
|
|
401
|
+
* A thin, generic helper over {@link composeIdentityResolvers} for the per-route case:
|
|
402
|
+
* pick a resolver by `new URL(request.url).pathname`. Keys are matched by longest
|
|
403
|
+
* path prefix; `"*"` is the fallback. Still fully generic — a route→resolver map,
|
|
404
|
+
* with no portal / preview / tenant concepts baked in (those live in the app's
|
|
405
|
+
* own resolvers).
|
|
406
|
+
* @example
|
|
407
|
+
* routeIdentityResolvers({ "/admin": adminResolver, "/partner": partnerResolver, "*": sessionResolver })
|
|
408
|
+
*/
|
|
409
|
+
declare const routeIdentityResolvers: (routes: Record<string, IdentityResolver>) => IdentityResolver;
|
|
410
|
+
/** The result of validating a candidate identity against an {@link IdentityContractLike}. */
|
|
411
|
+
type IdentityValidation = {
|
|
412
|
+
ok: true;
|
|
413
|
+
} | {
|
|
414
|
+
error: string;
|
|
415
|
+
ok: false;
|
|
416
|
+
};
|
|
417
|
+
/**
|
|
418
|
+
* Structural view of `@lunora/server`'s `IdentityContract` (from `defineIdentity`).
|
|
419
|
+
* Kept structural so `@lunora/runtime` stays free of an `@lunora/server` dependency.
|
|
420
|
+
* Keep the `onInvalid` union and `validate`/`IdentityValidation` shapes in sync with
|
|
421
|
+
* `@lunora/server`'s `IdentityContract` — they are projected by hand, not imported.
|
|
422
|
+
* The generated worker entry passes the app's `defineIdentity(...)` result here;
|
|
423
|
+
* the worker validates every resolver's returned claims against it at the trust
|
|
424
|
+
* boundary before they become `ctx.auth`.
|
|
425
|
+
*/
|
|
426
|
+
interface IdentityContractLike {
|
|
427
|
+
/** Reject policy applied when validation fails: downgrade to anonymous, or reject the request (401). */
|
|
428
|
+
readonly onInvalid: "anonymous" | "reject";
|
|
429
|
+
/** Validate resolver-returned claims against the declared contract. */
|
|
430
|
+
validate: (identity: Record<string, unknown>) => IdentityValidation;
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* The trust-boundary identity gate. Given the worker's `resolveIdentity` and an
|
|
434
|
+
* optional `defineIdentity(...)` contract, return a resolver that validates every
|
|
435
|
+
* resolved identity against the declared claims BEFORE it becomes `ctx.auth`.
|
|
436
|
+
*
|
|
437
|
+
* Claims arrive from untrusted tokens; a forged / malformed set is either
|
|
438
|
+
* downgraded to anonymous (`onInvalid: "anonymous"`, the safe default — the bad
|
|
439
|
+
* identity never reaches a policy as valid) or rejected with a `401`
|
|
440
|
+
* (`onInvalid: "reject"`), rather than flowing in as an unchecked cast. A valid
|
|
441
|
+
* identity is returned unchanged, so undeclared claims are forwarded verbatim.
|
|
442
|
+
*
|
|
443
|
+
* When no contract is configured (or there is no `resolveIdentity`), the original
|
|
444
|
+
* resolver is returned untouched — zero overhead and byte-identical behaviour.
|
|
445
|
+
* Only the public data paths (RPC / WebSocket / HTTP-action / server-query) use
|
|
446
|
+
* the wrapped resolver; the admin path keeps the raw one (admin is gated by the
|
|
447
|
+
* bearer / Access, not the app's identity contract).
|
|
448
|
+
*/
|
|
449
|
+
/**
|
|
340
450
|
* Observability hooks for the Lunora runtime.
|
|
341
451
|
*
|
|
342
452
|
* A user-supplied {@link ObservabilitySink} receives one event per dispatched
|
|
@@ -1223,35 +1333,6 @@ interface HttpRouterLike {
|
|
|
1223
1333
|
fetch(request: Request, env?: unknown, context?: ExecutionContextLike): Promise<Response> | Response;
|
|
1224
1334
|
}
|
|
1225
1335
|
/**
|
|
1226
|
-
* Identity resolved from the inbound request by {@link WorkerOptions.resolveIdentity}.
|
|
1227
|
-
*
|
|
1228
|
-
* The `userId` field is special — it becomes `ctx.auth.userId` inside the
|
|
1229
|
-
* Durable Object. Any other keys (`email`, `name`, custom roles, etc.) are
|
|
1230
|
-
* forwarded verbatim as `ctx.auth.getIdentity()`'s return value.
|
|
1231
|
-
*
|
|
1232
|
-
* Return `null` to signal that the request is anonymous; the runtime will
|
|
1233
|
-
* skip both `x-lunora-userid` and `x-lunora-identity` headers, and
|
|
1234
|
-
* `ctx.auth.userId` will be `undefined` on the shard side.
|
|
1235
|
-
*/
|
|
1236
|
-
interface ResolvedIdentity {
|
|
1237
|
-
/** Arbitrary additional claims. Must be JSON-serialisable. */
|
|
1238
|
-
[key: string]: unknown;
|
|
1239
|
-
/**
|
|
1240
|
-
* JWT-standard expiry in epoch SECONDS. When present (and `expiresAtMs` is
|
|
1241
|
-
* absent), the runtime forwards it as the socket's credential expiry — the
|
|
1242
|
-
* DO drops the socket once it lapses. Used only on the WebSocket path.
|
|
1243
|
-
*/
|
|
1244
|
-
exp?: number;
|
|
1245
|
-
/**
|
|
1246
|
-
* Credential expiry in epoch MILLISECONDS. Preferred over `exp` when
|
|
1247
|
-
* both are present. Forwarded as the socket's expiry on the WebSocket path
|
|
1248
|
-
* so the DO drops the socket once it lapses; omit for non-expiring sessions.
|
|
1249
|
-
*/
|
|
1250
|
-
expiresAtMs?: number;
|
|
1251
|
-
/** Stable user identifier (e.g. `"user_2k3..."` or `"u_42"`). */
|
|
1252
|
-
userId: string;
|
|
1253
|
-
}
|
|
1254
|
-
/**
|
|
1255
1336
|
* Per-table sharding metadata the admin import endpoint needs to route rows.
|
|
1256
1337
|
* Structural so this package stays free of `@lunora/server`. The codegen-
|
|
1257
1338
|
* generated worker entry passes a thin projection of the user's schema.
|
|
@@ -1814,6 +1895,17 @@ interface WorkerOptions {
|
|
|
1814
1895
|
*/
|
|
1815
1896
|
httpRouter?: HttpRouterLike;
|
|
1816
1897
|
/**
|
|
1898
|
+
* The declared identity claim contract (`defineIdentity(...)` from
|
|
1899
|
+
* `@lunora/server`), passed by the generated worker entry. When present, the
|
|
1900
|
+
* worker validates every `resolveIdentity` result against it at the trust
|
|
1901
|
+
* boundary (on the public data paths — RPC / WebSocket / HTTP-action /
|
|
1902
|
+
* server-query, never the admin path) *before* the claims become `ctx.auth`.
|
|
1903
|
+
* A resolver output that violates the contract is downgraded to anonymous or
|
|
1904
|
+
* rejected with a `401`, per the contract's `onInvalid`. Omitted → no
|
|
1905
|
+
* validation, and the identity stays the historical untyped claim bag.
|
|
1906
|
+
*/
|
|
1907
|
+
identity?: IdentityContractLike;
|
|
1908
|
+
/**
|
|
1817
1909
|
* Insert `.global()` rows for the admin import endpoint. When omitted,
|
|
1818
1910
|
* rows targeting global tables are reported as hard errors.
|
|
1819
1911
|
*/
|
|
@@ -2423,4 +2515,4 @@ declare const analyticsEngineSink: (options: AnalyticsEngineSinkOptions) => Obse
|
|
|
2423
2515
|
*/
|
|
2424
2516
|
declare const combineSinks: (...sinks: ObservabilitySink[]) => ObservabilitySink;
|
|
2425
2517
|
declare const VERSION: string;
|
|
2426
|
-
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 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 ImportFanOutRequest, type ImportFanOutResult, 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, composeWorker, consoleSink, createCrossShardRelationCapabilities, createDynamicShardRegistry, createLunoraHandler, createQueryCoordinator, createStaticShardRegistry, createWorker, decorateResponse, defineRpcEnvelope, emitLogEvent, emitRpcEvent, enforceOrigin, handleCorsPreflight, mergeStrategyForAggregate, resolveLunoraOptions, resolveSecurity, resolveShard, sentrySink, toAirbyteMessages, toErrorResponse, toFivetranResponse, webhookSink, withFrameworkWorker };
|
|
2518
|
+
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 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 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 };
|
package/dist/index.d.ts
CHANGED
|
@@ -337,6 +337,116 @@ interface FunctionArgumentDescriptor {
|
|
|
337
337
|
* kind only — enough for a signature view without a deep recursive walk.
|
|
338
338
|
*/
|
|
339
339
|
/**
|
|
340
|
+
* Identity resolved from the inbound request by `WorkerOptions.resolveIdentity`.
|
|
341
|
+
*
|
|
342
|
+
* The `userId` field is special — it becomes `ctx.auth.userId` inside the
|
|
343
|
+
* Durable Object. Any other keys (`email`, `name`, custom roles, etc.) are
|
|
344
|
+
* forwarded verbatim as `ctx.auth.getIdentity()`'s return value.
|
|
345
|
+
*
|
|
346
|
+
* Return `null` to signal that the request is anonymous; the runtime will
|
|
347
|
+
* skip both `x-lunora-userid` and `x-lunora-identity` headers, and
|
|
348
|
+
* `ctx.auth.userId` will be `undefined` on the shard side.
|
|
349
|
+
*/
|
|
350
|
+
interface ResolvedIdentity {
|
|
351
|
+
/** Arbitrary additional claims. Must be JSON-serialisable. */
|
|
352
|
+
[key: string]: unknown;
|
|
353
|
+
/**
|
|
354
|
+
* JWT-standard expiry in epoch SECONDS. When present (and `expiresAtMs` is
|
|
355
|
+
* absent), the runtime forwards it as the socket's credential expiry — the
|
|
356
|
+
* DO drops the socket once it lapses. Used only on the WebSocket path.
|
|
357
|
+
*/
|
|
358
|
+
exp?: number;
|
|
359
|
+
/**
|
|
360
|
+
* Credential expiry in epoch MILLISECONDS. Preferred over `exp` when
|
|
361
|
+
* both are present. Forwarded as the socket's expiry on the WebSocket path
|
|
362
|
+
* so the DO drops the socket once it lapses; omit for non-expiring sessions.
|
|
363
|
+
*/
|
|
364
|
+
expiresAtMs?: number;
|
|
365
|
+
/** Stable user identifier (e.g. `"user_2k3..."` or `"u_42"`). */
|
|
366
|
+
userId: string;
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* A verifier that turns an inbound request into a {@link ResolvedIdentity} (or
|
|
370
|
+
* `null` for anonymous). Structurally identical to `WorkerOptions.resolveIdentity`,
|
|
371
|
+
* so `.auth()`'s better-auth session resolver, a signed-preview-link verifier, a
|
|
372
|
+
* per-tenant bearer check, an upstream-JWT reader, … are all just `IdentityResolver`s
|
|
373
|
+
* — the identity layer is generic over every scheme, not coupled to any one.
|
|
374
|
+
*/
|
|
375
|
+
type IdentityResolver = (request: Request, env: unknown) => Promise<ResolvedIdentity | null> | ResolvedIdentity | null;
|
|
376
|
+
/** Error policy for {@link composeIdentityResolvers} when a participant resolver throws. */
|
|
377
|
+
type ComposeIdentityResolversErrorMode = "fail-closed" | "skip";
|
|
378
|
+
/** Options for {@link composeIdentityResolvers}. */
|
|
379
|
+
interface ComposeIdentityResolversOptions {
|
|
380
|
+
/**
|
|
381
|
+
* What to do when a resolver throws. `"fail-closed"` (default, safe)
|
|
382
|
+
* re-throws so a broken verifier fails the request rather than silently
|
|
383
|
+
* falling through to a weaker one; `"skip"` swallows the error and tries the
|
|
384
|
+
* next resolver (use only when a resolver's failure genuinely means "not my
|
|
385
|
+
* scheme").
|
|
386
|
+
*/
|
|
387
|
+
readonly onError?: ComposeIdentityResolversErrorMode;
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Compose several {@link IdentityResolver}s into one, first-match-wins: each is
|
|
391
|
+
* tried in order and the first that returns a non-null identity short-circuits.
|
|
392
|
+
* Generic over every scheme — the better-auth session resolver (obtained via the
|
|
393
|
+
* builder's `derived.resolveIdentity` escape hatch) is just one entry in the list,
|
|
394
|
+
* so composition never means losing it.
|
|
395
|
+
*
|
|
396
|
+
* A resolver that throws is handled per {@link ComposeIdentityResolversOptions.onError}
|
|
397
|
+
* (default `"fail-closed"`: the error propagates).
|
|
398
|
+
*/
|
|
399
|
+
declare const composeIdentityResolvers: (resolvers: ReadonlyArray<IdentityResolver>, options?: ComposeIdentityResolversOptions) => IdentityResolver;
|
|
400
|
+
/**
|
|
401
|
+
* A thin, generic helper over {@link composeIdentityResolvers} for the per-route case:
|
|
402
|
+
* pick a resolver by `new URL(request.url).pathname`. Keys are matched by longest
|
|
403
|
+
* path prefix; `"*"` is the fallback. Still fully generic — a route→resolver map,
|
|
404
|
+
* with no portal / preview / tenant concepts baked in (those live in the app's
|
|
405
|
+
* own resolvers).
|
|
406
|
+
* @example
|
|
407
|
+
* routeIdentityResolvers({ "/admin": adminResolver, "/partner": partnerResolver, "*": sessionResolver })
|
|
408
|
+
*/
|
|
409
|
+
declare const routeIdentityResolvers: (routes: Record<string, IdentityResolver>) => IdentityResolver;
|
|
410
|
+
/** The result of validating a candidate identity against an {@link IdentityContractLike}. */
|
|
411
|
+
type IdentityValidation = {
|
|
412
|
+
ok: true;
|
|
413
|
+
} | {
|
|
414
|
+
error: string;
|
|
415
|
+
ok: false;
|
|
416
|
+
};
|
|
417
|
+
/**
|
|
418
|
+
* Structural view of `@lunora/server`'s `IdentityContract` (from `defineIdentity`).
|
|
419
|
+
* Kept structural so `@lunora/runtime` stays free of an `@lunora/server` dependency.
|
|
420
|
+
* Keep the `onInvalid` union and `validate`/`IdentityValidation` shapes in sync with
|
|
421
|
+
* `@lunora/server`'s `IdentityContract` — they are projected by hand, not imported.
|
|
422
|
+
* The generated worker entry passes the app's `defineIdentity(...)` result here;
|
|
423
|
+
* the worker validates every resolver's returned claims against it at the trust
|
|
424
|
+
* boundary before they become `ctx.auth`.
|
|
425
|
+
*/
|
|
426
|
+
interface IdentityContractLike {
|
|
427
|
+
/** Reject policy applied when validation fails: downgrade to anonymous, or reject the request (401). */
|
|
428
|
+
readonly onInvalid: "anonymous" | "reject";
|
|
429
|
+
/** Validate resolver-returned claims against the declared contract. */
|
|
430
|
+
validate: (identity: Record<string, unknown>) => IdentityValidation;
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* The trust-boundary identity gate. Given the worker's `resolveIdentity` and an
|
|
434
|
+
* optional `defineIdentity(...)` contract, return a resolver that validates every
|
|
435
|
+
* resolved identity against the declared claims BEFORE it becomes `ctx.auth`.
|
|
436
|
+
*
|
|
437
|
+
* Claims arrive from untrusted tokens; a forged / malformed set is either
|
|
438
|
+
* downgraded to anonymous (`onInvalid: "anonymous"`, the safe default — the bad
|
|
439
|
+
* identity never reaches a policy as valid) or rejected with a `401`
|
|
440
|
+
* (`onInvalid: "reject"`), rather than flowing in as an unchecked cast. A valid
|
|
441
|
+
* identity is returned unchanged, so undeclared claims are forwarded verbatim.
|
|
442
|
+
*
|
|
443
|
+
* When no contract is configured (or there is no `resolveIdentity`), the original
|
|
444
|
+
* resolver is returned untouched — zero overhead and byte-identical behaviour.
|
|
445
|
+
* Only the public data paths (RPC / WebSocket / HTTP-action / server-query) use
|
|
446
|
+
* the wrapped resolver; the admin path keeps the raw one (admin is gated by the
|
|
447
|
+
* bearer / Access, not the app's identity contract).
|
|
448
|
+
*/
|
|
449
|
+
/**
|
|
340
450
|
* Observability hooks for the Lunora runtime.
|
|
341
451
|
*
|
|
342
452
|
* A user-supplied {@link ObservabilitySink} receives one event per dispatched
|
|
@@ -1223,35 +1333,6 @@ interface HttpRouterLike {
|
|
|
1223
1333
|
fetch(request: Request, env?: unknown, context?: ExecutionContextLike): Promise<Response> | Response;
|
|
1224
1334
|
}
|
|
1225
1335
|
/**
|
|
1226
|
-
* Identity resolved from the inbound request by {@link WorkerOptions.resolveIdentity}.
|
|
1227
|
-
*
|
|
1228
|
-
* The `userId` field is special — it becomes `ctx.auth.userId` inside the
|
|
1229
|
-
* Durable Object. Any other keys (`email`, `name`, custom roles, etc.) are
|
|
1230
|
-
* forwarded verbatim as `ctx.auth.getIdentity()`'s return value.
|
|
1231
|
-
*
|
|
1232
|
-
* Return `null` to signal that the request is anonymous; the runtime will
|
|
1233
|
-
* skip both `x-lunora-userid` and `x-lunora-identity` headers, and
|
|
1234
|
-
* `ctx.auth.userId` will be `undefined` on the shard side.
|
|
1235
|
-
*/
|
|
1236
|
-
interface ResolvedIdentity {
|
|
1237
|
-
/** Arbitrary additional claims. Must be JSON-serialisable. */
|
|
1238
|
-
[key: string]: unknown;
|
|
1239
|
-
/**
|
|
1240
|
-
* JWT-standard expiry in epoch SECONDS. When present (and `expiresAtMs` is
|
|
1241
|
-
* absent), the runtime forwards it as the socket's credential expiry — the
|
|
1242
|
-
* DO drops the socket once it lapses. Used only on the WebSocket path.
|
|
1243
|
-
*/
|
|
1244
|
-
exp?: number;
|
|
1245
|
-
/**
|
|
1246
|
-
* Credential expiry in epoch MILLISECONDS. Preferred over `exp` when
|
|
1247
|
-
* both are present. Forwarded as the socket's expiry on the WebSocket path
|
|
1248
|
-
* so the DO drops the socket once it lapses; omit for non-expiring sessions.
|
|
1249
|
-
*/
|
|
1250
|
-
expiresAtMs?: number;
|
|
1251
|
-
/** Stable user identifier (e.g. `"user_2k3..."` or `"u_42"`). */
|
|
1252
|
-
userId: string;
|
|
1253
|
-
}
|
|
1254
|
-
/**
|
|
1255
1336
|
* Per-table sharding metadata the admin import endpoint needs to route rows.
|
|
1256
1337
|
* Structural so this package stays free of `@lunora/server`. The codegen-
|
|
1257
1338
|
* generated worker entry passes a thin projection of the user's schema.
|
|
@@ -1814,6 +1895,17 @@ interface WorkerOptions {
|
|
|
1814
1895
|
*/
|
|
1815
1896
|
httpRouter?: HttpRouterLike;
|
|
1816
1897
|
/**
|
|
1898
|
+
* The declared identity claim contract (`defineIdentity(...)` from
|
|
1899
|
+
* `@lunora/server`), passed by the generated worker entry. When present, the
|
|
1900
|
+
* worker validates every `resolveIdentity` result against it at the trust
|
|
1901
|
+
* boundary (on the public data paths — RPC / WebSocket / HTTP-action /
|
|
1902
|
+
* server-query, never the admin path) *before* the claims become `ctx.auth`.
|
|
1903
|
+
* A resolver output that violates the contract is downgraded to anonymous or
|
|
1904
|
+
* rejected with a `401`, per the contract's `onInvalid`. Omitted → no
|
|
1905
|
+
* validation, and the identity stays the historical untyped claim bag.
|
|
1906
|
+
*/
|
|
1907
|
+
identity?: IdentityContractLike;
|
|
1908
|
+
/**
|
|
1817
1909
|
* Insert `.global()` rows for the admin import endpoint. When omitted,
|
|
1818
1910
|
* rows targeting global tables are reported as hard errors.
|
|
1819
1911
|
*/
|
|
@@ -2423,4 +2515,4 @@ declare const analyticsEngineSink: (options: AnalyticsEngineSinkOptions) => Obse
|
|
|
2423
2515
|
*/
|
|
2424
2516
|
declare const combineSinks: (...sinks: ObservabilitySink[]) => ObservabilitySink;
|
|
2425
2517
|
declare const VERSION: string;
|
|
2426
|
-
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 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 ImportFanOutRequest, type ImportFanOutResult, 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, composeWorker, consoleSink, createCrossShardRelationCapabilities, createDynamicShardRegistry, createLunoraHandler, createQueryCoordinator, createStaticShardRegistry, createWorker, decorateResponse, defineRpcEnvelope, emitLogEvent, emitRpcEvent, enforceOrigin, handleCorsPreflight, mergeStrategyForAggregate, resolveLunoraOptions, resolveSecurity, resolveShard, sentrySink, toAirbyteMessages, toErrorResponse, toFivetranResponse, webhookSink, withFrameworkWorker };
|
|
2518
|
+
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 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 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 };
|
package/dist/index.mjs
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
export { toAirbyteMessages, toFivetranResponse } from './packem_shared/toAirbyteMessages-DrHdplb4.mjs';
|
|
2
|
-
export { composeWorker, createLunoraHandler, createWorker, defineRpcEnvelope, resolveLunoraOptions, withFrameworkWorker } from './packem_shared/composeWorker-
|
|
2
|
+
export { composeWorker, createLunoraHandler, createWorker, defineRpcEnvelope, resolveLunoraOptions, withFrameworkWorker } from './packem_shared/composeWorker-BOB2YZ6v.mjs';
|
|
3
3
|
export { createCrossShardRelationCapabilities } from './packem_shared/createCrossShardRelationCapabilities-C0KOf7er.mjs';
|
|
4
4
|
export { DEFAULT_REGISTRY_CACHE_TTL_MS, SHARD_REGISTRY_DO_NAME, createDynamicShardRegistry } from './packem_shared/DEFAULT_REGISTRY_CACHE_TTL_MS-ocax8v0n.mjs';
|
|
5
5
|
export { LunoraError, toErrorResponse } from './packem_shared/LunoraError-CL0aOtpo.mjs';
|
|
6
6
|
export { emitLogEvent, emitRpcEvent } from './packem_shared/emitLogEvent-pEdtqAK8.mjs';
|
|
7
7
|
export { analyticsEngineSink, combineSinks, consoleSink, sentrySink, webhookSink } from './packem_shared/analyticsEngineSink-DqEvrQs0.mjs';
|
|
8
|
-
export { createQueryCoordinator, createStaticShardRegistry, mergeStrategyForAggregate } from './packem_shared/createQueryCoordinator-
|
|
8
|
+
export { createQueryCoordinator, createStaticShardRegistry, mergeStrategyForAggregate } from './packem_shared/createQueryCoordinator-ZeZYUPNu.mjs';
|
|
9
9
|
export { applyJurisdiction, resolveShard } from './packem_shared/applyJurisdiction-BkZtTkct.mjs';
|
|
10
10
|
export { decorateResponse, enforceOrigin, handleCorsPreflight, resolveSecurity } from './packem_shared/decorateResponse-DbISh_Wi.mjs';
|
|
11
11
|
export { NOOP_EXECUTION_CONTEXT } from './packem_shared/NOOP_EXECUTION_CONTEXT-CCTu0Bf1.mjs';
|
|
12
|
+
export { composeIdentityResolvers, routeIdentityResolvers } from './packem_shared/composeIdentityResolvers-YjvUKisc.mjs';
|
|
12
13
|
|
|
13
14
|
const VERSION = "0.0.0";
|
|
14
15
|
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { LunoraError } from './LunoraError-CL0aOtpo.mjs';
|
|
2
|
+
|
|
3
|
+
const composeIdentityResolvers = (resolvers, options = {}) => {
|
|
4
|
+
const onError = options.onError ?? "fail-closed";
|
|
5
|
+
return async (request, env) => {
|
|
6
|
+
for (const resolver of resolvers) {
|
|
7
|
+
let resolved;
|
|
8
|
+
try {
|
|
9
|
+
resolved = await resolver(request, env);
|
|
10
|
+
} catch (error) {
|
|
11
|
+
if (onError === "skip") {
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
14
|
+
throw error;
|
|
15
|
+
}
|
|
16
|
+
if (resolved) {
|
|
17
|
+
return resolved;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return null;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
const routeIdentityResolvers = (routes) => {
|
|
24
|
+
const prefixes = Object.keys(routes).filter((key) => key !== "*").toSorted((a, b) => b.length - a.length);
|
|
25
|
+
return (request, env) => {
|
|
26
|
+
const { pathname } = new URL(request.url);
|
|
27
|
+
const matched = prefixes.find((prefix) => pathname === prefix || pathname.startsWith(prefix.endsWith("/") ? prefix : `${prefix}/`));
|
|
28
|
+
const resolver = matched === void 0 ? routes["*"] : routes[matched];
|
|
29
|
+
if (resolver === void 0) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
return resolver(request, env);
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
const wrapResolverWithContract = (baseResolveIdentity, contract) => {
|
|
36
|
+
if (contract === void 0 || baseResolveIdentity === void 0) {
|
|
37
|
+
return baseResolveIdentity;
|
|
38
|
+
}
|
|
39
|
+
return async (request, env) => {
|
|
40
|
+
const resolved = await baseResolveIdentity(request, env);
|
|
41
|
+
if (!resolved) {
|
|
42
|
+
return resolved;
|
|
43
|
+
}
|
|
44
|
+
const result = contract.validate(resolved);
|
|
45
|
+
if (result.ok) {
|
|
46
|
+
return resolved;
|
|
47
|
+
}
|
|
48
|
+
if (contract.onInvalid === "reject") {
|
|
49
|
+
throw new LunoraError(`identity claims failed the declared contract: ${result.error}`, { code: "UNAUTHENTICATED", status: 401 });
|
|
50
|
+
}
|
|
51
|
+
return null;
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export { composeIdentityResolvers, routeIdentityResolvers, wrapResolverWithContract };
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { NOOP_EXECUTION_CONTEXT } from './NOOP_EXECUTION_CONTEXT-CCTu0Bf1.mjs';
|
|
2
2
|
import { LunoraError, toErrorResponse, isStructuralLunoraError, isStructuralConflictError } from './LunoraError-CL0aOtpo.mjs';
|
|
3
|
+
import { wrapResolverWithContract } from './composeIdentityResolvers-YjvUKisc.mjs';
|
|
4
|
+
export { composeIdentityResolvers, routeIdentityResolvers } from './composeIdentityResolvers-YjvUKisc.mjs';
|
|
3
5
|
import { emitRpcEvent } from './emitLogEvent-pEdtqAK8.mjs';
|
|
4
6
|
import { resolveShard, applyJurisdiction } from './applyJurisdiction-BkZtTkct.mjs';
|
|
5
7
|
import { resolveSecurity, handleCorsPreflight, enforceOrigin, decorateResponse } from './decorateResponse-DbISh_Wi.mjs';
|
|
@@ -1836,6 +1838,7 @@ const checkAdminWsToken = (request, expected) => {
|
|
|
1836
1838
|
};
|
|
1837
1839
|
const createWorker = (options) => {
|
|
1838
1840
|
const defaultShard = options.defaultShardKey ?? "__root__";
|
|
1841
|
+
const publicResolveIdentity = wrapResolverWithContract(options.resolveIdentity, options.identity);
|
|
1839
1842
|
const shardDO = applyJurisdiction(options.shardDO, options.jurisdiction);
|
|
1840
1843
|
const schedulerDO = options.schedulerDO === void 0 ? void 0 : applyJurisdiction(options.schedulerDO, options.jurisdiction);
|
|
1841
1844
|
let envAdminToken;
|
|
@@ -2108,7 +2111,7 @@ const createWorker = (options) => {
|
|
|
2108
2111
|
requireAdminOption
|
|
2109
2112
|
});
|
|
2110
2113
|
const buildHttpActionContext = async (request, env) => {
|
|
2111
|
-
const { claims, headers, userId } = await resolveForwardContext(request, env,
|
|
2114
|
+
const { claims, headers, userId } = await resolveForwardContext(request, env, publicResolveIdentity);
|
|
2112
2115
|
const run = async (reference, args = {}) => {
|
|
2113
2116
|
const functionPath = reference.__lunoraRef;
|
|
2114
2117
|
if (typeof functionPath !== "string") {
|
|
@@ -2157,7 +2160,7 @@ const createWorker = (options) => {
|
|
|
2157
2160
|
throw new LunoraError("WebSocket upgrade header missing", { code: "BAD_REQUEST", status: 426 });
|
|
2158
2161
|
}
|
|
2159
2162
|
const shardKey = url.searchParams.get("shard") ?? defaultShard;
|
|
2160
|
-
const { headers: forwardedHeaders, identity } = await resolveForwardContext(request, env,
|
|
2163
|
+
const { headers: forwardedHeaders, identity } = await resolveForwardContext(request, env, publicResolveIdentity);
|
|
2161
2164
|
if (options.authorizeShard) {
|
|
2162
2165
|
const allowed = await options.authorizeShard(identity, shardKey);
|
|
2163
2166
|
if (!allowed) {
|
|
@@ -2282,7 +2285,7 @@ const createWorker = (options) => {
|
|
|
2282
2285
|
status: 400
|
|
2283
2286
|
});
|
|
2284
2287
|
}
|
|
2285
|
-
const { headers: forwardedHeaders, identity } = await resolveForwardContext(request, env,
|
|
2288
|
+
const { headers: forwardedHeaders, identity } = await resolveForwardContext(request, env, publicResolveIdentity);
|
|
2286
2289
|
await authorizeRpcEnvelope(envelope, identity);
|
|
2287
2290
|
{
|
|
2288
2291
|
const rpcStartedAt = Date.now();
|
|
@@ -2457,7 +2460,7 @@ const createWorker = (options) => {
|
|
|
2457
2460
|
if (typeof functionPath !== "string") {
|
|
2458
2461
|
throw new LunoraError("serverQuery: expected a function reference from the generated `api`", { code: "BAD_REQUEST", status: 400 });
|
|
2459
2462
|
}
|
|
2460
|
-
const { headers: forwardedHeaders, identity } = await resolveForwardContext(request, env,
|
|
2463
|
+
const { headers: forwardedHeaders, identity } = await resolveForwardContext(request, env, publicResolveIdentity);
|
|
2461
2464
|
await authorizeRpcEnvelope({ functionPath, shardKey: callOptions.shardKey }, identity);
|
|
2462
2465
|
const shardKey = callOptions.shardKey ?? defaultShard;
|
|
2463
2466
|
return await dispatchSingleShard(functionPath, args, shardKey, forwardedHeaders);
|
|
@@ -547,7 +547,16 @@ const mergeTopK = (values, strategy) => {
|
|
|
547
547
|
}
|
|
548
548
|
}
|
|
549
549
|
const direction = strategy.direction ?? "desc";
|
|
550
|
-
|
|
550
|
+
const ascending = (x, y) => {
|
|
551
|
+
if (x < y) {
|
|
552
|
+
return -1;
|
|
553
|
+
}
|
|
554
|
+
if (x > y) {
|
|
555
|
+
return 1;
|
|
556
|
+
}
|
|
557
|
+
return 0;
|
|
558
|
+
};
|
|
559
|
+
collected.sort((a, b) => direction === "asc" ? ascending(a.score, b.score) : ascending(b.score, a.score));
|
|
551
560
|
return collected.slice(0, strategy.k).map((entry) => entry.row);
|
|
552
561
|
};
|
|
553
562
|
const mergeShardResults = (values, strategy) => {
|