@lunora/codegen 1.0.0-alpha.1 → 1.0.0-alpha.11

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 (30) hide show
  1. package/__assets__/package-og.svg +1 -1
  2. package/dist/index.d.mts +171 -14
  3. package/dist/index.d.ts +171 -14
  4. package/dist/index.mjs +24 -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-r1OZrKAx.mjs} +451 -43
  8. package/dist/packem_shared/{buildOpenRpcDocument-BZGY1-jT.mjs → OPENRPC_VERSION-BYRhDy-7.mjs} +1 -1
  9. package/dist/packem_shared/QUEUES_FILENAME-B5_eWCRe.mjs +119 -0
  10. package/dist/packem_shared/{createCodegenProject-DGJm0_Pk.mjs → SCHEMA_SNAPSHOT_FILENAME-CgJUPhI3.mjs} +102 -43
  11. package/dist/packem_shared/{buildSchemaSnapshot-DzLDbWk3.mjs → SCHEMA_SNAPSHOT_VERSION-wWGP2_5E.mjs} +12 -0
  12. package/dist/packem_shared/{discoverWorkflows-DRDQdhfq.mjs → WORKFLOWS_FILENAME-D62dcBGg.mjs} +1 -1
  13. package/dist/packem_shared/{buildOpenApiDocument-yHVN66Xd.mjs → buildOpenApiDocument-C8NKrvpw.mjs} +1 -1
  14. package/dist/packem_shared/{discoverAuthApiCalls-C35R6z0T.mjs → discoverAuthApiCalls-CoirYbg6.mjs} +1 -1
  15. package/dist/packem_shared/{discoverCrons-BL6iGuJ3.mjs → discoverCrons-Cev7RRAf.mjs} +20 -17
  16. package/dist/packem_shared/{discoverFunctions-DEgAcRuD.mjs → discoverFunctions-BWMczzBx.mjs} +7 -2
  17. package/dist/packem_shared/{discoverHttpRoutes-C978pBiG.mjs → discoverHttpRoutes-CfP6cMzt.mjs} +2 -2
  18. package/dist/packem_shared/{discoverInserts-CRQdXvHO.mjs → discoverInserts-C7zxXkUf.mjs} +24 -2
  19. package/dist/packem_shared/{discoverMaskProcedures-B64zA740.mjs → discoverMaskProcedures-Cm81kwrb.mjs} +1 -1
  20. package/dist/packem_shared/{discoverMigrations-Doj_-BAA.mjs → discoverMigrations-Bi5nJ0mJ.mjs} +10 -4
  21. package/dist/packem_shared/{discoverNondeterministicCalls-4KiPQxQU.mjs → discoverNondeterministicCalls-C4M8AXmQ.mjs} +1 -1
  22. package/dist/packem_shared/{discoverQueries-BkIi0dBD.mjs → discoverQueries-B0wGT-xe.mjs} +1 -1
  23. package/dist/packem_shared/discoverR2sqlCalls-BVNMd428.mjs +80 -0
  24. package/dist/packem_shared/{discoverRlsMetadata-DpRB1HMe.mjs → discoverRlsMetadata-BS9GOGC5.mjs} +1 -1
  25. package/dist/packem_shared/{discoverSchema-BBulgGbH.mjs → discoverSchema-BnWHHJ4T.mjs} +296 -16
  26. package/dist/packem_shared/{discoverStorageRulesMetadata-DAqJUxUv.mjs → discoverStorageRulesMetadata-Da8BKXcI.mjs} +1 -1
  27. package/dist/packem_shared/{emitApp-CzZ6GbrD.mjs → emitApp-B1DgLrM8.mjs} +28 -4
  28. package/dist/packem_shared/{lintSchema-DicbOHvH.mjs → formatAdvisories-8NIv1k0I.mjs} +2 -1
  29. package/dist/packem_shared/{parse-validator-tuQtHrsr.mjs → parse-validator-Cabb60UV.mjs} +2 -1
  30. package/package.json +8 -7
@@ -1,4 +1,4 @@
1
- import { GENERATED_HEADER } from './emitApi-hRVC-kE7.mjs';
1
+ import { GENERATED_HEADER } from './GENERATED_HEADER-r1OZrKAx.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
 
