@lunora/codegen 1.0.0-alpha.2 → 1.0.0-alpha.20

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 (34) hide show
  1. package/__assets__/package-og.svg +1 -1
  2. package/dist/index.d.mts +444 -16
  3. package/dist/index.d.ts +444 -16
  4. package/dist/index.mjs +28 -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/FLAGS_FILENAME-B1vE0LIo.mjs +139 -0
  8. package/dist/packem_shared/{emitApi-hRVC-kE7.mjs → GENERATED_HEADER-CI272wRm.mjs} +873 -54
  9. package/dist/packem_shared/LUNORA_SOLUTION_RULES-BTejmZp-.mjs +131 -0
  10. package/dist/packem_shared/MUTATORS_FILENAME-BhqdPtKp.mjs +81 -0
  11. package/dist/packem_shared/{buildOpenRpcDocument-BZGY1-jT.mjs → OPENRPC_VERSION-t5pV2NIN.mjs} +1 -1
  12. package/dist/packem_shared/QUEUES_FILENAME-B5_eWCRe.mjs +119 -0
  13. package/dist/packem_shared/{createCodegenProject-DGJm0_Pk.mjs → SCHEMA_SNAPSHOT_FILENAME-BMez-jgr.mjs} +187 -43
  14. package/dist/packem_shared/{buildSchemaSnapshot-DzLDbWk3.mjs → SCHEMA_SNAPSHOT_VERSION-wWGP2_5E.mjs} +12 -0
  15. package/dist/packem_shared/SHAPES_FILENAME-ChV7MqgE.mjs +94 -0
  16. package/dist/packem_shared/{discoverWorkflows-DRDQdhfq.mjs → WORKFLOWS_FILENAME-CCisG0Vy.mjs} +42 -2
  17. package/dist/packem_shared/{buildOpenApiDocument-yHVN66Xd.mjs → buildOpenApiDocument-Dx5QftUn.mjs} +1 -1
  18. package/dist/packem_shared/{discoverAuthApiCalls-C35R6z0T.mjs → discoverAuthApiCalls-CoirYbg6.mjs} +1 -1
  19. package/dist/packem_shared/{discoverCrons-BL6iGuJ3.mjs → discoverCrons-Cev7RRAf.mjs} +20 -17
  20. package/dist/packem_shared/{discoverFunctions-DEgAcRuD.mjs → discoverFunctions-BWMczzBx.mjs} +7 -2
  21. package/dist/packem_shared/{discoverHttpRoutes-C978pBiG.mjs → discoverHttpRoutes-CfP6cMzt.mjs} +2 -2
  22. package/dist/packem_shared/{discoverInserts-CRQdXvHO.mjs → discoverInserts-C7zxXkUf.mjs} +24 -2
  23. package/dist/packem_shared/{discoverMaskProcedures-B64zA740.mjs → discoverMaskProcedures-Cm81kwrb.mjs} +1 -1
  24. package/dist/packem_shared/{discoverMigrations-Doj_-BAA.mjs → discoverMigrations-Bi5nJ0mJ.mjs} +10 -4
  25. package/dist/packem_shared/{discoverNondeterministicCalls-4KiPQxQU.mjs → discoverNondeterministicCalls-C4M8AXmQ.mjs} +1 -1
  26. package/dist/packem_shared/{discoverQueries-BkIi0dBD.mjs → discoverQueries-B0wGT-xe.mjs} +1 -1
  27. package/dist/packem_shared/discoverR2sqlCalls-BVNMd428.mjs +80 -0
  28. package/dist/packem_shared/{discoverRlsMetadata-DpRB1HMe.mjs → discoverRlsMetadata-BS9GOGC5.mjs} +1 -1
  29. package/dist/packem_shared/{discoverSchema-BBulgGbH.mjs → discoverSchema-KYq55qu3.mjs} +338 -16
  30. package/dist/packem_shared/{discoverStorageRulesMetadata-DAqJUxUv.mjs → discoverStorageRulesMetadata-Da8BKXcI.mjs} +1 -1
  31. package/dist/packem_shared/{emitApp-CzZ6GbrD.mjs → emitApp-cOSypPuO.mjs} +74 -8
  32. package/dist/packem_shared/{lintSchema-DicbOHvH.mjs → formatAdvisories-DdjK7sgh.mjs} +15 -2
  33. package/dist/packem_shared/{parse-validator-tuQtHrsr.mjs → parse-validator-Cabb60UV.mjs} +2 -1
  34. package/package.json +8 -7
