@lunora/codegen 1.0.0-alpha.4 → 1.0.0-alpha.40
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.
- package/LICENSE.md +6 -0
- package/__assets__/package-og.svg +1 -1
- package/dist/index.d.mts +1175 -25
- package/dist/index.d.ts +1175 -25
- package/dist/index.mjs +30 -24
- package/dist/packem_shared/{CONTAINERS_FILENAME-0K-pjNb8.mjs → CONTAINERS_FILENAME-DjpXMqhp.mjs} +1 -1
- package/dist/packem_shared/{CodegenDiagnosticError-54jWDxA9.mjs → CodegenDiagnosticError-DyQ5FwkM.mjs} +7 -5
- package/dist/packem_shared/FLAGS_FILENAME-fEZtzWXi.mjs +139 -0
- package/dist/packem_shared/GENERATED_HEADER-cUyu5VOU.mjs +3 -0
- package/dist/packem_shared/{LUNORA_ERROR_CODES-CySpQPD3.mjs → LUNORA_ERROR_CODES-DvTLozCu.mjs} +8 -1
- package/dist/packem_shared/MUTATORS_FILENAME-BZOfUhlY.mjs +81 -0
- package/dist/packem_shared/{OPENRPC_VERSION-C7zKVeKi.mjs → OPENRPC_VERSION-DxbqafcJ.mjs} +2 -2
- package/dist/packem_shared/QUEUES_FILENAME-Xv6Vy-Ku.mjs +145 -0
- package/dist/packem_shared/SCHEMA_SNAPSHOT_FILENAME-C1yHg38Z.mjs +3438 -0
- package/dist/packem_shared/{SCHEMA_SNAPSHOT_VERSION-DzLDbWk3.mjs → SCHEMA_SNAPSHOT_VERSION-D0ARY6rL.mjs} +18 -2
- package/dist/packem_shared/SHAPES_FILENAME-DOhPGi-6.mjs +94 -0
- package/dist/packem_shared/{WORKFLOWS_FILENAME-DRDQdhfq.mjs → WORKFLOWS_FILENAME-B2By8S4s.mjs} +70 -2
- package/dist/packem_shared/{buildOpenApiDocument-Cns1EkCQ.mjs → buildOpenApiDocument-_mwWOc2N.mjs} +2 -2
- package/dist/packem_shared/{discoverAuthApiCalls-C35R6z0T.mjs → discoverAuthApiCalls-WMx8L2FG.mjs} +1 -1
- package/dist/packem_shared/{discoverCrons-BL6iGuJ3.mjs → discoverCrons-D-jrpm97.mjs} +22 -20
- package/dist/packem_shared/{discoverFunctions-DEgAcRuD.mjs → discoverFunctions-CZ91aenA.mjs} +72 -14
- package/dist/packem_shared/{discoverHttpRoutes-C978pBiG.mjs → discoverHttpRoutes-DNZLDCmm.mjs} +2 -2
- package/dist/packem_shared/{discoverInserts-CRQdXvHO.mjs → discoverInserts-DFRJhmCv.mjs} +24 -2
- package/dist/packem_shared/{discoverMaskProcedures-B64zA740.mjs → discoverMaskProcedures-B5iFnZ-X.mjs} +58 -2
- package/dist/packem_shared/{discoverMigrations-Doj_-BAA.mjs → discoverMigrations-CdKtMlBc.mjs} +10 -4
- package/dist/packem_shared/{discoverNondeterministicCalls-4KiPQxQU.mjs → discoverNondeterministicCalls-C_PrQPkA.mjs} +1 -1
- package/dist/packem_shared/{discoverQueries-BkIi0dBD.mjs → discoverQueries-B8W9UPrt.mjs} +1 -1
- package/dist/packem_shared/{discoverR2sqlCalls-BpDqvcUn.mjs → discoverR2sqlCalls-Bm7Rfnf4.mjs} +1 -1
- package/dist/packem_shared/{discoverRlsMetadata-DpRB1HMe.mjs → discoverRlsMetadata-B4GL4ZKm.mjs} +1 -1
- package/dist/packem_shared/{discoverSchema-BBulgGbH.mjs → discoverSchema-DFWVgU5o.mjs} +349 -17
- package/dist/packem_shared/{discoverStorageRulesMetadata-DAqJUxUv.mjs → discoverStorageRulesMetadata-nosjYcvN.mjs} +1 -1
- package/dist/packem_shared/{GENERATED_HEADER-DF1hQcix.mjs → emit-CFz-9dcD.mjs} +1100 -122
- package/dist/packem_shared/{emitApp-Ci_hcJNO.mjs → emitApp-D8rVuM__.mjs} +99 -30
- package/dist/packem_shared/formatAdvisories-DRhEiJUz.mjs +114 -0
- package/dist/packem_shared/{parse-validator-tuQtHrsr.mjs → parse-validator-BSJo1HGP.mjs} +18 -6
- package/dist/packem_shared/redact-oTmsol5A.mjs +33 -0
- package/package.json +9 -7
- package/dist/packem_shared/SCHEMA_SNAPSHOT_FILENAME-C5UlpWKF.mjs +0 -922
- package/dist/packem_shared/formatAdvisories-8NIv1k0I.mjs +0 -69
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { LunoraError } from '@lunora/errors';
|
|
2
|
+
|
|
1
3
|
const SCHEMA_SNAPSHOT_VERSION = 1;
|
|
2
4
|
const encodeShardMode = (mode) => {
|
|
3
5
|
if (mode === "global" || mode === "root") {
|
|
@@ -39,6 +41,7 @@ const buildSchemaSnapshot = (schema, migrationIds) => {
|
|
|
39
41
|
tables[table.name] = tableSnapshotOf(table);
|
|
40
42
|
}
|
|
41
43
|
return {
|
|
44
|
+
jurisdiction: schema.jurisdiction,
|
|
42
45
|
migrationIds: [...migrationIds].toSorted((a, b) => a.localeCompare(b)),
|
|
43
46
|
tables: sortKeys(tables),
|
|
44
47
|
version: SCHEMA_SNAPSHOT_VERSION
|
|
@@ -46,8 +49,10 @@ const buildSchemaSnapshot = (schema, migrationIds) => {
|
|
|
46
49
|
};
|
|
47
50
|
const serializeSchemaSnapshot = (snapshot) => `${JSON.stringify(snapshot, void 0, 2)}
|
|
48
51
|
`;
|
|
49
|
-
class SchemaSnapshotParseError extends
|
|
50
|
-
|
|
52
|
+
class SchemaSnapshotParseError extends LunoraError {
|
|
53
|
+
constructor(message) {
|
|
54
|
+
super("SCHEMA_SNAPSHOT_PARSE", message, { name: "SchemaSnapshotParseError" });
|
|
55
|
+
}
|
|
51
56
|
}
|
|
52
57
|
const isRecord = (value) => typeof value === "object" && value !== null;
|
|
53
58
|
const isValidTableSnapshot = (value) => isRecord(value) && isRecord(value.fields) && isRecord(value.indexes) && isRecord(value.relations) && typeof value.shardMode === "string";
|
|
@@ -69,7 +74,9 @@ const parseSchemaSnapshot = (content) => {
|
|
|
69
74
|
throw new SchemaSnapshotParseError(`baseline table "${name}" has an invalid structure`);
|
|
70
75
|
}
|
|
71
76
|
}
|
|
77
|
+
const jurisdiction = typeof parsed.jurisdiction === "string" ? parsed.jurisdiction : void 0;
|
|
72
78
|
return {
|
|
79
|
+
jurisdiction,
|
|
73
80
|
migrationIds: Array.isArray(parsed.migrationIds) ? parsed.migrationIds : [],
|
|
74
81
|
tables: parsed.tables,
|
|
75
82
|
version: SCHEMA_SNAPSHOT_VERSION
|
|
@@ -189,6 +196,15 @@ const diffSchemaSnapshots = (baseline, current) => {
|
|
|
189
196
|
});
|
|
190
197
|
}
|
|
191
198
|
}
|
|
199
|
+
if (baseline !== void 0 && baseline.jurisdiction !== current.jurisdiction) {
|
|
200
|
+
const from = baseline.jurisdiction ?? "(none)";
|
|
201
|
+
const to = current.jurisdiction ?? "(none)";
|
|
202
|
+
changes.push({
|
|
203
|
+
severity: "breaking",
|
|
204
|
+
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.`,
|
|
205
|
+
type: "changedJurisdiction"
|
|
206
|
+
});
|
|
207
|
+
}
|
|
192
208
|
return { changes };
|
|
193
209
|
};
|
|
194
210
|
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-DyQ5FwkM.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 };
|
package/dist/packem_shared/{WORKFLOWS_FILENAME-DRDQdhfq.mjs → WORKFLOWS_FILENAME-B2By8S4s.mjs}
RENAMED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
+
import { LunoraError } from '@lunora/errors';
|
|
3
4
|
import { workflowDefaultName, workflowClassName, workflowBindingName } from '@lunora/workflow';
|
|
4
5
|
import { SyntaxKind, Node } from 'ts-morph';
|
|
5
|
-
import { diagnosticAt } from './CodegenDiagnosticError-
|
|
6
|
+
import { diagnosticAt } from './CodegenDiagnosticError-DyQ5FwkM.mjs';
|
|
6
7
|
|
|
7
8
|
const WORKFLOWS_FILENAME = "workflows.ts";
|
|
9
|
+
const STEP_METHODS = /* @__PURE__ */ new Set(["do", "sleep", "sleepUntil", "waitForEvent"]);
|
|
8
10
|
const isDefineWorkflow = (identifier) => {
|
|
9
11
|
const symbol = identifier.getSymbol();
|
|
10
12
|
if (!symbol) {
|
|
@@ -30,6 +32,44 @@ const stringProperty = (expression, exportName, property) => {
|
|
|
30
32
|
`workflow "${exportName}": \`${property}\` must be a static string literal — it is deploy configuration codegen writes into wrangler.jsonc`
|
|
31
33
|
);
|
|
32
34
|
};
|
|
35
|
+
const isStepCall = (call) => {
|
|
36
|
+
const callee = call.getExpression();
|
|
37
|
+
if (!Node.isPropertyAccessExpression(callee) || !STEP_METHODS.has(callee.getName())) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
const receiver = callee.getExpression();
|
|
41
|
+
if (Node.isPropertyAccessExpression(receiver) && receiver.getName() === "step") {
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
return Node.isIdentifier(receiver) && receiver.getText() === "step";
|
|
45
|
+
};
|
|
46
|
+
const stepsFromHandler = (argument) => {
|
|
47
|
+
const handlerProperty = argument.getProperty("handler");
|
|
48
|
+
if (!handlerProperty) {
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
const body = Node.isPropertyAssignment(handlerProperty) ? handlerProperty.getInitializer() : handlerProperty;
|
|
52
|
+
if (!body) {
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
const steps = [];
|
|
56
|
+
for (const call of body.getDescendantsOfKind(SyntaxKind.CallExpression)) {
|
|
57
|
+
if (!isStepCall(call)) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
const nameArgument = call.getArguments()[0];
|
|
61
|
+
if (!nameArgument || !(Node.isStringLiteral(nameArgument) || Node.isNoSubstitutionTemplateLiteral(nameArgument))) {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
steps.push({
|
|
65
|
+
line: call.getStartLineNumber(),
|
|
66
|
+
// `isStepCall` already proved the callee is a `.<method>` property access.
|
|
67
|
+
method: call.getExpression().getName(),
|
|
68
|
+
name: nameArgument.getLiteralValue()
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
return steps;
|
|
72
|
+
};
|
|
33
73
|
const workflowFromCall = (call, exportName) => {
|
|
34
74
|
const argument = call.getArguments()[0];
|
|
35
75
|
if (!argument || !Node.isObjectLiteralExpression(argument)) {
|
|
@@ -39,7 +79,8 @@ const workflowFromCall = (call, exportName) => {
|
|
|
39
79
|
bindingName: workflowBindingName(exportName),
|
|
40
80
|
className: workflowClassName(exportName),
|
|
41
81
|
exportName,
|
|
42
|
-
name: workflowDefaultName(exportName)
|
|
82
|
+
name: workflowDefaultName(exportName),
|
|
83
|
+
steps: stepsFromHandler(argument)
|
|
43
84
|
};
|
|
44
85
|
const nameProperty = argument.getProperty("name");
|
|
45
86
|
if (nameProperty && Node.isPropertyAssignment(nameProperty)) {
|
|
@@ -70,6 +111,32 @@ const workflowsFromSource = (source) => {
|
|
|
70
111
|
}
|
|
71
112
|
return workflows;
|
|
72
113
|
};
|
|
114
|
+
const assertUniqueNames = (workflows) => {
|
|
115
|
+
const seenNames = /* @__PURE__ */ new Map();
|
|
116
|
+
const seenBindings = /* @__PURE__ */ new Map();
|
|
117
|
+
for (const workflow of workflows) {
|
|
118
|
+
const priorName = seenNames.get(workflow.name);
|
|
119
|
+
if (priorName !== void 0) {
|
|
120
|
+
throw new LunoraError(
|
|
121
|
+
// eslint-disable-next-line no-secrets/no-secrets -- an error code, not a secret
|
|
122
|
+
"DUPLICATE_WORKFLOW_NAME",
|
|
123
|
+
`Duplicate workflow name "${workflow.name}": produced by both "${priorName}" and "${workflow.exportName}". Deployed workflow names must be unique across the project.`,
|
|
124
|
+
{ status: 500 }
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
seenNames.set(workflow.name, workflow.exportName);
|
|
128
|
+
const priorBinding = seenBindings.get(workflow.bindingName);
|
|
129
|
+
if (priorBinding !== void 0) {
|
|
130
|
+
throw new LunoraError(
|
|
131
|
+
// eslint-disable-next-line no-secrets/no-secrets -- an error code, not a secret
|
|
132
|
+
"DUPLICATE_WORKFLOW_BINDING",
|
|
133
|
+
`Duplicate workflow binding "${workflow.bindingName}": produced by both "${priorBinding}" and "${workflow.exportName}". Workflow export names must yield unique binding names.`,
|
|
134
|
+
{ status: 500 }
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
seenBindings.set(workflow.bindingName, workflow.exportName);
|
|
138
|
+
}
|
|
139
|
+
};
|
|
73
140
|
const discoverWorkflows = (project, lunoraDirectory) => {
|
|
74
141
|
const workflowsPath = join(lunoraDirectory, WORKFLOWS_FILENAME);
|
|
75
142
|
if (!existsSync(workflowsPath)) {
|
|
@@ -78,6 +145,7 @@ const discoverWorkflows = (project, lunoraDirectory) => {
|
|
|
78
145
|
const source = project.getSourceFile(workflowsPath) ?? project.addSourceFileAtPath(workflowsPath);
|
|
79
146
|
const workflows = workflowsFromSource(source);
|
|
80
147
|
workflows.sort((a, b) => a.exportName.localeCompare(b.exportName));
|
|
148
|
+
assertUniqueNames(workflows);
|
|
81
149
|
return workflows;
|
|
82
150
|
};
|
|
83
151
|
|
package/dist/packem_shared/{buildOpenApiDocument-Cns1EkCQ.mjs → buildOpenApiDocument-_mwWOc2N.mjs}
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { GENERATED_HEADER } from './
|
|
1
|
+
import { G as GENERATED_HEADER } from './emit-CFz-9dcD.mjs';
|
|
2
2
|
import { s as sanitizeNamespace } from './paths-BRd6JHuF.mjs';
|
|
3
|
-
import { LUNORA_ERROR_CODES, objectSchema, argsObjectSchema, validatorIrToJsonSchema } from './LUNORA_ERROR_CODES-
|
|
3
|
+
import { LUNORA_ERROR_CODES, objectSchema, argsObjectSchema, validatorIrToJsonSchema } from './LUNORA_ERROR_CODES-DvTLozCu.mjs';
|
|
4
4
|
|
|
5
5
|
const ERROR_COMPONENT_REF = "#/components/responses/LunoraError";
|
|
6
6
|
const ROUTE_PARAM_RE = /:([A-Za-z_$][\w$]*)/gu;
|
package/dist/packem_shared/{discoverAuthApiCalls-C35R6z0T.mjs → discoverAuthApiCalls-WMx8L2FG.mjs}
RENAMED
|
@@ -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-
|
|
4
|
+
import { listLunoraSourceFiles } from './discoverFunctions-CZ91aenA.mjs';
|
|
5
5
|
|
|
6
6
|
const isAuthApiCall = (call) => {
|
|
7
7
|
const callee = call.getExpression();
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { LunoraError } from '@lunora/errors';
|
|
1
2
|
import { isValidCronExpression, compileCronSchedule, CRON_SCHEDULE_KINDS } from '@lunora/scheduler';
|
|
2
3
|
import { SyntaxKind, Node } from 'ts-morph';
|
|
3
|
-
import {
|
|
4
|
+
import { diagnosticAt } from './CodegenDiagnosticError-DyQ5FwkM.mjs';
|
|
5
|
+
import { listLunoraSourceFiles } from './discoverFunctions-CZ91aenA.mjs';
|
|
4
6
|
import { s as sanitizeNamespace } from './paths-BRd6JHuF.mjs';
|
|
5
7
|
|
|
6
8
|
const CRON_METHODS = /* @__PURE__ */ new Set([...CRON_SCHEDULE_KINDS, "cron"]);
|
|
@@ -71,7 +73,7 @@ const literalValue = (node, jobName) => {
|
|
|
71
73
|
if (Node.isArrayLiteralExpression(node)) {
|
|
72
74
|
return node.getElements().map((element) => literalValue(element, jobName));
|
|
73
75
|
}
|
|
74
|
-
throw
|
|
76
|
+
throw diagnosticAt(node, `Cron job "${jobName}" passes a non-static value where a literal is required; codegen can only read literals.`, {
|
|
75
77
|
code: "CRON_NON_STATIC_VALUE",
|
|
76
78
|
name: "LunoraError",
|
|
77
79
|
status: 500
|
|
@@ -81,8 +83,9 @@ const objectLiteralValue = (object, jobName) => {
|
|
|
81
83
|
const result = {};
|
|
82
84
|
for (const property of object.getProperties()) {
|
|
83
85
|
if (!Node.isPropertyAssignment(property)) {
|
|
84
|
-
throw
|
|
85
|
-
|
|
86
|
+
throw diagnosticAt(
|
|
87
|
+
property,
|
|
88
|
+
`Cron job "${jobName}" uses an unsupported object property (shorthand/spread/method) where a static literal is required.`,
|
|
86
89
|
{
|
|
87
90
|
code: "CRON_NON_STATIC_VALUE",
|
|
88
91
|
name: "LunoraError",
|
|
@@ -100,8 +103,9 @@ const objectLiteralValue = (object, jobName) => {
|
|
|
100
103
|
const functionPathFromArgument = (call, index, jobName) => {
|
|
101
104
|
const argument = call.getArguments()[index];
|
|
102
105
|
if (!argument || !Node.isPropertyAccessExpression(argument)) {
|
|
103
|
-
throw
|
|
104
|
-
|
|
106
|
+
throw diagnosticAt(
|
|
107
|
+
call,
|
|
108
|
+
`Cron job "${jobName}" must reference a function statically (e.g. internal.email.digest); codegen cannot resolve a dynamic reference.`,
|
|
105
109
|
{
|
|
106
110
|
code: "CRON_NON_STATIC_FN",
|
|
107
111
|
name: "LunoraError",
|
|
@@ -112,7 +116,7 @@ const functionPathFromArgument = (call, index, jobName) => {
|
|
|
112
116
|
const functionName = argument.getName();
|
|
113
117
|
const receiver = argument.getExpression();
|
|
114
118
|
if (!Node.isPropertyAccessExpression(receiver)) {
|
|
115
|
-
throw
|
|
119
|
+
throw diagnosticAt(argument, `Cron job "${jobName}" function reference must be of the form internal.file.fn (two property accesses).`, {
|
|
116
120
|
code: "CRON_NON_STATIC_FN",
|
|
117
121
|
name: "LunoraError",
|
|
118
122
|
status: 500
|
|
@@ -133,8 +137,9 @@ const resolveTarget = (call, index, jobName, workflowsByName) => {
|
|
|
133
137
|
if (workflow) {
|
|
134
138
|
return workflowTarget(workflow);
|
|
135
139
|
}
|
|
136
|
-
throw
|
|
137
|
-
|
|
140
|
+
throw diagnosticAt(
|
|
141
|
+
argument,
|
|
142
|
+
`Cron job "${jobName}" targets workflows.${argument.getName()}, but no such workflow is declared in lunora/workflows.ts.`,
|
|
138
143
|
{
|
|
139
144
|
code: "CRON_NON_STATIC_FN",
|
|
140
145
|
name: "LunoraError",
|
|
@@ -149,10 +154,9 @@ const resolveTarget = (call, index, jobName, workflowsByName) => {
|
|
|
149
154
|
if (workflow) {
|
|
150
155
|
return workflowTarget(workflow);
|
|
151
156
|
}
|
|
152
|
-
throw
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
),
|
|
157
|
+
throw diagnosticAt(
|
|
158
|
+
argument,
|
|
159
|
+
`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
160
|
{
|
|
157
161
|
code: "CRON_NON_STATIC_FN",
|
|
158
162
|
name: "LunoraError",
|
|
@@ -172,7 +176,7 @@ const cronFromCall = (call, callee, builderNames, workflowsByName) => {
|
|
|
172
176
|
}
|
|
173
177
|
const name = stringArgument(call, 0);
|
|
174
178
|
if (name === void 0 || name.trim() === "") {
|
|
175
|
-
throw
|
|
179
|
+
throw diagnosticAt(call, `A cron ".${method}(...)" registration must pass a non-empty string-literal name as its first argument.`, {
|
|
176
180
|
code: "CRON_NAME_NOT_STATIC",
|
|
177
181
|
name: "LunoraError",
|
|
178
182
|
status: 500
|
|
@@ -182,14 +186,14 @@ const cronFromCall = (call, callee, builderNames, workflowsByName) => {
|
|
|
182
186
|
if (method === "cron") {
|
|
183
187
|
const expression = stringArgument(call, 1);
|
|
184
188
|
if (expression === void 0) {
|
|
185
|
-
throw
|
|
189
|
+
throw diagnosticAt(call, `Cron job "${name}" must pass a string-literal cron expression to ".cron(...)".`, {
|
|
186
190
|
code: "CRON_EXPR_NOT_STATIC",
|
|
187
191
|
name: "LunoraError",
|
|
188
192
|
status: 500
|
|
189
193
|
});
|
|
190
194
|
}
|
|
191
195
|
if (!isValidCronExpression(expression)) {
|
|
192
|
-
throw
|
|
196
|
+
throw diagnosticAt(call, `Cron job "${name}" has an invalid cron expression "${expression}" — expected 5 or 6 space-separated fields.`, {
|
|
193
197
|
code: "CRON_EXPR_INVALID",
|
|
194
198
|
name: "LunoraError",
|
|
195
199
|
status: 500
|
|
@@ -199,7 +203,7 @@ const cronFromCall = (call, callee, builderNames, workflowsByName) => {
|
|
|
199
203
|
} else {
|
|
200
204
|
const scheduleArgument = call.getArguments()[1];
|
|
201
205
|
if (!scheduleArgument || !Node.isObjectLiteralExpression(scheduleArgument)) {
|
|
202
|
-
throw
|
|
206
|
+
throw diagnosticAt(call, `Cron job "${name}" must pass an object-literal schedule to ".${method}(...)".`, {
|
|
203
207
|
code: "CRON_SCHEDULE_NOT_STATIC",
|
|
204
208
|
name: "LunoraError",
|
|
205
209
|
status: 500
|
|
@@ -216,9 +220,7 @@ const assertUniqueNames = (crons) => {
|
|
|
216
220
|
const seen = /* @__PURE__ */ new Set();
|
|
217
221
|
for (const cron of crons) {
|
|
218
222
|
if (seen.has(cron.name)) {
|
|
219
|
-
throw
|
|
220
|
-
code: "DUPLICATE_CRON_NAME",
|
|
221
|
-
name: "LunoraError",
|
|
223
|
+
throw new LunoraError("DUPLICATE_CRON_NAME", `Duplicate cron job name "${cron.name}": cron names must be unique across the project.`, {
|
|
222
224
|
status: 500
|
|
223
225
|
});
|
|
224
226
|
}
|
package/dist/packem_shared/{discoverFunctions-DEgAcRuD.mjs → discoverFunctions-CZ91aenA.mjs}
RENAMED
|
@@ -1,11 +1,12 @@
|
|
|
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-
|
|
4
|
+
import { a as parseObjectShape } from './parse-validator-BSJo1HGP.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"]);
|
|
8
|
-
const ANY_TOKEN_RE = /\bany\b/u;
|
|
8
|
+
const ANY_TOKEN_RE = /\bany\b(?!\s*(?:\?\s*)?:)/u;
|
|
9
|
+
const STRING_LITERAL_SPAN_RE = /"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|`(?:[^`\\]|\\.)*`/gu;
|
|
9
10
|
const IDENTIFIER_RE = /^[A-Za-z_$][\w$]*$/u;
|
|
10
11
|
const renderExpandedPropertyKey = (propertyName) => IDENTIFIER_RE.test(propertyName) ? propertyName : JSON.stringify(propertyName);
|
|
11
12
|
const TS_EXTENSION_RE = /\.ts$/u;
|
|
@@ -221,7 +222,7 @@ const unwrapHandlerReturn = (handler) => {
|
|
|
221
222
|
if (!rendered || rendered === "any" || rendered === "never") {
|
|
222
223
|
return "unknown";
|
|
223
224
|
}
|
|
224
|
-
if (ANY_TOKEN_RE.test(rendered)) {
|
|
225
|
+
if (ANY_TOKEN_RE.test(rendered.replaceAll(STRING_LITERAL_SPAN_RE, ""))) {
|
|
225
226
|
return "unknown";
|
|
226
227
|
}
|
|
227
228
|
const handlerFilePath = handler.getSourceFile().getFilePath();
|
|
@@ -290,24 +291,73 @@ const classifyProcedureCall = (call) => {
|
|
|
290
291
|
if (!Node.isIdentifier(callee)) {
|
|
291
292
|
return void 0;
|
|
292
293
|
}
|
|
293
|
-
const
|
|
294
|
-
if (!
|
|
294
|
+
const calleeName2 = resolveCalleeKind(callee);
|
|
295
|
+
if (!calleeName2) {
|
|
295
296
|
return void 0;
|
|
296
297
|
}
|
|
297
|
-
if (FUNCTION_KINDS.has(
|
|
298
|
-
return { kind:
|
|
298
|
+
if (FUNCTION_KINDS.has(calleeName2)) {
|
|
299
|
+
return { kind: calleeName2, visibility: "public" };
|
|
299
300
|
}
|
|
300
|
-
const internalKind = INTERNAL_FACTORIES[
|
|
301
|
+
const internalKind = INTERNAL_FACTORIES[calleeName2];
|
|
301
302
|
if (internalKind) {
|
|
302
303
|
return { kind: internalKind, visibility: "internal" };
|
|
303
304
|
}
|
|
304
|
-
const lifecycle = LIFECYCLE_FACTORIES[
|
|
305
|
+
const lifecycle = LIFECYCLE_FACTORIES[calleeName2];
|
|
305
306
|
if (lifecycle) {
|
|
306
307
|
return { kind: "mutation", lifecycle, visibility: "internal" };
|
|
307
308
|
}
|
|
308
309
|
return void 0;
|
|
309
310
|
};
|
|
310
|
-
const
|
|
311
|
+
const inlineHandler = (argument) => argument !== void 0 && (Node.isArrowFunction(argument) || Node.isFunctionExpression(argument)) ? argument : void 0;
|
|
312
|
+
const isDatabaseAccessor = (receiver) => Node.isPropertyAccessExpression(receiver) && receiver.getName() === "db" || Node.isIdentifier(receiver) && receiver.getText() === "db";
|
|
313
|
+
const procedureHandler = (initializer) => {
|
|
314
|
+
const argument = initializer.getArguments()[0];
|
|
315
|
+
const direct = inlineHandler(argument);
|
|
316
|
+
if (direct !== void 0) {
|
|
317
|
+
return direct;
|
|
318
|
+
}
|
|
319
|
+
if (argument === void 0 || !Node.isObjectLiteralExpression(argument)) {
|
|
320
|
+
return void 0;
|
|
321
|
+
}
|
|
322
|
+
const property = argument.getProperty("handler");
|
|
323
|
+
return property !== void 0 && Node.isPropertyAssignment(property) ? inlineHandler(property.getInitializer()) : void 0;
|
|
324
|
+
};
|
|
325
|
+
const calleeName = (callee) => {
|
|
326
|
+
if (Node.isIdentifier(callee)) {
|
|
327
|
+
return callee.getText();
|
|
328
|
+
}
|
|
329
|
+
return Node.isPropertyAccessExpression(callee) ? callee.getName() : "";
|
|
330
|
+
};
|
|
331
|
+
const chainHasStep = (receiver, method) => {
|
|
332
|
+
let node = receiver;
|
|
333
|
+
while (Node.isCallExpression(node)) {
|
|
334
|
+
const callee = node.getExpression();
|
|
335
|
+
if (!Node.isPropertyAccessExpression(callee)) {
|
|
336
|
+
break;
|
|
337
|
+
}
|
|
338
|
+
if (callee.getName() === method) {
|
|
339
|
+
return true;
|
|
340
|
+
}
|
|
341
|
+
node = callee.getExpression();
|
|
342
|
+
}
|
|
343
|
+
return false;
|
|
344
|
+
};
|
|
345
|
+
const chainUsesWrappedCall = (receiver, method, wrappedCallee) => {
|
|
346
|
+
let node = receiver;
|
|
347
|
+
while (Node.isCallExpression(node)) {
|
|
348
|
+
const callee = node.getExpression();
|
|
349
|
+
if (!Node.isPropertyAccessExpression(callee)) {
|
|
350
|
+
break;
|
|
351
|
+
}
|
|
352
|
+
const argument = node.getArguments()[0];
|
|
353
|
+
if (callee.getName() === method && argument !== void 0 && Node.isCallExpression(argument) && calleeName(argument.getExpression()) === wrappedCallee) {
|
|
354
|
+
return true;
|
|
355
|
+
}
|
|
356
|
+
node = callee.getExpression();
|
|
357
|
+
}
|
|
358
|
+
return false;
|
|
359
|
+
};
|
|
360
|
+
const listLunoraSourceFiles = (directory, accumulator = [], root = directory) => {
|
|
311
361
|
let entries;
|
|
312
362
|
try {
|
|
313
363
|
entries = readdirSync(directory);
|
|
@@ -321,8 +371,11 @@ const listLunoraSourceFiles = (directory, accumulator = []) => {
|
|
|
321
371
|
if (entry === "_generated" || entry === "node_modules") {
|
|
322
372
|
continue;
|
|
323
373
|
}
|
|
324
|
-
listLunoraSourceFiles(full, accumulator);
|
|
325
|
-
} else if (info.isFile() && extname(entry) === ".ts"
|
|
374
|
+
listLunoraSourceFiles(full, accumulator, root);
|
|
375
|
+
} else if (info.isFile() && extname(entry) === ".ts") {
|
|
376
|
+
if (entry === "schema.ts" && directory === root) {
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
326
379
|
accumulator.push(full);
|
|
327
380
|
}
|
|
328
381
|
}
|
|
@@ -344,7 +397,12 @@ const discoverFromCall = (call) => {
|
|
|
344
397
|
if (classified.lifecycle) {
|
|
345
398
|
return { args: {}, kind: classified.kind, lifecycle: classified.lifecycle, returnType: "void", visibility: classified.visibility };
|
|
346
399
|
}
|
|
347
|
-
return {
|
|
400
|
+
return {
|
|
401
|
+
args: argsFromCall(call),
|
|
402
|
+
kind: classified.kind,
|
|
403
|
+
returnType: returnTypeFromCall(call),
|
|
404
|
+
visibility: classified.visibility
|
|
405
|
+
};
|
|
348
406
|
};
|
|
349
407
|
const RE_EXPORT_RESOLVE_LIMIT = 8;
|
|
350
408
|
const resolveExpressionToCall = (node, depth = 0) => {
|
|
@@ -457,4 +515,4 @@ const discoverFunctions = (project, lunoraDirectory) => {
|
|
|
457
515
|
return functions;
|
|
458
516
|
};
|
|
459
517
|
|
|
460
|
-
export { classifyProcedureCall, discoverFunctions, listLunoraSourceFiles, lunoraRelativePath };
|
|
518
|
+
export { chainHasStep, chainUsesWrappedCall, classifyProcedureCall, discoverFunctions, inlineHandler, isDatabaseAccessor, listLunoraSourceFiles, lunoraRelativePath, procedureHandler };
|
package/dist/packem_shared/{discoverHttpRoutes-C978pBiG.mjs → discoverHttpRoutes-DNZLDCmm.mjs}
RENAMED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { relative, sep } from 'node:path';
|
|
2
2
|
import { Node } from 'ts-morph';
|
|
3
|
-
import { listLunoraSourceFiles } from './discoverFunctions-
|
|
4
|
-
import { p as parseValidator, a as parseObjectShape } from './parse-validator-
|
|
3
|
+
import { listLunoraSourceFiles } from './discoverFunctions-CZ91aenA.mjs';
|
|
4
|
+
import { p as parseValidator, a as parseObjectShape } from './parse-validator-BSJo1HGP.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-
|
|
3
|
+
import { listLunoraSourceFiles, lunoraRelativePath } from './discoverFunctions-CZ91aenA.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
|
-
|
|
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-
|
|
2
|
+
import { listLunoraSourceFiles, lunoraRelativePath, classifyProcedureCall } from './discoverFunctions-CZ91aenA.mjs';
|
|
3
3
|
|
|
4
4
|
const isMaskCall = (node) => {
|
|
5
5
|
if (!Node.isCallExpression(node)) {
|
|
@@ -213,5 +213,61 @@ const discoverMaskMetadata = (project, lunoraDirectory) => {
|
|
|
213
213
|
}
|
|
214
214
|
return { columns: [...columnsByKey.values()] };
|
|
215
215
|
};
|
|
216
|
+
const extractMaskStrategyRows = (maskCall, exportName, relativePath) => {
|
|
217
|
+
const argument = maskCall.getArguments()[0];
|
|
218
|
+
if (!argument || !Node.isObjectLiteralExpression(argument)) {
|
|
219
|
+
return [];
|
|
220
|
+
}
|
|
221
|
+
const rows = [];
|
|
222
|
+
for (const tableProperty of argument.getProperties()) {
|
|
223
|
+
const table = memberName(tableProperty);
|
|
224
|
+
if (table === void 0 || !Node.isPropertyAssignment(tableProperty)) {
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
const initializer = tableProperty.getInitializer();
|
|
228
|
+
if (!initializer || !Node.isObjectLiteralExpression(initializer)) {
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
for (const columnProperty of initializer.getProperties()) {
|
|
232
|
+
const column = memberName(columnProperty);
|
|
233
|
+
if (column === void 0) {
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
const strategy = strategyOf(columnProperty);
|
|
237
|
+
if (strategy === "custom") {
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
rows.push({ column, exportName, file: relativePath, line: columnProperty.getStartLineNumber(), strategy, table });
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return rows;
|
|
244
|
+
};
|
|
245
|
+
const maskStrategyRowsFromDeclaration = (declaration, relativePath) => {
|
|
246
|
+
if (!Node.isVariableDeclaration(declaration)) {
|
|
247
|
+
return [];
|
|
248
|
+
}
|
|
249
|
+
const initializer = declaration.getInitializer();
|
|
250
|
+
const classified = initializer && Node.isCallExpression(initializer) ? classifyProcedureCall(initializer) : void 0;
|
|
251
|
+
if (!classified?.receiver) {
|
|
252
|
+
return [];
|
|
253
|
+
}
|
|
254
|
+
return maskCallsInChain(classified.receiver).flatMap((maskCall) => extractMaskStrategyRows(maskCall, declaration.getName(), relativePath));
|
|
255
|
+
};
|
|
256
|
+
const discoverMaskStrategies = (project, lunoraDirectory) => {
|
|
257
|
+
const rows = [];
|
|
258
|
+
for (const filePath of listLunoraSourceFiles(lunoraDirectory)) {
|
|
259
|
+
const sourceFile = project.getSourceFile(filePath) ?? project.addSourceFileAtPath(filePath);
|
|
260
|
+
const relativePath = lunoraRelativePath(lunoraDirectory, filePath);
|
|
261
|
+
for (const statement of sourceFile.getVariableStatements()) {
|
|
262
|
+
if (!statement.isExported()) {
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
for (const declaration of statement.getDeclarations()) {
|
|
266
|
+
rows.push(...maskStrategyRowsFromDeclaration(declaration, relativePath));
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return rows;
|
|
271
|
+
};
|
|
216
272
|
|
|
217
|
-
export { discoverMaskProcedures as default, discoverMaskMetadata };
|
|
273
|
+
export { discoverMaskProcedures as default, discoverMaskMetadata, discoverMaskStrategies };
|