@@ -0,0 +1,119 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { queueDefaultName, queueBindingName } from '@lunora/queue';
4
+ import { SyntaxKind, Node } from 'ts-morph';
5
+ import { diagnosticAt } from './CodegenDiagnosticError-DeblMkzO.mjs';
6
+
7
+ const QUEUES_FILENAME = "queues.ts";
8
+ const isDefineQueue = (identifier) => {
9
+ const symbol = identifier.getSymbol();
10
+ if (!symbol) {
11
+ return identifier.getText() === "defineQueue";
12
+ }
13
+ for (const declaration of symbol.getDeclarations()) {
14
+ if (!Node.isImportSpecifier(declaration)) {
15
+ continue;
16
+ }
17
+ if (declaration.getImportDeclaration().getModuleSpecifierValue() !== "@lunora/queue") {
18
+ return false;
19
+ }
20
+ return declaration.getNameNode().getText() === "defineQueue";
21
+ }
22
+ return false;
23
+ };
24
+ const stringProperty = (expression, exportName, property) => {
25
+ if (Node.isStringLiteral(expression) || Node.isNoSubstitutionTemplateLiteral(expression)) {
26
+ return expression.getLiteralValue();
27
+ }
28
+ throw diagnosticAt(
29
+ expression,
30
+ `queue "${exportName}": \`${property}\` must be a static string literal — it is deploy configuration codegen writes into wrangler.jsonc`
31
+ );
32
+ };
33
+ const numberProperty = (expression, exportName, property) => {
34
+ if (Node.isNumericLiteral(expression)) {
35
+ return expression.getLiteralValue();
36
+ }
37
+ throw diagnosticAt(
38
+ expression,
39
+ `queue "${exportName}": \`${property}\` must be a static numeric literal — it is deploy configuration codegen writes into wrangler.jsonc`
40
+ );
41
+ };
42
+ const queueNameOverride = (argument, exportName) => {
43
+ const nameProperty = argument.getProperty("name");
44
+ if (!nameProperty || !Node.isPropertyAssignment(nameProperty)) {
45
+ return void 0;
46
+ }
47
+ const name = stringProperty(nameProperty.getInitializerOrThrow(), exportName, "name");
48
+ if (name.length === 0) {
49
+ throw diagnosticAt(nameProperty, `queue "${exportName}": \`name\` must be a non-empty string when provided`);
50
+ }
51
+ return name;
52
+ };
53
+ const queueFromCall = (call, exportName) => {
54
+ const argument = call.getArguments()[0];
55
+ if (!argument || !Node.isObjectLiteralExpression(argument)) {
56
+ throw diagnosticAt(call, `queue "${exportName}": defineQueue must be passed an inline object literal`);
57
+ }
58
+ const ir = {
59
+ bindingName: queueBindingName(exportName),
60
+ exportName,
61
+ mode: "push",
62
+ name: queueNameOverride(argument, exportName) ?? queueDefaultName(exportName),
63
+ tuning: {}
64
+ };
65
+ const modeProperty = argument.getProperty("mode");
66
+ if (modeProperty && Node.isPropertyAssignment(modeProperty)) {
67
+ const mode = stringProperty(modeProperty.getInitializerOrThrow(), exportName, "mode");
68
+ if (mode !== "push" && mode !== "pull") {
69
+ throw diagnosticAt(modeProperty, `queue "${exportName}": \`mode\` must be "push" or "pull" (got ${JSON.stringify(mode)})`);
70
+ }
71
+ ir.mode = mode;
72
+ }
73
+ const dlqProperty = argument.getProperty("deadLetterQueue");
74
+ if (dlqProperty && Node.isPropertyAssignment(dlqProperty)) {
75
+ ir.tuning.deadLetterQueue = stringProperty(dlqProperty.getInitializerOrThrow(), exportName, "deadLetterQueue");
76
+ }
77
+ for (const property of ["maxBatchSize", "maxBatchTimeout", "maxRetries", "retryDelay"]) {
78
+ const node = argument.getProperty(property);
79
+ if (node && Node.isPropertyAssignment(node)) {
80
+ ir.tuning[property] = numberProperty(node.getInitializerOrThrow(), exportName, property);
81
+ }
82
+ }
83
+ return ir;
84
+ };
85
+ const queuesFromSource = (source) => {
86
+ const queues = [];
87
+ for (const declaration of source.getVariableDeclarations()) {
88
+ if (!declaration.isExported()) {
89
+ continue;
90
+ }
91
+ const initializer = declaration.getInitializer();
92
+ if (initializer?.getKind() !== SyntaxKind.CallExpression) {
93
+ continue;
94
+ }
95
+ const call = initializer;
96
+ const callee = call.getExpression();
97
+ if (!Node.isIdentifier(callee) || !isDefineQueue(callee)) {
98
+ continue;
99
+ }
100
+ const nameNode = declaration.getNameNode();
101
+ if (!Node.isIdentifier(nameNode)) {
102
+ throw diagnosticAt(nameNode, "defineQueue exports must be plain named exports (no destructuring)");
103
+ }
104
+ queues.push(queueFromCall(call, nameNode.getText()));
105
+ }
106
+ return queues;
107
+ };
108
+ const discoverQueues = (project, lunoraDirectory) => {
109
+ const queuesPath = join(lunoraDirectory, QUEUES_FILENAME);
110
+ if (!existsSync(queuesPath)) {
111
+ return [];
112
+ }
113
+ const source = project.getSourceFile(queuesPath) ?? project.addSourceFileAtPath(queuesPath);
114
+ const queues = queuesFromSource(source);
115
+ queues.sort((a, b) => a.exportName.localeCompare(b.exportName));
116
+ return queues;
117
+ };
118
+
119
+ export { QUEUES_FILENAME, discoverQueues };
@@ -1,27 +1,30 @@
1
1
  import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'node:fs';
2
2
  import { join, dirname } from 'node:path';
3
+ import { performance } from 'node:perf_hooks';
3
4
  import { Node, SyntaxKind, Project } from 'ts-morph';