@@ -1,27 +1,33 @@
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-DdjK7sgh.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 { discoverFlagKeys } from './FLAGS_FILENAME-B1vE0LIo.mjs';
11
+ import discoverHttpRoutes from './discoverHttpRoutes-CfP6cMzt.mjs';
12
+ import discoverInserts from './discoverInserts-C7zxXkUf.mjs';
13
+ import discoverMaskProcedures, { discoverMaskMetadata } from './discoverMaskProcedures-Cm81kwrb.mjs';
14
+ import discoverMigrations from './discoverMigrations-Bi5nJ0mJ.mjs';
15
+ import { MUTATORS_FILENAME, isDefineMutatorCallee, discoverMutators } from './MUTATORS_FILENAME-BhqdPtKp.mjs';
16
+ import discoverNondeterministicCalls from './discoverNondeterministicCalls-C4M8AXmQ.mjs';
17
+ import discoverQueries from './discoverQueries-B0wGT-xe.mjs';
18
+ import { discoverQueues } from './QUEUES_FILENAME-B5_eWCRe.mjs';
19
+ import discoverR2sqlCalls from './discoverR2sqlCalls-BVNMd428.mjs';
20
+ import discoverRlsProcedures, { discoverRlsMetadata } from './discoverRlsMetadata-BS9GOGC5.mjs';
21
+ import discoverSchema from './discoverSchema-KYq55qu3.mjs';
22
+ import { discoverShapes } from './SHAPES_FILENAME-ChV7MqgE.mjs';
23
+ import discoverStorageRulesMetadata from './discoverStorageRulesMetadata-Da8BKXcI.mjs';
18
24
  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';
25
+ import { discoverWorkflows } from './WORKFLOWS_FILENAME-CCisG0Vy.mjs';
26
+ import { buildStorageColumns, emitDataModel, emitApi, emitServer, emitFunctions, emitShard, emitCollections, emitContainers, emitWorkflows, emitQueues, emitCrons, emitVectors, emitDrizzleSchema, emitSeed, emitWranglerCronTriggers } from './GENERATED_HEADER-CI272wRm.mjs';
27
+ import { emitApp } from './emitApp-cOSypPuO.mjs';
28
+ import { buildOpenApiDocument, emitOpenApiModule } from './buildOpenApiDocument-Dx5QftUn.mjs';
29
+ import { buildOpenRpcDocument, emitOpenRpcModule } from './OPENRPC_VERSION-t5pV2NIN.mjs';
30
+ import { buildSchemaSnapshot, serializeSchemaSnapshot } from './SCHEMA_SNAPSHOT_VERSION-wWGP2_5E.mjs';
25
31
 
26
32
  const HTTP_VERBS = /* @__PURE__ */ new Set(["delete", "get", "head", "options", "patch", "post", "put"]);
27
33
  const TERMINAL_STEPS = /* @__PURE__ */ new Set(["handler", "stream"]);
@@ -237,18 +243,28 @@ const discoverArgumentValidators = (project, lunoraDirectory) => {
237
243
  };
238
244
 
239
245
  const PROBES = {
246
+ // The middleware (`accessContext()` / `accessRoles()`) imports the `/context`
247
+ // and `/roles` subpaths, NOT the bare `@lunora/cloudflare-access` specifier —
248
+ // so the per-procedure middleware never trips the global `ctx.access` wiring.
249
+ // A handler reading `ctx.access` is the signal that wires it onto every ctx.
250
+ access: { contextProperty: "access", moduleSpecifier: "@lunora/cloudflare-access" },
240
251
  ai: { contextProperty: "ai", moduleSpecifier: "@lunora/ai" },
241
- analytics: { contextProperty: "analytics", moduleSpecifier: "@lunora/analytics" },
252
+ analytics: { contextProperty: "analytics", moduleSpecifier: "@lunora/bindings/analytics" },
242
253
  browser: { contextProperty: "browser", moduleSpecifier: "@lunora/browser" },
254
+ flags: { contextProperty: "flags", moduleSpecifier: "@lunora/flags" },
243
255
  hyperdrive: { contextProperty: "sql", moduleSpecifier: "@lunora/hyperdrive" },
244
- images: { contextProperty: "images", moduleSpecifier: "@lunora/images" },
245
- kv: { contextProperty: "kv", moduleSpecifier: "@lunora/kv" },
256
+ images: { contextProperty: "images", moduleSpecifier: "@lunora/bindings/images" },
257
+ kv: { contextProperty: "kv", moduleSpecifier: "@lunora/bindings/kv" },
246
258
  mail: { moduleSpecifier: "@lunora/mail" },
247
259
  payments: { contextProperty: "payments", moduleSpecifier: "@lunora/payment" },
248
- pipelines: { contextProperty: "pipelines", moduleSpecifier: "@lunora/pipelines" },
260
+ // Pipelines is its own `@lunora/bindings/pipelines` subpath (distinct from
261
+ // `/analytics`), so a real import is a clean signal that won't be flipped by a
262
+ // plain analytics import; `ctx.pipelines` reads flip it too.
263
+ pipelines: { contextProperty: "pipelines", moduleSpecifier: "@lunora/bindings/pipelines" },
264
+ r2sql: { contextProperty: "r2sql", moduleSpecifier: "@lunora/bindings/r2sql" },
249
265
  scheduler: { contextProperty: "scheduler", moduleSpecifier: "@lunora/scheduler" },
250
266
  storage: { contextProperty: "storage", moduleSpecifier: "@lunora/storage" },
251
- vectors: { contextProperty: "vectors", moduleSpecifier: "@lunora/vectors" },
267
+ vectors: { contextProperty: "vectors", moduleSpecifier: "@lunora/bindings/vectors" },
252
268
  workflows: { contextProperty: "workflows", moduleSpecifier: "@lunora/workflow" }
253
269
  };
