@lunora/codegen 1.0.0-alpha.7 → 1.0.0-alpha.9

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 (27) hide show
  1. package/__assets__/package-og.svg +1 -1
  2. package/dist/index.d.mts +26 -2
  3. package/dist/index.d.ts +26 -2
  4. package/dist/index.mjs +21 -21
  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/{GENERATED_HEADER-DF1hQcix.mjs → GENERATED_HEADER-LceqJ_qp.mjs} +200 -7
  8. package/dist/packem_shared/{OPENRPC_VERSION-C7zKVeKi.mjs → OPENRPC_VERSION-s7lhywUr.mjs} +1 -1
  9. package/dist/packem_shared/{SCHEMA_SNAPSHOT_FILENAME-g7kpX8hu.mjs → SCHEMA_SNAPSHOT_FILENAME-Ct0NDRLY.mjs} +35 -20
  10. package/dist/packem_shared/{WORKFLOWS_FILENAME-DRDQdhfq.mjs → WORKFLOWS_FILENAME-D62dcBGg.mjs} +1 -1
  11. package/dist/packem_shared/{buildOpenApiDocument-Cns1EkCQ.mjs → buildOpenApiDocument-DSPTssJI.mjs} +1 -1
  12. package/dist/packem_shared/{discoverAuthApiCalls-C35R6z0T.mjs → discoverAuthApiCalls-CoirYbg6.mjs} +1 -1
  13. package/dist/packem_shared/{discoverCrons-BL6iGuJ3.mjs → discoverCrons-Cev7RRAf.mjs} +20 -17
  14. package/dist/packem_shared/{discoverFunctions-DEgAcRuD.mjs → discoverFunctions-BWMczzBx.mjs} +7 -2
  15. package/dist/packem_shared/{discoverHttpRoutes-C978pBiG.mjs → discoverHttpRoutes-CfP6cMzt.mjs} +2 -2
  16. package/dist/packem_shared/{discoverInserts-pLBFsSAV.mjs → discoverInserts-C7zxXkUf.mjs} +1 -1
  17. package/dist/packem_shared/{discoverMaskProcedures-B64zA740.mjs → discoverMaskProcedures-Cm81kwrb.mjs} +1 -1
  18. package/dist/packem_shared/{discoverMigrations-Doj_-BAA.mjs → discoverMigrations-Bi5nJ0mJ.mjs} +10 -4
  19. package/dist/packem_shared/{discoverNondeterministicCalls-4KiPQxQU.mjs → discoverNondeterministicCalls-C4M8AXmQ.mjs} +1 -1
  20. package/dist/packem_shared/{discoverQueries-BkIi0dBD.mjs → discoverQueries-B0wGT-xe.mjs} +1 -1
  21. package/dist/packem_shared/{discoverR2sqlCalls-BpDqvcUn.mjs → discoverR2sqlCalls-BVNMd428.mjs} +1 -1
  22. package/dist/packem_shared/{discoverRlsMetadata-DpRB1HMe.mjs → discoverRlsMetadata-BS9GOGC5.mjs} +1 -1
  23. package/dist/packem_shared/{discoverSchema-DYl0QfDH.mjs → discoverSchema-BF6L4mrU.mjs} +22 -2
  24. package/dist/packem_shared/{discoverStorageRulesMetadata-DAqJUxUv.mjs → discoverStorageRulesMetadata-Da8BKXcI.mjs} +1 -1
  25. package/dist/packem_shared/{emitApp-Ci_hcJNO.mjs → emitApp-CDg72pVv.mjs} +1 -1
  26. package/dist/packem_shared/{parse-validator-tuQtHrsr.mjs → parse-validator-Cabb60UV.mjs} +2 -1
  27. package/package.json +4 -4
package/dist/index.d.mts CHANGED
@@ -25,6 +25,15 @@ interface ValidatorIR {
25
25
  bucket?: string;
26
26
  /** Column modifiers (`.unique()`, `.default()`, `.nullable()`, …) when present. */
27
27
  column?: ColumnMetaIR;
28
+ /**
29
+ * `true` when this validator carries a `.check(...)` refinement. The predicate
30
+ * is a runtime closure the AST→IR step can't represent, so the node keeps its
31
+ * base `kind` but records the refinement's presence here. The AOT args-validator
32
+ * compiler declines any node with this flag (compiling it would silently skip
33
+ * the predicate). `.meta(...)` is pure metadata with no parse effect and does
34
+ * NOT set this.
35
+ */
36
+ hasRefinement?: boolean;
28
37
  /** For `v.optional(inner)` / `v.array(inner)`. */
29
38
  inner?: ValidatorIR;
30
39
  /** For `v.record(key, value)`. */
@@ -126,6 +135,10 @@ interface TableIR {
126
135
  field: string;
127
136
  kind: "shardBy";
128
137
  };
138
+ /** Set when the chain carried `.softDelete()` — the marker column's name (default `deletedAt`). The column is injected into `shape` so `Doc_*` carries it. */
139
+ softDelete?: {
140
+ field: string;
141
+ };
129
142
  /** Vector indexes declared inline via `.vectorize()` (DSL Shape A). */
130
143
  vectorIndexes: ReadonlyArray<VectorIndexIR>;
131
144
  }