4
- import { lintSchema } from './lintSchema-DicbOHvH.mjs';
5
- import { listLunoraSourceFiles, lunoraRelativePath, classifyProcedureCall, discoverFunctions } from './discoverFunctions-DEgAcRuD.mjs';
6
- import discoverAuthApiCalls from './discoverAuthApiCalls-C35R6z0T.mjs';
7
- import { discoverContainers } from './CONTAINERS_FILENAME-0K-pjNb8.mjs';
8
- import discoverCrons from './discoverCrons-BL6iGuJ3.mjs';
9
- import discoverHttpRoutes from './discoverHttpRoutes-C978pBiG.mjs';
10
- import discoverInserts from './discoverInserts-CRQdXvHO.mjs';
11
- import discoverMaskProcedures, { discoverMaskMetadata } from './discoverMaskProcedures-B64zA740.mjs';
12
- import discoverMigrations from './discoverMigrations-Doj_-BAA.mjs';
13
- import discoverNondeterministicCalls from './discoverNondeterministicCalls-4KiPQxQU.mjs';
14
- import discoverQueries from './discoverQueries-BkIi0dBD.mjs';
15
- import discoverRlsProcedures, { discoverRlsMetadata } from './discoverRlsMetadata-DpRB1HMe.mjs';
16
- import discoverSchema from './discoverSchema-BBulgGbH.mjs';
17
- import discoverStorageRulesMetadata from './discoverStorageRulesMetadata-DAqJUxUv.mjs';
5
+ import { lintSchema } from './formatAdvisories-8NIv1k0I.mjs';
6
+ import { listLunoraSourceFiles, lunoraRelativePath, classifyProcedureCall, discoverFunctions } from './discoverFunctions-BWMczzBx.mjs';
7
+ import discoverAuthApiCalls from './discoverAuthApiCalls-CoirYbg6.mjs';
8
+ import { discoverContainers } from './CONTAINERS_FILENAME-DlP6YM_Q.mjs';
9
+ import discoverCrons from './discoverCrons-Cev7RRAf.mjs';
10
+ import discoverHttpRoutes from './discoverHttpRoutes-CfP6cMzt.mjs';
11
+ import discoverInserts from './discoverInserts-C7zxXkUf.mjs';
12
+ import discoverMaskProcedures, { discoverMaskMetadata } from './discoverMaskProcedures-Cm81kwrb.mjs';
13
+ import discoverMigrations from './discoverMigrations-Bi5nJ0mJ.mjs';
14
+ import discoverNondeterministicCalls from './discoverNondeterministicCalls-C4M8AXmQ.mjs';
15
+ import discoverQueries from './discoverQueries-B0wGT-xe.mjs';
16
+ import { discoverQueues } from './QUEUES_FILENAME-B5_eWCRe.mjs';
17
+ import discoverR2sqlCalls from './discoverR2sqlCalls-BVNMd428.mjs';
18
+ import discoverRlsProcedures, { discoverRlsMetadata } from './discoverRlsMetadata-BS9GOGC5.mjs';
19
+ import discoverSchema from './discoverSchema-BnWHHJ4T.mjs';
20
+ import discoverStorageRulesMetadata from './discoverStorageRulesMetadata-Da8BKXcI.mjs';
18
21
  import { e as enclosingExportName$1 } from './discover-ast-CT6BgBr4.mjs';
19
- import { discoverWorkflows } from './discoverWorkflows-DRDQdhfq.mjs';
20
- import { buildStorageColumns, emitDataModel, emitApi, emitServer, emitFunctions, emitShard, emitContainers, emitWorkflows, emitCrons, emitVectors, emitDrizzleSchema, emitSeed, emitWranglerCronTriggers } from './emitApi-hRVC-kE7.mjs';
21
- import { emitApp } from './emitApp-CzZ6GbrD.mjs';
22
- import { buildOpenApiDocument, emitOpenApiModule } from './buildOpenApiDocument-yHVN66Xd.mjs';
23
- import { buildOpenRpcDocument, emitOpenRpcModule } from './buildOpenRpcDocument-BZGY1-jT.mjs';
24
- import { buildSchemaSnapshot, serializeSchemaSnapshot } from './buildSchemaSnapshot-DzLDbWk3.mjs';
22
+ import { discoverWorkflows } from './WORKFLOWS_FILENAME-D62dcBGg.mjs';
23
+ import { buildStorageColumns, emitDataModel, emitApi, emitServer, emitFunctions, emitShard, emitContainers, emitWorkflows, emitQueues, emitCrons, emitVectors, emitDrizzleSchema, emitSeed, emitWranglerCronTriggers } from './GENERATED_HEADER-r1OZrKAx.mjs';
24
+ import { emitApp } from './emitApp-B1DgLrM8.mjs';
25
+ import { buildOpenApiDocument, emitOpenApiModule } from './buildOpenApiDocument-C8NKrvpw.mjs';
26
+ import { buildOpenRpcDocument, emitOpenRpcModule } from './OPENRPC_VERSION-BYRhDy-7.mjs';
27
+ import { buildSchemaSnapshot, serializeSchemaSnapshot } from './SCHEMA_SNAPSHOT_VERSION-wWGP2_5E.mjs';
25
28
 
26
29
  const HTTP_VERBS = /* @__PURE__ */ new Set(["delete", "get", "head", "options", "patch", "post", "put"]);
27
30
  const TERMINAL_STEPS = /* @__PURE__ */ new Set(["handler", "stream"]);
