@lunora/codegen 0.0.0 → 1.0.0-alpha.2
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 +105 -0
- package/README.md +117 -9
- package/__assets__/package-og.svg +14 -0
- package/dist/index.d.mts +1473 -0
- package/dist/index.d.ts +1473 -0
- package/dist/index.mjs +28 -0
- package/dist/packem_shared/CONTAINERS_FILENAME-0K-pjNb8.mjs +224 -0
- package/dist/packem_shared/CodegenDiagnosticError-54jWDxA9.mjs +22 -0
- package/dist/packem_shared/LUNORA_ERROR_CODES-CySpQPD3.mjs +61 -0
- package/dist/packem_shared/buildOpenApiDocument-yHVN66Xd.mjs +183 -0
- package/dist/packem_shared/buildOpenRpcDocument-BZGY1-jT.mjs +60 -0
- package/dist/packem_shared/buildSchemaSnapshot-DzLDbWk3.mjs +233 -0
- package/dist/packem_shared/createCodegenProject-DGJm0_Pk.mjs +895 -0
- package/dist/packem_shared/discover-ast-CT6BgBr4.mjs +13 -0
- package/dist/packem_shared/discoverAuthApiCalls-C35R6z0T.mjs +62 -0
- package/dist/packem_shared/discoverCrons-BL6iGuJ3.mjs +254 -0
- package/dist/packem_shared/discoverFunctions-DEgAcRuD.mjs +460 -0
- package/dist/packem_shared/discoverHttpRoutes-C978pBiG.mjs +131 -0
- package/dist/packem_shared/discoverInserts-CRQdXvHO.mjs +39 -0
- package/dist/packem_shared/discoverMaskProcedures-B64zA740.mjs +217 -0
- package/dist/packem_shared/discoverMigrations-Doj_-BAA.mjs +91 -0
- package/dist/packem_shared/discoverNondeterministicCalls-4KiPQxQU.mjs +122 -0
- package/dist/packem_shared/discoverQueries-BkIi0dBD.mjs +62 -0
- package/dist/packem_shared/discoverRlsMetadata-DpRB1HMe.mjs +280 -0
- package/dist/packem_shared/discoverSchema-BBulgGbH.mjs +542 -0
- package/dist/packem_shared/discoverStorageRulesMetadata-DAqJUxUv.mjs +97 -0
- package/dist/packem_shared/discoverWorkflows-DRDQdhfq.mjs +84 -0
- package/dist/packem_shared/emitApi-hRVC-kE7.mjs +2426 -0
- package/dist/packem_shared/emitApp-CzZ6GbrD.mjs +593 -0
- package/dist/packem_shared/lintSchema-DicbOHvH.mjs +68 -0
- package/dist/packem_shared/parse-validator-tuQtHrsr.mjs +132 -0
- package/dist/packem_shared/paths-BRd6JHuF.mjs +11 -0
- package/dist/packem_shared/schemaFromIr-DTYsLBaA.mjs +57 -0
- package/package.json +45 -17
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import { Node, SyntaxKind } from 'ts-morph';
|
|
2
|
+
import { listLunoraSourceFiles, lunoraRelativePath, classifyProcedureCall } from './discoverFunctions-DEgAcRuD.mjs';
|
|
3
|
+
|
|
4
|
+
const isRlsCall = (node) => {
|
|
5
|
+
if (!Node.isCallExpression(node)) {
|
|
6
|
+
return false;
|
|
7
|
+
}
|
|
8
|
+
const callee = node.getExpression();
|
|
9
|
+
if (Node.isIdentifier(callee)) {
|
|
10
|
+
return callee.getText() === "rls";
|
|
11
|
+
}
|
|
12
|
+
if (Node.isPropertyAccessExpression(callee)) {
|
|
13
|
+
return callee.getName() === "rls";
|
|
14
|
+
}
|
|
15
|
+
return false;
|
|
16
|
+
};
|
|
17
|
+
const extractPolicyTables = (rlsCall) => {
|
|
18
|
+
const argument = rlsCall.getArguments()[0];
|
|
19
|
+
if (!argument || !Node.isArrayLiteralExpression(argument)) {
|
|
20
|
+
return [];
|
|
21
|
+
}
|
|
22
|
+
const tables = [];
|
|
23
|
+
for (const element of argument.getElements()) {
|
|
24
|
+
if (!Node.isObjectLiteralExpression(element)) {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
const tableProperty = element.getProperty("table");
|
|
28
|
+
if (!tableProperty || !Node.isPropertyAssignment(tableProperty)) {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
const initializer = tableProperty.getInitializer();
|
|
32
|
+
if (initializer && Node.isStringLiteral(initializer)) {
|
|
33
|
+
tables.push(initializer.getLiteralText());
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return tables;
|
|
37
|
+
};
|
|
38
|
+
const rlsCallsInChain = (receiver) => {
|
|
39
|
+
const calls = [];
|
|
40
|
+
let node = receiver;
|
|
41
|
+
while (Node.isCallExpression(node)) {
|
|
42
|
+
const chainCallee = node.getExpression();
|
|
43
|
+
if (!Node.isPropertyAccessExpression(chainCallee)) {
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
if (chainCallee.getName() === "use") {
|
|
47
|
+
const argument = node.getArguments()[0];
|
|
48
|
+
if (argument && isRlsCall(argument)) {
|
|
49
|
+
calls.push(argument);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
node = chainCallee.getExpression();
|
|
53
|
+
}
|
|
54
|
+
return calls;
|
|
55
|
+
};
|
|
56
|
+
const rlsFromBuilderChain = (receiver) => {
|
|
57
|
+
const calls = rlsCallsInChain(receiver);
|
|
58
|
+
return { rlsTables: calls.flatMap((call) => extractPolicyTables(call)), usesRls: calls.length > 0 };
|
|
59
|
+
};
|
|
60
|
+
const READ_METHODS = /* @__PURE__ */ new Set(["findFirst", "findFirstOrThrow", "findMany", "get", "query"]);
|
|
61
|
+
const WRITE_METHODS = /* @__PURE__ */ new Set(["delete", "deleteMany", "insert", "insertMany", "patch", "patchMany", "replace"]);
|
|
62
|
+
const isDatabaseCall = (call, methodSet) => {
|
|
63
|
+
const callee = call.getExpression();
|
|
64
|
+
if (!Node.isPropertyAccessExpression(callee) || !methodSet.has(callee.getName())) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
const receiver = callee.getExpression();
|
|
68
|
+
if (Node.isPropertyAccessExpression(receiver)) {
|
|
69
|
+
return receiver.getName() === "db";
|
|
70
|
+
}
|
|
71
|
+
return Node.isIdentifier(receiver) && receiver.getText() === "db";
|
|
72
|
+
};
|
|
73
|
+
const tableArgumentOf = (call) => {
|
|
74
|
+
const argument = call.getArguments()[0];
|
|
75
|
+
return argument && Node.isStringLiteral(argument) ? argument.getLiteralText() : "";
|
|
76
|
+
};
|
|
77
|
+
const tablesAccessedIn = (declaration) => {
|
|
78
|
+
const tablesRead = /* @__PURE__ */ new Set();
|
|
79
|
+
const tablesWritten = /* @__PURE__ */ new Set();
|
|
80
|
+
for (const call of declaration.getDescendantsOfKind(SyntaxKind.CallExpression)) {
|
|
81
|
+
if (isDatabaseCall(call, READ_METHODS)) {
|
|
82
|
+
const table = tableArgumentOf(call);
|
|
83
|
+
if (table !== "") {
|
|
84
|
+
tablesRead.add(table);
|
|
85
|
+
}
|
|
86
|
+
} else if (isDatabaseCall(call, WRITE_METHODS)) {
|
|
87
|
+
const table = tableArgumentOf(call);
|
|
88
|
+
if (table !== "") {
|
|
89
|
+
tablesWritten.add(table);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return { tablesRead: [...tablesRead], tablesWritten: [...tablesWritten] };
|
|
94
|
+
};
|
|
95
|
+
const procedureIrFromDeclaration = (declaration, relativePath) => {
|
|
96
|
+
if (!Node.isVariableDeclaration(declaration)) {
|
|
97
|
+
return void 0;
|
|
98
|
+
}
|
|
99
|
+
const initializer = declaration.getInitializer();
|
|
100
|
+
if (!initializer || !Node.isCallExpression(initializer)) {
|
|
101
|
+
return void 0;
|
|
102
|
+
}
|
|
103
|
+
const classified = classifyProcedureCall(initializer);
|
|
104
|
+
if (!classified) {
|
|
105
|
+
return void 0;
|
|
106
|
+
}
|
|
107
|
+
const chain = classified.receiver ? rlsFromBuilderChain(classified.receiver) : { rlsTables: [], usesRls: false };
|
|
108
|
+
const { tablesRead, tablesWritten } = tablesAccessedIn(declaration);
|
|
109
|
+
return {
|
|
110
|
+
exportName: declaration.getName(),
|
|
111
|
+
file: relativePath,
|
|
112
|
+
rlsTables: chain.rlsTables,
|
|
113
|
+
tablesRead,
|
|
114
|
+
tablesWritten,
|
|
115
|
+
usesRls: chain.usesRls,
|
|
116
|
+
visibility: classified.visibility
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
const discoverRlsProcedures = (project, lunoraDirectory) => {
|
|
120
|
+
const procedures = [];
|
|
121
|
+
for (const filePath of listLunoraSourceFiles(lunoraDirectory)) {
|
|
122
|
+
const sourceFile = project.getSourceFile(filePath) ?? project.addSourceFileAtPath(filePath);
|
|
123
|
+
const relativePath = lunoraRelativePath(lunoraDirectory, filePath);
|
|
124
|
+
for (const statement of sourceFile.getVariableStatements()) {
|
|
125
|
+
if (!statement.isExported()) {
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
for (const declaration of statement.getDeclarations()) {
|
|
129
|
+
const ir = procedureIrFromDeclaration(declaration, relativePath);
|
|
130
|
+
if (ir) {
|
|
131
|
+
procedures.push(ir);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return procedures;
|
|
137
|
+
};
|
|
138
|
+
const POLICY_OPERATIONS = /* @__PURE__ */ new Set(["delete", "insert", "read", "update"]);
|
|
139
|
+
const stringPropertyOf = (object, name) => {
|
|
140
|
+
if (!Node.isObjectLiteralExpression(object)) {
|
|
141
|
+
return void 0;
|
|
142
|
+
}
|
|
143
|
+
const property = object.getProperty(name);
|
|
144
|
+
if (!property || !Node.isPropertyAssignment(property)) {
|
|
145
|
+
return void 0;
|
|
146
|
+
}
|
|
147
|
+
const initializer = property.getInitializer();
|
|
148
|
+
return initializer && Node.isStringLiteral(initializer) ? initializer.getLiteralText() : void 0;
|
|
149
|
+
};
|
|
150
|
+
const extractPolicies = (rlsCall, file, procedure) => {
|
|
151
|
+
const argument = rlsCall.getArguments()[0];
|
|
152
|
+
if (!argument || !Node.isArrayLiteralExpression(argument)) {
|
|
153
|
+
return [];
|
|
154
|
+
}
|
|
155
|
+
const policies = [];
|
|
156
|
+
for (const element of argument.getElements()) {
|
|
157
|
+
if (!Node.isObjectLiteralExpression(element)) {
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
const on = stringPropertyOf(element, "on");
|
|
161
|
+
if (on === void 0 || !POLICY_OPERATIONS.has(on)) {
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
policies.push({ file, on, procedure, table: stringPropertyOf(element, "table") ?? "" });
|
|
165
|
+
}
|
|
166
|
+
return policies;
|
|
167
|
+
};
|
|
168
|
+
const extractRolePermissions = (roleObject) => {
|
|
169
|
+
if (!Node.isObjectLiteralExpression(roleObject)) {
|
|
170
|
+
return [];
|
|
171
|
+
}
|
|
172
|
+
const property = roleObject.getProperty("permissions");
|
|
173
|
+
if (!property || !Node.isPropertyAssignment(property)) {
|
|
174
|
+
return [];
|
|
175
|
+
}
|
|
176
|
+
const initializer = property.getInitializer();
|
|
177
|
+
if (!initializer || !Node.isArrayLiteralExpression(initializer)) {
|
|
178
|
+
return [];
|
|
179
|
+
}
|
|
180
|
+
const permissions = [];
|
|
181
|
+
for (const element of initializer.getElements()) {
|
|
182
|
+
if (Node.isStringLiteral(element)) {
|
|
183
|
+
permissions.push(element.getLiteralText());
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
if (Node.isCallExpression(element)) {
|
|
187
|
+
const firstArgument = element.getArguments()[0];
|
|
188
|
+
if (firstArgument && Node.isStringLiteral(firstArgument)) {
|
|
189
|
+
permissions.push(firstArgument.getLiteralText());
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return permissions;
|
|
194
|
+
};
|
|
195
|
+
const withDescription = (role, description) => description === void 0 ? role : { ...role, description };
|
|
196
|
+
const roleFromElement = (element) => {
|
|
197
|
+
if (Node.isCallExpression(element)) {
|
|
198
|
+
const nameArgument = element.getArguments()[0];
|
|
199
|
+
if (!nameArgument || !Node.isStringLiteral(nameArgument)) {
|
|
200
|
+
return void 0;
|
|
201
|
+
}
|
|
202
|
+
const optionsObject = element.getArguments()[1];
|
|
203
|
+
const description = optionsObject ? stringPropertyOf(optionsObject, "description") : void 0;
|
|
204
|
+
const permissions = optionsObject ? extractRolePermissions(optionsObject) : [];
|
|
205
|
+
return withDescription({ name: nameArgument.getLiteralText(), permissions }, description);
|
|
206
|
+
}
|
|
207
|
+
if (Node.isObjectLiteralExpression(element)) {
|
|
208
|
+
const name = stringPropertyOf(element, "name");
|
|
209
|
+
if (name === void 0) {
|
|
210
|
+
return void 0;
|
|
211
|
+
}
|
|
212
|
+
return withDescription({ name, permissions: extractRolePermissions(element) }, stringPropertyOf(element, "description"));
|
|
213
|
+
}
|
|
214
|
+
return void 0;
|
|
215
|
+
};
|
|
216
|
+
const extractRoles = (rlsCall) => {
|
|
217
|
+
const optionsArgument = rlsCall.getArguments()[1];
|
|
218
|
+
if (!optionsArgument || !Node.isObjectLiteralExpression(optionsArgument)) {
|
|
219
|
+
return [];
|
|
220
|
+
}
|
|
221
|
+
const rolesProperty = optionsArgument.getProperty("roles");
|
|
222
|
+
if (!rolesProperty || !Node.isPropertyAssignment(rolesProperty)) {
|
|
223
|
+
return [];
|
|
224
|
+
}
|
|
225
|
+
const initializer = rolesProperty.getInitializer();
|
|
226
|
+
if (!initializer || !Node.isArrayLiteralExpression(initializer)) {
|
|
227
|
+
return [];
|
|
228
|
+
}
|
|
229
|
+
const roles = [];
|
|
230
|
+
for (const element of initializer.getElements()) {
|
|
231
|
+
const role = roleFromElement(element);
|
|
232
|
+
if (role) {
|
|
233
|
+
roles.push(role);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return roles;
|
|
237
|
+
};
|
|
238
|
+
const rlsMetadataFromChain = (receiver, file, procedure) => {
|
|
239
|
+
const calls = rlsCallsInChain(receiver);
|
|
240
|
+
return {
|
|
241
|
+
policies: calls.flatMap((call) => extractPolicies(call, file, procedure)),
|
|
242
|
+
roles: calls.flatMap((call) => extractRoles(call))
|
|
243
|
+
};
|
|
244
|
+
};
|
|
245
|
+
const exportedProcedureChains = (sourceFile) => {
|
|
246
|
+
const chains = [];
|
|
247
|
+
for (const statement of sourceFile.getVariableStatements()) {
|
|
248
|
+
if (!statement.isExported()) {
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
for (const declaration of statement.getDeclarations()) {
|
|
252
|
+
const initializer = declaration.getInitializer();
|
|
253
|
+
const classified = initializer && Node.isCallExpression(initializer) ? classifyProcedureCall(initializer) : void 0;
|
|
254
|
+
if (classified?.receiver) {
|
|
255
|
+
chains.push({ name: declaration.getName(), receiver: classified.receiver });
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return chains;
|
|
260
|
+
};
|
|
261
|
+
const discoverRlsMetadata = (project, lunoraDirectory) => {
|
|
262
|
+
const policies = [];
|
|
263
|
+
const rolesByName = /* @__PURE__ */ new Map();
|
|
264
|
+
for (const filePath of listLunoraSourceFiles(lunoraDirectory)) {
|
|
265
|
+
const sourceFile = project.getSourceFile(filePath) ?? project.addSourceFileAtPath(filePath);
|
|
266
|
+
const relativePath = lunoraRelativePath(lunoraDirectory, filePath);
|
|
267
|
+
for (const { name, receiver } of exportedProcedureChains(sourceFile)) {
|
|
268
|
+
const metadata = rlsMetadataFromChain(receiver, relativePath, name);
|
|
269
|
+
policies.push(...metadata.policies);
|
|
270
|
+
for (const role of metadata.roles) {
|
|
271
|
+
if (!rolesByName.has(role.name)) {
|
|
272
|
+
rolesByName.set(role.name, role);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return { policies, roles: [...rolesByName.values()] };
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
export { discoverRlsProcedures as default, discoverRlsMetadata };
|