@@ -731,8 +744,14 @@ declare class CodegenDiagnosticError extends Error {
731
744
  * ts-morph `Node`'s position in its source file.
732
745
  *
733
746
  * Message format: `@lunora/codegen: &lt;detail> (&lt;file>:&lt;line>:&lt;column>)`
747
+ *
748
+ * `meta` is merged onto the returned error for callers that also carry the
749
+ * project-wide `LunoraError` envelope (`code`/`name`/`status`) — it never
750
+ * touches `file`/`line`/`column`, and the error stays an instance of
751
+ * {@link CodegenDiagnosticError} so the Vite overlay's `instanceof` location
752
+ * lookup is unaffected.
734
753
  */
735
- declare const diagnosticAt: (node: Node, detail: string) => CodegenDiagnosticError;
754
+ declare const diagnosticAt: (node: Node, detail: string, meta?: Record<string, unknown>) => CodegenDiagnosticError;
736
755
  /**
737
756
  * Discover `ctx.authApi.&lt;method>(...)` (and bare `authApi.&lt;method>(...)`) calls
738
757
  * under the lunora source directory and attribute each to the exported function
@@ -934,7 +953,7 @@ declare const emitServer: ({
934
953
  useUmbrella,
935
954
  workflows
936
955
  }?: EmitServerOptions) => string;
937
- declare const emitFunctions: (functions: ReadonlyArray<FunctionIR>, migrations?: ReadonlyArray<MigrationIR>) => string;
956
+ declare const emitFunctions: (functions: ReadonlyArray<FunctionIR>, migrations?: ReadonlyArray<MigrationIR>, useUmbrella?: boolean) => string;
938
957
  /**
939
958
  * Storage-column map per table for the file browser: `{ table: [field, …] }` for
940
959
  * every field declared `v.storage(...)` (unwrapping `v.optional(...)`). The
@@ -1357,6 +1376,11 @@ declare const refreshCodegenProject: (project: Project, lunoraDirectory: string)
1357
1376
  * Top-level codegen entry. Parses `&lt;projectRoot>/lunora/schema.ts` and every
1358
1377
  * function file under `&lt;projectRoot>/lunora/`, then writes
1359
1378
  * `_generated/{api,server,dataModel}.ts` next to them.
1379
+ *
1380
+ * When `LUNORA_CODEGEN_TIMING` is set (truthy), a single diagnostic summary
1381
+ * line is written to stderr with the total wall time and the discovery-vs-emit
1382
+ * split — opt-in instrumentation that is otherwise zero-cost and side-effect-free
1383
+ * on the returned {@link CodegenResult}.
1360
1384
  */
1361
1385
  declare const runCodegen: (options: CodegenOptions) => CodegenResult;
1362
1386
  interface CodegenOptions {
package/dist/index.d.ts CHANGED
@@ -25,6 +25,15 @@ interface ValidatorIR {
25
25
  bucket?: string;
26
26
  /** Column modifiers (`.unique()`, `.default()`, `.nullable()`, …) when present. */
27
27
  column?: ColumnMetaIR;
28
+ /**
29
+ * `true` when this validator carries a `.check(...)` refinement. The predicate
30
+ * is a runtime closure the AST→IR step can't represent, so the node keeps its
31
+ * base `kind` but records the refinement's presence here. The AOT args-validator
32
+ * compiler declines any node with this flag (compiling it would silently skip
33
+ * the predicate). `.meta(...)` is pure metadata with no parse effect and does
34
+ * NOT set this.
35
+ */
36
+ hasRefinement?: boolean;
28
37
  /** For `v.optional(inner)` / `v.array(inner)`. */
29
38
  inner?: ValidatorIR;
30
39
  /** For `v.record(key, value)`. */
@@ -126,6 +135,10 @@ interface TableIR {
126
135
  field: string;
127
136
  kind: "shardBy";
128
137
  };
138
+ /** Set when the chain carried `.softDelete()` — the marker column's name (default `deletedAt`). The column is injected into `shape` so `Doc_*` carries it. */
139
+ softDelete?: {
140
+ field: string;
141
+ };
129
142
  /** Vector indexes declared inline via `.vectorize()` (DSL Shape A). */
130
143
  vectorIndexes: ReadonlyArray<VectorIndexIR>;
131
144
  }
@@ -731,8 +744,14 @@ declare class CodegenDiagnosticError extends Error {
731
744
  * ts-morph `Node`'s position in its source file.
732
745
  *
733
746
  * Message format: `@lunora/codegen: &lt;detail> (&lt;file>:&lt;line>:&lt;column>)`
747
+ *
748
+ * `meta` is merged onto the returned error for callers that also carry the
749
+ * project-wide `LunoraError` envelope (`code`/`name`/`status`) — it never
750
+ * touches `file`/`line`/`column`, and the error stays an instance of
751
+ * {@link CodegenDiagnosticError} so the Vite overlay's `instanceof` location
752
+ * lookup is unaffected.
734
753
  */
735
- declare const diagnosticAt: (node: Node, detail: string) => CodegenDiagnosticError;
754
+ declare const diagnosticAt: (node: Node, detail: string, meta?: Record<string, unknown>) => CodegenDiagnosticError;
736
755
  /**
737
756
  * Discover `ctx.authApi.&lt;method>(...)` (and bare `authApi.&lt;method>(...)`) calls
738
757
  * under the lunora source directory and attribute each to the exported function
@@ -934,7 +953,7 @@ declare const emitServer: ({
934
953
  useUmbrella,
935
954
  workflows
936
955
  }?: EmitServerOptions) => string;
937
- declare const emitFunctions: (functions: ReadonlyArray<FunctionIR>, migrations?: ReadonlyArray<MigrationIR>) => string;
956
+ declare const emitFunctions: (functions: ReadonlyArray<FunctionIR>, migrations?: ReadonlyArray<MigrationIR>, useUmbrella?: boolean) => string;
938
957
  /**
939
958
  * Storage-column map per table for the file browser: `{ table: [field, …] }` for
940
959
  * every field declared `v.storage(...)` (unwrapping `v.optional(...)`). The
@@ -1357,6 +1376,11 @@ declare const refreshCodegenProject: (project: Project, lunoraDirectory: string)
1357
1376
  * Top-level codegen entry. Parses `&lt;projectRoot>/lunora/schema.ts` and every
1358
1377
  * function file under `&lt;projectRoot>/lunora/`, then writes
1359
1378
  * `_generated/{api,server,dataModel}.ts` next to them.
1379
+ *
1380
+ * When `LUNORA_CODEGEN_TIMING` is set (truthy), a single diagnostic summary
1381
+ * line is written to stderr with the total wall time and the discovery-vs-emit
1382
+ * split — opt-in instrumentation that is otherwise zero-cost and side-effect-free
1383
+ * on the returned {@link CodegenResult}.
1360
1384
  */
1361
1385
  declare const runCodegen: (options: CodegenOptions) => CodegenResult;
1362
1386
  interface CodegenOptions {
package/dist/index.mjs CHANGED
@@ -1,25 +1,25 @@
1
1
  export { formatAdvisories, lintSchema } from './packem_shared/formatAdvisories-8NIv1k0I.mjs';
2
- export { CodegenDiagnosticError, diagnosticAt } from './packem_shared/CodegenDiagnosticError-54jWDxA9.mjs';
3
- export { default as discoverAuthApiCalls } from './packem_shared/discoverAuthApiCalls-C35R6z0T.mjs';
4
- export { CONTAINERS_FILENAME, discoverContainers } from './packem_shared/CONTAINERS_FILENAME-0K-pjNb8.mjs';
5
- export { default as discoverCrons } from './packem_shared/discoverCrons-BL6iGuJ3.mjs';
6
- export { discoverFunctions } from './packem_shared/discoverFunctions-DEgAcRuD.mjs';
7
- export { default as discoverHttpRoutes } from './packem_shared/discoverHttpRoutes-C978pBiG.mjs';
8
- export { default as discoverInserts } from './packem_shared/discoverInserts-pLBFsSAV.mjs';
9
- export { default as discoverMaskProcedures } from './packem_shared/discoverMaskProcedures-B64zA740.mjs';
10
- export { default as discoverMigrations } from './packem_shared/discoverMigrations-Doj_-BAA.mjs';
11
- export { default as discoverNondeterministicCalls } from './packem_shared/discoverNondeterministicCalls-4KiPQxQU.mjs';
12
- export { default as discoverQueries } from './packem_shared/discoverQueries-BkIi0dBD.mjs';
13
- export { default as discoverR2sqlCalls } from './packem_shared/discoverR2sqlCalls-BpDqvcUn.mjs';
14
- export { discoverRlsMetadata, default as discoverRlsProcedures } from './packem_shared/discoverRlsMetadata-DpRB1HMe.mjs';
15
- export { default as discoverSchema } from './packem_shared/discoverSchema-DYl0QfDH.mjs';
16
- export { default as discoverStorageRulesMetadata } from './packem_shared/discoverStorageRulesMetadata-DAqJUxUv.mjs';
17
- export { WORKFLOWS_FILENAME, discoverWorkflows } from './packem_shared/WORKFLOWS_FILENAME-DRDQdhfq.mjs';
18
- export { GENERATED_HEADER, emitApi, emitContainers, emitCrons, emitDataModel, emitDrizzleSchema, emitFunctions, emitServer, emitShard, emitVectors, emitWorkflows, emitWranglerCronTriggers } from './packem_shared/GENERATED_HEADER-DF1hQcix.mjs';
19
- export { emitApp } from './packem_shared/emitApp-Ci_hcJNO.mjs';
20
- export { buildOpenApiDocument, emitOpenApi, emitOpenApiModule } from './packem_shared/buildOpenApiDocument-Cns1EkCQ.mjs';
21
- export { OPENRPC_VERSION, buildOpenRpcDocument, emitOpenRpc, emitOpenRpcModule } from './packem_shared/OPENRPC_VERSION-C7zKVeKi.mjs';
22
- export { SCHEMA_SNAPSHOT_FILENAME, createCodegenProject, refreshCodegenProject, runCodegen } from './packem_shared/SCHEMA_SNAPSHOT_FILENAME-g7kpX8hu.mjs';
2
+ export { CodegenDiagnosticError, diagnosticAt } from './packem_shared/CodegenDiagnosticError-DeblMkzO.mjs';
3
+ export { default as discoverAuthApiCalls } from './packem_shared/discoverAuthApiCalls-CoirYbg6.mjs';
4
+ export { CONTAINERS_FILENAME, discoverContainers } from './packem_shared/CONTAINERS_FILENAME-DlP6YM_Q.mjs';
5
+ export { default as discoverCrons } from './packem_shared/discoverCrons-Cev7RRAf.mjs';
6
+ export { discoverFunctions } from './packem_shared/discoverFunctions-BWMczzBx.mjs';
7
+ export { default as discoverHttpRoutes } from './packem_shared/discoverHttpRoutes-CfP6cMzt.mjs';
8
+ export { default as discoverInserts } from './packem_shared/discoverInserts-C7zxXkUf.mjs';
9
+ export { default as discoverMaskProcedures } from './packem_shared/discoverMaskProcedures-Cm81kwrb.mjs';
10
+ export { default as discoverMigrations } from './packem_shared/discoverMigrations-Bi5nJ0mJ.mjs';
11
+ export { default as discoverNondeterministicCalls } from './packem_shared/discoverNondeterministicCalls-C4M8AXmQ.mjs';
12
+ export { default as discoverQueries } from './packem_shared/discoverQueries-B0wGT-xe.mjs';
13
+ export { default as discoverR2sqlCalls } from './packem_shared/discoverR2sqlCalls-BVNMd428.mjs';
14
+ export { discoverRlsMetadata, default as discoverRlsProcedures } from './packem_shared/discoverRlsMetadata-BS9GOGC5.mjs';
15
+ export { default as discoverSchema } from './packem_shared/discoverSchema-BF6L4mrU.mjs';
16
+ export { default as discoverStorageRulesMetadata } from './packem_shared/discoverStorageRulesMetadata-Da8BKXcI.mjs';
17
+ export { WORKFLOWS_FILENAME, discoverWorkflows } from './packem_shared/WORKFLOWS_FILENAME-D62dcBGg.mjs';
18
+ export { GENERATED_HEADER, emitApi, emitContainers, emitCrons, emitDataModel, emitDrizzleSchema, emitFunctions, emitServer, emitShard, emitVectors, emitWorkflows, emitWranglerCronTriggers } from './packem_shared/GENERATED_HEADER-LceqJ_qp.mjs';
19
+ export { emitApp } from './packem_shared/emitApp-CDg72pVv.mjs';
20
+ export { buildOpenApiDocument, emitOpenApi, emitOpenApiModule } from './packem_shared/buildOpenApiDocument-DSPTssJI.mjs';
21
+ export { OPENRPC_VERSION, buildOpenRpcDocument, emitOpenRpc, emitOpenRpcModule } from './packem_shared/OPENRPC_VERSION-s7lhywUr.mjs';
22
+ export { SCHEMA_SNAPSHOT_FILENAME, createCodegenProject, refreshCodegenProject, runCodegen } from './packem_shared/SCHEMA_SNAPSHOT_FILENAME-Ct0NDRLY.mjs';
23
23
  export { SCHEMA_SNAPSHOT_VERSION, SchemaSnapshotParseError, buildSchemaSnapshot, diffSchemaSnapshots, evaluateSchemaDrift, parseSchemaSnapshot, serializeSchemaSnapshot } from './packem_shared/SCHEMA_SNAPSHOT_VERSION-DzLDbWk3.mjs';
24
24
  export { schemaFromIr } from './packem_shared/schemaFromIr-DTYsLBaA.mjs';
25
25
  export { LUNORA_ERROR_CODES, validatorIrToJsonSchema } from './packem_shared/LUNORA_ERROR_CODES-CySpQPD3.mjs';
@@ -2,7 +2,7 @@ import { existsSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  import { containerClassName, containerBindingName, normalizeContainerImage } from '@lunora/container';
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 CONTAINERS_FILENAME = "containers.ts";
8
8
  const isDefineContainer = (identifier) => {
@@ -10,13 +10,14 @@ class CodegenDiagnosticError extends Error {
10
10
  this.column = column;
11
11
  }
12
12
  }
13
- const diagnosticAt = (node, detail) => {
13
+ const diagnosticAt = (node, detail, meta) => {
14
14
  const sourceFile = node.getSourceFile();
15
15
  const file = sourceFile.getFilePath();
16
16
  const line = node.getStartLineNumber();
17
17
  const { column } = sourceFile.getLineAndColumnAtPos(node.getStart());
18
18
  const message = `@lunora/codegen: ${detail} (${file}:${line.toString()}:${column.toString()})`;
19
- return new CodegenDiagnosticError(message, file, line, column);
19
+ const error = new CodegenDiagnosticError(message, file, line, column);
20
+ return meta ? Object.assign(error, meta) : error;
20
21
  };
21
22
 
22
23
  export { CodegenDiagnosticError, diagnosticAt };
@@ -1,5 +1,174 @@
1
1
  import { s as sanitizeNamespace } from './paths-BRd6JHuF.mjs';
2
2
 
3
+ const LITERAL_VALUE_RE = /^(?:"[^"\\]*"|'[^'\\]*'|-?\d+(?:\.\d+)?|true|false|null)$/u;
4
+
5
+ const PASS_THROUGH_KINDS = /* @__PURE__ */ new Set(["any", "bigint", "boolean", "date", "id", "null", "number", "storage", "string", "timestamp"]);
6
+ const hasColumnModifier = (node) => node.column !== void 0;
7
+ const emitScalarGuard = (kind, inExpr) => {
8
+ switch (kind) {
9
+ case "bigint": {
10
+ return `if (typeof ${inExpr} !== "bigint") return DEFER;
11
+ `;
12
+ }
13
+ case "boolean": {
14
+ return `if (typeof ${inExpr} !== "boolean") return DEFER;
15
+ `;
16
+ }
17
+ case "date":
18
+ case "number":
19
+ case "timestamp": {
20
+ return `if (typeof ${inExpr} !== "number" || !Number.isFinite(${inExpr})) return DEFER;
21
+ `;
22
+ }
23
+ case "null": {
24
+ return `if (${inExpr} !== null) return DEFER;
25
+ `;
26
+ }
27
+ // string / id / storage all parse as a bare string at runtime.
28
+ default: {
29
+ return `if (typeof ${inExpr} !== "string") return DEFER;
30
+ `;
31
+ }
32
+ }
33
+ };
34
+ const compileLiteral = (node, inExpr) => {
35
+ const literal = node.literalValue?.trim();
36
+ if (literal === void 0 || !LITERAL_VALUE_RE.test(literal)) {
37
+ return void 0;
38
+ }
39
+ return { out: inExpr, pre: `if (${inExpr} !== ${literal}) return DEFER;
40
+ ` };
41
+ };
42
+ const compileNode = (node, inExpr, context) => {
43
+ if (hasColumnModifier(node)) {
44
+ return void 0;
45
+ }
46
+ if (node.hasRefinement || node.sourceText !== void 0) {
47
+ return void 0;
48
+ }
49
+ if (PASS_THROUGH_KINDS.has(node.kind)) {
50
+ if (node.kind === "any") {
51
+ return { out: inExpr, pre: "" };
52
+ }
53
+ return { out: inExpr, pre: emitScalarGuard(node.kind, inExpr) };
54
+ }
55
+ switch (node.kind) {
56
+ case "array": {
57
+ return compileArray(node, inExpr, context);
58
+ }
59
+ case "literal": {
60
+ return compileLiteral(node, inExpr);
61
+ }
62
+ case "object": {
63
+ return compileObject(node, inExpr, context);
64
+ }
65
+ default: {
66
+ return void 0;
67
+ }
68
+ }
69
+ };
70
+ const compileArray = (node, inExpr, context) => {
71
+ const { inner } = node;
72
+ if (!inner) {
73
+ return void 0;
74
+ }
75
+ const id = context.next();
76
+ const array = `__arr${String(id)}`;
77
+ const index = `__i${String(id)}`;
78
+ const element = `__e${String(id)}`;
79
+ const innerEmit = compileNode(inner, element, context);
80
+ if (!innerEmit) {
81
+ return void 0;
82
+ }
83
+ const pre = `if (!Array.isArray(${inExpr})) return DEFER;
84
+ const ${array} = new Array(${inExpr}.length);
85
+ for (let ${index} = 0; ${index} < ${inExpr}.length; ${index}++) {
86
+ const ${element} = ${inExpr}[${index}];
87
+ ${innerEmit.pre}${array}[${index}] = ${innerEmit.out};
88
+ }
89
+ `;
90
+ return { out: array, pre };
91
+ };
92
+ const compileField = (key, node, access, context) => {
93
+ const keyLiteral = JSON.stringify(key);
94
+ if (node.kind === "optional") {
95
+ if (node.hasRefinement || node.sourceText !== void 0 || hasColumnModifier(node)) {
96
+ return void 0;
97
+ }
98
+ const { inner } = node;
99
+ if (!inner) {
100
+ return void 0;
101
+ }
102
+ const innerEmit = compileNode(inner, access, context);
103
+ if (!innerEmit) {
104
+ return void 0;
105
+ }
106
+ const id = context.next();
107
+ const has = `__has${String(id)}`;
108
+ const value = `__val${String(id)}`;
109
+ const pre = `let ${has} = false;
110
+ let ${value};
111
+ if (${access} !== undefined) {
112
+ ${innerEmit.pre}${value} = ${innerEmit.out};
113
+ ${has} = true;
114
+ }
115
+ `;
116
+ return { entry: `...(${has} ? { ${keyLiteral}: ${value} } : {})`, pre };
117
+ }
118
+ const emit = compileNode(node, access, context);
119
+ if (!emit) {
120
+ return void 0;
121
+ }
122
+ return { entry: `${keyLiteral}: ${emit.out}`, pre: emit.pre };
123
+ };
124
+ const compileFields = (shape, accessFor, context) => {
125
+ let pre = "";
126
+ const entries = [];
127
+ for (const key of Object.keys(shape)) {
128
+ const node = shape[key];
129
+ if (!node) {
130
+ return void 0;
131
+ }
132
+ const field = compileField(key, node, accessFor(key), context);
133
+ if (!field) {
134
+ return void 0;
135
+ }
136
+ pre += field.pre;
137
+ entries.push(field.entry);
138
+ }
139
+ return { entries: entries.join(", "), pre };
140
+ };
141
+ const compileObject = (node, inExpr, context) => {
142
+ const shape = node.shape ?? {};
143
+ const fields = compileFields(shape, (key) => `${inExpr}[${JSON.stringify(key)}]`, context);
144
+ if (!fields) {
145
+ return void 0;
146
+ }
147
+ const id = context.next();
148
+ const object = `__obj${String(id)}`;
149
+ const pre = `if (typeof ${inExpr} !== "object" || ${inExpr} === null || Array.isArray(${inExpr})) return DEFER;
150
+ ${fields.pre}const ${object} = { ${fields.entries} };
151
+ `;
152
+ return { out: object, pre };
153
+ };
154
+ const compileArgsValidator = (args) => {
155
+ let counter = 0;
156
+ const context = {
157
+ next: () => {
158
+ counter += 1;
159
+ return counter;
160
+ }
161
+ };
162
+ const fields = compileFields(args, (key) => `source[${JSON.stringify(key)}]`, context);
163
+ if (!fields) {
164
+ return void 0;
165
+ }
166
+ return `(source) => {
167
+ if (typeof source !== "object" || source === null || Array.isArray(source)) return DEFER;
168
+ ${fields.pre}return { ${fields.entries} };
169
+ }`;
170
+ };
171
+
3
172
  const GENERATED_HEADER = "// GENERATED by @lunora/codegen — do not edit.\n// Run `lunora codegen` to regenerate.\n\n";
4
173
  const baseSpecifiers = (useUmbrella = false) => useUmbrella ? {
5
174
  client: "lunorash/client",
@@ -7,17 +176,18 @@ const baseSpecifiers = (useUmbrella = false) => useUmbrella ? {
7
176
  server: "lunorash/server",
8
177
  serverDataModel: "lunorash/server/data-model",
9
178
  serverDrizzle: "lunorash/server/drizzle",
10
- serverTypes: "lunorash/server/types"
179
+ serverTypes: "lunorash/server/types",
180
+ values: "lunorash/values"
11
181
  } : {
12
182
  client: "@lunora/client",
13
183
  do: "@lunora/do",
14
184
  server: "@lunora/server",
15
185
  serverDataModel: "@lunora/server/data-model",
16
186
  serverDrizzle: "@lunora/server/drizzle",
17
- serverTypes: "@lunora/server/types"
187
+ serverTypes: "@lunora/server/types",
188
+ values: "@lunora/values"
18
189
  };
19
190
  const IDENTIFIER_RE = /^[A-Za-z_$][\w$]*$/u;
20
- const LITERAL_VALUE_RE = /^(?:"[^"\\]*"|'[^'\\]*'|-?\d+(?:\.\d+)?|true|false|null)$/u;
21
191
  const IMPORT_PATH_RE = /^[\w./-]+$/u;
22
192
  const assertIdentifier = (value, context) => {
23
193
  if (!IDENTIFIER_RE.test(value)) {
@@ -469,6 +639,17 @@ const renderFunctionRegistry = (functions, migrations) => {
469
639
  const migrationEntries = migrations.map(
470
640
  (migration) => ` ${JSON.stringify(migration.id)}: ${aliasByPath.get(migration.filePath) ?? ""}.${migration.exportName} as unknown as RegisteredDataMigration,`
471
641
  ).join("\n");
642
+ const installLines = functions.map((definition) => {
643
+ if (definition.lifecycle || Object.keys(definition.args).length === 0) {
644
+ return void 0;
645
+ }
646
+ const compiled = compileArgsValidator(definition.args);
647
+ if (compiled === void 0) {
648
+ return void 0;
649
+ }
650
+ const alias = aliasByPath.get(definition.filePath) ?? "";
651
+ return `installCompiledValidatorMap(${alias}.${definition.exportName}.args, ${compiled});`;
652
+ }).filter((line) => line !== void 0).join("\n");
472
653
  return {
473
654
  dispatchBody: dispatchEntries.length > 0 ? `
474
655
  ${dispatchEntries}
@@ -476,6 +657,7 @@ ${dispatchEntries}
476
657
  importBlock: importLines.length > 0 ? `${importLines}
477
658
 
478
659
  ` : "",
660
+ installBlock: installLines,
479
661
  migrationBody: migrationEntries.length > 0 ? `
480
662
  ${migrationEntries}
481
663
  ` : ""
@@ -777,10 +959,21 @@ const renderLifecycleManifest = (functions) => {
777
959
  }
778
960
  return manifest;
779
961
  };
780
- const emitFunctions = (functions, migrations = []) => {
962
+ const emitFunctions = (functions, migrations = [], useUmbrella = false) => {
781
963
  const hasFunctions = functions.length > 0;
782
- const { dispatchBody, importBlock, migrationBody } = renderFunctionRegistry(functions, migrations);
964
+ const base = baseSpecifiers(useUmbrella);
965
+ const { dispatchBody, importBlock, installBlock, migrationBody } = renderFunctionRegistry(functions, migrations);
783
966
  const lifecycleHooks = renderLifecycleManifest(functions);
967
+ const compiledArgsImport = installBlock.length > 0 ? `import { DEFER_VALIDATION as DEFER, installCompiledValidatorMap } from "${base.values}";
968
+ ` : "";
969
+ const compiledArgsInstall = installBlock.length > 0 ? `
970
+ /**
971
+ * AOT-compiled argument validators (Worker-safe, no \`eval\`). Each is installed
972
+ * onto its function's live \`.args\` object and consulted by the interpreted
973
+ * parser as a zero-allocation fast path; anything it can't model is deferred.
974
+ */
975
+ ${installBlock}
976
+ ` : "";
784
977
  const caller = renderCaller(functions);
785
978
  const callerTypes = caller.types ? `
786
979
  ${caller.types}
@@ -795,7 +988,7 @@ ${caller.implementation}
795
988
  const callRegisteredHelper = hasFunctions ? `${CALL_REGISTERED_HELPER}
796
989
 
797
990
  ` : "";
798
- return `${GENERATED_HEADER}${importBlock}import type { ActionCtx, MutationCtx, QueryCtx } from "./server.js";
991
+ return `${GENERATED_HEADER}${importBlock}${compiledArgsImport}import type { ActionCtx, MutationCtx, QueryCtx } from "./server.js";
799
992
  ${dataModelImport}
800
993
  /**
801
994
  * Single registered function, narrowed to the shape \`handleRpc\` needs.
@@ -821,7 +1014,7 @@ export interface RegisteredLunoraFunction {
821
1014
  * emits (\`api[namespace][fn].__lunoraRef === "namespace:fn"\`).
822
1015
  */
823
1016
  export const LUNORA_FUNCTIONS: Record<string, RegisteredLunoraFunction> = {${dispatchBody}};
824
-
1017
+ ${compiledArgsInstall}
825
1018
  /**
826
1019
  * Connection-lifecycle manifest: the function paths the generated ShardDO
827
1020
  * dispatches when a client's WebSocket connects (\`connect\`) or disconnects
@@ -1,4 +1,4 @@
1
- import { GENERATED_HEADER } from './GENERATED_HEADER-DF1hQcix.mjs';
1
+ import { GENERATED_HEADER } from './GENERATED_HEADER-LceqJ_qp.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
 
@@ -1,27 +1,28 @@
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
5
  import { lintSchema } from './formatAdvisories-8NIv1k0I.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-pLBFsSAV.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 discoverR2sqlCalls from './discoverR2sqlCalls-BpDqvcUn.mjs';
16
- import discoverRlsProcedures, { discoverRlsMetadata } from './discoverRlsMetadata-DpRB1HMe.mjs';
17
- import discoverSchema from './discoverSchema-DYl0QfDH.mjs';
18
- import discoverStorageRulesMetadata from './discoverStorageRulesMetadata-DAqJUxUv.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-BF6L4mrU.mjs';
19
+ import discoverStorageRulesMetadata from './discoverStorageRulesMetadata-Da8BKXcI.mjs';
19
20
  import { e as enclosingExportName$1 } from './discover-ast-CT6BgBr4.mjs';
20
- import { discoverWorkflows } from './WORKFLOWS_FILENAME-DRDQdhfq.mjs';
21
- import { buildStorageColumns, emitDataModel, emitApi, emitServer, emitFunctions, emitShard, emitContainers, emitWorkflows, emitCrons, emitVectors, emitDrizzleSchema, emitSeed, emitWranglerCronTriggers } from './GENERATED_HEADER-DF1hQcix.mjs';
22
- import { emitApp } from './emitApp-Ci_hcJNO.mjs';
23
- import { buildOpenApiDocument, emitOpenApiModule } from './buildOpenApiDocument-Cns1EkCQ.mjs';
24
- import { buildOpenRpcDocument, emitOpenRpcModule } from './OPENRPC_VERSION-C7zKVeKi.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-LceqJ_qp.mjs';
23
+ import { emitApp } from './emitApp-CDg72pVv.mjs';
24
+ import { buildOpenApiDocument, emitOpenApiModule } from './buildOpenApiDocument-DSPTssJI.mjs';
25
+ import { buildOpenRpcDocument, emitOpenRpcModule } from './OPENRPC_VERSION-s7lhywUr.mjs';
25
26
  import { buildSchemaSnapshot, serializeSchemaSnapshot } from './SCHEMA_SNAPSHOT_VERSION-DzLDbWk3.mjs';
26
27
 
27
28
  const HTTP_VERBS = /* @__PURE__ */ new Set(["delete", "get", "head", "options", "patch", "post", "put"]);
@@ -676,6 +677,10 @@ const readProjectVersion = (projectRoot) => {
676
677
  return void 0;
677
678
  }
678
679
  };
680
+ const isTimingEnabled = () => {
681
+ const flag = process.env["LUNORA_CODEGEN_TIMING"];
682
+ return flag !== void 0 && flag !== "";
683
+ };
679
684
  const findTsconfig = (startPath) => {
680
685
  let directory = existsSync(startPath) ? startPath : dirname(startPath);
681
686
  while (directory && directory !== dirname(directory)) {
@@ -717,6 +722,8 @@ const refreshCodegenProject = (project, lunoraDirectory) => {
717
722
  }
718
723
  };
719
724
  const runCodegen = (options) => {
725
+ const timingEnabled = isTimingEnabled();
726
+ const startedAt = timingEnabled ? performance.now() : 0;
720
727
  const lunoraDirectory = join(options.projectRoot, options.lunoraDirectory ?? "lunora");
721
728
  const schemaPath = join(lunoraDirectory, "schema.ts");
722
729
  if (!existsSync(schemaPath)) {
@@ -771,6 +778,7 @@ const runCodegen = (options) => {
771
778
  workflowCount: workflows.length
772
779
  });
773
780
  const useUmbrella = dependencies.has("lunorash");
781
+ const emitStartedAt = timingEnabled ? performance.now() : 0;
774
782
  const dataModelContent = emitDataModel(schema, useUmbrella);
775
783
  const apiContent = emitApi(functions, workflows, useUmbrella);
776
784
  const serverContent = emitServer({
@@ -789,7 +797,7 @@ const runCodegen = (options) => {
789
797
  useUmbrella,
790
798
  workflows
791
799
  });
792
- const functionsContent = emitFunctions(functions, migrations);
800
+ const functionsContent = emitFunctions(functions, migrations, useUmbrella);
793
801
  const shardContent = emitShard({
794
802
  advisories,
795
803
  containers,
@@ -889,6 +897,13 @@ const runCodegen = (options) => {
889
897
  writeIfChanged(schemaSnapshotPath, serializeSchemaSnapshot(schemaSnapshot));
890
898
  }
891
899
  }
900
+ if (timingEnabled) {
901
+ const finishedAt = performance.now();
902
+ const total = Math.round(finishedAt - startedAt);
903
+ const discovery = Math.round(emitStartedAt - startedAt);
904
+ const emit = Math.round(finishedAt - emitStartedAt);
905
+ console.error(`@lunora/codegen: codegen took ${total.toString()}ms (discovery ${discovery.toString()}ms, emit ${emit.toString()}ms)`);
906
+ }
892
907
  return {
893
908
  advisories,
894
909
  containers,
@@ -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 './GENERATED_HEADER-DF1hQcix.mjs';
1
+ import { GENERATED_HEADER } from './GENERATED_HEADER-LceqJ_qp.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();