@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,27 +1,29 @@
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 discoverR2sqlCalls from './discoverR2sqlCalls-BVNMd428.mjs';
17
+ import discoverRlsProcedures, { discoverRlsMetadata } from './discoverRlsMetadata-BS9GOGC5.mjs';
18
+ import discoverSchema from './discoverSchema-BnWHHJ4T.mjs';
19
+ import discoverStorageRulesMetadata from './discoverStorageRulesMetadata-Da8BKXcI.mjs';
18
20
  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';
21
+ import { discoverWorkflows } from './WORKFLOWS_FILENAME-D62dcBGg.mjs';
22
+ import { buildStorageColumns, emitDataModel, emitApi, emitServer, emitFunctions, emitShard, emitContainers, emitWorkflows, emitCrons, emitVectors, emitDrizzleSchema, emitSeed, emitWranglerCronTriggers } from './GENERATED_HEADER-BiFXNUvo.mjs';
23
+ import { emitApp } from './emitApp-KfMaKXWe.mjs';
24
+ import { buildOpenApiDocument, emitOpenApiModule } from './buildOpenApiDocument-Cu3mZl-8.mjs';
25
+ import { buildOpenRpcDocument, emitOpenRpcModule } from './OPENRPC_VERSION-B4i9Qp3v.mjs';
26
+ import { buildSchemaSnapshot, serializeSchemaSnapshot } from './SCHEMA_SNAPSHOT_VERSION-wWGP2_5E.mjs';
25
27
 
26
28
  const HTTP_VERBS = /* @__PURE__ */ new Set(["delete", "get", "head", "options", "patch", "post", "put"]);
27
29
  const TERMINAL_STEPS = /* @__PURE__ */ new Set(["handler", "stream"]);
@@ -246,6 +248,7 @@ const PROBES = {
246
248
  mail: { moduleSpecifier: "@lunora/mail" },
247
249
  payments: { contextProperty: "payments", moduleSpecifier: "@lunora/payment" },
248
250
  pipelines: { contextProperty: "pipelines", moduleSpecifier: "@lunora/pipelines" },
251
+ r2sql: { contextProperty: "r2sql", moduleSpecifier: "@lunora/r2sql" },
249
252
  scheduler: { contextProperty: "scheduler", moduleSpecifier: "@lunora/scheduler" },
250
253
  storage: { contextProperty: "storage", moduleSpecifier: "@lunora/storage" },
251
254
  vectors: { contextProperty: "vectors", moduleSpecifier: "@lunora/vectors" },
@@ -277,6 +280,7 @@ const discoverFeatureUsage = (project, lunoraDirectory) => {
277
280
  mail: false,
278
281
  payments: false,
279
282
  pipelines: false,
283
+ r2sql: false,
280
284
  scheduler: false,
281
285
  storage: false,
282
286
  vectors: false,
@@ -364,26 +368,45 @@ const protectPublicFlags = (call) => {
364
368
  }
365
369
  return { usesCaptcha: Boolean(argument.getProperty("captcha")), usesRateLimit: Boolean(argument.getProperty("rateLimit")) };
366
370
  };
371
+ const resolveUseArgumentCall = (argument) => {
372
+ if (Node.isCallExpression(argument)) {
373
+ return argument;
374
+ }
375
+ if (!Node.isIdentifier(argument)) {
376
+ return void 0;
377
+ }
378
+ const declaration = argument.getSourceFile().getVariableDeclaration(argument.getText());
379
+ const initializer = declaration?.getInitializer();
380
+ return initializer && Node.isCallExpression(initializer) ? initializer : void 0;
381
+ };
382
+ const NO_PROTECTIONS = { usesCaptcha: false, usesMask: false, usesRateLimit: false, usesRls: false };
383
+ const useStepProtections = (useArgument) => {
384
+ const argument = resolveUseArgumentCall(useArgument);
385
+ const name = argument ? calleeNameOf(argument) : void 0;
386
+ if (argument && name === "protectPublic") {
387
+ const bundle = protectPublicFlags(argument);
388
+ return { ...NO_PROTECTIONS, usesCaptcha: bundle.usesCaptcha, usesRateLimit: bundle.usesRateLimit };
389
+ }
390
+ if (name !== void 0 && name in MIDDLEWARE_FLAGS) {
391
+ return { ...NO_PROTECTIONS, [MIDDLEWARE_FLAGS[name]]: true };
392
+ }
393
+ return NO_PROTECTIONS;
394
+ };
367
395
  const protectionsInChain = (receiver) => {
368
- const protections = { usesCaptcha: false, usesMask: false, usesRateLimit: false, usesRls: false };
396
+ const protections = { ...NO_PROTECTIONS };
369
397
  let node = receiver;
370
398
  while (Node.isCallExpression(node)) {
371
399
  const chainCallee = node.getExpression();
372
400
  if (!Node.isPropertyAccessExpression(chainCallee)) {
373
401
  break;
374
402
  }
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
- }
403
+ const useArgument = chainCallee.getName() === "use" ? node.getArguments()[0] : void 0;
404
+ if (useArgument) {
405
+ const step = useStepProtections(useArgument);
406
+ protections.usesCaptcha ||= step.usesCaptcha;
407
+ protections.usesMask ||= step.usesMask;
408
+ protections.usesRateLimit ||= step.usesRateLimit;
409
+ protections.usesRls ||= step.usesRls;
387
410
  }
388
411
  node = chainCallee.getExpression();
389
412
  }
@@ -654,6 +677,10 @@ const readProjectVersion = (projectRoot) => {
654
677
  return void 0;
655
678
  }
656
679
  };
