@lunora/runtime 1.0.0-alpha.15 → 1.0.0-alpha.17
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 +35 -40
- package/dist/index.d.ts +35 -40
- package/dist/index.mjs +7 -7
- package/dist/packem_shared/{DEFAULT_REGISTRY_CACHE_TTL_MS-ocax8v0n.mjs → DEFAULT_REGISTRY_CACHE_TTL_MS-B3pA7aXp.mjs} +5 -4
- package/dist/packem_shared/LunoraError-Bpb9EFJ3.mjs +22 -0
- package/dist/packem_shared/{composeIdentityResolvers-YjvUKisc.mjs → composeIdentityResolvers-XGjO7V1J.mjs} +1 -1
- package/dist/packem_shared/{composeWorker-BUf56-tZ.mjs → composeWorker-DU8vDrgA.mjs} +8 -7
- package/dist/packem_shared/{createCrossShardRelationCapabilities-C0KOf7er.mjs → createCrossShardRelationCapabilities-CbcWjkAn.mjs} +4 -2
- package/dist/packem_shared/{createQueryCoordinator-ZeZYUPNu.mjs → createQueryCoordinator-DNCJzOZE.mjs} +1 -1
- package/dist/packem_shared/{decorateResponse-HRXo-oOD.mjs → decorateResponse-CsZc49QC.mjs} +7 -6
- package/package.json +4 -1
- package/dist/packem_shared/LunoraError-CL0aOtpo.mjs +0 -46
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).
|
|
@@ -1194,24 +1195,6 @@ interface ShardTrafficFanOutResult {
|
|
|
1194
1195
|
shards: ReadonlyArray<ShardTrafficEntry>;
|
|
1195
1196
|
}
|
|
1196
1197
|
declare const createQueryCoordinator: (options: QueryCoordinatorOptions) => QueryCoordinator;
|
|
1197
|
-
/**
|
|
1198
|
-
* Secure-by-default HTTP edge for the Lunora worker.
|
|
1199
|
-
*
|
|
1200
|
-
* The worker's top-level `fetch` (see `./create-worker`) is the single choke
|
|
1201
|
-
* point every response passes through — RPC, auth, admin, `httpRoute` handlers,
|
|
1202
|
-
* and the SSR fallback alike. This module supplies what is applied there:
|
|
1203
|
-
* `decorateResponse` adds baseline security headers plus, for allowed
|
|
1204
|
-
* cross-origin requests, the matching `Access-Control-Allow-*` headers (never
|
|
1205
|
-
* overwriting a header the inner handler set); `handleCorsPreflight` answers
|
|
1206
|
-
* `OPTIONS` preflights for allowlisted origins; `enforceOrigin` is a CSRF guard
|
|
1207
|
-
* that rejects state-changing, cookie-authenticated requests from untrusted
|
|
1208
|
-
* origins.
|
|
1209
|
-
*
|
|
1210
|
-
* Every layer is on by default and individually disable-able through the
|
|
1211
|
-
* `SecurityOptions` passed to `createWorker`. Resolution (`resolveSecurity`) is
|
|
1212
|
-
* pure and platform-agnostic — it touches only the global `Request`/`Response`/
|
|
1213
|
-
* `Headers`/`URL`, so it unit-tests under plain Node without workerd.
|
|
1214
|
-
*/
|
|
1215
1198
|
/** Per-header overrides for {@link SecurityHeadersOptions}. `false` omits the header. */
|
|
1216
1199
|
interface SecurityHeadersOptions {
|
|
1217
1200
|
/**
|
|
@@ -1808,15 +1791,19 @@ interface WorkerOptions {
|
|
|
1808
1791
|
/**
|
|
1809
1792
|
* Opt into an authorization-open posture for sharded and fan-out access.
|
|
1810
1793
|
*
|
|
1811
|
-
* By default (this flag unset/`false`) the runtime FAILS CLOSED
|
|
1812
|
-
*
|
|
1813
|
-
*
|
|
1814
|
-
*
|
|
1815
|
-
* (`
|
|
1816
|
-
*
|
|
1817
|
-
*
|
|
1818
|
-
*
|
|
1819
|
-
*
|
|
1794
|
+
* By default (this flag unset/`false`) the runtime FAILS CLOSED per
|
|
1795
|
+
* operation: naming a non-default shard (a potential cross-tenant hop) is
|
|
1796
|
+
* rejected with a `403` (`FORBIDDEN_SHARD`) unless
|
|
1797
|
+
* {@link WorkerOptions.authorizeShard} is configured, and a fan-out
|
|
1798
|
+
* envelope is rejected (`FORBIDDEN_FANOUT`) unless
|
|
1799
|
+
* {@link WorkerOptions.authorizeFanOut} is. Set this to `true` to allow
|
|
1800
|
+
* such requests from any caller (including unauthenticated ones) —
|
|
1801
|
+
* appropriate only when every table is protected by per-row RLS. The
|
|
1802
|
+
* runtime then emits a single `console.warn` so the open posture stays
|
|
1803
|
+
* visible in logs. The flag is consulted per operation: it has no effect
|
|
1804
|
+
* on an operation whose own `authorize*` callback is configured (that
|
|
1805
|
+
* callback gates directly), but configuring only one of the two callbacks
|
|
1806
|
+
* does NOT cover the other operation.
|
|
1820
1807
|
*
|
|
1821
1808
|
* NOTE: this is a behaviour change from earlier alphas, where the same
|
|
1822
1809
|
* situation was warn-once-then-allow. Apps that relied on client-chosen
|
|
@@ -2463,18 +2450,30 @@ interface DynamicShardRegistry extends ShardRegistry {
|
|
|
2463
2450
|
}
|
|
2464
2451
|
declare const createDynamicShardRegistry: (options: DynamicShardRegistryOptions) => DynamicShardRegistry;
|
|
2465
2452
|
interface LunoraErrorBody {
|
|
2466
|
-
error:
|
|
2467
|
-
code: string;
|
|
2468
|
-
message: string;
|
|
2469
|
-
};
|
|
2453
|
+
error: ErrorBody;
|
|
2470
2454
|
}
|
|
2471
2455
|
/**
|
|
2472
|
-
*
|
|
2473
|
-
*
|
|
2456
|
+
* Convert any thrown value into a JSON error response.
|
|
2457
|
+
*
|
|
2458
|
+
* Delegates the envelope + redaction to `@lunora/errors`' {@link toErrorBody} —
|
|
2459
|
+
* a non-internal `LunoraError` (from `@lunora/server`, this runtime, or the
|
|
2460
|
+
* `@lunora/do` data layer) is echoed with its `code`/`message`/`hint`/`docsUrl`;
|
|
2461
|
+
* an internal-coded error keeps its status but its message is redacted; anything
|
|
2462
|
+
* else becomes a generic `INTERNAL` 500. All of these share the unified shape
|
|
2463
|
+
* recognized by `isLunoraError`.
|
|
2464
|
+
*/
|
|
2465
|
+
declare const toErrorResponse: (error: unknown) => Response;
|
|
2466
|
+
/**
|
|
2467
|
+
* Transport-level error for the worker entry. A thin ergonomic wrapper over the
|
|
2468
|
+
* shared `@lunora/errors` `LunoraError` that keeps the runtime's historical
|
|
2469
|
+
* `(message, { code, status })` signature — the runtime mints these with
|
|
2470
|
+
* dispatch-specific codes (`METHOD_NOT_ALLOWED`, `*_NOT_CONFIGURED`, …) and an
|
|
2471
|
+
* explicit status, so they don't need a central catalog entry. Because it is a
|
|
2472
|
+
* real `LunoraError`, it carries the unified wire shape and is recognized by
|
|
2473
|
+
* `isLunoraError` everywhere. Anything thrown that isn't a `LunoraError`
|
|
2474
|
+
* is mapped to a generic 500 with code `INTERNAL`.
|
|
2474
2475
|
*/
|
|
2475
|
-
declare class LunoraError extends
|
|
2476
|
-
readonly code: string;
|
|
2477
|
-
readonly status: number;
|
|
2476
|
+
declare class LunoraError extends LunoraError$1 {
|
|
2478
2477
|
constructor(message: string, options?: {
|
|
2479
2478
|
cause?: unknown;
|
|
2480
2479
|
code?: string;
|
|
@@ -2482,10 +2481,6 @@ declare class LunoraError extends Error {
|
|
|
2482
2481
|
});
|
|
2483
2482
|
toResponse(): Response;
|
|
2484
2483
|
}
|
|
2485
|
-
/** Shape recognised by the runtime's structural error checks. */
|
|
2486
|
-
|
|
2487
|
-
/** Convert any thrown value into a JSON error response. */
|
|
2488
|
-
declare const toErrorResponse: (error: unknown) => Response;
|
|
2489
2484
|
/** Shared shape for sinks that can be limited to error events only. */
|
|
2490
2485
|
interface OnlyErrorsOption {
|
|
2491
2486
|
/** When true, only events with `ok === false` are forwarded. */
|
package/dist/index.d.ts
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).
|
|
@@ -1194,24 +1195,6 @@ interface ShardTrafficFanOutResult {
|
|
|
1194
1195
|
shards: ReadonlyArray<ShardTrafficEntry>;
|
|
1195
1196
|
}
|
|
1196
1197
|
declare const createQueryCoordinator: (options: QueryCoordinatorOptions) => QueryCoordinator;
|
|
1197
|
-
/**
|
|
1198
|
-
* Secure-by-default HTTP edge for the Lunora worker.
|
|
1199
|
-
*
|
|
1200
|
-
* The worker's top-level `fetch` (see `./create-worker`) is the single choke
|
|
1201
|
-
* point every response passes through — RPC, auth, admin, `httpRoute` handlers,
|
|
1202
|
-
* and the SSR fallback alike. This module supplies what is applied there:
|
|
1203
|
-
* `decorateResponse` adds baseline security headers plus, for allowed
|
|
1204
|
-
* cross-origin requests, the matching `Access-Control-Allow-*` headers (never
|
|
1205
|
-
* overwriting a header the inner handler set); `handleCorsPreflight` answers
|
|
1206
|
-
* `OPTIONS` preflights for allowlisted origins; `enforceOrigin` is a CSRF guard
|
|
1207
|
-
* that rejects state-changing, cookie-authenticated requests from untrusted
|
|
1208
|
-
* origins.
|
|
1209
|
-
*
|
|
1210
|
-
* Every layer is on by default and individually disable-able through the
|
|
1211
|
-
* `SecurityOptions` passed to `createWorker`. Resolution (`resolveSecurity`) is
|
|
1212
|
-
* pure and platform-agnostic — it touches only the global `Request`/`Response`/
|
|
1213
|
-
* `Headers`/`URL`, so it unit-tests under plain Node without workerd.
|
|
1214
|
-
*/
|
|
1215
1198
|
/** Per-header overrides for {@link SecurityHeadersOptions}. `false` omits the header. */
|
|
1216
1199
|
interface SecurityHeadersOptions {
|
|
1217
1200
|
/**
|
|
@@ -1808,15 +1791,19 @@ interface WorkerOptions {
|
|
|
1808
1791
|
/**
|
|
1809
1792
|
* Opt into an authorization-open posture for sharded and fan-out access.
|
|
1810
1793
|
*
|
|
1811
|
-
* By default (this flag unset/`false`) the runtime FAILS CLOSED
|
|
1812
|
-
*
|
|
1813
|
-
*
|
|
1814
|
-
*
|
|
1815
|
-
* (`
|
|
1816
|
-
*
|
|
1817
|
-
*
|
|
1818
|
-
*
|
|
1819
|
-
*
|
|
1794
|
+
* By default (this flag unset/`false`) the runtime FAILS CLOSED per
|
|
1795
|
+
* operation: naming a non-default shard (a potential cross-tenant hop) is
|
|
1796
|
+
* rejected with a `403` (`FORBIDDEN_SHARD`) unless
|
|
1797
|
+
* {@link WorkerOptions.authorizeShard} is configured, and a fan-out
|
|
1798
|
+
* envelope is rejected (`FORBIDDEN_FANOUT`) unless
|
|
1799
|
+
* {@link WorkerOptions.authorizeFanOut} is. Set this to `true` to allow
|
|
1800
|
+
* such requests from any caller (including unauthenticated ones) —
|
|
1801
|
+
* appropriate only when every table is protected by per-row RLS. The
|
|
1802
|
+
* runtime then emits a single `console.warn` so the open posture stays
|
|
1803
|
+
* visible in logs. The flag is consulted per operation: it has no effect
|
|
1804
|
+
* on an operation whose own `authorize*` callback is configured (that
|
|
1805
|
+
* callback gates directly), but configuring only one of the two callbacks
|
|
1806
|
+
* does NOT cover the other operation.
|
|
1820
1807
|
*
|
|
1821
1808
|
* NOTE: this is a behaviour change from earlier alphas, where the same
|
|
1822
1809
|
* situation was warn-once-then-allow. Apps that relied on client-chosen
|
|
@@ -2463,18 +2450,30 @@ interface DynamicShardRegistry extends ShardRegistry {
|
|
|
2463
2450
|
}
|
|
2464
2451
|
declare const createDynamicShardRegistry: (options: DynamicShardRegistryOptions) => DynamicShardRegistry;
|
|
2465
2452
|
interface LunoraErrorBody {
|
|
2466
|
-
error:
|
|
2467
|
-
code: string;
|
|
2468
|
-
message: string;
|
|
2469
|
-
};
|
|
2453
|
+
error: ErrorBody;
|
|
2470
2454
|
}
|
|
2471
2455
|
/**
|
|
2472
|
-
*
|
|
2473
|
-
*
|
|
2456
|
+
* Convert any thrown value into a JSON error response.
|
|
2457
|
+
*
|
|
2458
|
+
* Delegates the envelope + redaction to `@lunora/errors`' {@link toErrorBody} —
|
|
2459
|
+
* a non-internal `LunoraError` (from `@lunora/server`, this runtime, or the
|
|
2460
|
+
* `@lunora/do` data layer) is echoed with its `code`/`message`/`hint`/`docsUrl`;
|
|
2461
|
+
* an internal-coded error keeps its status but its message is redacted; anything
|
|
2462
|
+
* else becomes a generic `INTERNAL` 500. All of these share the unified shape
|
|
2463
|
+
* recognized by `isLunoraError`.
|
|
2464
|
+
*/
|
|
2465
|
+
declare const toErrorResponse: (error: unknown) => Response;
|
|
2466
|
+
/**
|
|
2467
|
+
* Transport-level error for the worker entry. A thin ergonomic wrapper over the
|
|
2468
|
+
* shared `@lunora/errors` `LunoraError` that keeps the runtime's historical
|
|
2469
|
+
* `(message, { code, status })` signature — the runtime mints these with
|
|
2470
|
+
* dispatch-specific codes (`METHOD_NOT_ALLOWED`, `*_NOT_CONFIGURED`, …) and an
|
|
2471
|
+
* explicit status, so they don't need a central catalog entry. Because it is a
|
|
2472
|
+
* real `LunoraError`, it carries the unified wire shape and is recognized by
|
|
2473
|
+
* `isLunoraError` everywhere. Anything thrown that isn't a `LunoraError`
|
|
2474
|
+
* is mapped to a generic 500 with code `INTERNAL`.
|
|
2474
2475
|
*/
|
|
2475
|
-
declare class LunoraError extends
|
|
2476
|
-
readonly code: string;
|
|
2477
|
-
readonly status: number;
|
|
2476
|
+
declare class LunoraError extends LunoraError$1 {
|
|
2478
2477
|
constructor(message: string, options?: {
|
|
2479
2478
|
cause?: unknown;
|
|
2480
2479
|
code?: string;
|
|
@@ -2482,10 +2481,6 @@ declare class LunoraError extends Error {
|
|
|
2482
2481
|
});
|
|
2483
2482
|
toResponse(): Response;
|
|
2484
2483
|
}
|
|
2485
|
-
/** Shape recognised by the runtime's structural error checks. */
|
|
2486
|
-
|
|
2487
|
-
/** Convert any thrown value into a JSON error response. */
|
|
2488
|
-
declare const toErrorResponse: (error: unknown) => Response;
|
|
2489
2484
|
/** Shared shape for sinks that can be limited to error events only. */
|
|
2490
2485
|
interface OnlyErrorsOption {
|
|
2491
2486
|
/** When true, only events with `ok === false` are forwarded. */
|
package/dist/index.mjs
CHANGED
|
@@ -1,15 +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-
|
|
3
|
-
export { createCrossShardRelationCapabilities } from './packem_shared/createCrossShardRelationCapabilities-
|
|
4
|
-
export { DEFAULT_REGISTRY_CACHE_TTL_MS, SHARD_REGISTRY_DO_NAME, createDynamicShardRegistry } from './packem_shared/DEFAULT_REGISTRY_CACHE_TTL_MS-
|
|
5
|
-
export { LunoraError, toErrorResponse } from './packem_shared/LunoraError-
|
|
2
|
+
export { composeWorker, createLunoraHandler, createWorker, defineRpcEnvelope, resolveLunoraOptions, withFrameworkWorker } from './packem_shared/composeWorker-DU8vDrgA.mjs';
|
|
3
|
+
export { createCrossShardRelationCapabilities } from './packem_shared/createCrossShardRelationCapabilities-CbcWjkAn.mjs';
|
|
4
|
+
export { DEFAULT_REGISTRY_CACHE_TTL_MS, SHARD_REGISTRY_DO_NAME, createDynamicShardRegistry } from './packem_shared/DEFAULT_REGISTRY_CACHE_TTL_MS-B3pA7aXp.mjs';
|
|
5
|
+
export { LunoraError, toErrorResponse } from './packem_shared/LunoraError-Bpb9EFJ3.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-DNCJzOZE.mjs';
|
|
9
9
|
export { applyJurisdiction, resolveShard } from './packem_shared/applyJurisdiction-BkZtTkct.mjs';
|
|
10
|
-
export { decorateResponse, enforceOrigin, handleCorsPreflight, resolveSecurity } from './packem_shared/decorateResponse-
|
|
10
|
+
export { decorateResponse, enforceOrigin, handleCorsPreflight, resolveSecurity } from './packem_shared/decorateResponse-CsZc49QC.mjs';
|
|
11
11
|
export { NOOP_EXECUTION_CONTEXT } from './packem_shared/NOOP_EXECUTION_CONTEXT-CCTu0Bf1.mjs';
|
|
12
|
-
export { composeIdentityResolvers, routeIdentityResolvers } from './packem_shared/composeIdentityResolvers-
|
|
12
|
+
export { composeIdentityResolvers, routeIdentityResolvers } from './packem_shared/composeIdentityResolvers-XGjO7V1J.mjs';
|
|
13
13
|
|
|
14
14
|
const VERSION = "0.0.0";
|
|
15
15
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { LunoraError } from './LunoraError-Bpb9EFJ3.mjs';
|
|
1
2
|
import { applyJurisdiction } from './applyJurisdiction-BkZtTkct.mjs';
|
|
2
3
|
|
|
3
4
|
const SHARD_REGISTRY_DO_NAME = "__lunora_shard_registry__";
|
|
@@ -42,7 +43,7 @@ const createDynamicShardRegistry = (options) => {
|
|
|
42
43
|
}
|
|
43
44
|
const response = await get(`/list?table=${encodeURIComponent(table)}`);
|
|
44
45
|
if (!response.ok) {
|
|
45
|
-
throw new
|
|
46
|
+
throw new LunoraError(`shard registry /list returned ${String(response.status)}`);
|
|
46
47
|
}
|
|
47
48
|
const { shardKeys } = await decodeJson(response);
|
|
48
49
|
if (cacheTtlMs > 0) {
|
|
@@ -53,14 +54,14 @@ const createDynamicShardRegistry = (options) => {
|
|
|
53
54
|
async register(table, shardKey) {
|
|
54
55
|
const response = await post("/register", { shardKey, table });
|
|
55
56
|
if (!response.ok) {
|
|
56
|
-
throw new
|
|
57
|
+
throw new LunoraError(`shard registry /register returned ${String(response.status)}`);
|
|
57
58
|
}
|
|
58
59
|
cache.delete(table);
|
|
59
60
|
},
|
|
60
61
|
async snapshot() {
|
|
61
62
|
const response = await get("/snapshot");
|
|
62
63
|
if (!response.ok) {
|
|
63
|
-
throw new
|
|
64
|
+
throw new LunoraError(`shard registry /snapshot returned ${String(response.status)}`);
|
|
64
65
|
}
|
|
65
66
|
const { tables } = await decodeJson(response);
|
|
66
67
|
return tables;
|
|
@@ -68,7 +69,7 @@ const createDynamicShardRegistry = (options) => {
|
|
|
68
69
|
async unregister(table, shardKey) {
|
|
69
70
|
const response = await post("/unregister", { shardKey, table });
|
|
70
71
|
if (!response.ok) {
|
|
71
|
-
throw new
|
|
72
|
+
throw new LunoraError(`shard registry /unregister returned ${String(response.status)}`);
|
|
72
73
|
}
|
|
73
74
|
cache.delete(table);
|
|
74
75
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { LunoraError as LunoraError$1, toErrorBody } from '@lunora/errors';
|
|
2
|
+
|
|
3
|
+
const toErrorResponse = (error) => {
|
|
4
|
+
const { body, redacted, status } = toErrorBody(error, { fallbackCode: "INTERNAL", redactedMessage: "Internal error" });
|
|
5
|
+
if (redacted) {
|
|
6
|
+
console.error("[lunora] internal error:", error);
|
|
7
|
+
}
|
|
8
|
+
return Response.json({ error: body }, {
|
|
9
|
+
headers: { "content-type": "application/json" },
|
|
10
|
+
status
|
|
11
|
+
});
|
|
12
|
+
};
|
|
13
|
+
class LunoraError extends LunoraError$1 {
|
|
14
|
+
constructor(message, options) {
|
|
15
|
+
super(options?.code ?? "INTERNAL", message, { cause: options?.cause, status: options?.status });
|
|
16
|
+
}
|
|
17
|
+
toResponse() {
|
|
18
|
+
return toErrorResponse(this);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export { LunoraError, toErrorResponse };
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import { isLunoraError } from '@lunora/errors';
|
|
1
2
|
import { NOOP_EXECUTION_CONTEXT } from './NOOP_EXECUTION_CONTEXT-CCTu0Bf1.mjs';
|
|
2
|
-
import { LunoraError, toErrorResponse
|
|
3
|
-
import { wrapResolverWithContract } from './composeIdentityResolvers-
|
|
4
|
-
export { composeIdentityResolvers, routeIdentityResolvers } from './composeIdentityResolvers-
|
|
3
|
+
import { LunoraError, toErrorResponse } from './LunoraError-Bpb9EFJ3.mjs';
|
|
4
|
+
import { wrapResolverWithContract } from './composeIdentityResolvers-XGjO7V1J.mjs';
|
|
5
|
+
export { composeIdentityResolvers, routeIdentityResolvers } from './composeIdentityResolvers-XGjO7V1J.mjs';
|
|
5
6
|
import { emitRpcEvent } from './emitLogEvent-pEdtqAK8.mjs';
|
|
6
7
|
import { resolveShard, applyJurisdiction } from './applyJurisdiction-BkZtTkct.mjs';
|
|
7
|
-
import { resolveSecurity, handleCorsPreflight, enforceOrigin, decorateResponse, enforceWebSocketOrigin } from './decorateResponse-
|
|
8
|
+
import { resolveSecurity, handleCorsPreflight, enforceOrigin, decorateResponse, enforceWebSocketOrigin } from './decorateResponse-CsZc49QC.mjs';
|
|
8
9
|
|
|
9
10
|
const RELAY_NAME_INFIX = "::relay::";
|
|
10
11
|
const relayName = (ownerKey, index) => `${ownerKey}${RELAY_NAME_INFIX}${String(index)}`;
|
|
@@ -251,7 +252,7 @@ const buildAuthAdminRoutes = (deps) => {
|
|
|
251
252
|
const candidate = error;
|
|
252
253
|
const code = typeof candidate.code === "string" ? candidate.code : "AUTH_ADMIN_ERROR";
|
|
253
254
|
console.error("[lunora] auth admin operation failed:", error);
|
|
254
|
-
throw new LunoraError("auth admin operation failed", { code, status: AUTH_ADMIN_ERROR_STATUS[code] ??
|
|
255
|
+
throw new LunoraError("auth admin operation failed", { code, status: AUTH_ADMIN_ERROR_STATUS[code] ?? 500 });
|
|
255
256
|
}
|
|
256
257
|
};
|
|
257
258
|
const handle = async (request, descriptor) => {
|
|
@@ -1740,7 +1741,7 @@ const isAuthAttemptPath = (pathname, basePath) => {
|
|
|
1740
1741
|
return AUTH_ATTEMPT_SEGMENTS.some((segment) => suffix === segment || suffix.startsWith(`${segment}/`));
|
|
1741
1742
|
};
|
|
1742
1743
|
const buildErrorEvent = (functionPath, durationMs, error, extra) => {
|
|
1743
|
-
const mappable =
|
|
1744
|
+
const mappable = isLunoraError(error);
|
|
1744
1745
|
const code = mappable ? error.code : "INTERNAL_SERVER_ERROR";
|
|
1745
1746
|
const status = mappable ? error.status : 500;
|
|
1746
1747
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -2911,7 +2912,7 @@ const resolveLunoraOptions = (options, env) => {
|
|
|
2911
2912
|
}
|
|
2912
2913
|
const shardDO = options.shardDO ?? env?.SHARD;
|
|
2913
2914
|
if (!shardDO) {
|
|
2914
|
-
throw new
|
|
2915
|
+
throw new LunoraError(
|
|
2915
2916
|
"@lunora/runtime: no shard Durable Object namespace found. Bind `SHARD` in wrangler.jsonc, or pass `createLunoraHandler({ shardDO: env.MY_SHARD })`."
|
|
2916
2917
|
);
|
|
2917
2918
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { LunoraError } from './LunoraError-Bpb9EFJ3.mjs';
|
|
2
|
+
|
|
1
3
|
const RPC_ENDPOINT = "/_lunora/rpc";
|
|
2
4
|
const buildIdentityHeaders = (options) => {
|
|
3
5
|
const headers = { "content-type": "application/json" };
|
|
@@ -19,12 +21,12 @@ const fanOutRelation = async (options, body, label) => {
|
|
|
19
21
|
})
|
|
20
22
|
);
|
|
21
23
|
if (!response.ok) {
|
|
22
|
-
throw new
|
|
24
|
+
throw new LunoraError(`cross-shard relation ${label} failed: worker returned ${String(response.status)}`);
|
|
23
25
|
}
|
|
24
26
|
const result = await response.json();
|
|
25
27
|
if (typeof result.failed === "number" && result.failed > 0) {
|
|
26
28
|
const reached = (typeof result.ok === "number" ? result.ok : 0) + result.failed;
|
|
27
|
-
throw new
|
|
29
|
+
throw new LunoraError(
|
|
28
30
|
`cross-shard relation ${label} failed on ${String(result.failed)} of ${String(reached)} shard(s) — refusing to return a partial result`
|
|
29
31
|
);
|
|
30
32
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { LunoraError } from './LunoraError-Bpb9EFJ3.mjs';
|
|
2
|
+
|
|
1
3
|
const DEFAULT_CSP = "default-src 'none'; frame-ancestors 'none'; base-uri 'none'; form-action 'none'";
|
|
2
4
|
const htmlCspFor = (frameOptions) => {
|
|
3
5
|
const parts = ["base-uri 'none'", "object-src 'none'"];
|
|
@@ -75,15 +77,14 @@ const resolveCors = (input) => {
|
|
|
75
77
|
if (typeof origins === "function") {
|
|
76
78
|
isAllowed = origins;
|
|
77
79
|
isExplicitlyAllowed = origins;
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
80
|
+
const credentialsNote = allowCredentials ? " AND reflects matching origins with credentials (`allowCredentials: true`)" : "";
|
|
81
|
+
console.warn(
|
|
82
|
+
`@lunora/runtime: security.cors uses a custom \`allowedOrigins\` predicate. It is trusted by the CSRF and WebSocket origin checks${credentialsNote} — ensure it matches ONLY trusted origins by exact equality; an over-broad predicate (e.g. \`() => true\`, or \`endsWith\`/\`includes\` checks) defeats the allowlist.`
|
|
83
|
+
);
|
|
83
84
|
} else {
|
|
84
85
|
const originsList = origins;
|
|
85
86
|
if (originsList.includes("*") && allowCredentials) {
|
|
86
|
-
throw new
|
|
87
|
+
throw new LunoraError(
|
|
87
88
|
'@lunora/runtime: security.cors cannot combine a wildcard origin ("*") with allowCredentials: true — browsers reject it and it defeats the allowlist.'
|
|
88
89
|
);
|
|
89
90
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lunora/runtime",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.17",
|
|
4
4
|
"description": "Lunora Worker runtime: the RPC router, shard resolver, and query coordinator",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cloudflare",
|
|
@@ -45,6 +45,9 @@
|
|
|
45
45
|
"publishConfig": {
|
|
46
46
|
"access": "public"
|
|
47
47
|
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@lunora/errors": "1.0.0-alpha.1"
|
|
50
|
+
},
|
|
48
51
|
"engines": {
|
|
49
52
|
"node": "^22.15.0 || >=24.11.0"
|
|
50
53
|
}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
class LunoraError extends Error {
|
|
2
|
-
code;
|
|
3
|
-
status;
|
|
4
|
-
constructor(message, options) {
|
|
5
|
-
super(message, { cause: options?.cause });
|
|
6
|
-
this.name = "LunoraError";
|
|
7
|
-
this.code = options?.code ?? "INTERNAL";
|
|
8
|
-
this.status = options?.status ?? 500;
|
|
9
|
-
}
|
|
10
|
-
toResponse() {
|
|
11
|
-
const body = { error: { code: this.code, message: this.message } };
|
|
12
|
-
return Response.json(body, {
|
|
13
|
-
headers: { "content-type": "application/json" },
|
|
14
|
-
status: this.status
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
const hasErrorShape = (error, name) => {
|
|
19
|
-
if (!error || typeof error !== "object") {
|
|
20
|
-
return false;
|
|
21
|
-
}
|
|
22
|
-
const candidate = error;
|
|
23
|
-
return candidate.name === name && typeof candidate.code === "string" && typeof candidate.status === "number" && typeof candidate.message === "string";
|
|
24
|
-
};
|
|
25
|
-
const isStructuralConflictError = (error) => hasErrorShape(error, "ConflictError");
|
|
26
|
-
const isStructuralLunoraError = (error) => hasErrorShape(error, "LunoraError");
|
|
27
|
-
const toErrorResponse = (error) => {
|
|
28
|
-
if (error instanceof LunoraError) {
|
|
29
|
-
return error.toResponse();
|
|
30
|
-
}
|
|
31
|
-
if (isStructuralLunoraError(error) || isStructuralConflictError(error)) {
|
|
32
|
-
const body2 = { error: { code: error.code, message: error.message } };
|
|
33
|
-
return Response.json(body2, {
|
|
34
|
-
headers: { "content-type": "application/json" },
|
|
35
|
-
status: error.status
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
console.error("[lunora] unhandled error:", error);
|
|
39
|
-
const body = { error: { code: "INTERNAL", message: "Internal error" } };
|
|
40
|
-
return Response.json(body, {
|
|
41
|
-
headers: { "content-type": "application/json" },
|
|
42
|
-
status: 500
|
|
43
|
-
});
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
export { LunoraError, isStructuralConflictError, isStructuralLunoraError, toErrorResponse };
|