254
270
  const readsContextProperty = (sourceFile, property) => {
@@ -268,15 +284,18 @@ const readsContextProperty = (sourceFile, property) => {
268
284
  };
269
285
  const discoverFeatureUsage = (project, lunoraDirectory) => {
270
286
  const usage = {
287
+ access: false,
271
288
  ai: false,
272
289
  analytics: false,
273
290
  browser: false,
291
+ flags: false,
274
292
  hyperdrive: false,
275
293
  images: false,
276
294
  kv: false,
277
295
  mail: false,
278
296
  payments: false,
279
297
  pipelines: false,
298
+ r2sql: false,
280
299
  scheduler: false,
281
300
  storage: false,
282
301
  vectors: false,
@@ -307,15 +326,72 @@ const discoverFeatureUsage = (project, lunoraDirectory) => {
307
326
  };
308
327
  const buildStudioFeatures = (usage, signals) => {
309
328
  return {
329
+ flags: usage.flags || signals.dependencies.has("@lunora/flags"),
310
330
  mail: usage.mail || signals.dependencies.has("@lunora/mail"),
311
331
  payments: usage.payments || signals.dependencies.has("@lunora/payment"),
332
+ queues: signals.queueCount > 0 || signals.dependencies.has("@lunora/queue"),
312
333
  scheduler: usage.scheduler || signals.cronCount > 0 || signals.dependencies.has("@lunora/scheduler"),
313
334
  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"),
335
+ vectors: usage.vectors || signals.vectorIndexCount > 0 || signals.dependencies.has("@lunora/bindings/vectors"),
315
336
  workflows: usage.workflows || signals.workflowCount > 0 || signals.dependencies.has("@lunora/workflow")
316
337
  };
317
338
  };
318
339
 
340
+ const isDatabaseReplaceCall = (call) => {
341
+ const callee = call.getExpression();
342
+ if (!Node.isPropertyAccessExpression(callee) || callee.getName() !== "replace") {
343
+ return false;
344
+ }
345
+ const receiver = callee.getExpression();
346
+ if (Node.isPropertyAccessExpression(receiver)) {
347
+ return receiver.getName() === "db";
348
+ }
349
+ return Node.isIdentifier(receiver) && receiver.getText() === "db";
350
+ };
351
+ const serverImplNode = (call) => {
352
+ const argument = call.getArguments()[0];
353
+ if (argument === void 0 || !Node.isObjectLiteralExpression(argument)) {
354
+ return void 0;
355
+ }
356
+ const property = argument.getProperty("server");
357
+ if (property === void 0) {
358
+ return void 0;
359
+ }
360
+ return Node.isPropertyAssignment(property) ? property.getInitializer() : property;
361
+ };
362
+ const exportedDefineMutatorCall = (declaration) => {
363
+ if (!declaration.isExported()) {
364
+ return void 0;
365
+ }
366
+ const initializer = declaration.getInitializer();
367
+ if (initializer?.getKind() !== SyntaxKind.CallExpression) {
368
+ return void 0;
369
+ }
370
+ const call = initializer;
371
+ return isDefineMutatorCallee(call.getExpression()) ? call : void 0;
372
+ };
373
+ const writesFromDeclaration = (declaration) => {
374
+ const call = exportedDefineMutatorCall(declaration);
375
+ const serverImpl = call === void 0 ? void 0 : serverImplNode(call);
376
+ if (serverImpl === void 0) {
377
+ return [];
378
+ }
379
+ const nameNode = declaration.getNameNode();
380
+ const exportName = Node.isIdentifier(nameNode) ? nameNode.getText() : "";
381
+ return serverImpl.getDescendantsOfKind(SyntaxKind.CallExpression).filter((replaceCall) => isDatabaseReplaceCall(replaceCall)).map((replaceCall) => {
382
+ return { exportName, file: "lunora/mutators.ts", line: replaceCall.getStartLineNumber() };
383
+ });
384
+ };
385
+ const mutatorWritesFromSource = (source) => source.getVariableDeclarations().flatMap((declaration) => writesFromDeclaration(declaration));
386
+ const discoverMutatorWrites = (project, lunoraDirectory) => {
387
+ const mutatorsPath = join(lunoraDirectory, MUTATORS_FILENAME);
388
+ if (!existsSync(mutatorsPath)) {
389
+ return [];
390
+ }
391
+ const source = project.getSourceFile(mutatorsPath) ?? project.addSourceFileAtPath(mutatorsPath);
392
+ return mutatorWritesFromSource(source);
393
+ };
394
+
319
395
  const discoverPackageDependencies = (projectRoot) => {
320
396
  const manifestPath = join(projectRoot, "package.json");
321
397
  if (!existsSync(manifestPath)) {
@@ -364,26 +440,45 @@ const protectPublicFlags = (call) => {
364
440
  }
365
441
  return { usesCaptcha: Boolean(argument.getProperty("captcha")), usesRateLimit: Boolean(argument.getProperty("rateLimit")) };
366
442
  };
443
+ const resolveUseArgumentCall = (argument) => {
444
+ if (Node.isCallExpression(argument)) {
445
+ return argument;
446
+ }
447
+ if (!Node.isIdentifier(argument)) {
448
+ return void 0;
449
+ }
450
+ const declaration = argument.getSourceFile().getVariableDeclaration(argument.getText());
451
+ const initializer = declaration?.getInitializer();
452
+ return initializer && Node.isCallExpression(initializer) ? initializer : void 0;
453
+ };
454
+ const NO_PROTECTIONS = { usesCaptcha: false, usesMask: false, usesRateLimit: false, usesRls: false };
455
+ const useStepProtections = (useArgument) => {
456
+ const argument = resolveUseArgumentCall(useArgument);
457
+ const name = argument ? calleeNameOf(argument) : void 0;
458
+ if (argument && name === "protectPublic") {
459
+ const bundle = protectPublicFlags(argument);
460
+ return { ...NO_PROTECTIONS, usesCaptcha: bundle.usesCaptcha, usesRateLimit: bundle.usesRateLimit };
461
+ }
462
+ if (name !== void 0 && name in MIDDLEWARE_FLAGS) {
463
+ return { ...NO_PROTECTIONS, [MIDDLEWARE_FLAGS[name]]: true };
464
+ }
465
+ return NO_PROTECTIONS;
466
+ };
367
467
  const protectionsInChain = (receiver) => {
368
- const protections = { usesCaptcha: false, usesMask: false, usesRateLimit: false, usesRls: false };
468
+ const protections = { ...NO_PROTECTIONS };
369
469
  let node = receiver;
370
470
  while (Node.isCallExpression(node)) {
371
471
  const chainCallee = node.getExpression();
372
472
  if (!Node.isPropertyAccessExpression(chainCallee)) {
373
473
  break;
374
474
  }
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
- }
475
+ const useArgument = chainCallee.getName() === "use" ? node.getArguments()[0] : void 0;
476
+ if (useArgument) {
477
+ const step = useStepProtections(useArgument);
478
+ protections.usesCaptcha ||= step.usesCaptcha;
479
+ protections.usesMask ||= step.usesMask;
480
+ protections.usesRateLimit ||= step.usesRateLimit;
481
+ protections.usesRls ||= step.usesRls;
387
482
  }
388
483
  node = chainCallee.getExpression();
389
484
  }
@@ -654,6 +749,10 @@ const readProjectVersion = (projectRoot) => {
654
749
  return void 0;
655
750
  }
656
751
  };
752
+ const isTimingEnabled = () => {
753
+ const flag = process.env["LUNORA_CODEGEN_TIMING"];
754
+ return flag !== void 0 && flag !== "";
755
+ };
657
756
  const findTsconfig = (startPath) => {
658
757
  let directory = existsSync(startPath) ? startPath : dirname(startPath);
659
758
  while (directory && directory !== dirname(directory)) {
@@ -695,17 +794,22 @@ const refreshCodegenProject = (project, lunoraDirectory) => {
695
794
  }
696
795
  };
697
796
  const runCodegen = (options) => {
797
+ const timingEnabled = isTimingEnabled();
798
+ const startedAt = timingEnabled ? performance.now() : 0;
698
799
  const lunoraDirectory = join(options.projectRoot, options.lunoraDirectory ?? "lunora");
699
800
  const schemaPath = join(lunoraDirectory, "schema.ts");
700
801
  if (!existsSync(schemaPath)) {
701
802
  throw new Error(`schema.ts not found at ${schemaPath}`);
702
803
  }
703
804
  const project = options.project ?? createCodegenProject(lunoraDirectory);
704
- const schema = discoverSchema(project, schemaPath);
805
+ const schema = discoverSchema(project, schemaPath, options.projectRoot);
705
806
  const functions = discoverFunctions(project, lunoraDirectory);
706
807
  const httpRoutes = discoverHttpRoutes(project, lunoraDirectory);
707
808
  const migrations = discoverMigrations(project, lunoraDirectory);
809
+ const shapes = discoverShapes(project, lunoraDirectory);
810
+ const mutators = discoverMutators(project, lunoraDirectory);
708
811
  const workflows = discoverWorkflows(project, lunoraDirectory);
812
+ const queues = discoverQueues(project, lunoraDirectory);
709
813
  const crons = discoverCrons(project, lunoraDirectory, workflows);
710
814
  const containers = discoverContainers(project, lunoraDirectory);
711
815
  const advisories = options.lint === false ? [] : lintSchema(
@@ -723,7 +827,10 @@ const runCodegen = (options) => {
723
827
  discoverArgumentValidators(project, lunoraDirectory),
724
828
  discoverSecrets(project, lunoraDirectory),
725
829
  discoverSqlInterpolation(project, lunoraDirectory),
726
- discoverAdminRoutes(project, lunoraDirectory)
830
+ discoverAdminRoutes(project, lunoraDirectory),
831
+ discoverR2sqlCalls(project, lunoraDirectory),
832
+ shapes,
833
+ discoverMutatorWrites(project, lunoraDirectory)
727
834
  );
728
835
  const rlsMetadata = discoverRlsMetadata(project, lunoraDirectory);
729
836
  const maskMetadata = discoverMaskMetadata(project, lunoraDirectory);
@@ -732,59 +839,79 @@ const runCodegen = (options) => {
732
839
  const hasAi = featureUsage.ai;
733
840
  const hasPayments = featureUsage.payments;
734
841
  const hasKv = featureUsage.kv;
842
+ const hasAccessFacade = featureUsage.access;
843
+ const hasFlags = existsSync(join(lunoraDirectory, "flags.ts"));
844
+ const flagKeys = hasFlags ? discoverFlagKeys(project, lunoraDirectory) : [];
735
845
  const hasHyperdrive = featureUsage.hyperdrive;
736
846
  const hasBrowser = featureUsage.browser;
737
847
  const hasImages = featureUsage.images;
738
848
  const hasAnalytics = featureUsage.analytics;
739
849
  const hasPipelines = featureUsage.pipelines;
850
+ const hasR2sql = featureUsage.r2sql;
740
851
  const dependencies = discoverPackageDependencies(options.projectRoot);
741
852
  const studioFeatures = buildStudioFeatures(featureUsage, {
742
853
  cronCount: crons.length,
743
854
  dependencies,
855
+ queueCount: queues.length,
744
856
  storageColumnCount: Object.keys(buildStorageColumns(schema)).length,
745
857
  storageRuleCount: storageRulesMetadata.rules.length,
746
858
  vectorIndexCount: schema.vectorIndexes.length,
747
859
  workflowCount: workflows.length
748
860
  });
749
861
  const useUmbrella = dependencies.has("lunorash");
862
+ const emitStartedAt = timingEnabled ? performance.now() : 0;
750
863
  const dataModelContent = emitDataModel(schema, useUmbrella);
751
864
  const apiContent = emitApi(functions, workflows, useUmbrella);
752
865
  const serverContent = emitServer({
753
866
  containers,
867
+ hasAccessFacade,
754
868
  hasAi,
755
869
  hasAnalytics,
756
870
  hasBrowser,
871
+ hasFlags,
757
872
  hasHyperdrive,
758
873
  hasImages,
759
874
  hasKv,
760
875
  hasPayments,
761
876
  hasPipelines,
877
+ hasR2sql,
878
+ queues,
762
879
  schema,
763
880
  storageRuleBuckets: storageRulesMetadata.rules.map((rule) => rule.bucket),
764
881
  useUmbrella,
765
882
  workflows
766
883
  });
767
- const functionsContent = emitFunctions(functions, migrations);
884
+ const functionsContent = emitFunctions(functions, migrations, useUmbrella, mutators, shapes);
768
885
  const shardContent = emitShard({
769
886
  advisories,
770
887
  containers,
888
+ flagKeys,
889
+ hasAccessFacade,
771
890
  hasAi,
772
891
  hasAnalytics,
773
892
  hasBrowser,
893
+ hasFlags,
774
894
  hasHyperdrive,
775
895
  hasImages,
776
896
  hasKv,
777
897
  hasPayments,
898
+ hasPipelines,
899
+ hasR2sql,
778
900
  maskMetadata,
901
+ mutators,
902
+ queues,
779
903
  rlsMetadata,
780
904
  schema,
905
+ shapes,
781
906
  storageRules: storageRulesMetadata,
782
907
  studioFeatures,
783
908
  useUmbrella,
784
909
  workflows
785
910
  });
786
- const containersContent = emitContainers(containers);
911
+ const collectionsContent = emitCollections(shapes, dependencies.has("@lunora/db"), useUmbrella);
912
+ const containersContent = emitContainers(containers, schema.jurisdiction);
787
913
  const workflowsContent = emitWorkflows(workflows);
914
+ const queuesContent = emitQueues(queues);
788
915
  const cronsContent = emitCrons(crons);
789
916
  const vectorsContent = emitVectors(schema.vectorIndexes);
790
917
  const drizzleFiles = emitDrizzleSchema(schema, useUmbrella);
@@ -793,6 +920,7 @@ const runCodegen = (options) => {
793
920
  const wantsOpenApi = apiSpec === "openapi" || apiSpec === "both";
794
921
  const wantsOpenRpc = apiSpec === "openrpc" || apiSpec === "both";
795
922
  const appContent = emitApp({
923
+ hasAccess: dependencies.has("@lunora/cloudflare-access"),
796
924
  hasAi,
797
925
  hasAnalytics,
798
926
  hasAuth: dependencies.has("@lunora/auth"),
@@ -809,10 +937,14 @@ const runCodegen = (options) => {
809
937
  hasImages,
810
938
  hasKv,
811
939
  hasPayments,
940
+ hasR2sql,
941
+ hasQueue: queues.some((queue) => queue.mode === "push"),
812
942
  hasScheduler: studioFeatures.scheduler,
813
943
  hasStorage: studioFeatures.storage,
814
944
  hasVectors: schema.vectorIndexes.length > 0,
815
945
  hasWorkflow: workflows.length > 0,
946
+ // Schema `.jurisdiction("…")` → pin the generated worker's DOs to the region.
947
+ jurisdiction: schema.jurisdiction,
816
948
  useUmbrella,
817
949
  wantsOpenApi,
818
950
  wantsOpenRpc
@@ -849,7 +981,9 @@ const runCodegen = (options) => {
849
981
  writeIfChanged(join(outputDirectory, "drizzle.shard.ts"), drizzleFiles.shard);
850
982
  writeIfPresent(join(outputDirectory, "containers.ts"), containersContent);
851
983
  writeIfPresent(join(outputDirectory, "workflows.ts"), workflowsContent);
984
+ writeIfPresent(join(outputDirectory, "queues.ts"), queuesContent);
852
985
  writeIfPresent(join(outputDirectory, "seed.ts"), seedContent);
986
+ writeIfPresent(join(outputDirectory, "collections.ts"), collectionsContent);
853
987
  if (wantsOpenApi) {
854
988
  writeIfChanged(join(outputDirectory, "openapi.json"), openApiContent);
855
989
  writeIfChanged(join(outputDirectory, "openapi.ts"), openApiModuleContent);
@@ -862,6 +996,13 @@ const runCodegen = (options) => {
862
996
  writeIfChanged(schemaSnapshotPath, serializeSchemaSnapshot(schemaSnapshot));
863
997
  }
864
998
  }
999
+ if (timingEnabled) {
1000
+ const finishedAt = performance.now();
1001
+ const total = Math.round(finishedAt - startedAt);
1002
+ const discovery = Math.round(emitStartedAt - startedAt);
1003
+ const emit = Math.round(finishedAt - emitStartedAt);
1004
+ console.error(`@lunora/codegen: codegen took ${total.toString()}ms (discovery ${discovery.toString()}ms, emit ${emit.toString()}ms)`);
1005
+ }
865
1006
  return {
866
1007
  advisories,
867
1008
  containers,
@@ -869,6 +1010,7 @@ const runCodegen = (options) => {
869
1010
  generated: {
870
1011
  api: apiContent,
871
1012
  app: appContent,
1013
+ collections: collectionsContent,
872
1014
  containers: containersContent,
873
1015
  crons: cronsContent,
874
1016
  dataModel: dataModelContent,
@@ -879,6 +1021,7 @@ const runCodegen = (options) => {
879
1021
  openApiModule: openApiModuleContent,
880
1022
  openRpc: openRpcContent,
881
1023
  openRpcModule: openRpcModuleContent,
1024
+ queues: queuesContent,
882
1025
  seed: seedContent,
883
1026
  server: serverContent,
884
1027
  shard: shardContent,
@@ -886,6 +1029,7 @@ const runCodegen = (options) => {
886
1029
  workflows: workflowsContent
887
1030
  },
888
1031
  outputDirectory,
1032
+ queues,
889
1033
  schemaSnapshot,
890
1034
  schemaSnapshotPath,
891
1035
  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) => {
@@ -0,0 +1,94 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { SyntaxKind, Node } from 'ts-morph';
4
+ import { diagnosticAt } from './CodegenDiagnosticError-DeblMkzO.mjs';
5
+
6
+ const SHAPES_FILENAME = "shapes.ts";
7
+ const SHAPE_MODULE_SPECIFIERS = /* @__PURE__ */ new Set(["@lunora/server", "lunorash/server"]);
8
+ const isDefineShape = (identifier) => {
9
+ const symbol = identifier.getSymbol();
10
+ if (!symbol) {
11
+ return identifier.getText() === "defineShape";
12
+ }
13
+ for (const declaration of symbol.getDeclarations()) {
14
+ if (!Node.isImportSpecifier(declaration)) {
15
+ continue;
16
+ }
17
+ if (!SHAPE_MODULE_SPECIFIERS.has(declaration.getImportDeclaration().getModuleSpecifierValue())) {
18
+ return false;
19
+ }
20
+ return declaration.getNameNode().getText() === "defineShape";
21
+ }
22
+ return false;
23
+ };
24
+ const isShapeNamespaceImport = (identifier) => {
25
+ const symbol = identifier.getSymbol();
26
+ if (!symbol) {
27
+ return false;
28
+ }
29
+ for (const declaration of symbol.getDeclarations()) {
30
+ if (!Node.isNamespaceImport(declaration)) {
31
+ continue;
32
+ }
33
+ const importDeclaration = declaration.getFirstAncestorByKind(SyntaxKind.ImportDeclaration);
34
+ return importDeclaration !== void 0 && SHAPE_MODULE_SPECIFIERS.has(importDeclaration.getModuleSpecifierValue());
35
+ }
36
+ return false;
37
+ };
38
+ const isDefineShapeCallee = (callee) => {
39
+ if (Node.isIdentifier(callee)) {
40
+ return isDefineShape(callee);
41
+ }
42
+ if (Node.isPropertyAccessExpression(callee)) {
43
+ const object = callee.getExpression();
44
+ return callee.getName() === "defineShape" && Node.isIdentifier(object) && isShapeNamespaceImport(object);
45
+ }
46
+ return false;
47
+ };
48
+ const tableLiteralFrom = (call) => {
49
+ const [config] = call.getArguments();
50
+ if (!config || !Node.isObjectLiteralExpression(config)) {
51
+ return void 0;
52
+ }
53
+ const tableProperty = config.getProperty("table");
54
+ if (!tableProperty || !Node.isPropertyAssignment(tableProperty)) {
55
+ return void 0;
56
+ }
57
+ const value = tableProperty.getInitializer();
58
+ return value && Node.isStringLiteral(value) ? value.getLiteralValue() : void 0;
59
+ };
60
+ const shapesFromSource = (source) => {
61
+ const shapes = [];
62
+ for (const declaration of source.getVariableDeclarations()) {
63
+ if (!declaration.isExported()) {
64
+ continue;
65
+ }
66
+ const initializer = declaration.getInitializer();
67
+ if (initializer?.getKind() !== SyntaxKind.CallExpression) {
68
+ continue;
69
+ }
70
+ const callExpression = initializer;
71
+ const callee = callExpression.getExpression();
72
+ if (!isDefineShapeCallee(callee)) {
73
+ continue;
74
+ }
75
+ const nameNode = declaration.getNameNode();
76
+ if (!Node.isIdentifier(nameNode)) {
77
+ throw diagnosticAt(nameNode, "defineShape exports must be plain named exports (no destructuring)");
78
+ }
79
+ shapes.push({ exportName: nameNode.getText(), filePath: "shapes", table: tableLiteralFrom(callExpression) });
80
+ }
81
+ return shapes;
82
+ };
83
+ const discoverShapes = (project, lunoraDirectory) => {
84
+ const shapesPath = join(lunoraDirectory, SHAPES_FILENAME);
85
+ if (!existsSync(shapesPath)) {
86
+ return [];
87
+ }
88
+ const source = project.getSourceFile(shapesPath) ?? project.addSourceFileAtPath(shapesPath);
89
+ const shapes = shapesFromSource(source);
90
+ shapes.sort((a, b) => a.exportName.localeCompare(b.exportName));
91
+ return shapes;
92
+ };
93
+
94
+ export { SHAPES_FILENAME, discoverShapes };
@@ -2,9 +2,10 @@ 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
+ const STEP_METHODS = /* @__PURE__ */ new Set(["do", "sleep", "sleepUntil", "waitForEvent"]);
8
9
  const isDefineWorkflow = (identifier) => {
9
10
  const symbol = identifier.getSymbol();
10
11
  if (!symbol) {
@@ -30,6 +31,44 @@ const stringProperty = (expression, exportName, property) => {
30
31
  `workflow "${exportName}": \`${property}\` must be a static string literal — it is deploy configuration codegen writes into wrangler.jsonc`
31
32
  );
32
33
  };
34
+ const isStepCall = (call) => {
35
+ const callee = call.getExpression();
36
+ if (!Node.isPropertyAccessExpression(callee) || !STEP_METHODS.has(callee.getName())) {
37
+ return false;
38
+ }
39
+ const receiver = callee.getExpression();
40
+ if (Node.isPropertyAccessExpression(receiver) && receiver.getName() === "step") {
41
+ return true;
42
+ }
43
+ return Node.isIdentifier(receiver) && receiver.getText() === "step";
44
+ };
45
+ const stepsFromHandler = (argument) => {
46
+ const handlerProperty = argument.getProperty("handler");
47
+ if (!handlerProperty) {
48
+ return [];
49
+ }
50
+ const body = Node.isPropertyAssignment(handlerProperty) ? handlerProperty.getInitializer() : handlerProperty;
51
+ if (!body) {
52
+ return [];
53
+ }
54
+ const steps = [];
55
+ for (const call of body.getDescendantsOfKind(SyntaxKind.CallExpression)) {
56
+ if (!isStepCall(call)) {
57
+ continue;
58
+ }
59
+ const nameArgument = call.getArguments()[0];
60
+ if (!nameArgument || !(Node.isStringLiteral(nameArgument) || Node.isNoSubstitutionTemplateLiteral(nameArgument))) {
61
+ continue;
62
+ }
63
+ steps.push({
64
+ line: call.getStartLineNumber(),
65
+ // `isStepCall` already proved the callee is a `.<method>` property access.
66
+ method: call.getExpression().getName(),
67
+ name: nameArgument.getLiteralValue()
68
+ });
69
+ }
70
+ return steps;
71
+ };
33
72
  const workflowFromCall = (call, exportName) => {
34
73
  const argument = call.getArguments()[0];
35
74
  if (!argument || !Node.isObjectLiteralExpression(argument)) {
@@ -39,7 +78,8 @@ const workflowFromCall = (call, exportName) => {
39
78
  bindingName: workflowBindingName(exportName),
40
79
  className: workflowClassName(exportName),
41
80
  exportName,
42
- name: workflowDefaultName(exportName)
81
+ name: workflowDefaultName(exportName),
82
+ steps: stepsFromHandler(argument)
43
83
  };
44
84
  const nameProperty = argument.getProperty("name");
45
85
  if (nameProperty && Node.isPropertyAssignment(nameProperty)) {
@@ -1,4 +1,4 @@
1
- import { GENERATED_HEADER } from './emitApi-hRVC-kE7.mjs';
1
+ import { GENERATED_HEADER } from './GENERATED_HEADER-CI272wRm.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();