@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.
Files changed (42) hide show
  1. package/LICENSE.md +6 -0
  2. package/__assets__/package-og.svg +1 -1
  3. package/dist/index.d.mts +1950 -734
  4. package/dist/index.d.ts +1950 -734
  5. package/dist/index.mjs +32 -24
  6. package/dist/packem_shared/AGENTS_FILENAME-Cxx7wiSs.mjs +158 -0
  7. package/dist/packem_shared/{CONTAINERS_FILENAME-0K-pjNb8.mjs → CONTAINERS_FILENAME-DjpXMqhp.mjs} +1 -1
  8. package/dist/packem_shared/{CodegenDiagnosticError-54jWDxA9.mjs → CodegenDiagnosticError-DyQ5FwkM.mjs} +7 -5
  9. package/dist/packem_shared/FLAGS_FILENAME-Dg4mKUuz.mjs +139 -0
  10. package/dist/packem_shared/GENERATED_HEADER-C5paWNYO.mjs +4 -0
  11. package/dist/packem_shared/{LUNORA_ERROR_CODES-CySpQPD3.mjs → LUNORA_ERROR_CODES-DvTLozCu.mjs} +8 -1
  12. package/dist/packem_shared/MUTATORS_FILENAME-BZOfUhlY.mjs +81 -0
  13. package/dist/packem_shared/{OPENRPC_VERSION-C7zKVeKi.mjs → OPENRPC_VERSION-7A2weq2u.mjs} +2 -2
  14. package/dist/packem_shared/QUEUES_FILENAME-Xv6Vy-Ku.mjs +145 -0
  15. package/dist/packem_shared/SCHEMA_SNAPSHOT_FILENAME-G5dkcxPR.mjs +3502 -0
  16. package/dist/packem_shared/{SCHEMA_SNAPSHOT_VERSION-DzLDbWk3.mjs → SCHEMA_SNAPSHOT_VERSION-D0ARY6rL.mjs} +18 -2
  17. package/dist/packem_shared/SHAPES_FILENAME-DOhPGi-6.mjs +94 -0
  18. package/dist/packem_shared/WORKFLOWS_FILENAME-Dtuzvluc.mjs +158 -0
  19. package/dist/packem_shared/{buildOpenApiDocument-Cns1EkCQ.mjs → buildOpenApiDocument-B7mEdzA2.mjs} +2 -2
  20. package/dist/packem_shared/{discoverAuthApiCalls-C35R6z0T.mjs → discoverAuthApiCalls-Dx3K42rk.mjs} +1 -1
  21. package/dist/packem_shared/{discoverCrons-BL6iGuJ3.mjs → discoverCrons-DvqkEWdx.mjs} +68 -36
  22. package/dist/packem_shared/{discoverFunctions-DEgAcRuD.mjs → discoverFunctions-BJ-qR7Rg.mjs} +72 -14
  23. package/dist/packem_shared/{discoverHttpRoutes-C978pBiG.mjs → discoverHttpRoutes-daCzuTe8.mjs} +10 -2
  24. package/dist/packem_shared/{discoverInserts-CRQdXvHO.mjs → discoverInserts-DI4q5NxE.mjs} +24 -2
  25. package/dist/packem_shared/{discoverMaskProcedures-B64zA740.mjs → discoverMaskProcedures-BcTOEKNU.mjs} +58 -2
  26. package/dist/packem_shared/{discoverMigrations-Doj_-BAA.mjs → discoverMigrations-VNUFvCwr.mjs} +10 -4
  27. package/dist/packem_shared/{discoverNondeterministicCalls-4KiPQxQU.mjs → discoverNondeterministicCalls-S0N2xLCq.mjs} +1 -1
  28. package/dist/packem_shared/{discoverQueries-BkIi0dBD.mjs → discoverQueries-CJnnnLpd.mjs} +1 -1
  29. package/dist/packem_shared/{discoverR2sqlCalls-BpDqvcUn.mjs → discoverR2sqlCalls-pnpicWfz.mjs} +1 -1
  30. package/dist/packem_shared/{discoverRlsMetadata-DpRB1HMe.mjs → discoverRlsMetadata-DppniPUH.mjs} +1 -1
  31. package/dist/packem_shared/discoverSandboxUsage-BUM2r90k.mjs +38 -0
  32. package/dist/packem_shared/{discoverSchema-DB_QrIQe.mjs → discoverSchema-BaSAvooG.mjs} +326 -14
  33. package/dist/packem_shared/{discoverStorageRulesMetadata-DAqJUxUv.mjs → discoverStorageRulesMetadata-CnHl2rXD.mjs} +1 -1
  34. package/dist/packem_shared/{GENERATED_HEADER-DF1hQcix.mjs → emit-ClTbCLy4.mjs} +1458 -154
  35. package/dist/packem_shared/{emitApp-Ci_hcJNO.mjs → emitApp-CzzrjVqH.mjs} +129 -31
  36. package/dist/packem_shared/formatAdvisories-BmF7Mplc.mjs +115 -0
  37. package/dist/packem_shared/{parse-validator-tuQtHrsr.mjs → parse-validator-BSJo1HGP.mjs} +18 -6
  38. package/dist/packem_shared/redact-oTmsol5A.mjs +33 -0
  39. package/package.json +10 -7
  40. package/dist/packem_shared/SCHEMA_SNAPSHOT_FILENAME-BqOtU2lC.mjs +0 -922
  41. package/dist/packem_shared/WORKFLOWS_FILENAME-DRDQdhfq.mjs +0 -84
  42. package/dist/packem_shared/formatAdvisories-8NIv1k0I.mjs +0 -69
