@lunora/codegen 1.0.0-alpha.16 → 1.0.0-alpha.18
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/dist/index.d.mts +120 -3
- package/dist/index.d.ts +120 -3
- package/dist/index.mjs +8 -6
- package/dist/packem_shared/{GENERATED_HEADER-CkZUgM4_.mjs → GENERATED_HEADER-D-AbhmRh.mjs} +210 -20
- package/dist/packem_shared/MUTATORS_FILENAME-BhqdPtKp.mjs +81 -0
- package/dist/packem_shared/{OPENRPC_VERSION-VINwUAxf.mjs → OPENRPC_VERSION-DDT4mIzD.mjs} +1 -1
- package/dist/packem_shared/{SCHEMA_SNAPSHOT_FILENAME-DsBxQ5C_.mjs → SCHEMA_SNAPSHOT_FILENAME-ExbveiC6.mjs} +83 -7
- package/dist/packem_shared/SHAPES_FILENAME-ChV7MqgE.mjs +94 -0
- package/dist/packem_shared/{buildOpenApiDocument-BSoDzIn8.mjs → buildOpenApiDocument-DoK7_x1g.mjs} +1 -1
- package/dist/packem_shared/{emitApp-DGmlt5fq.mjs → emitApp-CV81L74c.mjs} +48 -6
- package/dist/packem_shared/{formatAdvisories-8NIv1k0I.mjs → formatAdvisories-LM8-Ixjw.mjs} +8 -2
- package/package.json +2 -2
|
@@ -2,7 +2,7 @@ import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
|
2
2
|
import { join, dirname } from 'node:path';
|
|
3
3
|
import { performance } from 'node:perf_hooks';
|
|
4
4
|
import { Node, SyntaxKind, Project } from 'ts-morph';
|
|
5
|
-
import { lintSchema } from './formatAdvisories-
|
|
5
|
+
import { lintSchema } from './formatAdvisories-LM8-Ixjw.mjs';
|
|
6
6
|
import { listLunoraSourceFiles, lunoraRelativePath, classifyProcedureCall, discoverFunctions } from './discoverFunctions-BWMczzBx.mjs';
|
|
7
7
|
import discoverAuthApiCalls from './discoverAuthApiCalls-CoirYbg6.mjs';
|
|
8
8
|
import { discoverContainers } from './CONTAINERS_FILENAME-DlP6YM_Q.mjs';
|
|
@@ -12,19 +12,21 @@ import discoverHttpRoutes from './discoverHttpRoutes-CfP6cMzt.mjs';
|
|
|
12
12
|
import discoverInserts from './discoverInserts-C7zxXkUf.mjs';
|
|
13
13
|
import discoverMaskProcedures, { discoverMaskMetadata } from './discoverMaskProcedures-Cm81kwrb.mjs';
|
|
14
14
|
import discoverMigrations from './discoverMigrations-Bi5nJ0mJ.mjs';
|
|
15
|
+
import { MUTATORS_FILENAME, isDefineMutatorCallee, discoverMutators } from './MUTATORS_FILENAME-BhqdPtKp.mjs';
|
|
15
16
|
import discoverNondeterministicCalls from './discoverNondeterministicCalls-C4M8AXmQ.mjs';
|
|
16
17
|
import discoverQueries from './discoverQueries-B0wGT-xe.mjs';
|
|
17
18
|
import { discoverQueues } from './QUEUES_FILENAME-B5_eWCRe.mjs';
|
|
18
19
|
import discoverR2sqlCalls from './discoverR2sqlCalls-BVNMd428.mjs';
|
|
19
20
|
import discoverRlsProcedures, { discoverRlsMetadata } from './discoverRlsMetadata-BS9GOGC5.mjs';
|
|
20
21
|
import discoverSchema from './discoverSchema-BnWHHJ4T.mjs';
|
|
22
|
+
import { discoverShapes } from './SHAPES_FILENAME-ChV7MqgE.mjs';
|
|
21
23
|
import discoverStorageRulesMetadata from './discoverStorageRulesMetadata-Da8BKXcI.mjs';
|
|
22
24
|
import { e as enclosingExportName$1 } from './discover-ast-CT6BgBr4.mjs';
|
|
23
25
|
import { discoverWorkflows } from './WORKFLOWS_FILENAME-D62dcBGg.mjs';
|
|
24
|
-
import { buildStorageColumns, emitDataModel, emitApi, emitServer, emitFunctions, emitShard, emitContainers, emitWorkflows, emitQueues, emitCrons, emitVectors, emitDrizzleSchema, emitSeed, emitWranglerCronTriggers } from './GENERATED_HEADER-
|
|
25
|
-
import { emitApp } from './emitApp-
|
|
26
|
-
import { buildOpenApiDocument, emitOpenApiModule } from './buildOpenApiDocument-
|
|
27
|
-
import { buildOpenRpcDocument, emitOpenRpcModule } from './OPENRPC_VERSION-
|
|
26
|
+
import { buildStorageColumns, emitDataModel, emitApi, emitServer, emitFunctions, emitShard, emitCollections, emitContainers, emitWorkflows, emitQueues, emitCrons, emitVectors, emitDrizzleSchema, emitSeed, emitWranglerCronTriggers } from './GENERATED_HEADER-D-AbhmRh.mjs';
|
|
27
|
+
import { emitApp } from './emitApp-CV81L74c.mjs';
|
|
28
|
+
import { buildOpenApiDocument, emitOpenApiModule } from './buildOpenApiDocument-DoK7_x1g.mjs';
|
|
29
|
+
import { buildOpenRpcDocument, emitOpenRpcModule } from './OPENRPC_VERSION-DDT4mIzD.mjs';
|
|
28
30
|
import { buildSchemaSnapshot, serializeSchemaSnapshot } from './SCHEMA_SNAPSHOT_VERSION-wWGP2_5E.mjs';
|
|
29
31
|
|
|
30
32
|
const HTTP_VERBS = /* @__PURE__ */ new Set(["delete", "get", "head", "options", "patch", "post", "put"]);
|
|
@@ -241,6 +243,11 @@ const discoverArgumentValidators = (project, lunoraDirectory) => {
|
|
|
241
243
|
};
|
|
242
244
|
|
|
243
245
|
const PROBES = {
|
|
246
|
+
// The middleware (`accessContext()` / `accessRoles()`) imports the `/context`
|
|
247
|
+
// and `/roles` subpaths, NOT the bare `@lunora/cloudflare-access` specifier —
|
|
248
|
+
// so the per-procedure middleware never trips the global `ctx.access` wiring.
|
|
249
|
+
// A handler reading `ctx.access` is the signal that wires it onto every ctx.
|
|
250
|
+
access: { contextProperty: "access", moduleSpecifier: "@lunora/cloudflare-access" },
|
|
244
251
|
ai: { contextProperty: "ai", moduleSpecifier: "@lunora/ai" },
|
|
245
252
|
analytics: { contextProperty: "analytics", moduleSpecifier: "@lunora/bindings/analytics" },
|
|
246
253
|
browser: { contextProperty: "browser", moduleSpecifier: "@lunora/browser" },
|
|
@@ -277,6 +284,7 @@ const readsContextProperty = (sourceFile, property) => {
|
|
|
277
284
|
};
|
|
278
285
|
const discoverFeatureUsage = (project, lunoraDirectory) => {
|
|
279
286
|
const usage = {
|
|
287
|
+
access: false,
|
|
280
288
|
ai: false,
|
|
281
289
|
analytics: false,
|
|
282
290
|
browser: false,
|
|
@@ -329,6 +337,61 @@ const buildStudioFeatures = (usage, signals) => {
|
|
|
329
337
|
};
|
|
330
338
|
};
|
|
331
339
|
|
|
340
|
+
const isDatabaseReplaceCall = (call) => {
|
|
341
|
+
const callee = call.getExpression();
|
|
342
|
+
if (!Node.isPropertyAccessExpression(callee) || callee.getName() !== "replace") {
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
345
|
+
const receiver = callee.getExpression();
|
|
346
|
+
if (Node.isPropertyAccessExpression(receiver)) {
|
|
347
|
+
return receiver.getName() === "db";
|
|
348
|
+
}
|
|
349
|
+
return Node.isIdentifier(receiver) && receiver.getText() === "db";
|
|
350
|
+
};
|
|
351
|
+
const serverImplNode = (call) => {
|
|
352
|
+
const argument = call.getArguments()[0];
|
|
353
|
+
if (argument === void 0 || !Node.isObjectLiteralExpression(argument)) {
|
|
354
|
+
return void 0;
|
|
355
|
+
}
|
|
356
|
+
const property = argument.getProperty("server");
|
|
357
|
+
if (property === void 0) {
|
|
358
|
+
return void 0;
|
|
359
|
+
}
|
|
360
|
+
return Node.isPropertyAssignment(property) ? property.getInitializer() : property;
|
|
361
|
+
};
|
|
362
|
+
const exportedDefineMutatorCall = (declaration) => {
|
|
363
|
+
if (!declaration.isExported()) {
|
|
364
|
+
return void 0;
|
|
365
|
+
}
|
|
366
|
+
const initializer = declaration.getInitializer();
|
|
367
|
+
if (initializer?.getKind() !== SyntaxKind.CallExpression) {
|
|
368
|
+
return void 0;
|
|
369
|
+
}
|
|
370
|
+
const call = initializer;
|
|
371
|
+
return isDefineMutatorCallee(call.getExpression()) ? call : void 0;
|
|
372
|
+
};
|
|
373
|
+
const writesFromDeclaration = (declaration) => {
|
|
374
|
+
const call = exportedDefineMutatorCall(declaration);
|
|
375
|
+
const serverImpl = call === void 0 ? void 0 : serverImplNode(call);
|
|
376
|
+
if (serverImpl === void 0) {
|
|
377
|
+
return [];
|
|
378
|
+
}
|
|
379
|
+
const nameNode = declaration.getNameNode();
|
|
380
|
+
const exportName = Node.isIdentifier(nameNode) ? nameNode.getText() : "";
|
|
381
|
+
return serverImpl.getDescendantsOfKind(SyntaxKind.CallExpression).filter((replaceCall) => isDatabaseReplaceCall(replaceCall)).map((replaceCall) => {
|
|
382
|
+
return { exportName, file: "lunora/mutators.ts", line: replaceCall.getStartLineNumber() };
|
|
383
|
+
});
|
|
384
|
+
};
|
|
385
|
+
const mutatorWritesFromSource = (source) => source.getVariableDeclarations().flatMap((declaration) => writesFromDeclaration(declaration));
|
|
386
|
+
const discoverMutatorWrites = (project, lunoraDirectory) => {
|
|
387
|
+
const mutatorsPath = join(lunoraDirectory, MUTATORS_FILENAME);
|
|
388
|
+
if (!existsSync(mutatorsPath)) {
|
|
389
|
+
return [];
|
|
390
|
+
}
|
|
391
|
+
const source = project.getSourceFile(mutatorsPath) ?? project.addSourceFileAtPath(mutatorsPath);
|
|
392
|
+
return mutatorWritesFromSource(source);
|
|
393
|
+
};
|
|
394
|
+
|
|
332
395
|
const discoverPackageDependencies = (projectRoot) => {
|
|
333
396
|
const manifestPath = join(projectRoot, "package.json");
|
|
334
397
|
if (!existsSync(manifestPath)) {
|
|
@@ -743,6 +806,8 @@ const runCodegen = (options) => {
|
|
|
743
806
|
const functions = discoverFunctions(project, lunoraDirectory);
|
|
744
807
|
const httpRoutes = discoverHttpRoutes(project, lunoraDirectory);
|
|
745
808
|
const migrations = discoverMigrations(project, lunoraDirectory);
|
|
809
|
+
const shapes = discoverShapes(project, lunoraDirectory);
|
|
810
|
+
const mutators = discoverMutators(project, lunoraDirectory);
|
|
746
811
|
const workflows = discoverWorkflows(project, lunoraDirectory);
|
|
747
812
|
const queues = discoverQueues(project, lunoraDirectory);
|
|
748
813
|
const crons = discoverCrons(project, lunoraDirectory, workflows);
|
|
@@ -763,7 +828,9 @@ const runCodegen = (options) => {
|
|
|
763
828
|
discoverSecrets(project, lunoraDirectory),
|
|
764
829
|
discoverSqlInterpolation(project, lunoraDirectory),
|
|
765
830
|
discoverAdminRoutes(project, lunoraDirectory),
|
|
766
|
-
discoverR2sqlCalls(project, lunoraDirectory)
|
|
831
|
+
discoverR2sqlCalls(project, lunoraDirectory),
|
|
832
|
+
shapes,
|
|
833
|
+
discoverMutatorWrites(project, lunoraDirectory)
|
|
767
834
|
);
|
|
768
835
|
const rlsMetadata = discoverRlsMetadata(project, lunoraDirectory);
|
|
769
836
|
const maskMetadata = discoverMaskMetadata(project, lunoraDirectory);
|
|
@@ -772,6 +839,7 @@ const runCodegen = (options) => {
|
|
|
772
839
|
const hasAi = featureUsage.ai;
|
|
773
840
|
const hasPayments = featureUsage.payments;
|
|
774
841
|
const hasKv = featureUsage.kv;
|
|
842
|
+
const hasAccessFacade = featureUsage.access;
|
|
775
843
|
const hasFlags = existsSync(join(lunoraDirectory, "flags.ts"));
|
|
776
844
|
const flagKeys = hasFlags ? discoverFlagKeys(project, lunoraDirectory) : [];
|
|
777
845
|
const hasHyperdrive = featureUsage.hyperdrive;
|
|
@@ -796,6 +864,7 @@ const runCodegen = (options) => {
|
|
|
796
864
|
const apiContent = emitApi(functions, workflows, useUmbrella);
|
|
797
865
|
const serverContent = emitServer({
|
|
798
866
|
containers,
|
|
867
|
+
hasAccessFacade,
|
|
799
868
|
hasAi,
|
|
800
869
|
hasAnalytics,
|
|
801
870
|
hasBrowser,
|
|
@@ -812,11 +881,12 @@ const runCodegen = (options) => {
|
|
|
812
881
|
useUmbrella,
|
|
813
882
|
workflows
|
|
814
883
|
});
|
|
815
|
-
const functionsContent = emitFunctions(functions, migrations, useUmbrella);
|
|
884
|
+
const functionsContent = emitFunctions(functions, migrations, useUmbrella, mutators, shapes);
|
|
816
885
|
const shardContent = emitShard({
|
|
817
886
|
advisories,
|
|
818
887
|
containers,
|
|
819
888
|
flagKeys,
|
|
889
|
+
hasAccessFacade,
|
|
820
890
|
hasAi,
|
|
821
891
|
hasAnalytics,
|
|
822
892
|
hasBrowser,
|
|
@@ -828,14 +898,17 @@ const runCodegen = (options) => {
|
|
|
828
898
|
hasPipelines,
|
|
829
899
|
hasR2sql,
|
|
830
900
|
maskMetadata,
|
|
901
|
+
mutators,
|
|
831
902
|
queues,
|
|
832
903
|
rlsMetadata,
|
|
833
904
|
schema,
|
|
905
|
+
shapes,
|
|
834
906
|
storageRules: storageRulesMetadata,
|
|
835
907
|
studioFeatures,
|
|
836
908
|
useUmbrella,
|
|
837
909
|
workflows
|
|
838
910
|
});
|
|
911
|
+
const collectionsContent = emitCollections(shapes, dependencies.has("@lunora/db"), useUmbrella);
|
|
839
912
|
const containersContent = emitContainers(containers, schema.jurisdiction);
|
|
840
913
|
const workflowsContent = emitWorkflows(workflows);
|
|
841
914
|
const queuesContent = emitQueues(queues);
|
|
@@ -847,6 +920,7 @@ const runCodegen = (options) => {
|
|
|
847
920
|
const wantsOpenApi = apiSpec === "openapi" || apiSpec === "both";
|
|
848
921
|
const wantsOpenRpc = apiSpec === "openrpc" || apiSpec === "both";
|
|
849
922
|
const appContent = emitApp({
|
|
923
|
+
hasAccess: dependencies.has("@lunora/cloudflare-access"),
|
|
850
924
|
hasAi,
|
|
851
925
|
hasAnalytics,
|
|
852
926
|
hasAuth: dependencies.has("@lunora/auth"),
|
|
@@ -909,6 +983,7 @@ const runCodegen = (options) => {
|
|
|
909
983
|
writeIfPresent(join(outputDirectory, "workflows.ts"), workflowsContent);
|
|
910
984
|
writeIfPresent(join(outputDirectory, "queues.ts"), queuesContent);
|
|
911
985
|
writeIfPresent(join(outputDirectory, "seed.ts"), seedContent);
|
|
986
|
+
writeIfPresent(join(outputDirectory, "collections.ts"), collectionsContent);
|
|
912
987
|
if (wantsOpenApi) {
|
|
913
988
|
writeIfChanged(join(outputDirectory, "openapi.json"), openApiContent);
|
|
914
989
|
writeIfChanged(join(outputDirectory, "openapi.ts"), openApiModuleContent);
|
|
@@ -935,6 +1010,7 @@ const runCodegen = (options) => {
|
|
|
935
1010
|
generated: {
|
|
936
1011
|
api: apiContent,
|
|
937
1012
|
app: appContent,
|
|
1013
|
+
collections: collectionsContent,
|
|
938
1014
|
containers: containersContent,
|
|
939
1015
|
crons: cronsContent,
|
|
940
1016
|
dataModel: dataModelContent,
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { SyntaxKind, Node } from 'ts-morph';
|
|
4
|
+
import { diagnosticAt } from './CodegenDiagnosticError-DeblMkzO.mjs';
|
|
5
|
+
|
|
6
|
+
const SHAPES_FILENAME = "shapes.ts";
|
|
7
|
+
const SHAPE_MODULE_SPECIFIERS = /* @__PURE__ */ new Set(["@lunora/server", "lunorash/server"]);
|
|
8
|
+
const isDefineShape = (identifier) => {
|
|
9
|
+
const symbol = identifier.getSymbol();
|
|
10
|
+
if (!symbol) {
|
|
11
|
+
return identifier.getText() === "defineShape";
|
|
12
|
+
}
|
|
13
|
+
for (const declaration of symbol.getDeclarations()) {
|
|
14
|
+
if (!Node.isImportSpecifier(declaration)) {
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
if (!SHAPE_MODULE_SPECIFIERS.has(declaration.getImportDeclaration().getModuleSpecifierValue())) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
return declaration.getNameNode().getText() === "defineShape";
|
|
21
|
+
}
|
|
22
|
+
return false;
|
|
23
|
+
};
|
|
24
|
+
const isShapeNamespaceImport = (identifier) => {
|
|
25
|
+
const symbol = identifier.getSymbol();
|
|
26
|
+
if (!symbol) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
for (const declaration of symbol.getDeclarations()) {
|
|
30
|
+
if (!Node.isNamespaceImport(declaration)) {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
const importDeclaration = declaration.getFirstAncestorByKind(SyntaxKind.ImportDeclaration);
|
|
34
|
+
return importDeclaration !== void 0 && SHAPE_MODULE_SPECIFIERS.has(importDeclaration.getModuleSpecifierValue());
|
|
35
|
+
}
|
|
36
|
+
return false;
|
|
37
|
+
};
|
|
38
|
+
const isDefineShapeCallee = (callee) => {
|
|
39
|
+
if (Node.isIdentifier(callee)) {
|
|
40
|
+
return isDefineShape(callee);
|
|
41
|
+
}
|
|
42
|
+
if (Node.isPropertyAccessExpression(callee)) {
|
|
43
|
+
const object = callee.getExpression();
|
|
44
|
+
return callee.getName() === "defineShape" && Node.isIdentifier(object) && isShapeNamespaceImport(object);
|
|
45
|
+
}
|
|
46
|
+
return false;
|
|
47
|
+
};
|
|
48
|
+
const tableLiteralFrom = (call) => {
|
|
49
|
+
const [config] = call.getArguments();
|
|
50
|
+
if (!config || !Node.isObjectLiteralExpression(config)) {
|
|
51
|
+
return void 0;
|
|
52
|
+
}
|
|
53
|
+
const tableProperty = config.getProperty("table");
|
|
54
|
+
if (!tableProperty || !Node.isPropertyAssignment(tableProperty)) {
|
|
55
|
+
return void 0;
|
|
56
|
+
}
|
|
57
|
+
const value = tableProperty.getInitializer();
|
|
58
|
+
return value && Node.isStringLiteral(value) ? value.getLiteralValue() : void 0;
|
|
59
|
+
};
|
|
60
|
+
const shapesFromSource = (source) => {
|
|
61
|
+
const shapes = [];
|
|
62
|
+
for (const declaration of source.getVariableDeclarations()) {
|
|
63
|
+
if (!declaration.isExported()) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
const initializer = declaration.getInitializer();
|
|
67
|
+
if (initializer?.getKind() !== SyntaxKind.CallExpression) {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
const callExpression = initializer;
|
|
71
|
+
const callee = callExpression.getExpression();
|
|
72
|
+
if (!isDefineShapeCallee(callee)) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
const nameNode = declaration.getNameNode();
|
|
76
|
+
if (!Node.isIdentifier(nameNode)) {
|
|
77
|
+
throw diagnosticAt(nameNode, "defineShape exports must be plain named exports (no destructuring)");
|
|
78
|
+
}
|
|
79
|
+
shapes.push({ exportName: nameNode.getText(), filePath: "shapes", table: tableLiteralFrom(callExpression) });
|
|
80
|
+
}
|
|
81
|
+
return shapes;
|
|
82
|
+
};
|
|
83
|
+
const discoverShapes = (project, lunoraDirectory) => {
|
|
84
|
+
const shapesPath = join(lunoraDirectory, SHAPES_FILENAME);
|
|
85
|
+
if (!existsSync(shapesPath)) {
|
|
86
|
+
return [];
|
|
87
|
+
}
|
|
88
|
+
const source = project.getSourceFile(shapesPath) ?? project.addSourceFileAtPath(shapesPath);
|
|
89
|
+
const shapes = shapesFromSource(source);
|
|
90
|
+
shapes.sort((a, b) => a.exportName.localeCompare(b.exportName));
|
|
91
|
+
return shapes;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export { SHAPES_FILENAME, discoverShapes };
|
package/dist/packem_shared/{buildOpenApiDocument-BSoDzIn8.mjs → buildOpenApiDocument-DoK7_x1g.mjs}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { GENERATED_HEADER } from './GENERATED_HEADER-
|
|
1
|
+
import { GENERATED_HEADER } from './GENERATED_HEADER-D-AbhmRh.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,4 +1,4 @@
|
|
|
1
|
-
import { GENERATED_HEADER } from './GENERATED_HEADER-
|
|
1
|
+
import { GENERATED_HEADER } from './GENERATED_HEADER-D-AbhmRh.mjs';
|
|
2
2
|
|
|
3
3
|
const LONG_TAIL = [
|
|
4
4
|
["hasAi", "ai", "ai", "Override the Workers AI binding backing `ctx.ai` (defaults to `env.AI`)."],
|
|
@@ -22,8 +22,25 @@ const LONG_TAIL = [
|
|
|
22
22
|
["hasVectors", "vectors", "vectors", "Wire the Vectorize index map backing `ctx.vectors`."]
|
|
23
23
|
];
|
|
24
24
|
const hasAnyLongTail = (options) => LONG_TAIL.some(([flag]) => options[flag]);
|
|
25
|
+
const buildAccessImports = (hasAccess, hasAuth) => hasAccess ? [
|
|
26
|
+
`import type { CreateAccessResolverOptions } from "@lunora/cloudflare-access";`,
|
|
27
|
+
`import { createAccessResolver${hasAuth ? ", composeResolvers" : ""} } from "@lunora/cloudflare-access";`
|
|
28
|
+
] : [];
|
|
25
29
|
const buildImportLines = (options) => {
|
|
26
|
-
const {
|
|
30
|
+
const {
|
|
31
|
+
hasAccess,
|
|
32
|
+
hasAuth,
|
|
33
|
+
hasFramework,
|
|
34
|
+
hasGlobal,
|
|
35
|
+
hasHyperdriveGlobal,
|
|
36
|
+
hasQueue,
|
|
37
|
+
hasScheduler,
|
|
38
|
+
hasStorage,
|
|
39
|
+
hasWorkflow,
|
|
40
|
+
useUmbrella,
|
|
41
|
+
wantsOpenApi,
|
|
42
|
+
wantsOpenRpc
|
|
43
|
+
} = options;
|
|
27
44
|
const runtimeModule = useUmbrella ? "lunorash/runtime" : "@lunora/runtime";
|
|
28
45
|
const runtimeTypeImports = ["ExecutionContextLike", "LunoraWorker", "Route", "ScheduledControllerLike", "ShardNamespaceLike", "WorkerOptions"];
|
|
29
46
|
if (hasGlobal) {
|
|
@@ -42,6 +59,7 @@ const buildImportLines = (options) => {
|
|
|
42
59
|
`import type { LunoraAuth, LunoraAuthOptions } from "@lunora/auth";`,
|
|
43
60
|
`import { createAuth, createAuthAdmin, ensureMigrated, handleAuthRequest, lunoraD1Adapter } from "@lunora/auth";`
|
|
44
61
|
] : [],
|
|
62
|
+
...buildAccessImports(hasAccess, hasAuth),
|
|
45
63
|
...hasGlobal ? [
|
|
46
64
|
`import type { D1CtxDbOptions, D1DatabaseLike, D1Exec } from "@lunora/d1";`,
|
|
47
65
|
`import { createD1CtxDb, facetGlobalColumn, listGlobalTables, readGlobalTablePage } from "@lunora/d1";`
|
|
@@ -120,9 +138,10 @@ interface AuthDeclaration<Env> {
|
|
|
120
138
|
] : []
|
|
121
139
|
];
|
|
122
140
|
const buildFieldLines = (options) => [
|
|
141
|
+
...options.hasAccess ? [` private accessSelector?: Selector<Env, CreateAccessResolverOptions>;`] : [],
|
|
123
142
|
` private adminToken?: Selector<Env, string>;`,
|
|
124
143
|
...options.hasAuth ? [` private authDeclaration?: AuthDeclaration<Env>;`] : [],
|
|
125
|
-
` private readonly extendFns: ((env: Env) => Partial<WorkerOptions>)[] = [];`,
|
|
144
|
+
` private readonly extendFns: ((env: Env, derived: Readonly<WorkerOptions>) => Partial<WorkerOptions>)[] = [];`,
|
|
126
145
|
...options.hasGlobal ? [` private globalDeclaration?: GlobalDeclaration<Env>;`] : [],
|
|
127
146
|
...options.hasHyperdriveGlobal ? [` private hyperdriveGlobalDeclaration?: HyperdriveGlobalDeclaration<Env>;`] : [],
|
|
128
147
|
` private readonly routeMap: Record<string, Route> = {};`,
|
|
@@ -140,6 +159,14 @@ const buildLongTailMethods = (options) => LONG_TAIL.filter(([flag]) => options[f
|
|
|
140
159
|
}`
|
|
141
160
|
);
|
|
142
161
|
const buildMethodBlocks = (options) => [
|
|
162
|
+
...options.hasAccess ? [
|
|
163
|
+
` /** Wire Cloudflare Access (Zero Trust) — verifies the \`Cf-Access-Jwt-Assertion\` JWT and feeds the identity into \`ctx.auth\` / RLS via \`resolveIdentity\`. When \`.auth(...)\` is also configured, Access is composed ahead of it (Access wins when its JWT is present; everyone else falls through to the app session). */
|
|
164
|
+
public access(selector: Selector<Env, CreateAccessResolverOptions>): this {
|
|
165
|
+
this.accessSelector = selector;
|
|
166
|
+
|
|
167
|
+
return this;
|
|
168
|
+
}`
|
|
169
|
+
] : [],
|
|
143
170
|
` /** Bearer token gating the \`/_lunora/admin/*\` endpoints the studio calls. */
|
|
144
171
|
public admin(selector: Selector<Env, string>): this {
|
|
145
172
|
this.adminToken = selector;
|
|
@@ -154,8 +181,8 @@ const buildMethodBlocks = (options) => [
|
|
|
154
181
|
return this;
|
|
155
182
|
}`
|
|
156
183
|
] : [],
|
|
157
|
-
` /** Escape hatch — merge raw \`WorkerOptions\` (anything not yet sugared) over the derived options at build time. */
|
|
158
|
-
public extend(fn: (env: Env) => Partial<WorkerOptions>): this {
|
|
184
|
+
` /** Escape hatch — merge raw \`WorkerOptions\` (anything not yet sugared) over the derived options at build time. The second \`derived\` argument is a snapshot of the options assembled so far (after \`.auth(...)\` etc.), so you can compose rather than clobber — e.g. wrap \`derived.resolveIdentity\` instead of replacing it. */
|
|
185
|
+
public extend(fn: (env: Env, derived: Readonly<WorkerOptions>) => Partial<WorkerOptions>): this {
|
|
159
186
|
this.extendFns.push(fn);
|
|
160
187
|
|
|
161
188
|
return this;
|
|
@@ -344,6 +371,21 @@ const buildWorkerOptionLines = (options) => [
|
|
|
344
371
|
|
|
345
372
|
options.authAdmin = authInstance ? createAuthAdmin(authInstance) : undefined;
|
|
346
373
|
}`
|
|
374
|
+
] : [],
|
|
375
|
+
// Cloudflare Access — runs AFTER the auth block so it can compose ahead of
|
|
376
|
+
// the better-auth resolver rather than clobber it. With `.auth()` present,
|
|
377
|
+
// a request carrying a verified Access JWT is authenticated by Access and
|
|
378
|
+
// everyone else falls through to the app session; without it, Access is the
|
|
379
|
+
// sole resolver.
|
|
380
|
+
...options.hasAccess ? [
|
|
381
|
+
options.hasAuth ? ` if (this.accessSelector) {
|
|
382
|
+
const accessResolver = createAccessResolver(this.accessSelector(env));
|
|
383
|
+
const fallback = options.resolveIdentity;
|
|
384
|
+
|
|
385
|
+
options.resolveIdentity = fallback ? composeResolvers(accessResolver, fallback) : accessResolver;
|
|
386
|
+
}` : ` if (this.accessSelector) {
|
|
387
|
+
options.resolveIdentity = createAccessResolver(this.accessSelector(env));
|
|
388
|
+
}`
|
|
347
389
|
] : []
|
|
348
390
|
];
|
|
349
391
|
const buildBaseWorkerOptions = (options) => [
|
|
@@ -599,7 +641,7 @@ ${buildBaseWorkerOptions(options).join("\n")}
|
|
|
599
641
|
}
|
|
600
642
|
|
|
601
643
|
${workerOptionLines.join("\n\n")}${workerOptionLines.length > 0 ? "\n\n" : ""} for (const fn of this.extendFns) {
|
|
602
|
-
Object.assign(options, fn(env));
|
|
644
|
+
Object.assign(options, fn(env, { ...options }));
|
|
603
645
|
}
|
|
604
646
|
|
|
605
647
|
return options;
|
|
@@ -31,12 +31,16 @@ const toAdvisorSchema = (schema) => {
|
|
|
31
31
|
references: relation.references,
|
|
32
32
|
table: relation.table
|
|
33
33
|
};
|
|
34
|
-
})
|
|
34
|
+
}),
|
|
35
|
+
shardKind: typeof table.shardMode === "string" ? table.shardMode : "shardBy"
|
|
35
36
|
};
|
|
36
37
|
})
|
|
37
38
|
};
|
|
38
39
|
};
|
|
39
|
-
const
|
|
40
|
+
const toAdvisorShapes = (shapes) => shapes.map((shape) => {
|
|
41
|
+
return { exportName: shape.exportName, file: `lunora/${shape.filePath}.ts`, table: shape.table };
|
|
42
|
+
});
|
|
43
|
+
const lintSchema = (schema, queries = [], inserts, authApiCalls, rlsProcedures, containers, workflows, workflowCalls, maskProcedures, nondeterministicCalls, procedureProtections, argumentValidators, secretLiterals, sqlInterpolations, adminRoutes, r2sqlCalls, shapes, mutatorWrites) => runAdvisor(
|
|
40
44
|
{
|
|
41
45
|
adminRoutes,
|
|
42
46
|
argValidators: argumentValidators,
|
|
@@ -44,6 +48,7 @@ const lintSchema = (schema, queries = [], inserts, authApiCalls, rlsProcedures,
|
|
|
44
48
|
containers,
|
|
45
49
|
inserts,
|
|
46
50
|
maskProcedures,
|
|
51
|
+
mutatorWrites,
|
|
47
52
|
nondeterministicCalls,
|
|
48
53
|
procedureProtections,
|
|
49
54
|
queries,
|
|
@@ -51,6 +56,7 @@ const lintSchema = (schema, queries = [], inserts, authApiCalls, rlsProcedures,
|
|
|
51
56
|
rlsProcedures,
|
|
52
57
|
schema: toAdvisorSchema(schema),
|
|
53
58
|
secretLiterals,
|
|
59
|
+
shapes: shapes === void 0 ? void 0 : toAdvisorShapes(shapes),
|
|
54
60
|
sqlInterpolations,
|
|
55
61
|
workflowCalls,
|
|
56
62
|
workflows
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lunora/codegen",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.18",
|
|
4
4
|
"description": "Code generator for Lunora: emits _generated/{api,server,dataModel}.ts from your schema",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cloudflare",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"access": "public"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@lunora/advisor": "1.0.0-alpha.
|
|
49
|
+
"@lunora/advisor": "1.0.0-alpha.8",
|
|
50
50
|
"@lunora/container": "1.0.0-alpha.5",
|
|
51
51
|
"@lunora/queue": "1.0.0-alpha.1",
|
|
52
52
|
"@lunora/scheduler": "1.0.0-alpha.3",
|