@@ -238,17 +241,21 @@ const discoverArgumentValidators = (project, lunoraDirectory) => {
238
241
 
239
242
  const PROBES = {
240
243
  ai: { contextProperty: "ai", moduleSpecifier: "@lunora/ai" },
241
- analytics: { contextProperty: "analytics", moduleSpecifier: "@lunora/analytics" },
244
+ analytics: { contextProperty: "analytics", moduleSpecifier: "@lunora/bindings/analytics" },
242
245
  browser: { contextProperty: "browser", moduleSpecifier: "@lunora/browser" },
243
246
  hyperdrive: { contextProperty: "sql", moduleSpecifier: "@lunora/hyperdrive" },
244
- images: { contextProperty: "images", moduleSpecifier: "@lunora/images" },
245
- kv: { contextProperty: "kv", moduleSpecifier: "@lunora/kv" },
247
+ images: { contextProperty: "images", moduleSpecifier: "@lunora/bindings/images" },
248
+ kv: { contextProperty: "kv", moduleSpecifier: "@lunora/bindings/kv" },
246
249
  mail: { moduleSpecifier: "@lunora/mail" },
247
250
  payments: { contextProperty: "payments", moduleSpecifier: "@lunora/payment" },
248
- pipelines: { contextProperty: "pipelines", moduleSpecifier: "@lunora/pipelines" },
251
+ // Pipelines is its own `@lunora/bindings/pipelines` subpath (distinct from
252
+ // `/analytics`), so a real import is a clean signal that won't be flipped by a
253
+ // plain analytics import; `ctx.pipelines` reads flip it too.
254
+ pipelines: { contextProperty: "pipelines", moduleSpecifier: "@lunora/bindings/pipelines" },
255
+ r2sql: { contextProperty: "r2sql", moduleSpecifier: "@lunora/bindings/r2sql" },
249
256
  scheduler: { contextProperty: "scheduler", moduleSpecifier: "@lunora/scheduler" },
250
257
  storage: { contextProperty: "storage", moduleSpecifier: "@lunora/storage" },
251
- vectors: { contextProperty: "vectors", moduleSpecifier: "@lunora/vectors" },
258
+ vectors: { contextProperty: "vectors", moduleSpecifier: "@lunora/bindings/vectors" },
252
259
  workflows: { contextProperty: "workflows", moduleSpecifier: "@lunora/workflow" }
253
260
  };
254
261
  const readsContextProperty = (sourceFile, property) => {
@@ -277,6 +284,7 @@ const discoverFeatureUsage = (project, lunoraDirectory) => {
277
284
  mail: false,
278
285
  payments: false,
279
286
  pipelines: false,
287
+ r2sql: false,
280
288
  scheduler: false,
281
289
  storage: false,
282
290
  vectors: false,
@@ -309,9 +317,10 @@ const buildStudioFeatures = (usage, signals) => {
309
317
  return {
310
318
  mail: usage.mail || signals.dependencies.has("@lunora/mail"),
311
319
  payments: usage.payments || signals.dependencies.has("@lunora/payment"),
320
+ queues: signals.queueCount > 0 || signals.dependencies.has("@lunora/queue"),
312
321
  scheduler: usage.scheduler || signals.cronCount > 0 || signals.dependencies.has("@lunora/scheduler"),
313
322
  storage: usage.storage || signals.storageRuleCount > 0 || signals.storageColumnCount > 0 || signals.dependencies.has("@lunora/storage"),
314
- vectors: usage.vectors || signals.vectorIndexCount > 0 || signals.dependencies.has("@lunora/vectors"),
323
+ vectors: usage.vectors || signals.vectorIndexCount > 0 || signals.dependencies.has("@lunora/bindings/vectors"),
315
324
  workflows: usage.workflows || signals.workflowCount > 0 || signals.dependencies.has("@lunora/workflow")
316
325
  };
317
326
  };
@@ -364,26 +373,45 @@ const protectPublicFlags = (call) => {
364
373
  }
365
374
  return { usesCaptcha: Boolean(argument.getProperty("captcha")), usesRateLimit: Boolean(argument.getProperty("rateLimit")) };
366
375
  };
376
+ const resolveUseArgumentCall = (argument) => {
377
+ if (Node.isCallExpression(argument)) {
378
+ return argument;
379
+ }
380
+ if (!Node.isIdentifier(argument)) {
381
+ return void 0;
382
+ }
383
+ const declaration = argument.getSourceFile().getVariableDeclaration(argument.getText());
384
+ const initializer = declaration?.getInitializer();
385
+ return initializer && Node.isCallExpression(initializer) ? initializer : void 0;
386
+ };
387
+ const NO_PROTECTIONS = { usesCaptcha: false, usesMask: false, usesRateLimit: false, usesRls: false };
388
+ const useStepProtections = (useArgument) => {
389
+ const argument = resolveUseArgumentCall(useArgument);
390
+ const name = argument ? calleeNameOf(argument) : void 0;
391
+ if (argument && name === "protectPublic") {
392
+ const bundle = protectPublicFlags(argument);
393
+ return { ...NO_PROTECTIONS, usesCaptcha: bundle.usesCaptcha, usesRateLimit: bundle.usesRateLimit };
394
+ }
395
+ if (name !== void 0 && name in MIDDLEWARE_FLAGS) {
396
+ return { ...NO_PROTECTIONS, [MIDDLEWARE_FLAGS[name]]: true };
397
+ }
398
+ return NO_PROTECTIONS;
399
+ };
367
400
  const protectionsInChain = (receiver) => {
368
- const protections = { usesCaptcha: false, usesMask: false, usesRateLimit: false, usesRls: false };
401
+ const protections = { ...NO_PROTECTIONS };
369
402
  let node = receiver;
370
403
  while (Node.isCallExpression(node)) {
371
404
  const chainCallee = node.getExpression();
372
405
  if (!Node.isPropertyAccessExpression(chainCallee)) {
373
406
  break;
374
407
  }
375
- if (chainCallee.getName() === "use") {
376
- const argument = node.getArguments()[0];
377
- if (argument && Node.isCallExpression(argument)) {
378
- const name = calleeNameOf(argument);
379
- if (name === "protectPublic") {
380
- const bundle = protectPublicFlags(argument);
381
- protections.usesRateLimit ||= bundle.usesRateLimit;
382
- protections.usesCaptcha ||= bundle.usesCaptcha;
383
- } else if (name !== void 0 && name in MIDDLEWARE_FLAGS) {
384
- protections[MIDDLEWARE_FLAGS[name]] = true;
385
- }
386
- }
408
+ const useArgument = chainCallee.getName() === "use" ? node.getArguments()[0] : void 0;
409
+ if (useArgument) {
410
+ const step = useStepProtections(useArgument);
411
+ protections.usesCaptcha ||= step.usesCaptcha;
412
+ protections.usesMask ||= step.usesMask;
413
+ protections.usesRateLimit ||= step.usesRateLimit;
414
+ protections.usesRls ||= step.usesRls;
387
415
  }
388
416
  node = chainCallee.getExpression();
389
417
  }
@@ -654,6 +682,10 @@ const readProjectVersion = (projectRoot) => {
654
682
  return void 0;
655
683
  }
656
684
  };
685
+ const isTimingEnabled = () => {
686
+ const flag = process.env["LUNORA_CODEGEN_TIMING"];
687
+ return flag !== void 0 && flag !== "";
688
+ };
657
689
  const findTsconfig = (startPath) => {
658
690
  let directory = existsSync(startPath) ? startPath : dirname(startPath);
659
691
  while (directory && directory !== dirname(directory)) {
@@ -695,17 +727,20 @@ const refreshCodegenProject = (project, lunoraDirectory) => {
695
727
  }
696
728
  };
697
729
  const runCodegen = (options) => {
730
+ const timingEnabled = isTimingEnabled();
731
+ const startedAt = timingEnabled ? performance.now() : 0;
698
732
  const lunoraDirectory = join(options.projectRoot, options.lunoraDirectory ?? "lunora");
699
733
  const schemaPath = join(lunoraDirectory, "schema.ts");
700
734
  if (!existsSync(schemaPath)) {
701
735
  throw new Error(`schema.ts not found at ${schemaPath}`);
702
736
  }
703
737
  const project = options.project ?? createCodegenProject(lunoraDirectory);
704
- const schema = discoverSchema(project, schemaPath);
738
+ const schema = discoverSchema(project, schemaPath, options.projectRoot);
705
739
  const functions = discoverFunctions(project, lunoraDirectory);
706
740
  const httpRoutes = discoverHttpRoutes(project, lunoraDirectory);
707
741
  const migrations = discoverMigrations(project, lunoraDirectory);
708
742
  const workflows = discoverWorkflows(project, lunoraDirectory);
743
+ const queues = discoverQueues(project, lunoraDirectory);
709
744
  const crons = discoverCrons(project, lunoraDirectory, workflows);
710
745
  const containers = discoverContainers(project, lunoraDirectory);
711
746
  const advisories = options.lint === false ? [] : lintSchema(
@@ -723,7 +758,8 @@ const runCodegen = (options) => {
723
758
  discoverArgumentValidators(project, lunoraDirectory),
724
759
  discoverSecrets(project, lunoraDirectory),
725
760
  discoverSqlInterpolation(project, lunoraDirectory),
726
- discoverAdminRoutes(project, lunoraDirectory)
761
+ discoverAdminRoutes(project, lunoraDirectory),
762
+ discoverR2sqlCalls(project, lunoraDirectory)
727
763
  );
728
764
  const rlsMetadata = discoverRlsMetadata(project, lunoraDirectory);
729
765
  const maskMetadata = discoverMaskMetadata(project, lunoraDirectory);
@@ -737,16 +773,19 @@ const runCodegen = (options) => {
737
773
  const hasImages = featureUsage.images;
738
774
  const hasAnalytics = featureUsage.analytics;
739
775
  const hasPipelines = featureUsage.pipelines;
776
+ const hasR2sql = featureUsage.r2sql;
740
777
  const dependencies = discoverPackageDependencies(options.projectRoot);
741
778
  const studioFeatures = buildStudioFeatures(featureUsage, {
742
779
  cronCount: crons.length,
743
780
  dependencies,
781
+ queueCount: queues.length,
744
782
  storageColumnCount: Object.keys(buildStorageColumns(schema)).length,
745
783
  storageRuleCount: storageRulesMetadata.rules.length,
746
784
  vectorIndexCount: schema.vectorIndexes.length,
747
785
  workflowCount: workflows.length
748
786
  });
749
787
  const useUmbrella = dependencies.has("lunorash");
788
+ const emitStartedAt = timingEnabled ? performance.now() : 0;
750
789
  const dataModelContent = emitDataModel(schema, useUmbrella);
751
790
  const apiContent = emitApi(functions, workflows, useUmbrella);
752
791
  const serverContent = emitServer({
@@ -759,12 +798,14 @@ const runCodegen = (options) => {
759
798
  hasKv,
760
799
  hasPayments,
761
800
  hasPipelines,
801
+ hasR2sql,
802
+ queues,
762
803
  schema,
763
804
  storageRuleBuckets: storageRulesMetadata.rules.map((rule) => rule.bucket),
764
805
  useUmbrella,
765
806
  workflows
766
807
  });
767
- const functionsContent = emitFunctions(functions, migrations);
808
+ const functionsContent = emitFunctions(functions, migrations, useUmbrella);
768
809
  const shardContent = emitShard({
769
810
  advisories,
770
811
  containers,
@@ -775,7 +816,10 @@ const runCodegen = (options) => {
775
816
  hasImages,
776
817
  hasKv,
777
818
  hasPayments,
819
+ hasPipelines,
820
+ hasR2sql,
778
821
  maskMetadata,
822
+ queues,
779
823
  rlsMetadata,
780
824
  schema,
781
825
  storageRules: storageRulesMetadata,
@@ -783,8 +827,9 @@ const runCodegen = (options) => {
783
827
  useUmbrella,
784
828
  workflows
785
829
  });
786
- const containersContent = emitContainers(containers);
830
+ const containersContent = emitContainers(containers, schema.jurisdiction);
787
831
  const workflowsContent = emitWorkflows(workflows);
832
+ const queuesContent = emitQueues(queues);
788
833
  const cronsContent = emitCrons(crons);
789
834
  const vectorsContent = emitVectors(schema.vectorIndexes);
790
835
  const drizzleFiles = emitDrizzleSchema(schema, useUmbrella);
@@ -809,10 +854,14 @@ const runCodegen = (options) => {
809
854
  hasImages,
810
855
  hasKv,
811
856
  hasPayments,
857
+ hasR2sql,
858
+ hasQueue: queues.some((queue) => queue.mode === "push"),
812
859
  hasScheduler: studioFeatures.scheduler,
813
860
  hasStorage: studioFeatures.storage,
814
861
  hasVectors: schema.vectorIndexes.length > 0,
815
862
  hasWorkflow: workflows.length > 0,
863
+ // Schema `.jurisdiction("…")` → pin the generated worker's DOs to the region.
864
+ jurisdiction: schema.jurisdiction,
816
865
  useUmbrella,
817
866
  wantsOpenApi,
818
867
  wantsOpenRpc
@@ -849,6 +898,7 @@ const runCodegen = (options) => {
849
898
  writeIfChanged(join(outputDirectory, "drizzle.shard.ts"), drizzleFiles.shard);
850
899
  writeIfPresent(join(outputDirectory, "containers.ts"), containersContent);
851
900
  writeIfPresent(join(outputDirectory, "workflows.ts"), workflowsContent);
901
+ writeIfPresent(join(outputDirectory, "queues.ts"), queuesContent);
852
902
  writeIfPresent(join(outputDirectory, "seed.ts"), seedContent);
853
903
  if (wantsOpenApi) {
854
904
  writeIfChanged(join(outputDirectory, "openapi.json"), openApiContent);
@@ -862,6 +912,13 @@ const runCodegen = (options) => {
862
912
  writeIfChanged(schemaSnapshotPath, serializeSchemaSnapshot(schemaSnapshot));
863
913
  }
864
914
  }
915
+ if (timingEnabled) {
916
+ const finishedAt = performance.now();
917
+ const total = Math.round(finishedAt - startedAt);
918
+ const discovery = Math.round(emitStartedAt - startedAt);
919
+ const emit = Math.round(finishedAt - emitStartedAt);
920
+ console.error(`@lunora/codegen: codegen took ${total.toString()}ms (discovery ${discovery.toString()}ms, emit ${emit.toString()}ms)`);
921
+ }
865
922
  return {
866
923
  advisories,
867
924
  containers,
@@ -879,6 +936,7 @@ const runCodegen = (options) => {
879
936
  openApiModule: openApiModuleContent,
880
937
  openRpc: openRpcContent,
881
938
  openRpcModule: openRpcModuleContent,
939
+ queues: queuesContent,
882
940
  seed: seedContent,
883
941
  server: serverContent,
884
942
  shard: shardContent,
@@ -886,6 +944,7 @@ const runCodegen = (options) => {
886
944
  workflows: workflowsContent
887
945
  },
888
946
  outputDirectory,
947
+ queues,
889
948
  schemaSnapshot,
890
949
  schemaSnapshotPath,
891
950
  workflows
@@ -39,6 +39,7 @@ const buildSchemaSnapshot = (schema, migrationIds) => {
39
39
  tables[table.name] = tableSnapshotOf(table);
40
40
  }
41
41
  return {
42
+ jurisdiction: schema.jurisdiction,
42
43
  migrationIds: [...migrationIds].toSorted((a, b) => a.localeCompare(b)),
43
44
  tables: sortKeys(tables),
44
45
  version: SCHEMA_SNAPSHOT_VERSION
@@ -69,7 +70,9 @@ const parseSchemaSnapshot = (content) => {
69
70
  throw new SchemaSnapshotParseError(`baseline table "${name}" has an invalid structure`);
70
71
  }
71
72
  }
73
+ const jurisdiction = typeof parsed.jurisdiction === "string" ? parsed.jurisdiction : void 0;
72
74
  return {
75
+ jurisdiction,
73
76
  migrationIds: Array.isArray(parsed.migrationIds) ? parsed.migrationIds : [],
74
77
  tables: parsed.tables,
75
78
  version: SCHEMA_SNAPSHOT_VERSION
@@ -189,6 +192,15 @@ const diffSchemaSnapshots = (baseline, current) => {
189
192
  });
190
193
  }
191
194
  }
195
+ if (baseline !== void 0 && baseline.jurisdiction !== current.jurisdiction) {
196
+ const from = baseline.jurisdiction ?? "(none)";
197
+ const to = current.jurisdiction ?? "(none)";
198
+ changes.push({
199
+ severity: "breaking",
200
+ summary: `Durable Object jurisdiction changed from ${from} to ${to} — this re-homes every DO and strands all existing shard, scheduler, and session-DO data in the old region (no in-place migration; export then import to move it). Revert the change, or override the gate to proceed intentionally.`,
201
+ type: "changedJurisdiction"
202
+ });
203
+ }
192
204
  return { changes };
193
205
  };
194
206
  const evaluateSchemaDrift = (options) => {
@@ -2,7 +2,7 @@ import { existsSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  import { workflowDefaultName, workflowClassName, workflowBindingName } from '@lunora/workflow';
4
4
  import { SyntaxKind, Node } from 'ts-morph';
5
- import { diagnosticAt } from './CodegenDiagnosticError-54jWDxA9.mjs';
5
+ import { diagnosticAt } from './CodegenDiagnosticError-DeblMkzO.mjs';
6
6
 
7
7
  const WORKFLOWS_FILENAME = "workflows.ts";
8
8
  const isDefineWorkflow = (identifier) => {
@@ -1,4 +1,4 @@
1
- import { GENERATED_HEADER } from './emitApi-hRVC-kE7.mjs';
1
+ import { GENERATED_HEADER } from './GENERATED_HEADER-r1OZrKAx.mjs';
2
2
  import { s as sanitizeNamespace } from './paths-BRd6JHuF.mjs';
3
3
  import { LUNORA_ERROR_CODES, objectSchema, argsObjectSchema, validatorIrToJsonSchema } from './LUNORA_ERROR_CODES-CySpQPD3.mjs';
4
4
 
@@ -1,7 +1,7 @@
1
1
  import { relative, sep } from 'node:path';
2
2
  import { SyntaxKind, Node } from 'ts-morph';
3
3
  import { T as TS_EXTENSION_RE, e as enclosingExportName } from './discover-ast-CT6BgBr4.mjs';
4
- import { listLunoraSourceFiles } from './discoverFunctions-DEgAcRuD.mjs';
4
+ import { listLunoraSourceFiles } from './discoverFunctions-BWMczzBx.mjs';
5
5
 
6
6
  const isAuthApiCall = (call) => {
7
7
  const callee = call.getExpression();
@@ -1,6 +1,7 @@
1
1
  import { isValidCronExpression, compileCronSchedule, CRON_SCHEDULE_KINDS } from '@lunora/scheduler';
2
2
  import { SyntaxKind, Node } from 'ts-morph';
3
- import { listLunoraSourceFiles } from './discoverFunctions-DEgAcRuD.mjs';
3
+ import { diagnosticAt } from './CodegenDiagnosticError-DeblMkzO.mjs';
4
+ import { listLunoraSourceFiles } from './discoverFunctions-BWMczzBx.mjs';
4
5
  import { s as sanitizeNamespace } from './paths-BRd6JHuF.mjs';
5
6
 
6
7
  const CRON_METHODS = /* @__PURE__ */ new Set([...CRON_SCHEDULE_KINDS, "cron"]);
@@ -71,7 +72,7 @@ const literalValue = (node, jobName) => {
71
72
  if (Node.isArrayLiteralExpression(node)) {
72
73
  return node.getElements().map((element) => literalValue(element, jobName));
73
74
  }
74
- throw Object.assign(new Error(`Cron job "${jobName}" passes a non-static value where a literal is required; codegen can only read literals.`), {
75
+ throw diagnosticAt(node, `Cron job "${jobName}" passes a non-static value where a literal is required; codegen can only read literals.`, {
75
76
  code: "CRON_NON_STATIC_VALUE",
76
77
  name: "LunoraError",
77
78
  status: 500
@@ -81,8 +82,9 @@ const objectLiteralValue = (object, jobName) => {
81
82
  const result = {};
82
83
  for (const property of object.getProperties()) {
83
84
  if (!Node.isPropertyAssignment(property)) {
84
- throw Object.assign(
85
- new Error(`Cron job "${jobName}" uses an unsupported object property (shorthand/spread/method) where a static literal is required.`),
85
+ throw diagnosticAt(
86
+ property,
87
+ `Cron job "${jobName}" uses an unsupported object property (shorthand/spread/method) where a static literal is required.`,
86
88
  {
87
89
  code: "CRON_NON_STATIC_VALUE",
88
90
  name: "LunoraError",
@@ -100,8 +102,9 @@ const objectLiteralValue = (object, jobName) => {
100
102
  const functionPathFromArgument = (call, index, jobName) => {
101
103
  const argument = call.getArguments()[index];
102
104
  if (!argument || !Node.isPropertyAccessExpression(argument)) {
103
- throw Object.assign(
104
- new Error(`Cron job "${jobName}" must reference a function statically (e.g. internal.email.digest); codegen cannot resolve a dynamic reference.`),
105
+ throw diagnosticAt(
106
+ call,
107
+ `Cron job "${jobName}" must reference a function statically (e.g. internal.email.digest); codegen cannot resolve a dynamic reference.`,
105
108
  {
106
109
  code: "CRON_NON_STATIC_FN",
107
110
  name: "LunoraError",
@@ -112,7 +115,7 @@ const functionPathFromArgument = (call, index, jobName) => {
112
115
  const functionName = argument.getName();
113
116
  const receiver = argument.getExpression();
114
117
  if (!Node.isPropertyAccessExpression(receiver)) {
115
- throw Object.assign(new Error(`Cron job "${jobName}" function reference must be of the form internal.file.fn (two property accesses).`), {
118
+ throw diagnosticAt(argument, `Cron job "${jobName}" function reference must be of the form internal.file.fn (two property accesses).`, {
116
119
  code: "CRON_NON_STATIC_FN",
117
120
  name: "LunoraError",
118
121
  status: 500
@@ -133,8 +136,9 @@ const resolveTarget = (call, index, jobName, workflowsByName) => {
133
136
  if (workflow) {
134
137
  return workflowTarget(workflow);
135
138
  }
136
- throw Object.assign(
137
- new Error(`Cron job "${jobName}" targets workflows.${argument.getName()}, but no such workflow is declared in lunora/workflows.ts.`),
139
+ throw diagnosticAt(
140
+ argument,
141
+ `Cron job "${jobName}" targets workflows.${argument.getName()}, but no such workflow is declared in lunora/workflows.ts.`,
138
142
  {
139
143
  code: "CRON_NON_STATIC_FN",
140
144
  name: "LunoraError",
@@ -149,10 +153,9 @@ const resolveTarget = (call, index, jobName, workflowsByName) => {
149
153
  if (workflow) {
150
154
  return workflowTarget(workflow);
151
155
  }
152
- throw Object.assign(
153
- new Error(
154
- `Cron job "${jobName}" references "${argument.getText()}", which is neither a function (internal.file.fn / api.file.fn) nor a declared workflow in lunora/workflows.ts.`
155
- ),
156
+ throw diagnosticAt(
157
+ argument,
158
+ `Cron job "${jobName}" references "${argument.getText()}", which is neither a function (internal.file.fn / api.file.fn) nor a declared workflow in lunora/workflows.ts.`,
156
159
  {
157
160
  code: "CRON_NON_STATIC_FN",
158
161
  name: "LunoraError",
@@ -172,7 +175,7 @@ const cronFromCall = (call, callee, builderNames, workflowsByName) => {
172
175
  }
173
176
  const name = stringArgument(call, 0);
174
177
  if (name === void 0 || name.trim() === "") {
175
- throw Object.assign(new Error(`A cron ".${method}(...)" registration must pass a non-empty string-literal name as its first argument.`), {
178
+ throw diagnosticAt(call, `A cron ".${method}(...)" registration must pass a non-empty string-literal name as its first argument.`, {
176
179
  code: "CRON_NAME_NOT_STATIC",
177
180
  name: "LunoraError",
178
181
  status: 500
@@ -182,14 +185,14 @@ const cronFromCall = (call, callee, builderNames, workflowsByName) => {
182
185
  if (method === "cron") {
183
186
  const expression = stringArgument(call, 1);
184
187
  if (expression === void 0) {
185
- throw Object.assign(new Error(`Cron job "${name}" must pass a string-literal cron expression to ".cron(...)".`), {
188
+ throw diagnosticAt(call, `Cron job "${name}" must pass a string-literal cron expression to ".cron(...)".`, {
186
189
  code: "CRON_EXPR_NOT_STATIC",
187
190
  name: "LunoraError",
188
191
  status: 500
189
192
  });
190
193
  }
191
194
  if (!isValidCronExpression(expression)) {
192
- throw Object.assign(new Error(`Cron job "${name}" has an invalid cron expression "${expression}" — expected 5 or 6 space-separated fields.`), {
195
+ throw diagnosticAt(call, `Cron job "${name}" has an invalid cron expression "${expression}" — expected 5 or 6 space-separated fields.`, {
193
196
  code: "CRON_EXPR_INVALID",
194
197
  name: "LunoraError",
195
198
  status: 500
@@ -199,7 +202,7 @@ const cronFromCall = (call, callee, builderNames, workflowsByName) => {
199
202
  } else {
200
203
  const scheduleArgument = call.getArguments()[1];
201
204
  if (!scheduleArgument || !Node.isObjectLiteralExpression(scheduleArgument)) {
202
- throw Object.assign(new Error(`Cron job "${name}" must pass an object-literal schedule to ".${method}(...)".`), {
205
+ throw diagnosticAt(call, `Cron job "${name}" must pass an object-literal schedule to ".${method}(...)".`, {
203
206
  code: "CRON_SCHEDULE_NOT_STATIC",
204
207
  name: "LunoraError",
205
208
  status: 500
@@ -1,7 +1,7 @@
1
1
  import { readdirSync, lstatSync } from 'node:fs';
2
2
  import { join, extname, relative, sep } from 'node:path';
3
3
  import { Node, SyntaxKind } from 'ts-morph';
4
- import { a as parseObjectShape } from './parse-validator-tuQtHrsr.mjs';
4
+ import { a as parseObjectShape } from './parse-validator-Cabb60UV.mjs';
5
5
  import { s as sanitizeNamespace } from './paths-BRd6JHuF.mjs';
6
6
 
7
7
  const FUNCTION_KINDS = /* @__PURE__ */ new Set(["action", "mutation", "query", "stream"]);
@@ -344,7 +344,12 @@ const discoverFromCall = (call) => {
344
344
  if (classified.lifecycle) {
345
345
  return { args: {}, kind: classified.kind, lifecycle: classified.lifecycle, returnType: "void", visibility: classified.visibility };
346
346
  }
347
- return { args: argsFromCall(call), kind: classified.kind, returnType: returnTypeFromCall(call), visibility: classified.visibility };
347
+ return {
348
+ args: argsFromCall(call),
349
+ kind: classified.kind,
350
+ returnType: returnTypeFromCall(call),
351
+ visibility: classified.visibility
352
+ };
348
353
  };
349
354
  const RE_EXPORT_RESOLVE_LIMIT = 8;
350
355
  const resolveExpressionToCall = (node, depth = 0) => {
@@ -1,7 +1,7 @@
1
1
  import { relative, sep } from 'node:path';
2
2
  import { Node } from 'ts-morph';
3
- import { listLunoraSourceFiles } from './discoverFunctions-DEgAcRuD.mjs';
4
- import { p as parseValidator, a as parseObjectShape } from './parse-validator-tuQtHrsr.mjs';
3
+ import { listLunoraSourceFiles } from './discoverFunctions-BWMczzBx.mjs';
4
+ import { p as parseValidator, a as parseObjectShape } from './parse-validator-Cabb60UV.mjs';
5
5
 
6
6
  const TS_EXTENSION_RE = /\.ts$/u;
7
7
  const HTTP_VERBS = /* @__PURE__ */ new Set(["delete", "get", "head", "options", "patch", "post", "put"]);