@lunora/codegen 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 +96 -7
- package/dist/index.d.ts +96 -7
- package/dist/index.mjs +23 -22
- package/dist/packem_shared/{CONTAINERS_FILENAME-0K-pjNb8.mjs → CONTAINERS_FILENAME-DlP6YM_Q.mjs} +1 -1
- package/dist/packem_shared/{CodegenDiagnosticError-54jWDxA9.mjs → CodegenDiagnosticError-DeblMkzO.mjs} +3 -2
- package/dist/packem_shared/{emitApi-hRVC-kE7.mjs → GENERATED_HEADER-BiFXNUvo.mjs} +285 -19
- package/dist/packem_shared/{buildOpenRpcDocument-BZGY1-jT.mjs → OPENRPC_VERSION-B4i9Qp3v.mjs} +1 -1
- package/dist/packem_shared/{createCodegenProject-DGJm0_Pk.mjs → SCHEMA_SNAPSHOT_FILENAME-Bvv7qa_u.mjs} +81 -37
- package/dist/packem_shared/{buildSchemaSnapshot-DzLDbWk3.mjs → SCHEMA_SNAPSHOT_VERSION-wWGP2_5E.mjs} +12 -0
- package/dist/packem_shared/{discoverWorkflows-DRDQdhfq.mjs → WORKFLOWS_FILENAME-D62dcBGg.mjs} +1 -1
- package/dist/packem_shared/{buildOpenApiDocument-yHVN66Xd.mjs → buildOpenApiDocument-Cu3mZl-8.mjs} +1 -1
- package/dist/packem_shared/{discoverAuthApiCalls-C35R6z0T.mjs → discoverAuthApiCalls-CoirYbg6.mjs} +1 -1
- package/dist/packem_shared/{discoverCrons-BL6iGuJ3.mjs → discoverCrons-Cev7RRAf.mjs} +20 -17
- package/dist/packem_shared/{discoverFunctions-DEgAcRuD.mjs → discoverFunctions-BWMczzBx.mjs} +7 -2
- package/dist/packem_shared/{discoverHttpRoutes-C978pBiG.mjs → discoverHttpRoutes-CfP6cMzt.mjs} +2 -2
- package/dist/packem_shared/{discoverInserts-CRQdXvHO.mjs → discoverInserts-C7zxXkUf.mjs} +24 -2
- package/dist/packem_shared/{discoverMaskProcedures-B64zA740.mjs → discoverMaskProcedures-Cm81kwrb.mjs} +1 -1
- package/dist/packem_shared/{discoverMigrations-Doj_-BAA.mjs → discoverMigrations-Bi5nJ0mJ.mjs} +10 -4
- package/dist/packem_shared/{discoverNondeterministicCalls-4KiPQxQU.mjs → discoverNondeterministicCalls-C4M8AXmQ.mjs} +1 -1
- package/dist/packem_shared/{discoverQueries-BkIi0dBD.mjs → discoverQueries-B0wGT-xe.mjs} +1 -1
- package/dist/packem_shared/discoverR2sqlCalls-BVNMd428.mjs +80 -0
- package/dist/packem_shared/{discoverRlsMetadata-DpRB1HMe.mjs → discoverRlsMetadata-BS9GOGC5.mjs} +1 -1
- package/dist/packem_shared/{discoverSchema-BBulgGbH.mjs → discoverSchema-BnWHHJ4T.mjs} +296 -16
- package/dist/packem_shared/{discoverStorageRulesMetadata-DAqJUxUv.mjs → discoverStorageRulesMetadata-Da8BKXcI.mjs} +1 -1
- package/dist/packem_shared/{emitApp-CzZ6GbrD.mjs → emitApp-KfMaKXWe.mjs} +12 -2
- package/dist/packem_shared/{lintSchema-DicbOHvH.mjs → formatAdvisories-8NIv1k0I.mjs} +2 -1
- package/dist/packem_shared/{parse-validator-tuQtHrsr.mjs → parse-validator-Cabb60UV.mjs} +2 -1
- package/package.json +6 -6
|
@@ -1,5 +1,174 @@
|
|
|
1
1
|
import { s as sanitizeNamespace } from './paths-BRd6JHuF.mjs';
|
|
2
2
|
|
|
3
|
+
const LITERAL_VALUE_RE = /^(?:"[^"\\]*"|'[^'\\]*'|-?\d+(?:\.\d+)?|true|false|null)$/u;
|
|
4
|
+
|
|
5
|
+
const PASS_THROUGH_KINDS = /* @__PURE__ */ new Set(["any", "bigint", "boolean", "date", "id", "null", "number", "storage", "string", "timestamp"]);
|
|
6
|
+
const hasColumnModifier = (node) => node.column !== void 0;
|
|
7
|
+
const emitScalarGuard = (kind, inExpr) => {
|
|
8
|
+
switch (kind) {
|
|
9
|
+
case "bigint": {
|
|
10
|
+
return `if (typeof ${inExpr} !== "bigint") return DEFER;
|
|
11
|
+
`;
|
|
12
|
+
}
|
|
13
|
+
case "boolean": {
|
|
14
|
+
return `if (typeof ${inExpr} !== "boolean") return DEFER;
|
|
15
|
+
`;
|
|
16
|
+
}
|
|
17
|
+
case "date":
|
|
18
|
+
case "number":
|
|
19
|
+
case "timestamp": {
|
|
20
|
+
return `if (typeof ${inExpr} !== "number" || !Number.isFinite(${inExpr})) return DEFER;
|
|
21
|
+
`;
|
|
22
|
+
}
|
|
23
|
+
case "null": {
|
|
24
|
+
return `if (${inExpr} !== null) return DEFER;
|
|
25
|
+
`;
|
|
26
|
+
}
|
|
27
|
+
// string / id / storage all parse as a bare string at runtime.
|
|
28
|
+
default: {
|
|
29
|
+
return `if (typeof ${inExpr} !== "string") return DEFER;
|
|
30
|
+
`;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
const compileLiteral = (node, inExpr) => {
|
|
35
|
+
const literal = node.literalValue?.trim();
|
|
36
|
+
if (literal === void 0 || !LITERAL_VALUE_RE.test(literal)) {
|
|
37
|
+
return void 0;
|
|
38
|
+
}
|
|
39
|
+
return { out: inExpr, pre: `if (${inExpr} !== ${literal}) return DEFER;
|
|
40
|
+
` };
|
|
41
|
+
};
|
|
42
|
+
const compileNode = (node, inExpr, context) => {
|
|
43
|
+
if (hasColumnModifier(node)) {
|
|
44
|
+
return void 0;
|
|
45
|
+
}
|
|
46
|
+
if (node.hasRefinement || node.sourceText !== void 0) {
|
|
47
|
+
return void 0;
|
|
48
|
+
}
|
|
49
|
+
if (PASS_THROUGH_KINDS.has(node.kind)) {
|
|
50
|
+
if (node.kind === "any") {
|
|
51
|
+
return { out: inExpr, pre: "" };
|
|
52
|
+
}
|
|
53
|
+
return { out: inExpr, pre: emitScalarGuard(node.kind, inExpr) };
|
|
54
|
+
}
|
|
55
|
+
switch (node.kind) {
|
|
56
|
+
case "array": {
|
|
57
|
+
return compileArray(node, inExpr, context);
|
|
58
|
+
}
|
|
59
|
+
case "literal": {
|
|
60
|
+
return compileLiteral(node, inExpr);
|
|
61
|
+
}
|
|
62
|
+
case "object": {
|
|
63
|
+
return compileObject(node, inExpr, context);
|
|
64
|
+
}
|
|
65
|
+
default: {
|
|
66
|
+
return void 0;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
const compileArray = (node, inExpr, context) => {
|
|
71
|
+
const { inner } = node;
|
|
72
|
+
if (!inner) {
|
|
73
|
+
return void 0;
|
|
74
|
+
}
|
|
75
|
+
const id = context.next();
|
|
76
|
+
const array = `__arr${String(id)}`;
|
|
77
|
+
const index = `__i${String(id)}`;
|
|
78
|
+
const element = `__e${String(id)}`;
|
|
79
|
+
const innerEmit = compileNode(inner, element, context);
|
|
80
|
+
if (!innerEmit) {
|
|
81
|
+
return void 0;
|
|
82
|
+
}
|
|
83
|
+
const pre = `if (!Array.isArray(${inExpr})) return DEFER;
|
|
84
|
+
const ${array} = new Array(${inExpr}.length);
|
|
85
|
+
for (let ${index} = 0; ${index} < ${inExpr}.length; ${index}++) {
|
|
86
|
+
const ${element} = ${inExpr}[${index}];
|
|
87
|
+
${innerEmit.pre}${array}[${index}] = ${innerEmit.out};
|
|
88
|
+
}
|
|
89
|
+
`;
|
|
90
|
+
return { out: array, pre };
|
|
91
|
+
};
|
|
92
|
+
const compileField = (key, node, access, context) => {
|
|
93
|
+
const keyLiteral = JSON.stringify(key);
|
|
94
|
+
if (node.kind === "optional") {
|
|
95
|
+
if (node.hasRefinement || node.sourceText !== void 0 || hasColumnModifier(node)) {
|
|
96
|
+
return void 0;
|
|
97
|
+
}
|
|
98
|
+
const { inner } = node;
|
|
99
|
+
if (!inner) {
|
|
100
|
+
return void 0;
|
|
101
|
+
}
|
|
102
|
+
const innerEmit = compileNode(inner, access, context);
|
|
103
|
+
if (!innerEmit) {
|
|
104
|
+
return void 0;
|
|
105
|
+
}
|
|
106
|
+
const id = context.next();
|
|
107
|
+
const has = `__has${String(id)}`;
|
|
108
|
+
const value = `__val${String(id)}`;
|
|
109
|
+
const pre = `let ${has} = false;
|
|
110
|
+
let ${value};
|
|
111
|
+
if (${access} !== undefined) {
|
|
112
|
+
${innerEmit.pre}${value} = ${innerEmit.out};
|
|
113
|
+
${has} = true;
|
|
114
|
+
}
|
|
115
|
+
`;
|
|
116
|
+
return { entry: `...(${has} ? { ${keyLiteral}: ${value} } : {})`, pre };
|
|
117
|
+
}
|
|
118
|
+
const emit = compileNode(node, access, context);
|
|
119
|
+
if (!emit) {
|
|
120
|
+
return void 0;
|
|
121
|
+
}
|
|
122
|
+
return { entry: `${keyLiteral}: ${emit.out}`, pre: emit.pre };
|
|
123
|
+
};
|
|
124
|
+
const compileFields = (shape, accessFor, context) => {
|
|
125
|
+
let pre = "";
|
|
126
|
+
const entries = [];
|
|
127
|
+
for (const key of Object.keys(shape)) {
|
|
128
|
+
const node = shape[key];
|
|
129
|
+
if (!node) {
|
|
130
|
+
return void 0;
|
|
131
|
+
}
|
|
132
|
+
const field = compileField(key, node, accessFor(key), context);
|
|
133
|
+
if (!field) {
|
|
134
|
+
return void 0;
|
|
135
|
+
}
|
|
136
|
+
pre += field.pre;
|
|
137
|
+
entries.push(field.entry);
|
|
138
|
+
}
|
|
139
|
+
return { entries: entries.join(", "), pre };
|
|
140
|
+
};
|
|
141
|
+
const compileObject = (node, inExpr, context) => {
|
|
142
|
+
const shape = node.shape ?? {};
|
|
143
|
+
const fields = compileFields(shape, (key) => `${inExpr}[${JSON.stringify(key)}]`, context);
|
|
144
|
+
if (!fields) {
|
|
145
|
+
return void 0;
|
|
146
|
+
}
|
|
147
|
+
const id = context.next();
|
|
148
|
+
const object = `__obj${String(id)}`;
|
|
149
|
+
const pre = `if (typeof ${inExpr} !== "object" || ${inExpr} === null || Array.isArray(${inExpr})) return DEFER;
|
|
150
|
+
${fields.pre}const ${object} = { ${fields.entries} };
|
|
151
|
+
`;
|
|
152
|
+
return { out: object, pre };
|
|
153
|
+
};
|
|
154
|
+
const compileArgsValidator = (args) => {
|
|
155
|
+
let counter = 0;
|
|
156
|
+
const context = {
|
|
157
|
+
next: () => {
|
|
158
|
+
counter += 1;
|
|
159
|
+
return counter;
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
const fields = compileFields(args, (key) => `source[${JSON.stringify(key)}]`, context);
|
|
163
|
+
if (!fields) {
|
|
164
|
+
return void 0;
|
|
165
|
+
}
|
|
166
|
+
return `(source) => {
|
|
167
|
+
if (typeof source !== "object" || source === null || Array.isArray(source)) return DEFER;
|
|
168
|
+
${fields.pre}return { ${fields.entries} };
|
|
169
|
+
}`;
|
|
170
|
+
};
|
|
171
|
+
|
|
3
172
|
const GENERATED_HEADER = "// GENERATED by @lunora/codegen — do not edit.\n// Run `lunora codegen` to regenerate.\n\n";
|
|
4
173
|
const baseSpecifiers = (useUmbrella = false) => useUmbrella ? {
|
|
5
174
|
client: "lunorash/client",
|
|
@@ -7,17 +176,18 @@ const baseSpecifiers = (useUmbrella = false) => useUmbrella ? {
|
|
|
7
176
|
server: "lunorash/server",
|
|
8
177
|
serverDataModel: "lunorash/server/data-model",
|
|
9
178
|
serverDrizzle: "lunorash/server/drizzle",
|
|
10
|
-
serverTypes: "lunorash/server/types"
|
|
179
|
+
serverTypes: "lunorash/server/types",
|
|
180
|
+
values: "lunorash/values"
|
|
11
181
|
} : {
|
|
12
182
|
client: "@lunora/client",
|
|
13
183
|
do: "@lunora/do",
|
|
14
184
|
server: "@lunora/server",
|
|
15
185
|
serverDataModel: "@lunora/server/data-model",
|
|
16
186
|
serverDrizzle: "@lunora/server/drizzle",
|
|
17
|
-
serverTypes: "@lunora/server/types"
|
|
187
|
+
serverTypes: "@lunora/server/types",
|
|
188
|
+
values: "@lunora/values"
|
|
18
189
|
};
|
|
19
190
|
const IDENTIFIER_RE = /^[A-Za-z_$][\w$]*$/u;
|
|
20
|
-
const LITERAL_VALUE_RE = /^(?:"[^"\\]*"|'[^'\\]*'|-?\d+(?:\.\d+)?|true|false|null)$/u;
|
|
21
191
|
const IMPORT_PATH_RE = /^[\w./-]+$/u;
|
|
22
192
|
const assertIdentifier = (value, context) => {
|
|
23
193
|
if (!IDENTIFIER_RE.test(value)) {
|
|
@@ -469,6 +639,17 @@ const renderFunctionRegistry = (functions, migrations) => {
|
|
|
469
639
|
const migrationEntries = migrations.map(
|
|
470
640
|
(migration) => ` ${JSON.stringify(migration.id)}: ${aliasByPath.get(migration.filePath) ?? ""}.${migration.exportName} as unknown as RegisteredDataMigration,`
|
|
471
641
|
).join("\n");
|
|
642
|
+
const installLines = functions.map((definition) => {
|
|
643
|
+
if (definition.lifecycle || Object.keys(definition.args).length === 0) {
|
|
644
|
+
return void 0;
|
|
645
|
+
}
|
|
646
|
+
const compiled = compileArgsValidator(definition.args);
|
|
647
|
+
if (compiled === void 0) {
|
|
648
|
+
return void 0;
|
|
649
|
+
}
|
|
650
|
+
const alias = aliasByPath.get(definition.filePath) ?? "";
|
|
651
|
+
return `installCompiledValidatorMap(${alias}.${definition.exportName}.args, ${compiled});`;
|
|
652
|
+
}).filter((line) => line !== void 0).join("\n");
|
|
472
653
|
return {
|
|
473
654
|
dispatchBody: dispatchEntries.length > 0 ? `
|
|
474
655
|
${dispatchEntries}
|
|
@@ -476,6 +657,7 @@ ${dispatchEntries}
|
|
|
476
657
|
importBlock: importLines.length > 0 ? `${importLines}
|
|
477
658
|
|
|
478
659
|
` : "",
|
|
660
|
+
installBlock: installLines,
|
|
479
661
|
migrationBody: migrationEntries.length > 0 ? `
|
|
480
662
|
${migrationEntries}
|
|
481
663
|
` : ""
|
|
@@ -539,6 +721,7 @@ const emitServer = ({
|
|
|
539
721
|
hasKv = false,
|
|
540
722
|
hasPayments = false,
|
|
541
723
|
hasPipelines = false,
|
|
724
|
+
hasR2sql = false,
|
|
542
725
|
schema,
|
|
543
726
|
storageRuleBuckets = [],
|
|
544
727
|
useUmbrella = false,
|
|
@@ -612,6 +795,11 @@ export type Env = CloudflareBindings;`;
|
|
|
612
795
|
const pipelinesActionField = hasPipelines ? `
|
|
613
796
|
/** Pipelines ingestion sink (durable, R2-backed). Fire-and-forget and batched; do not read it back in-handler. */
|
|
614
797
|
readonly pipelines: import("@lunora/pipelines").PipelineClient;` : "";
|
|
798
|
+
const r2sqlActionField = hasR2sql ? `
|
|
799
|
+
/**
|
|
800
|
+
* R2 SQL over Apache Iceberg tables (window functions, DISTINCT, set operations). Non-deterministic — available only in actions. Reads here are NOT tracked by Lunora live queries.
|
|
801
|
+
*/
|
|
802
|
+
readonly r2sql: import("@lunora/r2sql").R2SqlClient;` : "";
|
|
615
803
|
const hasWorkflows = workflows.length > 0;
|
|
616
804
|
const workflowsTypeImport = hasWorkflows ? `import type { WorkflowHandle } from "@lunora/workflow";
|
|
617
805
|
import type * as lunoraWorkflowDefinitions from "../workflows.js";
|
|
@@ -701,7 +889,7 @@ export interface MutationCtx extends Omit<MutationCtxBase, "db" | "storage"${wor
|
|
|
701
889
|
export interface ActionCtx extends Omit<ActionCtxBase, "db" | "storage"${workflowsOmit}> {
|
|
702
890
|
readonly db: Omit<DatabaseWriter, "query" | "get"> & DatabaseWriterFacade & { query: TypedTableQuery; get: TypedTableGet };
|
|
703
891
|
readonly orm: OrmWriter;
|
|
704
|
-
readonly storage: StorageBase<StorageBucketName>;${aiActionField}${paymentsActionField}${containersActionField}${kvContextField}${hyperdriveActionField}${browserActionField}${imagesActionField}${analyticsContextField}${pipelinesActionField}${workflowsContextField}
|
|
892
|
+
readonly storage: StorageBase<StorageBucketName>;${aiActionField}${paymentsActionField}${containersActionField}${kvContextField}${hyperdriveActionField}${browserActionField}${imagesActionField}${analyticsContextField}${pipelinesActionField}${r2sqlActionField}${workflowsContextField}
|
|
705
893
|
}
|
|
706
894
|
|
|
707
895
|
/**
|
|
@@ -771,10 +959,21 @@ const renderLifecycleManifest = (functions) => {
|
|
|
771
959
|
}
|
|
772
960
|
return manifest;
|
|
773
961
|
};
|
|
774
|
-
const emitFunctions = (functions, migrations = []) => {
|
|
962
|
+
const emitFunctions = (functions, migrations = [], useUmbrella = false) => {
|
|
775
963
|
const hasFunctions = functions.length > 0;
|
|
776
|
-
const
|
|
964
|
+
const base = baseSpecifiers(useUmbrella);
|
|
965
|
+
const { dispatchBody, importBlock, installBlock, migrationBody } = renderFunctionRegistry(functions, migrations);
|
|
777
966
|
const lifecycleHooks = renderLifecycleManifest(functions);
|
|
967
|
+
const compiledArgsImport = installBlock.length > 0 ? `import { DEFER_VALIDATION as DEFER, installCompiledValidatorMap } from "${base.values}";
|
|
968
|
+
` : "";
|
|
969
|
+
const compiledArgsInstall = installBlock.length > 0 ? `
|
|
970
|
+
/**
|
|
971
|
+
* AOT-compiled argument validators (Worker-safe, no \`eval\`). Each is installed
|
|
972
|
+
* onto its function's live \`.args\` object and consulted by the interpreted
|
|
973
|
+
* parser as a zero-allocation fast path; anything it can't model is deferred.
|
|
974
|
+
*/
|
|
975
|
+
${installBlock}
|
|
976
|
+
` : "";
|
|
778
977
|
const caller = renderCaller(functions);
|
|
779
978
|
const callerTypes = caller.types ? `
|
|
780
979
|
${caller.types}
|
|
@@ -789,7 +988,7 @@ ${caller.implementation}
|
|
|
789
988
|
const callRegisteredHelper = hasFunctions ? `${CALL_REGISTERED_HELPER}
|
|
790
989
|
|
|
791
990
|
` : "";
|
|
792
|
-
return `${GENERATED_HEADER}${importBlock}import type { ActionCtx, MutationCtx, QueryCtx } from "./server.js";
|
|
991
|
+
return `${GENERATED_HEADER}${importBlock}${compiledArgsImport}import type { ActionCtx, MutationCtx, QueryCtx } from "./server.js";
|
|
793
992
|
${dataModelImport}
|
|
794
993
|
/**
|
|
795
994
|
* Single registered function, narrowed to the shape \`handleRpc\` needs.
|
|
@@ -815,7 +1014,7 @@ export interface RegisteredLunoraFunction {
|
|
|
815
1014
|
* emits (\`api[namespace][fn].__lunoraRef === "namespace:fn"\`).
|
|
816
1015
|
*/
|
|
817
1016
|
export const LUNORA_FUNCTIONS: Record<string, RegisteredLunoraFunction> = {${dispatchBody}};
|
|
818
|
-
|
|
1017
|
+
${compiledArgsInstall}
|
|
819
1018
|
/**
|
|
820
1019
|
* Connection-lifecycle manifest: the function paths the generated ShardDO
|
|
821
1020
|
* dispatches when a client's WebSocket connects (\`connect\`) or disconnects
|
|
@@ -1183,17 +1382,68 @@ const browserStub: Browser = {
|
|
|
1183
1382
|
`
|
|
1184
1383
|
};
|
|
1185
1384
|
};
|
|
1186
|
-
const
|
|
1385
|
+
const emitR2sqlFragments = (hasR2sql) => {
|
|
1386
|
+
if (!hasR2sql) {
|
|
1387
|
+
return EMPTY_HELPER_FRAGMENTS;
|
|
1388
|
+
}
|
|
1389
|
+
const r2sqlMissing = `throw new Error("ctx.r2sql: no R2 SQL credentials found. Set \\\`R2_SQL_TOKEN\\\`, \\\`R2_SQL_ACCOUNT_ID\\\` (or \\\`CLOUDFLARE_ACCOUNT_ID\\\`), and \\\`R2_SQL_BUCKET\\\` in your env/.dev.vars, or pass an \\\`r2sql\\\` config thunk to createShardDO().");`;
|
|
1390
|
+
return {
|
|
1391
|
+
build: `
|
|
1392
|
+
const r2sqlEnv = env as Record<string, unknown>;
|
|
1393
|
+
const r2sqlAccountId = (r2sqlEnv.R2_SQL_ACCOUNT_ID ?? r2sqlEnv.CLOUDFLARE_ACCOUNT_ID) as string | undefined;
|
|
1394
|
+
const r2sqlToken = r2sqlEnv.R2_SQL_TOKEN as string | undefined;
|
|
1395
|
+
const r2sqlBucket = r2sqlEnv.R2_SQL_BUCKET as string | undefined;
|
|
1396
|
+
const r2sql: R2SqlClient = config.r2sql
|
|
1397
|
+
? config.r2sql(env)
|
|
1398
|
+
: r2sqlAccountId && r2sqlToken && r2sqlBucket
|
|
1399
|
+
? createR2Sql({ accountId: r2sqlAccountId, apiToken: r2sqlToken, bucket: r2sqlBucket })
|
|
1400
|
+
: r2sqlStub;
|
|
1401
|
+
`,
|
|
1402
|
+
configField: `
|
|
1403
|
+
r2sql?: (env: Record<string, unknown>) => R2SqlClient;`,
|
|
1404
|
+
// ActionCtx-only: attached via the \`ctx.r2sql = r2sql\` assignment in the
|
|
1405
|
+
// \`isAction\` block, never the every-ctx object literal.
|
|
1406
|
+
contextField: "",
|
|
1407
|
+
importLines: [`import type { R2SqlClient } from "@lunora/r2sql";`, `import { createR2Sql } from "@lunora/r2sql";`],
|
|
1408
|
+
// The stub is typed `R2SqlClient`, so TS flags a missing method at build
|
|
1409
|
+
// time — but it must stay structurally in sync with that interface
|
|
1410
|
+
// (`@lunora/r2sql` client.ts) when a method is added there.
|
|
1411
|
+
stub: `
|
|
1412
|
+
const r2sqlStub: R2SqlClient = {
|
|
1413
|
+
describe: async () => {
|
|
1414
|
+
${r2sqlMissing}
|
|
1415
|
+
},
|
|
1416
|
+
explain: async () => {
|
|
1417
|
+
${r2sqlMissing}
|
|
1418
|
+
},
|
|
1419
|
+
from: () => {
|
|
1420
|
+
${r2sqlMissing}
|
|
1421
|
+
},
|
|
1422
|
+
query: async () => {
|
|
1423
|
+
${r2sqlMissing}
|
|
1424
|
+
},
|
|
1425
|
+
showDatabases: async () => {
|
|
1426
|
+
${r2sqlMissing}
|
|
1427
|
+
},
|
|
1428
|
+
showTables: async () => {
|
|
1429
|
+
${r2sqlMissing}
|
|
1430
|
+
},
|
|
1431
|
+
};
|
|
1432
|
+
`
|
|
1433
|
+
};
|
|
1434
|
+
};
|
|
1435
|
+
const emitContainers = (containers, jurisdiction) => {
|
|
1187
1436
|
if (containers.length === 0) {
|
|
1188
1437
|
return "";
|
|
1189
1438
|
}
|
|
1439
|
+
const jurisdictionArgument = jurisdiction ? `, ${JSON.stringify(jurisdiction)}` : "";
|
|
1190
1440
|
const classes = containers.map((container) => {
|
|
1191
1441
|
assertIdentifier(container.exportName, `container export "${container.exportName}"`);
|
|
1192
1442
|
assertIdentifier(container.className, `container class "${container.className}"`);
|
|
1193
1443
|
return `/** Container DO for the \`${container.exportName}\` definition (binding \`${container.bindingName}\`). */
|
|
1194
1444
|
export class ${container.className} extends LunoraContainer {
|
|
1195
1445
|
public constructor(ctx: ConstructorParameters<typeof LunoraContainer>[0], env: Record<string, unknown>) {
|
|
1196
|
-
super(ctx, env, ${container.exportName}, "${container.exportName}");
|
|
1446
|
+
super(ctx, env, ${container.exportName}, "${container.exportName}"${jurisdictionArgument});
|
|
1197
1447
|
}
|
|
1198
1448
|
}
|
|
1199
1449
|
`;
|
|
@@ -1212,7 +1462,7 @@ import { ${imports} } from "../containers.js";
|
|
|
1212
1462
|
|
|
1213
1463
|
${classes}`;
|
|
1214
1464
|
};
|
|
1215
|
-
const emitContainerFragments = (containers) => {
|
|
1465
|
+
const emitContainerFragments = (containers, jurisdiction) => {
|
|
1216
1466
|
if (containers.length === 0) {
|
|
1217
1467
|
return { build: "", contextField: "", importLines: [], specs: "" };
|
|
1218
1468
|
}
|
|
@@ -1225,8 +1475,11 @@ const emitContainerFragments = (containers) => {
|
|
|
1225
1475
|
return ` { binding: "${container.bindingName}", exportName: "${container.exportName}"${maxInstances} },`;
|
|
1226
1476
|
}).join("\n");
|
|
1227
1477
|
return {
|
|
1478
|
+
// Schema `.jurisdiction("…")` pins every container DO this shard reaches
|
|
1479
|
+
// to the data-residency region; the arg is omitted when undeclared so
|
|
1480
|
+
// existing generated output is unchanged.
|
|
1228
1481
|
build: `
|
|
1229
|
-
const containers = createContainerContext(env, LUNORA_CONTAINERS);
|
|
1482
|
+
const containers = createContainerContext(env, LUNORA_CONTAINERS${jurisdiction ? `, ${JSON.stringify(jurisdiction)}` : ""});
|
|
1230
1483
|
`,
|
|
1231
1484
|
contextField: `
|
|
1232
1485
|
containers,`,
|
|
@@ -1420,6 +1673,7 @@ const emitShard = ({
|
|
|
1420
1673
|
hasImages = false,
|
|
1421
1674
|
hasKv = false,
|
|
1422
1675
|
hasPayments = false,
|
|
1676
|
+
hasR2sql = false,
|
|
1423
1677
|
maskMetadata,
|
|
1424
1678
|
rlsMetadata,
|
|
1425
1679
|
schema,
|
|
@@ -1435,12 +1689,13 @@ const emitShard = ({
|
|
|
1435
1689
|
const imagesFragments = emitImagesFragments(hasImages);
|
|
1436
1690
|
const hyperdriveFragments = emitHyperdriveFragments(hasHyperdrive);
|
|
1437
1691
|
const browserFragments = emitBrowserFragments(hasBrowser);
|
|
1692
|
+
const r2sqlFragments = emitR2sqlFragments(hasR2sql);
|
|
1438
1693
|
const {
|
|
1439
1694
|
build: containersBuild,
|
|
1440
1695
|
contextField: containersContextField,
|
|
1441
1696
|
importLines: containerImportLines,
|
|
1442
1697
|
specs: containerSpecs
|
|
1443
|
-
} = emitContainerFragments(containers);
|
|
1698
|
+
} = emitContainerFragments(containers, schema.jurisdiction);
|
|
1444
1699
|
const {
|
|
1445
1700
|
build: workflowsBuild,
|
|
1446
1701
|
contextField: workflowsContextField,
|
|
@@ -1509,6 +1764,7 @@ const emitShard = ({
|
|
|
1509
1764
|
...imagesFragments.importLines,
|
|
1510
1765
|
...hyperdriveFragments.importLines,
|
|
1511
1766
|
...browserFragments.importLines,
|
|
1767
|
+
...r2sqlFragments.importLines,
|
|
1512
1768
|
...containerImportLines,
|
|
1513
1769
|
...workflowImportLines,
|
|
1514
1770
|
...paymentsImports,
|
|
@@ -1654,15 +1910,16 @@ ${schema.tables.map(
|
|
|
1654
1910
|
` : "";
|
|
1655
1911
|
const everyContextBuild = `${kvFragments.build}${analyticsFragments.build}`;
|
|
1656
1912
|
const everyContextField = `${kvFragments.contextField}${analyticsFragments.contextField}`;
|
|
1657
|
-
const actionOnlyHasAny = hasImages || hasHyperdrive || hasBrowser;
|
|
1913
|
+
const actionOnlyHasAny = hasImages || hasHyperdrive || hasBrowser || hasR2sql;
|
|
1658
1914
|
const actionOnlyBlock = actionOnlyHasAny ? `
|
|
1659
1915
|
// ActionCtx-only helpers (external, non-deterministic I/O): constructed
|
|
1660
1916
|
// and attached only for an \`action\` so query/mutation ctx never carry them.
|
|
1661
1917
|
if (isAction) {
|
|
1662
|
-
${imagesFragments.build}${hyperdriveFragments.build}${browserFragments.build}${[
|
|
1918
|
+
${imagesFragments.build}${hyperdriveFragments.build}${browserFragments.build}${r2sqlFragments.build}${[
|
|
1663
1919
|
...hasImages ? [" ctx.images = images;"] : [],
|
|
1664
1920
|
...hasHyperdrive ? [" ctx.sql = sql;"] : [],
|
|
1665
|
-
...hasBrowser ? [" ctx.browser = browser;"] : []
|
|
1921
|
+
...hasBrowser ? [" ctx.browser = browser;"] : [],
|
|
1922
|
+
...hasR2sql ? [" ctx.r2sql = r2sql;"] : []
|
|
1666
1923
|
].join("\n")}
|
|
1667
1924
|
}
|
|
1668
1925
|
` : "";
|
|
@@ -1709,7 +1966,7 @@ export interface ShardDOConfig {
|
|
|
1709
1966
|
/** Optional telemetry sink. When supplied, each \`ctx.log.*\` call is forwarded to \`sink.onLog\`. Pass the SAME sink you give \`createWorker({ observability })\` (which drives \`onRpc\`) to route both RPC and log events. */
|
|
1710
1967
|
observability?: (env: Record<string, unknown>) => LogSink | undefined;
|
|
1711
1968
|
scheduler?: (env: Record<string, unknown>) => unknown;
|
|
1712
|
-
storage?: (env: Record<string, unknown>) => unknown;${vectorsConfigField}${aiConfigField}${kvFragments.configField}${analyticsFragments.configField}${imagesFragments.configField}${hyperdriveFragments.configField}${browserFragments.configField}${paymentsConfigField}${d1ConfigField}${hyperdriveGlobalConfigField}
|
|
1969
|
+
storage?: (env: Record<string, unknown>) => unknown;${vectorsConfigField}${aiConfigField}${kvFragments.configField}${analyticsFragments.configField}${imagesFragments.configField}${hyperdriveFragments.configField}${browserFragments.configField}${r2sqlFragments.configField}${paymentsConfigField}${d1ConfigField}${hyperdriveGlobalConfigField}
|
|
1713
1970
|
}
|
|
1714
1971
|
|
|
1715
1972
|
const schedulerStub = {
|
|
@@ -1747,7 +2004,7 @@ const storageStub = {
|
|
|
1747
2004
|
throw new Error("ctx.storage: no storage configured. Pass \`storage\` to createShardDO().");
|
|
1748
2005
|
},
|
|
1749
2006
|
};
|
|
1750
|
-
${globalDatabaseStub}${vectorsStub}${aiStub}${kvFragments.stub}${analyticsFragments.stub}${imagesFragments.stub}${hyperdriveFragments.stub}${browserFragments.stub}${paymentStub}${bindTableHelper}
|
|
2007
|
+
${globalDatabaseStub}${vectorsStub}${aiStub}${kvFragments.stub}${analyticsFragments.stub}${imagesFragments.stub}${hyperdriveFragments.stub}${browserFragments.stub}${r2sqlFragments.stub}${paymentStub}${bindTableHelper}
|
|
1751
2008
|
// Bound in-process \`ctx.run*\` composition depth so a self- or cyclically-
|
|
1752
2009
|
// referencing call fails loudly with a clear error instead of overflowing the
|
|
1753
2010
|
// stack. Tracked across the awaited handler chain (one DO invocation is
|
|
@@ -2149,6 +2406,14 @@ ${facadeBlock}${paymentsBuild}
|
|
|
2149
2406
|
warn: (...args: unknown[]) => { this.recordUserLog(logFunctionPath, "warn", args, observability); },
|
|
2150
2407
|
};
|
|
2151
2408
|
|
|
2409
|
+
// \`ctx.now\`: the wall-clock instant (epoch ms) this function began,
|
|
2410
|
+
// captured ONCE so the whole handler body sees a single stable value.
|
|
2411
|
+
// Query/mutation handlers must be deterministic (they may be re-run on
|
|
2412
|
+
// OCC retry / subscription re-eval), so they must read time through
|
|
2413
|
+
// \`ctx.now\` instead of \`Date.now()\` — the \`nondeterministic_query_mutation\`
|
|
2414
|
+
// advisor flags the latter. Actions may still use ambient \`Date.now()\`.
|
|
2415
|
+
const now = Date.now();
|
|
2416
|
+
|
|
2152
2417
|
const ctx: Record<string, unknown> = {
|
|
2153
2418
|
auth: {
|
|
2154
2419
|
getIdentity: async () => identity ?? null,
|
|
@@ -2157,7 +2422,8 @@ ${facadeBlock}${paymentsBuild}
|
|
|
2157
2422
|
db,
|
|
2158
2423
|
fetch: globalThis.fetch.bind(globalThis),
|
|
2159
2424
|
ip: this.getCurrentIp(),
|
|
2160
|
-
log
|
|
2425
|
+
log,
|
|
2426
|
+
now,${ormContextField}
|
|
2161
2427
|
scheduler,
|
|
2162
2428
|
storage,${vectorsContextField}${aiContextField}${everyContextField}${paymentsContextField}${containersContextField}${workflowsContextField}
|
|
2163
2429
|
};
|
package/dist/packem_shared/{buildOpenRpcDocument-BZGY1-jT.mjs → OPENRPC_VERSION-B4i9Qp3v.mjs}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { GENERATED_HEADER } from './
|
|
1
|
+
import { GENERATED_HEADER } from './GENERATED_HEADER-BiFXNUvo.mjs';
|
|
2
2
|
import { s as sanitizeNamespace } from './paths-BRd6JHuF.mjs';
|
|
3
3
|
import { argsObjectSchema, LUNORA_ERROR_CODES } from './LUNORA_ERROR_CODES-CySpQPD3.mjs';
|
|
4
4
|
|