680
+ const isTimingEnabled = () => {
681
+ const flag = process.env["LUNORA_CODEGEN_TIMING"];
682
+ return flag !== void 0 && flag !== "";
683
+ };
657
684
  const findTsconfig = (startPath) => {
658
685
  let directory = existsSync(startPath) ? startPath : dirname(startPath);
659
686
  while (directory && directory !== dirname(directory)) {
@@ -695,13 +722,15 @@ const refreshCodegenProject = (project, lunoraDirectory) => {
695
722
  }
696
723
  };
697
724
  const runCodegen = (options) => {
725
+ const timingEnabled = isTimingEnabled();
726
+ const startedAt = timingEnabled ? performance.now() : 0;
698
727
  const lunoraDirectory = join(options.projectRoot, options.lunoraDirectory ?? "lunora");
699
728
  const schemaPath = join(lunoraDirectory, "schema.ts");
700
729
  if (!existsSync(schemaPath)) {
701
730
  throw new Error(`schema.ts not found at ${schemaPath}`);
702
731
  }
703
732
  const project = options.project ?? createCodegenProject(lunoraDirectory);
704
- const schema = discoverSchema(project, schemaPath);
733
+ const schema = discoverSchema(project, schemaPath, options.projectRoot);
705
734
  const functions = discoverFunctions(project, lunoraDirectory);
706
735
  const httpRoutes = discoverHttpRoutes(project, lunoraDirectory);
707
736
  const migrations = discoverMigrations(project, lunoraDirectory);
@@ -723,7 +752,8 @@ const runCodegen = (options) => {
723
752
  discoverArgumentValidators(project, lunoraDirectory),
724
753
  discoverSecrets(project, lunoraDirectory),
725
754
  discoverSqlInterpolation(project, lunoraDirectory),
726
- discoverAdminRoutes(project, lunoraDirectory)
755
+ discoverAdminRoutes(project, lunoraDirectory),
756
+ discoverR2sqlCalls(project, lunoraDirectory)
727
757
  );
728
758
  const rlsMetadata = discoverRlsMetadata(project, lunoraDirectory);
729
759
  const maskMetadata = discoverMaskMetadata(project, lunoraDirectory);
@@ -737,6 +767,7 @@ const runCodegen = (options) => {
737
767
  const hasImages = featureUsage.images;
738
768
  const hasAnalytics = featureUsage.analytics;
739
769
  const hasPipelines = featureUsage.pipelines;
770
+ const hasR2sql = featureUsage.r2sql;
740
771
  const dependencies = discoverPackageDependencies(options.projectRoot);
741
772
  const studioFeatures = buildStudioFeatures(featureUsage, {
742
773
  cronCount: crons.length,
@@ -747,6 +778,7 @@ const runCodegen = (options) => {
747
778
  workflowCount: workflows.length
748
779
  });
749
780
  const useUmbrella = dependencies.has("lunorash");
781
+ const emitStartedAt = timingEnabled ? performance.now() : 0;
750
782
  const dataModelContent = emitDataModel(schema, useUmbrella);
751
783
  const apiContent = emitApi(functions, workflows, useUmbrella);
752
784
  const serverContent = emitServer({
@@ -759,12 +791,13 @@ const runCodegen = (options) => {
759
791
  hasKv,
760
792
  hasPayments,
761
793
  hasPipelines,
794
+ hasR2sql,
762
795
  schema,
763
796
  storageRuleBuckets: storageRulesMetadata.rules.map((rule) => rule.bucket),
764
797
  useUmbrella,
765
798
  workflows
766
799
  });
767
- const functionsContent = emitFunctions(functions, migrations);
800
+ const functionsContent = emitFunctions(functions, migrations, useUmbrella);
768
801
  const shardContent = emitShard({
769
802
  advisories,
770
803
  containers,
@@ -775,6 +808,7 @@ const runCodegen = (options) => {
775
808
  hasImages,
776
809
  hasKv,
777
810
  hasPayments,
811
+ hasR2sql,
778
812
  maskMetadata,
779
813
  rlsMetadata,
780
814
  schema,
@@ -783,7 +817,7 @@ const runCodegen = (options) => {
783
817
  useUmbrella,
784
818
  workflows
785
819
  });
786
- const containersContent = emitContainers(containers);
820
+ const containersContent = emitContainers(containers, schema.jurisdiction);
787
821
  const workflowsContent = emitWorkflows(workflows);
788
822
  const cronsContent = emitCrons(crons);
789
823
  const vectorsContent = emitVectors(schema.vectorIndexes);
@@ -809,10 +843,13 @@ const runCodegen = (options) => {
809
843
  hasImages,
810
844
  hasKv,
811
845
  hasPayments,
846
+ hasR2sql,
812
847
  hasScheduler: studioFeatures.scheduler,
813
848
  hasStorage: studioFeatures.storage,
814
849
  hasVectors: schema.vectorIndexes.length > 0,
815
850
  hasWorkflow: workflows.length > 0,
851
+ // Schema `.jurisdiction("…")` → pin the generated worker's DOs to the region.
852
+ jurisdiction: schema.jurisdiction,
816
853
  useUmbrella,
817
854
  wantsOpenApi,
818
855
  wantsOpenRpc
@@ -862,6 +899,13 @@ const runCodegen = (options) => {
862
899
  writeIfChanged(schemaSnapshotPath, serializeSchemaSnapshot(schemaSnapshot));
863
900
  }
864
901
  }
902
+ if (timingEnabled) {
903
+ const finishedAt = performance.now();
904
+ const total = Math.round(finishedAt - startedAt);
905
+ const discovery = Math.round(emitStartedAt - startedAt);
906
+ const emit = Math.round(finishedAt - emitStartedAt);
907
+ console.error(`@lunora/codegen: codegen took ${total.toString()}ms (discovery ${discovery.toString()}ms, emit ${emit.toString()}ms)`);
908
+ }
865
909
  return {
866
910
  advisories,
867
911
  containers,
@@ -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-BiFXNUvo.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"]);
@@ -1,6 +1,6 @@
1
1
  import { SyntaxKind, Node } from 'ts-morph';
2
2
  import { e as enclosingExportName } from './discover-ast-CT6BgBr4.mjs';
3
- import { listLunoraSourceFiles, lunoraRelativePath } from './discoverFunctions-DEgAcRuD.mjs';
3
+ import { listLunoraSourceFiles, lunoraRelativePath } from './discoverFunctions-BWMczzBx.mjs';
4
4
 
5
5
  const isDatabaseInsertCall = (call) => {
6
6
  const callee = call.getExpression();
@@ -13,9 +13,31 @@ const isDatabaseInsertCall = (call) => {
13
13
  }
14
14
  return Node.isIdentifier(receiver) && receiver.getText() === "db";
15
15
  };
16
+ const resolveStringConst = (identifier) => {
17
+ if (!Node.isIdentifier(identifier)) {
18
+ return void 0;
19
+ }
20
+ const symbol = identifier.getSymbol();
21
+ const declarations = symbol?.getAliasedSymbol()?.getDeclarations() ?? symbol?.getDeclarations() ?? [];
22
+ for (const declaration of declarations) {
23
+ if (Node.isVariableDeclaration(declaration)) {
24
+ const initializer = declaration.getInitializer();
25
+ if (initializer && Node.isStringLiteral(initializer)) {
26
+ return initializer.getLiteralText();
27
+ }
28
+ }
29
+ }
30
+ return void 0;
31
+ };
16
32
  const tableOf = (call) => {
17
33
  const argument = call.getArguments()[0];
18
- return argument && Node.isStringLiteral(argument) ? argument.getLiteralText() : "";
34
+ if (!argument) {
35
+ return "";
36
+ }
37
+ if (Node.isStringLiteral(argument)) {
38
+ return argument.getLiteralText();
39
+ }
40
+ return resolveStringConst(argument) ?? "";
19
41
  };
20
42
  const discoverInserts = (project, lunoraDirectory) => {
21
43
  const writes = [];
@@ -1,5 +1,5 @@
1
1
  import { Node, SyntaxKind } from 'ts-morph';
2
- import { listLunoraSourceFiles, lunoraRelativePath, classifyProcedureCall } from './discoverFunctions-DEgAcRuD.mjs';
2
+ import { listLunoraSourceFiles, lunoraRelativePath, classifyProcedureCall } from './discoverFunctions-BWMczzBx.mjs';
3
3
 
4
4
  const isMaskCall = (node) => {
5
5
  if (!Node.isCallExpression(node)) {
@@ -1,6 +1,7 @@
1
1
  import { relative, sep } from 'node:path';
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
 
5
6
  const TS_EXTENSION_RE = /\.ts$/u;
6
7
  const isDefineMigration = (identifier) => {
@@ -43,9 +44,14 @@ const migrationFromDeclaration = (declaration, relativePath) => {
43
44
  const id = stringProperty(argument, "id");
44
45
  const exportName = declaration.getName();
45
46
  if (id === void 0 || id.trim() === "") {
46
- throw Object.assign(
47
- new Error(`Migration "${exportName}" in "${relativePath}" must declare \`id\` as a non-empty string literal so codegen can key the registry.`),
48
- { code: "MIGRATION_ID_NOT_STATIC", name: "LunoraError", status: 500 }
47
+ throw diagnosticAt(
48
+ argument,
49
+ `Migration "${exportName}" in "${relativePath}" must declare \`id\` as a non-empty string literal so codegen can key the registry.`,
50
+ {
51
+ code: "MIGRATION_ID_NOT_STATIC",
52
+ name: "LunoraError",
53
+ status: 500
54
+ }
49
55
  );
50
56
  }
51
57
  return { exportName, filePath: relativePath, id, table: stringProperty(argument, "table") ?? "" };
@@ -1,5 +1,5 @@
1
1
  import { Node, SyntaxKind } from 'ts-morph';
2
- import { listLunoraSourceFiles, lunoraRelativePath, classifyProcedureCall } from './discoverFunctions-DEgAcRuD.mjs';
2
+ import { listLunoraSourceFiles, lunoraRelativePath, classifyProcedureCall } from './discoverFunctions-BWMczzBx.mjs';
3
3
 
4
4
  const PROPERTY_CALLS = {
5
5
  "crypto.getRandomValues": "crypto.getRandomValues",
@@ -1,5 +1,5 @@
1
1
  import { SyntaxKind, Node } from 'ts-morph';
2
- import { listLunoraSourceFiles, lunoraRelativePath } from './discoverFunctions-DEgAcRuD.mjs';
2
+ import { listLunoraSourceFiles, lunoraRelativePath } from './discoverFunctions-BWMczzBx.mjs';
3
3
 
4
4
  const INDEX_METHODS = /* @__PURE__ */ new Set(["withIndex", "withSearchIndex"]);
5
5
  const isDatabaseQueryCall = (call) => {
@@ -0,0 +1,80 @@
1
+ import { Node, SyntaxKind } from 'ts-morph';
2
+ import { listLunoraSourceFiles, lunoraRelativePath, classifyProcedureCall } from './discoverFunctions-BWMczzBx.mjs';
3
+
4
+ const handlerOf = (call, receiver) => {
5
+ if (receiver) {
6
+ const handler = call.getArguments()[0];
7
+ return handler && (Node.isArrowFunction(handler) || Node.isFunctionExpression(handler)) ? handler : void 0;
8
+ }
9
+ const first = call.getArguments()[0];
10
+ if (!first || !Node.isObjectLiteralExpression(first)) {
11
+ return void 0;
12
+ }
13
+ const handlerProperty = first.getProperty("handler");
14
+ if (!handlerProperty || !Node.isPropertyAssignment(handlerProperty)) {
15
+ return void 0;
16
+ }
17
+ const initializer = handlerProperty.getInitializer();
18
+ return initializer && (Node.isArrowFunction(initializer) || Node.isFunctionExpression(initializer)) ? initializer : void 0;
19
+ };
20
+ const exportedProcedureHandler = (declaration) => {
21
+ const initializer = declaration.getInitializer();
22
+ if (!initializer || !Node.isCallExpression(initializer)) {
23
+ return void 0;
24
+ }
25
+ const classified = classifyProcedureCall(initializer);
26
+ if (!classified || classified.kind !== "query" && classified.kind !== "mutation") {
27
+ return void 0;
28
+ }
29
+ const handler = handlerOf(initializer, classified.receiver);
30
+ return handler ? { exportName: declaration.getName(), handler, kind: classified.kind } : void 0;
31
+ };
32
+ const r2sqlCalleeOf = (access) => {
33
+ if (!Node.isPropertyAccessExpression(access) || access.getName() !== "r2sql") {
34
+ return void 0;
35
+ }
36
+ const receiver = access.getExpression();
37
+ if (!Node.isIdentifier(receiver) || receiver.getText() !== "ctx") {
38
+ return void 0;
39
+ }
40
+ const parent = access.getParent();
41
+ if (parent && Node.isPropertyAccessExpression(parent) && parent.getExpression() === access) {
42
+ return `ctx.r2sql.${parent.getName()}`;
43
+ }
44
+ return "ctx.r2sql";
45
+ };
46
+ const accessesInHandler = (procedure, file) => {
47
+ const found = [];
48
+ for (const access of procedure.handler.getDescendantsOfKind(SyntaxKind.PropertyAccessExpression)) {
49
+ const callee = r2sqlCalleeOf(access);
50
+ if (callee !== void 0) {
51
+ found.push({ callee, exportName: procedure.exportName, file, kind: procedure.kind, line: access.getStartLineNumber() });
52
+ }
53
+ }
54
+ return found;
55
+ };
56
+ const accessesInSourceFile = (sourceFile, relativePath) => {
57
+ const found = [];
58
+ for (const statement of sourceFile.getVariableStatements()) {
59
+ if (!statement.isExported()) {
60
+ continue;
61
+ }
62
+ for (const declaration of statement.getDeclarations()) {
63
+ const procedure = exportedProcedureHandler(declaration);
64
+ if (procedure) {
65
+ found.push(...accessesInHandler(procedure, relativePath));
66
+ }
67
+ }
68
+ }
69
+ return found;
70
+ };
71
+ const discoverR2sqlCalls = (project, lunoraDirectory) => {
72
+ const calls = [];
73
+ for (const filePath of listLunoraSourceFiles(lunoraDirectory)) {
74
+ const sourceFile = project.getSourceFile(filePath) ?? project.addSourceFileAtPath(filePath);
75
+ calls.push(...accessesInSourceFile(sourceFile, lunoraRelativePath(lunoraDirectory, filePath)));
76
+ }
77
+ return calls;
78
+ };
79
+
80
+ export { discoverR2sqlCalls as default };
@@ -1,5 +1,5 @@
1
1
  import { Node, SyntaxKind } from 'ts-morph';
2
- import { listLunoraSourceFiles, lunoraRelativePath, classifyProcedureCall } from './discoverFunctions-DEgAcRuD.mjs';
2
+ import { listLunoraSourceFiles, lunoraRelativePath, classifyProcedureCall } from './discoverFunctions-BWMczzBx.mjs';
3
3
 
4
4
  const isRlsCall = (node) => {
5
5
  if (!Node.isCallExpression(node)) {