@lunora/runtime 1.0.0-alpha.1 → 1.0.0-alpha.10
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/__assets__/package-og.svg +1 -1
- package/dist/index.d.mts +172 -5
- package/dist/index.d.ts +172 -5
- package/dist/index.mjs +7 -6
- package/dist/packem_shared/{createDynamicShardRegistry-BpCwo_mo.mjs → DEFAULT_REGISTRY_CACHE_TTL_MS-ocax8v0n.mjs} +4 -1
- package/dist/packem_shared/NOOP_EXECUTION_CONTEXT-CCTu0Bf1.mjs +8 -0
- package/dist/packem_shared/applyJurisdiction-BkZtTkct.mjs +20 -0
- package/dist/packem_shared/{composeWorker-BYHiNH_V.mjs → composeWorker-BEv9IBWS.mjs} +165 -41
- package/dist/packem_shared/{createQueryCoordinator-DbxC7iUz.mjs → createQueryCoordinator-Cbds9cUI.mjs} +1 -1
- package/package.json +2 -2
- package/dist/packem_shared/resolveShard-DDkzWtrU.mjs +0 -9
- /package/dist/packem_shared/{consoleSink-DqEvrQs0.mjs → analyticsEngineSink-DqEvrQs0.mjs} +0 -0
- /package/dist/packem_shared/{emitRpcEvent-pEdtqAK8.mjs → emitLogEvent-pEdtqAK8.mjs} +0 -0
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
import { NOOP_EXECUTION_CONTEXT } from './NOOP_EXECUTION_CONTEXT-CCTu0Bf1.mjs';
|
|
1
2
|
import { LunoraError, toErrorResponse, isStructuralLunoraError, isStructuralConflictError } from './LunoraError-CL0aOtpo.mjs';
|
|
2
|
-
import { emitRpcEvent } from './
|
|
3
|
-
import { resolveShard } from './
|
|
3
|
+
import { emitRpcEvent } from './emitLogEvent-pEdtqAK8.mjs';
|
|
4
|
+
import { resolveShard, applyJurisdiction } from './applyJurisdiction-BkZtTkct.mjs';
|
|
4
5
|
import { resolveSecurity, handleCorsPreflight, enforceOrigin, decorateResponse } from './decorateResponse-DbISh_Wi.mjs';
|
|
5
6
|
|
|
7
|
+
const RELAY_NAME_INFIX = "::relay::";
|
|
8
|
+
const relayName = (ownerKey, index) => `${ownerKey}${RELAY_NAME_INFIX}${String(index)}`;
|
|
9
|
+
|
|
6
10
|
const AUTH_BASE = "/_lunora/admin/auth";
|
|
7
11
|
const AUTH_ADMIN_ERROR_STATUS = {
|
|
8
12
|
PASSWORD_TOO_LONG: 400,
|
|
@@ -597,14 +601,14 @@ const partitionExportTables = (options, tables) => {
|
|
|
597
601
|
}
|
|
598
602
|
return { globalTables, shardLocalTables };
|
|
599
603
|
};
|
|
600
|
-
const exportShardLocalRows = async (options, coordinator, forwardedHeaders, tables, shardLocalTables, writeRow) => {
|
|
604
|
+
const exportShardLocalRows = async (options, coordinator, forwardedHeaders, tables, shardLocalTables, writeRow, namespace) => {
|
|
601
605
|
if (tables !== void 0 && shardLocalTables.length === 0) {
|
|
602
606
|
return;
|
|
603
607
|
}
|
|
604
608
|
const exportTables = tables === void 0 ? [] : shardLocalTables;
|
|
605
609
|
const probeFallback = tables === void 0 ? collectKnownTables() : [];
|
|
606
610
|
const probeTables = exportTables.length > 0 ? exportTables : probeFallback;
|
|
607
|
-
const result = await coordinator.orchestrateExport(
|
|
611
|
+
const result = await coordinator.orchestrateExport(namespace, {
|
|
608
612
|
args: { tables: exportTables },
|
|
609
613
|
headers: forwardedHeaders,
|
|
610
614
|
tables: probeTables
|
|
@@ -618,9 +622,9 @@ const exportShardLocalRows = async (options, coordinator, forwardedHeaders, tabl
|
|
|
618
622
|
}
|
|
619
623
|
}
|
|
620
624
|
};
|
|
621
|
-
const streamExportRows = async (options, coordinator, forwardedHeaders, tables, writeRow) => {
|
|
625
|
+
const streamExportRows = async (options, coordinator, forwardedHeaders, tables, writeRow, namespace) => {
|
|
622
626
|
const { globalTables, shardLocalTables } = partitionExportTables(options, tables);
|
|
623
|
-
await exportShardLocalRows(options, coordinator, forwardedHeaders, tables, shardLocalTables, writeRow);
|
|
627
|
+
await exportShardLocalRows(options, coordinator, forwardedHeaders, tables, shardLocalTables, writeRow, namespace);
|
|
624
628
|
const exportGlobalsFunction = options.exportGlobals;
|
|
625
629
|
const wantGlobals = tables === void 0 || globalTables.length > 0;
|
|
626
630
|
if (wantGlobals && exportGlobalsFunction) {
|
|
@@ -740,7 +744,7 @@ const mergeImportResult = (totals, result) => {
|
|
|
740
744
|
}
|
|
741
745
|
totals.conflicts += result.conflicts;
|
|
742
746
|
};
|
|
743
|
-
const streamingImport = async (request, options, forwardedHeaders) => {
|
|
747
|
+
const streamingImport = async (request, options, forwardedHeaders, namespace) => {
|
|
744
748
|
const defaultShard = options.defaultShardKey ?? "__root__";
|
|
745
749
|
const { errors, globalRows, perShard } = await bucketImportStream(request, options, defaultShard);
|
|
746
750
|
const totals = { conflicts: 0, errors, inserted: {} };
|
|
@@ -749,7 +753,7 @@ const streamingImport = async (request, options, forwardedHeaders) => {
|
|
|
749
753
|
if (!coordinator) {
|
|
750
754
|
throw new LunoraError("Import endpoint requires a `queryCoordinator` on the worker", { code: "BAD_REQUEST", status: 400 });
|
|
751
755
|
}
|
|
752
|
-
const result = await coordinator.orchestrateImport(
|
|
756
|
+
const result = await coordinator.orchestrateImport(namespace, {
|
|
753
757
|
batches: [...perShard.values()],
|
|
754
758
|
headers: forwardedHeaders
|
|
755
759
|
});
|
|
@@ -966,7 +970,7 @@ const buildIntrospectionAdminRoutes = (deps) => {
|
|
|
966
970
|
};
|
|
967
971
|
};
|
|
968
972
|
|
|
969
|
-
const MIGRATE_PATH = "/_lunora/migrate";
|
|
973
|
+
const MIGRATE_PATH$1 = "/_lunora/migrate";
|
|
970
974
|
const PITR_PATH = "/_lunora/admin/pitr";
|
|
971
975
|
const RANK_PATH = "/_lunora/admin/rank";
|
|
972
976
|
const RANKPAGE_PATH = "/_lunora/admin/rankpage";
|
|
@@ -1223,7 +1227,7 @@ const buildOrchestrationAdminRoutes = (deps) => {
|
|
|
1223
1227
|
return forwardToShard(shardDO, pitr.shardKey ?? defaultShard, forwarded);
|
|
1224
1228
|
};
|
|
1225
1229
|
return {
|
|
1226
|
-
[MIGRATE_PATH]: handleMigrate,
|
|
1230
|
+
[MIGRATE_PATH$1]: handleMigrate,
|
|
1227
1231
|
[PITR_PATH]: handlePitr,
|
|
1228
1232
|
[RANK_PATH]: handleRank,
|
|
1229
1233
|
[RANKPAGE_PATH]: handleRankPage,
|
|
@@ -1502,7 +1506,7 @@ const buildWorkflowsAdminRoutes = (deps) => {
|
|
|
1502
1506
|
assertAdmin(request);
|
|
1503
1507
|
const client = resolveWorkflowsClient(env);
|
|
1504
1508
|
if (!client) {
|
|
1505
|
-
return
|
|
1509
|
+
return Response.json({ configured: false, instances: [], page: 1, perPage: 0, totalCount: 0 });
|
|
1506
1510
|
}
|
|
1507
1511
|
const workflowName = requireQuery(url, "name");
|
|
1508
1512
|
const status = toInstanceStatus(url.searchParams.get("status"));
|
|
@@ -1557,6 +1561,9 @@ const RPC_PATH = "/_lunora/rpc";
|
|
|
1557
1561
|
const WS_PATH = "/_lunora/ws";
|
|
1558
1562
|
const SCHEDULER_DISPATCH_PATH = "/_lunora/scheduler/dispatch";
|
|
1559
1563
|
const CRON_JOBS_RUN_PATH = "/_lunora/admin/cron-jobs/run";
|
|
1564
|
+
const ADMIN_PATH_PREFIX = "/_lunora/admin/";
|
|
1565
|
+
const MIGRATE_PATH = "/_lunora/migrate";
|
|
1566
|
+
const isAdminPath = (pathname) => pathname.startsWith(ADMIN_PATH_PREFIX) || pathname === MIGRATE_PATH;
|
|
1560
1567
|
const DEFAULT_AUTH_BASE_PATH = "/api/auth";
|
|
1561
1568
|
const RECORD_AUTH_EVENT_OP = "__lunora_admin__:recordAuthEvent";
|
|
1562
1569
|
const AUTH_ATTEMPT_SEGMENTS = ["/sign-in", "/sign-up", "/callback"];
|
|
@@ -1598,6 +1605,8 @@ const resolveForwardContext = async (request, env, resolveIdentity) => {
|
|
|
1598
1605
|
const cookie = request.headers.get("cookie");
|
|
1599
1606
|
const bookmark = request.headers.get("x-d1-bookmark");
|
|
1600
1607
|
const mutationId = request.headers.get("x-lunora-mutation-id");
|
|
1608
|
+
const clientId = request.headers.get("x-lunora-client-id");
|
|
1609
|
+
const clientSeq = request.headers.get("x-lunora-client-seq");
|
|
1601
1610
|
if (authorization) {
|
|
1602
1611
|
headers["authorization"] = authorization;
|
|
1603
1612
|
}
|
|
@@ -1610,6 +1619,12 @@ const resolveForwardContext = async (request, env, resolveIdentity) => {
|
|
|
1610
1619
|
if (mutationId) {
|
|
1611
1620
|
headers["x-lunora-mutation-id"] = mutationId;
|
|
1612
1621
|
}
|
|
1622
|
+
if (clientId) {
|
|
1623
|
+
headers["x-lunora-client-id"] = clientId;
|
|
1624
|
+
}
|
|
1625
|
+
if (clientSeq) {
|
|
1626
|
+
headers["x-lunora-client-seq"] = clientSeq;
|
|
1627
|
+
}
|
|
1613
1628
|
const clientIp = request.headers.get("cf-connecting-ip");
|
|
1614
1629
|
if (clientIp) {
|
|
1615
1630
|
headers["x-lunora-client-ip"] = clientIp;
|
|
@@ -1662,6 +1677,12 @@ const validateFanOut = (fanOut) => {
|
|
|
1662
1677
|
}
|
|
1663
1678
|
return spec;
|
|
1664
1679
|
};
|
|
1680
|
+
const logRpcDebug = (env, envelope) => {
|
|
1681
|
+
if (!env?.LUNORA_DEBUG_RPC) {
|
|
1682
|
+
return;
|
|
1683
|
+
}
|
|
1684
|
+
console.warn(`[lunora:rpc] ${envelope.fanOut ? "fan-out" : `shard=${envelope.shardKey ?? "(root)"}`} ${envelope.functionPath}`);
|
|
1685
|
+
};
|
|
1665
1686
|
const parseEnvelope = async (request) => {
|
|
1666
1687
|
const text = await readBodyTextWithLimit(request);
|
|
1667
1688
|
let body;
|
|
@@ -1692,6 +1713,41 @@ const forwardToShard = async (namespace, shardKey, request) => {
|
|
|
1692
1713
|
const stub = resolveShard(namespace, shardKey);
|
|
1693
1714
|
return stub.fetch(request);
|
|
1694
1715
|
};
|
|
1716
|
+
const relayProbeCache = /* @__PURE__ */ new Map();
|
|
1717
|
+
const RELAY_PROBE_TTL_MS = 5e3;
|
|
1718
|
+
const probeRelayCount = async (namespace, shardKey) => {
|
|
1719
|
+
const now = Date.now();
|
|
1720
|
+
const cached = relayProbeCache.get(shardKey);
|
|
1721
|
+
if (cached !== void 0 && cached.expiresMs > now) {
|
|
1722
|
+
return cached.relayCount;
|
|
1723
|
+
}
|
|
1724
|
+
let relayCount = 0;
|
|
1725
|
+
try {
|
|
1726
|
+
const response = await resolveShard(namespace, shardKey).fetch(new Request("https://shard.internal/_lunora/route"));
|
|
1727
|
+
if (response.ok) {
|
|
1728
|
+
const body = await response.json();
|
|
1729
|
+
const reported = body.relayCount;
|
|
1730
|
+
if (typeof reported === "number" && reported > 0) {
|
|
1731
|
+
relayCount = Math.floor(reported);
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
} catch {
|
|
1735
|
+
relayCount = 0;
|
|
1736
|
+
}
|
|
1737
|
+
relayProbeCache.set(shardKey, { expiresMs: now + RELAY_PROBE_TTL_MS, relayCount });
|
|
1738
|
+
return relayCount;
|
|
1739
|
+
};
|
|
1740
|
+
const resolveShardBindingName = (env, namespace) => {
|
|
1741
|
+
if (env === null || typeof env !== "object") {
|
|
1742
|
+
return void 0;
|
|
1743
|
+
}
|
|
1744
|
+
for (const [key, value] of Object.entries(env)) {
|
|
1745
|
+
if (value === namespace) {
|
|
1746
|
+
return key;
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
return void 0;
|
|
1750
|
+
};
|
|
1695
1751
|
const constantTimeEqual = (expected, supplied) => {
|
|
1696
1752
|
const max = Math.max(expected.length, supplied.length);
|
|
1697
1753
|
let diff = expected.length ^ supplied.length;
|
|
@@ -1740,6 +1796,31 @@ const checkAdminWsToken = (request, expected) => {
|
|
|
1740
1796
|
};
|
|
1741
1797
|
const createWorker = (options) => {
|
|
1742
1798
|
const defaultShard = options.defaultShardKey ?? "__root__";
|
|
1799
|
+
const shardDO = applyJurisdiction(options.shardDO, options.jurisdiction);
|
|
1800
|
+
const schedulerDO = options.schedulerDO === void 0 ? void 0 : applyJurisdiction(options.schedulerDO, options.jurisdiction);
|
|
1801
|
+
let envAdminToken;
|
|
1802
|
+
const effectiveAdminToken = () => options.adminToken ?? envAdminToken;
|
|
1803
|
+
const resolveAdminTokenFromEnv = (env) => {
|
|
1804
|
+
if (envAdminToken !== void 0 || options.adminToken !== void 0) {
|
|
1805
|
+
return;
|
|
1806
|
+
}
|
|
1807
|
+
const value = (env ?? {})["LUNORA_ADMIN_TOKEN"];
|
|
1808
|
+
if (typeof value === "string" && value.length > 0) {
|
|
1809
|
+
envAdminToken = value;
|
|
1810
|
+
}
|
|
1811
|
+
};
|
|
1812
|
+
const accessAdminGrants = /* @__PURE__ */ new WeakSet();
|
|
1813
|
+
const requestIsAdmin = (request) => checkAdminAuth(request, effectiveAdminToken()) || accessAdminGrants.has(request);
|
|
1814
|
+
const resolveAdminForwardContext = async (request, env) => {
|
|
1815
|
+
const context = await resolveForwardContext(request, env, options.resolveIdentity);
|
|
1816
|
+
if (accessAdminGrants.has(request) && context.headers["authorization"] === void 0) {
|
|
1817
|
+
const token = effectiveAdminToken();
|
|
1818
|
+
if (token !== void 0) {
|
|
1819
|
+
context.headers["authorization"] = `Bearer ${token}`;
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
return context;
|
|
1823
|
+
};
|
|
1743
1824
|
const hasAnyShardAuth = Boolean(options.authorizeShard) || Boolean(options.authorizeFanOut);
|
|
1744
1825
|
let warnedUnauthenticatedShardAccess = false;
|
|
1745
1826
|
const warnUnauthenticatedShardAccessOnce = (kind) => {
|
|
@@ -1758,10 +1839,10 @@ const createWorker = (options) => {
|
|
|
1758
1839
|
const orchestrationAdminRoutes = buildOrchestrationAdminRoutes({
|
|
1759
1840
|
defaultShard,
|
|
1760
1841
|
forwardToShard,
|
|
1761
|
-
isAdmin:
|
|
1842
|
+
isAdmin: requestIsAdmin,
|
|
1762
1843
|
queryCoordinator: options.queryCoordinator,
|
|
1763
|
-
resolveForwardContext:
|
|
1764
|
-
shardDO
|
|
1844
|
+
resolveForwardContext: resolveAdminForwardContext,
|
|
1845
|
+
shardDO
|
|
1765
1846
|
});
|
|
1766
1847
|
const dispatchToShard = async (functionPath, args, shardKey) => {
|
|
1767
1848
|
if (options.authorizeShard) {
|
|
@@ -1779,7 +1860,7 @@ const createWorker = (options) => {
|
|
|
1779
1860
|
headers: { "content-type": "application/json", "x-lunora-system": "1" },
|
|
1780
1861
|
method: "POST"
|
|
1781
1862
|
});
|
|
1782
|
-
return forwardToShard(
|
|
1863
|
+
return forwardToShard(shardDO, shardKey, forwarded);
|
|
1783
1864
|
};
|
|
1784
1865
|
const startCronWorkflow = async (binding, job, env) => {
|
|
1785
1866
|
const candidate = env?.[binding];
|
|
@@ -1824,7 +1905,7 @@ const createWorker = (options) => {
|
|
|
1824
1905
|
}
|
|
1825
1906
|
};
|
|
1826
1907
|
const handleRunCronJob = async (request, env) => {
|
|
1827
|
-
if (!
|
|
1908
|
+
if (!requestIsAdmin(request)) {
|
|
1828
1909
|
throw new LunoraError("admin endpoint requires a valid admin bearer", { code: "ADMIN_FORBIDDEN", status: 403 });
|
|
1829
1910
|
}
|
|
1830
1911
|
if (request.method !== "POST") {
|
|
@@ -1847,12 +1928,12 @@ const createWorker = (options) => {
|
|
|
1847
1928
|
};
|
|
1848
1929
|
const releasePoolSlot = async (candidate) => {
|
|
1849
1930
|
const pool = typeof candidate.pool === "string" && candidate.pool.length > 0 ? candidate.pool : void 0;
|
|
1850
|
-
if (!pool || !
|
|
1931
|
+
if (!pool || !schedulerDO || typeof candidate.id !== "string") {
|
|
1851
1932
|
return;
|
|
1852
1933
|
}
|
|
1853
1934
|
const instanceName = typeof candidate.instanceName === "string" && candidate.instanceName.length > 0 ? candidate.instanceName : "default";
|
|
1854
1935
|
try {
|
|
1855
|
-
await
|
|
1936
|
+
await schedulerDO.get(schedulerDO.idFromName(instanceName)).fetch(
|
|
1856
1937
|
new Request("https://scheduler.internal/complete", {
|
|
1857
1938
|
body: JSON.stringify({ id: candidate.id, pool }),
|
|
1858
1939
|
headers: { "content-type": "application/json" },
|
|
@@ -1898,17 +1979,17 @@ const createWorker = (options) => {
|
|
|
1898
1979
|
};
|
|
1899
1980
|
const dataMovementAdminRoutes = buildDataMovementAdminRoutes({
|
|
1900
1981
|
applyGlobals: options.applyGlobals,
|
|
1901
|
-
isAdmin:
|
|
1982
|
+
isAdmin: requestIsAdmin,
|
|
1902
1983
|
knownTables: () => collectKnownTables(),
|
|
1903
1984
|
queryCoordinator: options.queryCoordinator,
|
|
1904
|
-
resolveForwardContext:
|
|
1905
|
-
shardDO
|
|
1906
|
-
streamExportRows: (coordinator, headers, tables, writeRow) => streamExportRows(options, coordinator, headers, tables, writeRow),
|
|
1907
|
-
streamingImport: (request, headers) => streamingImport(request, options, headers),
|
|
1985
|
+
resolveForwardContext: resolveAdminForwardContext,
|
|
1986
|
+
shardDO,
|
|
1987
|
+
streamExportRows: (coordinator, headers, tables, writeRow) => streamExportRows(options, coordinator, headers, tables, writeRow, shardDO),
|
|
1988
|
+
streamingImport: (request, headers) => streamingImport(request, options, headers, shardDO),
|
|
1908
1989
|
syncGlobals: options.syncGlobals
|
|
1909
1990
|
});
|
|
1910
1991
|
const assertAdminAuthorized = (request) => {
|
|
1911
|
-
if (!
|
|
1992
|
+
if (!requestIsAdmin(request)) {
|
|
1912
1993
|
throw new LunoraError("admin endpoint requires a valid admin bearer", { code: "ADMIN_FORBIDDEN", status: 403 });
|
|
1913
1994
|
}
|
|
1914
1995
|
};
|
|
@@ -1935,17 +2016,17 @@ const createWorker = (options) => {
|
|
|
1935
2016
|
};
|
|
1936
2017
|
};
|
|
1937
2018
|
const requireSchedulerNamespace = () => {
|
|
1938
|
-
if (
|
|
2019
|
+
if (schedulerDO === void 0) {
|
|
1939
2020
|
throw new LunoraError("scheduled endpoints require a `schedulerDO` namespace on the worker", { code: "SCHEDULER_NOT_CONFIGURED", status: 400 });
|
|
1940
2021
|
}
|
|
1941
|
-
return
|
|
2022
|
+
return schedulerDO;
|
|
1942
2023
|
};
|
|
1943
2024
|
const resolveSchedulerStub = (request) => {
|
|
1944
2025
|
assertAdminAuthorized(request);
|
|
1945
2026
|
return resolveShard(requireSchedulerNamespace(), options.schedulerInstanceName ?? "default");
|
|
1946
2027
|
};
|
|
1947
2028
|
const scheduledAdminRoutes = buildScheduledAdminRoutes({
|
|
1948
|
-
checkWsAdmin: (request) =>
|
|
2029
|
+
checkWsAdmin: (request) => requestIsAdmin(request) || checkAdminWsToken(request, effectiveAdminToken()),
|
|
1949
2030
|
requireSchedulerNamespace,
|
|
1950
2031
|
resolveSchedulerStub,
|
|
1951
2032
|
schedulerInstanceName: options.schedulerInstanceName ?? "default"
|
|
@@ -1998,7 +2079,7 @@ const createWorker = (options) => {
|
|
|
1998
2079
|
headers,
|
|
1999
2080
|
method: "POST"
|
|
2000
2081
|
});
|
|
2001
|
-
const response = await forwardToShard(
|
|
2082
|
+
const response = await forwardToShard(shardDO, defaultShard, forwarded);
|
|
2002
2083
|
const payload = await response.json();
|
|
2003
2084
|
if (payload.error) {
|
|
2004
2085
|
throw new LunoraError(payload.error.message ?? "shard RPC failed", {
|
|
@@ -2061,7 +2142,16 @@ const createWorker = (options) => {
|
|
|
2061
2142
|
if (forwardedExp !== void 0) {
|
|
2062
2143
|
upgradeHeaders.set("x-lunora-identity-exp", forwardedExp);
|
|
2063
2144
|
}
|
|
2064
|
-
|
|
2145
|
+
const binding = resolveShardBindingName(env, options.shardDO);
|
|
2146
|
+
if (binding !== void 0) {
|
|
2147
|
+
upgradeHeaders.set("x-lunora-shard-binding", binding);
|
|
2148
|
+
const relayCount = await probeRelayCount(shardDO, shardKey);
|
|
2149
|
+
if (relayCount > 0) {
|
|
2150
|
+
const target = relayName(shardKey, Math.floor(Math.random() * relayCount));
|
|
2151
|
+
return forwardToShard(shardDO, target, new Request(request, { headers: upgradeHeaders }));
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
return forwardToShard(shardDO, shardKey, new Request(request, { headers: upgradeHeaders }));
|
|
2065
2155
|
};
|
|
2066
2156
|
const authorizeRpcEnvelope = async (envelope, identity) => {
|
|
2067
2157
|
if (envelope.fanOut) {
|
|
@@ -2107,7 +2197,7 @@ const createWorker = (options) => {
|
|
|
2107
2197
|
method: "POST"
|
|
2108
2198
|
});
|
|
2109
2199
|
try {
|
|
2110
|
-
const response = await forwardToShard(
|
|
2200
|
+
const response = await forwardToShard(shardDO, shardKey, forwarded);
|
|
2111
2201
|
emitRpcEvent(
|
|
2112
2202
|
observability,
|
|
2113
2203
|
{
|
|
@@ -2136,6 +2226,7 @@ const createWorker = (options) => {
|
|
|
2136
2226
|
throw new LunoraError("RPC endpoint requires POST", { code: "METHOD_NOT_ALLOWED", status: 405 });
|
|
2137
2227
|
}
|
|
2138
2228
|
const envelope = await parseEnvelope(request);
|
|
2229
|
+
logRpcDebug(env, envelope);
|
|
2139
2230
|
if (envelope.fanOut && envelope.shardKey) {
|
|
2140
2231
|
throw new LunoraError("RPC envelope cannot set both `shardKey` and `fanOut`", { code: "BAD_REQUEST", status: 400 });
|
|
2141
2232
|
}
|
|
@@ -2158,7 +2249,7 @@ const createWorker = (options) => {
|
|
|
2158
2249
|
const { observability } = options;
|
|
2159
2250
|
const sinkContext = context ? {
|
|
2160
2251
|
waitUntil: (promise) => {
|
|
2161
|
-
context.waitUntil(promise);
|
|
2252
|
+
context.waitUntil?.(promise);
|
|
2162
2253
|
}
|
|
2163
2254
|
} : void 0;
|
|
2164
2255
|
if (envelope.fanOut) {
|
|
@@ -2170,7 +2261,7 @@ const createWorker = (options) => {
|
|
|
2170
2261
|
});
|
|
2171
2262
|
}
|
|
2172
2263
|
try {
|
|
2173
|
-
const result = await coordinator.fanOut(
|
|
2264
|
+
const result = await coordinator.fanOut(shardDO, {
|
|
2174
2265
|
args: envelope.args ?? {},
|
|
2175
2266
|
fanOut: envelope.fanOut,
|
|
2176
2267
|
functionPath: envelope.functionPath,
|
|
@@ -2279,7 +2370,7 @@ const createWorker = (options) => {
|
|
|
2279
2370
|
streamController.enqueue(encoded);
|
|
2280
2371
|
};
|
|
2281
2372
|
try {
|
|
2282
|
-
await streamExportRows(options, coordinator, forwardedHeaders, tables, writeRow);
|
|
2373
|
+
await streamExportRows(options, coordinator, forwardedHeaders, tables, writeRow, shardDO);
|
|
2283
2374
|
streamController.close();
|
|
2284
2375
|
} catch (error) {
|
|
2285
2376
|
streamError = error instanceof Error ? error : new Error(String(error));
|
|
@@ -2348,7 +2439,7 @@ const createWorker = (options) => {
|
|
|
2348
2439
|
headers: { authorization: `Bearer ${adminBearer}`, "content-type": "application/json" },
|
|
2349
2440
|
method: "POST"
|
|
2350
2441
|
});
|
|
2351
|
-
await forwardToShard(
|
|
2442
|
+
await forwardToShard(shardDO, defaultShard, recordRequest);
|
|
2352
2443
|
} catch {
|
|
2353
2444
|
}
|
|
2354
2445
|
};
|
|
@@ -2362,10 +2453,11 @@ const createWorker = (options) => {
|
|
|
2362
2453
|
}
|
|
2363
2454
|
const basePath = options.authBasePath ?? DEFAULT_AUTH_BASE_PATH;
|
|
2364
2455
|
if (isAuthAttemptPath(url.pathname, basePath)) {
|
|
2365
|
-
context.waitUntil(recordAuthAttempt(env, authResponse.status >= 400 ? "fail" : "ok"));
|
|
2456
|
+
context.waitUntil?.(recordAuthAttempt(env, authResponse.status >= 400 ? "fail" : "ok"));
|
|
2366
2457
|
}
|
|
2367
2458
|
return authResponse;
|
|
2368
2459
|
};
|
|
2460
|
+
const customRoutes = options.routes !== void 0 && Object.keys(options.routes).length > 0 ? options.routes : void 0;
|
|
2369
2461
|
const internalRoutes = {
|
|
2370
2462
|
[WS_PATH]: (request, env, url) => handleWebSocketUpgrade(request, env, url),
|
|
2371
2463
|
[RPC_PATH]: (request, env, _url, context) => handleRpc(request, env, context),
|
|
@@ -2402,6 +2494,17 @@ const createWorker = (options) => {
|
|
|
2402
2494
|
resolvedSecurity = resolveSecurity(options.security, env ?? {});
|
|
2403
2495
|
}
|
|
2404
2496
|
};
|
|
2497
|
+
const applyAdminGate = async (request, pathname) => {
|
|
2498
|
+
if (options.adminGate === void 0 || !isAdminPath(pathname)) {
|
|
2499
|
+
return;
|
|
2500
|
+
}
|
|
2501
|
+
try {
|
|
2502
|
+
if (await options.adminGate(request)) {
|
|
2503
|
+
accessAdminGrants.add(request);
|
|
2504
|
+
}
|
|
2505
|
+
} catch {
|
|
2506
|
+
}
|
|
2507
|
+
};
|
|
2405
2508
|
const handle = async (request, env, context) => {
|
|
2406
2509
|
const url = new URL(request.url);
|
|
2407
2510
|
if (request.method === "POST" || request.method === "PUT") {
|
|
@@ -2414,13 +2517,16 @@ const createWorker = (options) => {
|
|
|
2414
2517
|
if (authResponse) {
|
|
2415
2518
|
return authResponse;
|
|
2416
2519
|
}
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2520
|
+
if (customRoutes) {
|
|
2521
|
+
const methodAndPath = `${request.method} ${url.pathname}`;
|
|
2522
|
+
const route = customRoutes[methodAndPath] ?? customRoutes[url.pathname];
|
|
2523
|
+
if (route) {
|
|
2524
|
+
return route(request, env, context);
|
|
2525
|
+
}
|
|
2421
2526
|
}
|
|
2422
2527
|
const internalRoute = internalRoutes[url.pathname];
|
|
2423
2528
|
if (internalRoute) {
|
|
2529
|
+
await applyAdminGate(request, url.pathname);
|
|
2424
2530
|
return internalRoute(request, env, url, context);
|
|
2425
2531
|
}
|
|
2426
2532
|
const httpRouteResponse = await dispatchHttpRoute(request, env, context);
|
|
@@ -2432,9 +2538,10 @@ const createWorker = (options) => {
|
|
|
2432
2538
|
return {
|
|
2433
2539
|
async fetch(request, env, context) {
|
|
2434
2540
|
if (options.passThroughOnException) {
|
|
2435
|
-
context.passThroughOnException();
|
|
2541
|
+
context.passThroughOnException?.();
|
|
2436
2542
|
}
|
|
2437
2543
|
ensureSecurityResolved(env);
|
|
2544
|
+
resolveAdminTokenFromEnv(env);
|
|
2438
2545
|
const preflight = handleCorsPreflight(request, resolvedSecurity);
|
|
2439
2546
|
if (preflight) {
|
|
2440
2547
|
return preflight;
|
|
@@ -2450,6 +2557,9 @@ const createWorker = (options) => {
|
|
|
2450
2557
|
return decorateResponse(toErrorResponse(error), request, resolvedSecurity);
|
|
2451
2558
|
}
|
|
2452
2559
|
},
|
|
2560
|
+
async queue(batch, env, context) {
|
|
2561
|
+
await options.queue?.(batch, env, context);
|
|
2562
|
+
},
|
|
2453
2563
|
async scheduled(controller, env, context) {
|
|
2454
2564
|
await handleScheduled(controller, env, context);
|
|
2455
2565
|
},
|
|
@@ -2480,10 +2590,24 @@ const withFrameworkWorker = (host, optionsInput) => {
|
|
|
2480
2590
|
const optionsFactory = optionsInput;
|
|
2481
2591
|
return {
|
|
2482
2592
|
fetch: (request, env, context) => build(optionsFactory(env)).fetch(request, env, context),
|
|
2593
|
+
queue: (batch, env, context) => build(optionsFactory(env)).queue?.(batch, env, context) ?? Promise.resolve(),
|
|
2483
2594
|
scheduled: (controller, env, context) => build(optionsFactory(env)).scheduled(controller, env, context),
|
|
2484
2595
|
serverQuery: (request, env, reference, args, options) => build(optionsFactory(env)).serverQuery(request, env, reference, args, options)
|
|
2485
2596
|
};
|
|
2486
2597
|
};
|
|
2598
|
+
const resolveLunoraOptions = (options, env) => {
|
|
2599
|
+
if (typeof options === "function") {
|
|
2600
|
+
return options(env);
|
|
2601
|
+
}
|
|
2602
|
+
const shardDO = options.shardDO ?? env?.SHARD;
|
|
2603
|
+
if (!shardDO) {
|
|
2604
|
+
throw new Error(
|
|
2605
|
+
"@lunora/runtime: no shard Durable Object namespace found. Bind `SHARD` in wrangler.jsonc, or pass `createLunoraHandler({ shardDO: env.MY_SHARD })`."
|
|
2606
|
+
);
|
|
2607
|
+
}
|
|
2608
|
+
return { ...options, shardDO };
|
|
2609
|
+
};
|
|
2610
|
+
const createLunoraHandler = (options = {}) => (request, env, context) => createWorker(resolveLunoraOptions(options, env)).fetch(request, env, context ?? NOOP_EXECUTION_CONTEXT);
|
|
2487
2611
|
const defineRpcEnvelope = (envelope) => envelope;
|
|
2488
2612
|
|
|
2489
|
-
export { composeWorker, createWorker, defineRpcEnvelope, withFrameworkWorker };
|
|
2613
|
+
export { NOOP_EXECUTION_CONTEXT, composeWorker, createLunoraHandler, createWorker, defineRpcEnvelope, resolveLunoraOptions, withFrameworkWorker };
|
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.10",
|
|
4
4
|
"description": "Lunora Worker runtime: the RPC router, shard resolver, and query coordinator",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cloudflare",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"directory": "packages/runtime"
|
|
26
26
|
},
|
|
27
27
|
"files": [
|
|
28
|
-
"dist",
|
|
28
|
+
"./dist",
|
|
29
29
|
"README.md",
|
|
30
30
|
"LICENSE.md",
|
|
31
31
|
"__assets__"
|
|
File without changes
|
|
File without changes
|