@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.
Files changed (29) hide show
  1. package/__assets__/package-og.svg +1 -1
  2. package/dist/index.d.mts +96 -7
  3. package/dist/index.d.ts +96 -7
  4. package/dist/index.mjs +23 -22
  5. package/dist/packem_shared/{CONTAINERS_FILENAME-0K-pjNb8.mjs → CONTAINERS_FILENAME-DlP6YM_Q.mjs} +1 -1
  6. package/dist/packem_shared/{CodegenDiagnosticError-54jWDxA9.mjs → CodegenDiagnosticError-DeblMkzO.mjs} +3 -2
  7. package/dist/packem_shared/{emitApi-hRVC-kE7.mjs → GENERATED_HEADER-BiFXNUvo.mjs} +285 -19
  8. package/dist/packem_shared/{buildOpenRpcDocument-BZGY1-jT.mjs → OPENRPC_VERSION-B4i9Qp3v.mjs} +1 -1
  9. package/dist/packem_shared/{createCodegenProject-DGJm0_Pk.mjs → SCHEMA_SNAPSHOT_FILENAME-Bvv7qa_u.mjs} +81 -37
  10. package/dist/packem_shared/{buildSchemaSnapshot-DzLDbWk3.mjs → SCHEMA_SNAPSHOT_VERSION-wWGP2_5E.mjs} +12 -0
  11. package/dist/packem_shared/{discoverWorkflows-DRDQdhfq.mjs → WORKFLOWS_FILENAME-D62dcBGg.mjs} +1 -1
  12. package/dist/packem_shared/{buildOpenApiDocument-yHVN66Xd.mjs → buildOpenApiDocument-Cu3mZl-8.mjs} +1 -1
  13. package/dist/packem_shared/{discoverAuthApiCalls-C35R6z0T.mjs → discoverAuthApiCalls-CoirYbg6.mjs} +1 -1
  14. package/dist/packem_shared/{discoverCrons-BL6iGuJ3.mjs → discoverCrons-Cev7RRAf.mjs} +20 -17
  15. package/dist/packem_shared/{discoverFunctions-DEgAcRuD.mjs → discoverFunctions-BWMczzBx.mjs} +7 -2
  16. package/dist/packem_shared/{discoverHttpRoutes-C978pBiG.mjs → discoverHttpRoutes-CfP6cMzt.mjs} +2 -2
  17. package/dist/packem_shared/{discoverInserts-CRQdXvHO.mjs → discoverInserts-C7zxXkUf.mjs} +24 -2
  18. package/dist/packem_shared/{discoverMaskProcedures-B64zA740.mjs → discoverMaskProcedures-Cm81kwrb.mjs} +1 -1
  19. package/dist/packem_shared/{discoverMigrations-Doj_-BAA.mjs → discoverMigrations-Bi5nJ0mJ.mjs} +10 -4
  20. package/dist/packem_shared/{discoverNondeterministicCalls-4KiPQxQU.mjs → discoverNondeterministicCalls-C4M8AXmQ.mjs} +1 -1
  21. package/dist/packem_shared/{discoverQueries-BkIi0dBD.mjs → discoverQueries-B0wGT-xe.mjs} +1 -1
  22. package/dist/packem_shared/discoverR2sqlCalls-BVNMd428.mjs +80 -0
  23. package/dist/packem_shared/{discoverRlsMetadata-DpRB1HMe.mjs → discoverRlsMetadata-BS9GOGC5.mjs} +1 -1
  24. package/dist/packem_shared/{discoverSchema-BBulgGbH.mjs → discoverSchema-BnWHHJ4T.mjs} +296 -16
  25. package/dist/packem_shared/{discoverStorageRulesMetadata-DAqJUxUv.mjs → discoverStorageRulesMetadata-Da8BKXcI.mjs} +1 -1
  26. package/dist/packem_shared/{emitApp-CzZ6GbrD.mjs → emitApp-KfMaKXWe.mjs} +12 -2
  27. package/dist/packem_shared/{lintSchema-DicbOHvH.mjs → formatAdvisories-8NIv1k0I.mjs} +2 -1
  28. package/dist/packem_shared/{parse-validator-tuQtHrsr.mjs → parse-validator-Cabb60UV.mjs} +2 -1
  29. 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 { dispatchBody, importBlock, migrationBody } = renderFunctionRegistry(functions, migrations);
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 emitContainers = (containers) => {
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,${ormContextField}
2425
+ log,
2426
+ now,${ormContextField}
2161
2427
  scheduler,
2162
2428
  storage,${vectorsContextField}${aiContextField}${everyContextField}${paymentsContextField}${containersContextField}${workflowsContextField}
2163
2429
  };
@@ -1,4 +1,4 @@
1
- import { GENERATED_HEADER } from './emitApi-hRVC-kE7.mjs';
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