@lunora/codegen 1.0.0-alpha.5 → 1.0.0-alpha.51
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 +1950 -734
- package/dist/index.d.ts +1950 -734
- package/dist/index.mjs +32 -24
- package/dist/packem_shared/AGENTS_FILENAME-Cxx7wiSs.mjs +158 -0
- 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-Dg4mKUuz.mjs +139 -0
- package/dist/packem_shared/GENERATED_HEADER-C5paWNYO.mjs +4 -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-7A2weq2u.mjs} +2 -2
- package/dist/packem_shared/QUEUES_FILENAME-Xv6Vy-Ku.mjs +145 -0
- package/dist/packem_shared/SCHEMA_SNAPSHOT_FILENAME-G5dkcxPR.mjs +3502 -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-Dtuzvluc.mjs +158 -0
- package/dist/packem_shared/{buildOpenApiDocument-Cns1EkCQ.mjs → buildOpenApiDocument-B7mEdzA2.mjs} +2 -2
- package/dist/packem_shared/{discoverAuthApiCalls-C35R6z0T.mjs → discoverAuthApiCalls-Dx3K42rk.mjs} +1 -1
- package/dist/packem_shared/{discoverCrons-BL6iGuJ3.mjs → discoverCrons-DvqkEWdx.mjs} +68 -36
- package/dist/packem_shared/{discoverFunctions-DEgAcRuD.mjs → discoverFunctions-BJ-qR7Rg.mjs} +72 -14
- package/dist/packem_shared/{discoverHttpRoutes-C978pBiG.mjs → discoverHttpRoutes-daCzuTe8.mjs} +10 -2
- package/dist/packem_shared/{discoverInserts-CRQdXvHO.mjs → discoverInserts-DI4q5NxE.mjs} +24 -2
- package/dist/packem_shared/{discoverMaskProcedures-B64zA740.mjs → discoverMaskProcedures-BcTOEKNU.mjs} +58 -2
- package/dist/packem_shared/{discoverMigrations-Doj_-BAA.mjs → discoverMigrations-VNUFvCwr.mjs} +10 -4
- package/dist/packem_shared/{discoverNondeterministicCalls-4KiPQxQU.mjs → discoverNondeterministicCalls-S0N2xLCq.mjs} +1 -1
- package/dist/packem_shared/{discoverQueries-BkIi0dBD.mjs → discoverQueries-CJnnnLpd.mjs} +1 -1
- package/dist/packem_shared/{discoverR2sqlCalls-BpDqvcUn.mjs → discoverR2sqlCalls-pnpicWfz.mjs} +1 -1
- package/dist/packem_shared/{discoverRlsMetadata-DpRB1HMe.mjs → discoverRlsMetadata-DppniPUH.mjs} +1 -1
- package/dist/packem_shared/discoverSandboxUsage-BUM2r90k.mjs +38 -0
- package/dist/packem_shared/{discoverSchema-DB_QrIQe.mjs → discoverSchema-BaSAvooG.mjs} +326 -14
- package/dist/packem_shared/{discoverStorageRulesMetadata-DAqJUxUv.mjs → discoverStorageRulesMetadata-CnHl2rXD.mjs} +1 -1
- package/dist/packem_shared/{GENERATED_HEADER-DF1hQcix.mjs → emit-ClTbCLy4.mjs} +1458 -154
- package/dist/packem_shared/{emitApp-Ci_hcJNO.mjs → emitApp-CzzrjVqH.mjs} +129 -31
- package/dist/packem_shared/formatAdvisories-BmF7Mplc.mjs +115 -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 +10 -7
- package/dist/packem_shared/SCHEMA_SNAPSHOT_FILENAME-BqOtU2lC.mjs +0 -922
- package/dist/packem_shared/WORKFLOWS_FILENAME-DRDQdhfq.mjs +0 -84
- 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 };
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { LunoraError } from '@lunora/errors';
|
|
4
|
+
import { workflowDefaultName, workflowClassName, workflowBindingName } from '@lunora/workflow';
|
|
5
|
+
import { Node, SyntaxKind } from 'ts-morph';
|
|
6
|
+
import { diagnosticAt } from './CodegenDiagnosticError-DyQ5FwkM.mjs';
|
|
7
|
+
|
|
8
|
+
const WORKFLOWS_FILENAME = "workflows.ts";
|
|
9
|
+
const STEP_METHODS = /* @__PURE__ */ new Set(["do", "sleep", "sleepUntil", "waitForEvent"]);
|
|
10
|
+
const isDefineWorkflow = (identifier) => {
|
|
11
|
+
const symbol = identifier.getSymbol();
|
|
12
|
+
if (!symbol) {
|
|
13
|
+
return identifier.getText() === "defineWorkflow";
|
|
14
|
+
}
|
|
15
|
+
for (const declaration of symbol.getDeclarations()) {
|
|
16
|
+
if (!Node.isImportSpecifier(declaration)) {
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
if (declaration.getImportDeclaration().getModuleSpecifierValue() !== "@lunora/workflow") {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
return declaration.getNameNode().getText() === "defineWorkflow";
|
|
23
|
+
}
|
|
24
|
+
return false;
|
|
25
|
+
};
|
|
26
|
+
const stringProperty = (expression, exportName, property) => {
|
|
27
|
+
if (Node.isStringLiteral(expression) || Node.isNoSubstitutionTemplateLiteral(expression)) {
|
|
28
|
+
return expression.getLiteralValue();
|
|
29
|
+
}
|
|
30
|
+
throw diagnosticAt(
|
|
31
|
+
expression,
|
|
32
|
+
`workflow "${exportName}": \`${property}\` must be a static string literal — it is deploy configuration codegen writes into wrangler.jsonc`
|
|
33
|
+
);
|
|
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
|
+
};
|
|
73
|
+
const workflowFromCall = (call, exportName) => {
|
|
74
|
+
const argument = call.getArguments()[0];
|
|
75
|
+
if (!argument || !Node.isObjectLiteralExpression(argument)) {
|
|
76
|
+
throw diagnosticAt(call, `workflow "${exportName}": defineWorkflow must be passed an inline object literal`);
|
|
77
|
+
}
|
|
78
|
+
const ir = {
|
|
79
|
+
bindingName: workflowBindingName(exportName),
|
|
80
|
+
className: workflowClassName(exportName),
|
|
81
|
+
exportName,
|
|
82
|
+
name: workflowDefaultName(exportName),
|
|
83
|
+
steps: stepsFromHandler(argument)
|
|
84
|
+
};
|
|
85
|
+
const nameProperty = argument.getProperty("name");
|
|
86
|
+
if (nameProperty && Node.isPropertyAssignment(nameProperty)) {
|
|
87
|
+
ir.name = stringProperty(nameProperty.getInitializerOrThrow(), exportName, "name");
|
|
88
|
+
}
|
|
89
|
+
return ir;
|
|
90
|
+
};
|
|
91
|
+
const unwrapToCallExpression = (node) => {
|
|
92
|
+
let current = node;
|
|
93
|
+
while (current && (Node.isAsExpression(current) || Node.isSatisfiesExpression(current) || Node.isParenthesizedExpression(current))) {
|
|
94
|
+
current = current.getExpression();
|
|
95
|
+
}
|
|
96
|
+
return current && Node.isCallExpression(current) ? current : void 0;
|
|
97
|
+
};
|
|
98
|
+
const workflowsFromSource = (source) => {
|
|
99
|
+
const workflows = [];
|
|
100
|
+
for (const declaration of source.getVariableDeclarations()) {
|
|
101
|
+
if (!declaration.isExported()) {
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
const call = unwrapToCallExpression(declaration.getInitializer());
|
|
105
|
+
if (!call) {
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
const callee = call.getExpression();
|
|
109
|
+
if (!Node.isIdentifier(callee) || !isDefineWorkflow(callee)) {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
const nameNode = declaration.getNameNode();
|
|
113
|
+
if (!Node.isIdentifier(nameNode)) {
|
|
114
|
+
throw diagnosticAt(nameNode, "defineWorkflow exports must be plain named exports (no destructuring)");
|
|
115
|
+
}
|
|
116
|
+
workflows.push(workflowFromCall(call, nameNode.getText()));
|
|
117
|
+
}
|
|
118
|
+
return workflows;
|
|
119
|
+
};
|
|
120
|
+
const assertUniqueNames = (workflows) => {
|
|
121
|
+
const seenNames = /* @__PURE__ */ new Map();
|
|
122
|
+
const seenBindings = /* @__PURE__ */ new Map();
|
|
123
|
+
for (const workflow of workflows) {
|
|
124
|
+
const priorName = seenNames.get(workflow.name);
|
|
125
|
+
if (priorName !== void 0) {
|
|
126
|
+
throw new LunoraError(
|
|
127
|
+
// eslint-disable-next-line no-secrets/no-secrets -- an error code, not a secret
|
|
128
|
+
"DUPLICATE_WORKFLOW_NAME",
|
|
129
|
+
`Duplicate workflow name "${workflow.name}": produced by both "${priorName}" and "${workflow.exportName}". Deployed workflow names must be unique across the project.`,
|
|
130
|
+
{ status: 500 }
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
seenNames.set(workflow.name, workflow.exportName);
|
|
134
|
+
const priorBinding = seenBindings.get(workflow.bindingName);
|
|
135
|
+
if (priorBinding !== void 0) {
|
|
136
|
+
throw new LunoraError(
|
|
137
|
+
// eslint-disable-next-line no-secrets/no-secrets -- an error code, not a secret
|
|
138
|
+
"DUPLICATE_WORKFLOW_BINDING",
|
|
139
|
+
`Duplicate workflow binding "${workflow.bindingName}": produced by both "${priorBinding}" and "${workflow.exportName}". Workflow export names must yield unique binding names.`,
|
|
140
|
+
{ status: 500 }
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
seenBindings.set(workflow.bindingName, workflow.exportName);
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
const discoverWorkflows = (project, lunoraDirectory) => {
|
|
147
|
+
const workflowsPath = join(lunoraDirectory, WORKFLOWS_FILENAME);
|
|
148
|
+
if (!existsSync(workflowsPath)) {
|
|
149
|
+
return [];
|
|
150
|
+
}
|
|
151
|
+
const source = project.getSourceFile(workflowsPath) ?? project.addSourceFileAtPath(workflowsPath);
|
|
152
|
+
const workflows = workflowsFromSource(source);
|
|
153
|
+
workflows.sort((a, b) => a.exportName.localeCompare(b.exportName));
|
|
154
|
+
assertUniqueNames(workflows);
|
|
155
|
+
return workflows;
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
export { WORKFLOWS_FILENAME, discoverWorkflows };
|
package/dist/packem_shared/{buildOpenApiDocument-Cns1EkCQ.mjs → buildOpenApiDocument-B7mEdzA2.mjs}
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { GENERATED_HEADER } from './
|
|
1
|
+
import { G as GENERATED_HEADER } from './emit-ClTbCLy4.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-Dx3K42rk.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-BJ-qR7Rg.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-BJ-qR7Rg.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"]);
|
|
@@ -44,7 +46,7 @@ const rootIdentifierOf = (node) => {
|
|
|
44
46
|
};
|
|
45
47
|
const stringArgument = (call, index) => {
|
|
46
48
|
const argument = call.getArguments()[index];
|
|
47
|
-
return argument && Node.isStringLiteral(argument) ? argument.getLiteralValue() : void 0;
|
|
49
|
+
return argument && (Node.isStringLiteral(argument) || Node.isNoSubstitutionTemplateLiteral(argument)) ? argument.getLiteralValue() : void 0;
|
|
48
50
|
};
|
|
49
51
|
const literalValue = (node, jobName) => {
|
|
50
52
|
if (Node.isStringLiteral(node) || Node.isNoSubstitutionTemplateLiteral(node)) {
|
|
@@ -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
|
|
@@ -124,17 +128,48 @@ const functionPathFromArgument = (call, index, jobName) => {
|
|
|
124
128
|
const workflowTarget = (workflow) => {
|
|
125
129
|
return { workflow: { binding: workflow.bindingName, exportName: workflow.exportName } };
|
|
126
130
|
};
|
|
127
|
-
const
|
|
131
|
+
const agentTarget = (agent) => {
|
|
132
|
+
return { workflow: { binding: agent.bindingName, exportName: agent.exportName } };
|
|
133
|
+
};
|
|
134
|
+
const resolveReferenceAccess = (argument, receiverName, byName, jobName, kind, build) => {
|
|
135
|
+
const receiver = argument.getExpression();
|
|
136
|
+
if (!Node.isIdentifier(receiver) || receiver.getText() !== receiverName) {
|
|
137
|
+
return void 0;
|
|
138
|
+
}
|
|
139
|
+
const definition = byName.get(argument.getName());
|
|
140
|
+
if (definition) {
|
|
141
|
+
return build(definition);
|
|
142
|
+
}
|
|
143
|
+
throw diagnosticAt(
|
|
144
|
+
argument,
|
|
145
|
+
`Cron job "${jobName}" targets ${receiverName}.${argument.getName()}, but no such ${kind} is declared in lunora/${kind}s.ts.`,
|
|
146
|
+
{
|
|
147
|
+
code: "CRON_NON_STATIC_FN",
|
|
148
|
+
name: "LunoraError",
|
|
149
|
+
status: 500
|
|
150
|
+
}
|
|
151
|
+
);
|
|
152
|
+
};
|
|
153
|
+
const resolveTarget = (call, index, jobName, workflowsByName, agentsByName) => {
|
|
128
154
|
const argument = call.getArguments()[index];
|
|
129
155
|
if (argument && Node.isPropertyAccessExpression(argument)) {
|
|
130
|
-
const
|
|
131
|
-
if (
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
156
|
+
const workflowReference = resolveReferenceAccess(argument, "workflows", workflowsByName, jobName, "workflow", workflowTarget);
|
|
157
|
+
if (workflowReference) {
|
|
158
|
+
return workflowReference;
|
|
159
|
+
}
|
|
160
|
+
const agentReference = resolveReferenceAccess(argument, "agents", agentsByName, jobName, "agent", agentTarget);
|
|
161
|
+
if (agentReference) {
|
|
162
|
+
return agentReference;
|
|
163
|
+
}
|
|
164
|
+
return { functionPath: functionPathFromArgument(call, index, jobName) };
|
|
165
|
+
}
|
|
166
|
+
if (argument && Node.isIdentifier(argument)) {
|
|
167
|
+
const workflow = workflowsByName.get(argument.getText());
|
|
168
|
+
const agent = agentsByName.get(argument.getText());
|
|
169
|
+
if (workflow && agent) {
|
|
170
|
+
throw diagnosticAt(
|
|
171
|
+
argument,
|
|
172
|
+
`Cron job "${jobName}" references "${argument.getText()}", which is ambiguous: a workflow and an agent are both declared under that name.`,
|
|
138
173
|
{
|
|
139
174
|
code: "CRON_NON_STATIC_FN",
|
|
140
175
|
name: "LunoraError",
|
|
@@ -142,17 +177,15 @@ const resolveTarget = (call, index, jobName, workflowsByName) => {
|
|
|
142
177
|
}
|
|
143
178
|
);
|
|
144
179
|
}
|
|
145
|
-
return { functionPath: functionPathFromArgument(call, index, jobName) };
|
|
146
|
-
}
|
|
147
|
-
if (argument && Node.isIdentifier(argument)) {
|
|
148
|
-
const workflow = workflowsByName.get(argument.getText());
|
|
149
180
|
if (workflow) {
|
|
150
181
|
return workflowTarget(workflow);
|
|
151
182
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
183
|
+
if (agent) {
|
|
184
|
+
return agentTarget(agent);
|
|
185
|
+
}
|
|
186
|
+
throw diagnosticAt(
|
|
187
|
+
argument,
|
|
188
|
+
`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 nor a declared agent in lunora/agents.ts.`,
|
|
156
189
|
{
|
|
157
190
|
code: "CRON_NON_STATIC_FN",
|
|
158
191
|
name: "LunoraError",
|
|
@@ -162,7 +195,7 @@ const resolveTarget = (call, index, jobName, workflowsByName) => {
|
|
|
162
195
|
}
|
|
163
196
|
return { functionPath: functionPathFromArgument(call, index, jobName) };
|
|
164
197
|
};
|
|
165
|
-
const cronFromCall = (call, callee, builderNames, workflowsByName) => {
|
|
198
|
+
const cronFromCall = (call, callee, builderNames, workflowsByName, agentsByName) => {
|
|
166
199
|
const method = callee.getName();
|
|
167
200
|
if (!CRON_METHODS.has(method)) {
|
|
168
201
|
return void 0;
|
|
@@ -172,7 +205,7 @@ const cronFromCall = (call, callee, builderNames, workflowsByName) => {
|
|
|
172
205
|
}
|
|
173
206
|
const name = stringArgument(call, 0);
|
|
174
207
|
if (name === void 0 || name.trim() === "") {
|
|
175
|
-
throw
|
|
208
|
+
throw diagnosticAt(call, `A cron ".${method}(...)" registration must pass a non-empty string-literal name as its first argument.`, {
|
|
176
209
|
code: "CRON_NAME_NOT_STATIC",
|
|
177
210
|
name: "LunoraError",
|
|
178
211
|
status: 500
|
|
@@ -182,14 +215,14 @@ const cronFromCall = (call, callee, builderNames, workflowsByName) => {
|
|
|
182
215
|
if (method === "cron") {
|
|
183
216
|
const expression = stringArgument(call, 1);
|
|
184
217
|
if (expression === void 0) {
|
|
185
|
-
throw
|
|
218
|
+
throw diagnosticAt(call, `Cron job "${name}" must pass a string-literal cron expression to ".cron(...)".`, {
|
|
186
219
|
code: "CRON_EXPR_NOT_STATIC",
|
|
187
220
|
name: "LunoraError",
|
|
188
221
|
status: 500
|
|
189
222
|
});
|
|
190
223
|
}
|
|
191
224
|
if (!isValidCronExpression(expression)) {
|
|
192
|
-
throw
|
|
225
|
+
throw diagnosticAt(call, `Cron job "${name}" has an invalid cron expression "${expression}" — expected 5 or 6 space-separated fields.`, {
|
|
193
226
|
code: "CRON_EXPR_INVALID",
|
|
194
227
|
name: "LunoraError",
|
|
195
228
|
status: 500
|
|
@@ -199,7 +232,7 @@ const cronFromCall = (call, callee, builderNames, workflowsByName) => {
|
|
|
199
232
|
} else {
|
|
200
233
|
const scheduleArgument = call.getArguments()[1];
|
|
201
234
|
if (!scheduleArgument || !Node.isObjectLiteralExpression(scheduleArgument)) {
|
|
202
|
-
throw
|
|
235
|
+
throw diagnosticAt(call, `Cron job "${name}" must pass an object-literal schedule to ".${method}(...)".`, {
|
|
203
236
|
code: "CRON_SCHEDULE_NOT_STATIC",
|
|
204
237
|
name: "LunoraError",
|
|
205
238
|
status: 500
|
|
@@ -207,7 +240,7 @@ const cronFromCall = (call, callee, builderNames, workflowsByName) => {
|
|
|
207
240
|
}
|
|
208
241
|
cron = compileCronSchedule(method, objectLiteralValue(scheduleArgument, name));
|
|
209
242
|
}
|
|
210
|
-
const target = resolveTarget(call, 2, name, workflowsByName);
|
|
243
|
+
const target = resolveTarget(call, 2, name, workflowsByName, agentsByName);
|
|
211
244
|
const argumentsNode = call.getArguments()[3];
|
|
212
245
|
const args = argumentsNode && Node.isObjectLiteralExpression(argumentsNode) ? objectLiteralValue(argumentsNode, name) : {};
|
|
213
246
|
return { args, cron, name, ...target };
|
|
@@ -216,19 +249,18 @@ const assertUniqueNames = (crons) => {
|
|
|
216
249
|
const seen = /* @__PURE__ */ new Set();
|
|
217
250
|
for (const cron of crons) {
|
|
218
251
|
if (seen.has(cron.name)) {
|
|
219
|
-
throw
|
|
220
|
-
code: "DUPLICATE_CRON_NAME",
|
|
221
|
-
name: "LunoraError",
|
|
252
|
+
throw new LunoraError("DUPLICATE_CRON_NAME", `Duplicate cron job name "${cron.name}": cron names must be unique across the project.`, {
|
|
222
253
|
status: 500
|
|
223
254
|
});
|
|
224
255
|
}
|
|
225
256
|
seen.add(cron.name);
|
|
226
257
|
}
|
|
227
258
|
};
|
|
228
|
-
const discoverCrons = (project, lunoraDirectory, workflows = []) => {
|
|
259
|
+
const discoverCrons = (project, lunoraDirectory, workflows = [], agents = []) => {
|
|
229
260
|
const filePaths = listLunoraSourceFiles(lunoraDirectory);
|
|
230
261
|
const crons = [];
|
|
231
262
|
const workflowsByName = new Map(workflows.map((workflow) => [workflow.exportName, workflow]));
|
|
263
|
+
const agentsByName = new Map(agents.map((agent) => [agent.exportName, agent]));
|
|
232
264
|
for (const filePath of filePaths) {
|
|
233
265
|
const source = project.getSourceFile(filePath) ?? project.addSourceFileAtPath(filePath);
|
|
234
266
|
const builderNames = collectCronBuilderNames(source);
|
|
@@ -240,7 +272,7 @@ const discoverCrons = (project, lunoraDirectory, workflows = []) => {
|
|
|
240
272
|
if (!Node.isPropertyAccessExpression(callee)) {
|
|
241
273
|
continue;
|
|
242
274
|
}
|
|
243
|
-
const cron = cronFromCall(call, callee, builderNames, workflowsByName);
|
|
275
|
+
const cron = cronFromCall(call, callee, builderNames, workflowsByName, agentsByName);
|
|
244
276
|
if (cron) {
|
|
245
277
|
crons.push(cron);
|
|
246
278
|
}
|