@@ -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-tuQtHrsr.mjs';
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 calleeName = resolveCalleeKind(callee);
294
- if (!calleeName) {
294
+ const calleeName2 = resolveCalleeKind(callee);
295
+ if (!calleeName2) {
295
296
  return void 0;
296
297
  }
297
- if (FUNCTION_KINDS.has(calleeName)) {
298
- return { kind: calleeName, visibility: "public" };
298
+ if (FUNCTION_KINDS.has(calleeName2)) {
299
+ return { kind: calleeName2, visibility: "public" };
299
300
  }
300
- const internalKind = INTERNAL_FACTORIES[calleeName];
301
+ const internalKind = INTERNAL_FACTORIES[calleeName2];
301
302
  if (internalKind) {
302
303
  return { kind: internalKind, visibility: "internal" };
303
304
  }
304
- const lifecycle = LIFECYCLE_FACTORIES[calleeName];
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 listLunoraSourceFiles = (directory, accumulator = []) => {
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" && entry !== "schema.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 { args: argsFromCall(call), kind: classified.kind, returnType: returnTypeFromCall(call), visibility: classified.visibility };
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, unwrapHandlerReturn };
@@ -1,7 +1,7 @@
1
1
  import { relative, sep } from 'node:path';
2
2
  import { Node } from 'ts-morph';
3
- import { listLunoraSourceFiles } from './discoverFunctions-DEgAcRuD.mjs';
4
- import { p as parseValidator, a as parseObjectShape } from './parse-validator-tuQtHrsr.mjs';
3
+ import { listLunoraSourceFiles, unwrapHandlerReturn } from './discoverFunctions-BJ-qR7Rg.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"]);
@@ -72,6 +72,13 @@ const walkRouteChain = (terminalCall, terminalStep) => {
72
72
  state.path = root.path;
73
73
  return state;
74
74
  };
75
+ const chunkTypeFromStreamTerminal = (call) => {
76
+ const handler = call.getArguments()[0];
77
+ if (!handler || !(Node.isArrowFunction(handler) || Node.isFunctionExpression(handler))) {
78
+ return "unknown";
79
+ }
80
+ return unwrapHandlerReturn(handler);
81
+ };
75
82
  const routeFromTerminal = (call, callee, exportName, filePath) => {
76
83
  const step = callee.getName();
77
84
  if (!TERMINAL_STEPS.has(step)) {
@@ -90,6 +97,7 @@ const routeFromTerminal = (call, callee, exportName, filePath) => {
90
97
  path: state.path,
91
98
  searchParams: state.searchParams,
92
99
  stream: state.stream,
100
+ ...state.stream ? { chunkType: chunkTypeFromStreamTerminal(call) } : {},
93
101
  ...state.output ? { output: state.output } : {}
94
102
  };
95
103
  };
@@ -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-DEgAcRuD.mjs';
3
+ import { listLunoraSourceFiles, lunoraRelativePath } from './discoverFunctions-BJ-qR7Rg.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
- return argument && Node.isStringLiteral(argument) ? argument.getLiteralText() : "";
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-DEgAcRuD.mjs';
2
+ import { listLunoraSourceFiles, lunoraRelativePath, classifyProcedureCall } from './discoverFunctions-BJ-qR7Rg.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 };
@@ -1,6 +1,7 @@
1
1
  import { relative, sep } from 'node:path';
2
2
  import { SyntaxKind, Node } from 'ts-morph';
3
- import { listLunoraSourceFiles } from './discoverFunctions-DEgAcRuD.mjs';
3
+ import { diagnosticAt } from './CodegenDiagnosticError-DyQ5FwkM.mjs';
4
+ import { listLunoraSourceFiles } from './discoverFunctions-BJ-qR7Rg.mjs';
4
5
 
5
6
  const TS_EXTENSION_RE = /\.ts$/u;
6
7
  const isDefineMigration = (identifier) => {
@@ -43,9 +44,14 @@ const migrationFromDeclaration = (declaration, relativePath) => {
43
44
  const id = stringProperty(argument, "id");
44
45
  const exportName = declaration.getName();
45
46
  if (id === void 0 || id.trim() === "") {
46
- throw Object.assign(
47
- new Error(`Migration "${exportName}" in "${relativePath}" must declare \`id\` as a non-empty string literal so codegen can key the registry.`),
48
- { code: "MIGRATION_ID_NOT_STATIC", name: "LunoraError", status: 500 }
47
+ throw diagnosticAt(
48
+ argument,
49
+ `Migration "${exportName}" in "${relativePath}" must declare \`id\` as a non-empty string literal so codegen can key the registry.`,
50
+ {
51
+ code: "MIGRATION_ID_NOT_STATIC",
52
+ name: "LunoraError",
53
+ status: 500
54
+ }
49
55
  );
50
56
  }
51
57
  return { exportName, filePath: relativePath, id, table: stringProperty(argument, "table") ?? "" };
@@ -1,5 +1,5 @@
1
1
  import { Node, SyntaxKind } from 'ts-morph';
2
- import { listLunoraSourceFiles, lunoraRelativePath, classifyProcedureCall } from './discoverFunctions-DEgAcRuD.mjs';
2
+ import { listLunoraSourceFiles, lunoraRelativePath, classifyProcedureCall } from './discoverFunctions-BJ-qR7Rg.mjs';
3
3
 
4
4
  const PROPERTY_CALLS = {
5
5
  "crypto.getRandomValues": "crypto.getRandomValues",
@@ -1,5 +1,5 @@
1
1
  import { SyntaxKind, Node } from 'ts-morph';
2
- import { listLunoraSourceFiles, lunoraRelativePath } from './discoverFunctions-DEgAcRuD.mjs';
2
+ import { listLunoraSourceFiles, lunoraRelativePath } from './discoverFunctions-BJ-qR7Rg.mjs';
3
3
 
4
4
  const INDEX_METHODS = /* @__PURE__ */ new Set(["withIndex", "withSearchIndex"]);
5
5
  const isDatabaseQueryCall = (call) => {
@@ -1,5 +1,5 @@
1
1
  import { Node, SyntaxKind } from 'ts-morph';
2
- import { listLunoraSourceFiles, lunoraRelativePath, classifyProcedureCall } from './discoverFunctions-DEgAcRuD.mjs';
2
+ import { listLunoraSourceFiles, lunoraRelativePath, classifyProcedureCall } from './discoverFunctions-BJ-qR7Rg.mjs';
3
3
 
4
4
  const handlerOf = (call, receiver) => {
5
5
  if (receiver) {
@@ -1,5 +1,5 @@
1
1
  import { Node, SyntaxKind } from 'ts-morph';
2
- import { listLunoraSourceFiles, lunoraRelativePath, classifyProcedureCall } from './discoverFunctions-DEgAcRuD.mjs';
2
+ import { listLunoraSourceFiles, lunoraRelativePath, classifyProcedureCall } from './discoverFunctions-BJ-qR7Rg.mjs';
3
3
 
4
4
  const isRlsCall = (node) => {
5
5
  if (!Node.isCallExpression(node)) {
@@ -0,0 +1,38 @@
1
+ import { listLunoraSourceFiles } from './discoverFunctions-BJ-qR7Rg.mjs';
2
+
3
+ const SANDBOX_MODULE_SPECIFIERS = /* @__PURE__ */ new Set(["@lunora/agent", "@lunora/agent/sandbox"]);
4
+ const scanImportDeclaration = (declaration) => {
5
+ const found = { usesSandboxBrowser: false, usesSandboxContainer: false };
6
+ if (!SANDBOX_MODULE_SPECIFIERS.has(declaration.getModuleSpecifierValue()) || declaration.isTypeOnly()) {
7
+ return found;
8
+ }
9
+ for (const named of declaration.getNamedImports()) {
10
+ if (named.isTypeOnly()) {
11
+ continue;
12
+ }
13
+ const name = named.getNameNode().getText();
14
+ if (name === "browserTool") {
15
+ found.usesSandboxBrowser = true;
16
+ } else if (name === "containerTool") {
17
+ found.usesSandboxContainer = true;
18
+ }
19
+ }
20
+ return found;
21
+ };
22
+ const discoverSandboxUsage = (project, lunoraDirectory) => {
23
+ const usage = { usesSandboxBrowser: false, usesSandboxContainer: false };
24
+ for (const filePath of listLunoraSourceFiles(lunoraDirectory)) {
25
+ const sourceFile = project.getSourceFile(filePath) ?? project.addSourceFileAtPath(filePath);
26
+ for (const declaration of sourceFile.getImportDeclarations()) {
27
+ const found = scanImportDeclaration(declaration);
28
+ usage.usesSandboxBrowser ||= found.usesSandboxBrowser;
29
+ usage.usesSandboxContainer ||= found.usesSandboxContainer;
30
+ }
31
+ if (usage.usesSandboxBrowser && usage.usesSandboxContainer) {
32
+ break;
33
+ }
34
+ }
35
+ return usage;
36
+ };
37
+
38
+ export { discoverSandboxUsage };