@beignet/cli 0.0.40 → 0.0.41

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 (93) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +76 -10
  3. package/dist/analysis/source-index.d.ts +38 -0
  4. package/dist/analysis/source-index.d.ts.map +1 -0
  5. package/dist/analysis/source-index.js +271 -0
  6. package/dist/analysis/source-index.js.map +1 -0
  7. package/dist/analysis/workspace.d.ts +16 -0
  8. package/dist/analysis/workspace.d.ts.map +1 -0
  9. package/dist/analysis/workspace.js +134 -0
  10. package/dist/analysis/workspace.js.map +1 -0
  11. package/dist/app-map-schema.d.ts +5 -0
  12. package/dist/app-map-schema.d.ts.map +1 -0
  13. package/dist/app-map-schema.js +26 -0
  14. package/dist/app-map-schema.js.map +1 -0
  15. package/dist/app-map.d.ts +96 -0
  16. package/dist/app-map.d.ts.map +1 -0
  17. package/dist/app-map.js +1141 -0
  18. package/dist/app-map.js.map +1 -0
  19. package/dist/check.d.ts +7 -0
  20. package/dist/check.d.ts.map +1 -1
  21. package/dist/check.js +20 -5
  22. package/dist/check.js.map +1 -1
  23. package/dist/db.d.ts +32 -7
  24. package/dist/db.d.ts.map +1 -1
  25. package/dist/db.js +182 -14
  26. package/dist/db.js.map +1 -1
  27. package/dist/explain.d.ts +98 -0
  28. package/dist/explain.d.ts.map +1 -0
  29. package/dist/explain.js +512 -0
  30. package/dist/explain.js.map +1 -0
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +79 -2
  33. package/dist/index.js.map +1 -1
  34. package/dist/inspect.js +53 -3
  35. package/dist/inspect.js.map +1 -1
  36. package/dist/lib.d.ts +4 -0
  37. package/dist/lib.d.ts.map +1 -1
  38. package/dist/lib.js +2 -0
  39. package/dist/lib.js.map +1 -1
  40. package/dist/make.d.ts.map +1 -1
  41. package/dist/make.js +63 -2
  42. package/dist/make.js.map +1 -1
  43. package/dist/mcp.d.ts +1 -1
  44. package/dist/mcp.d.ts.map +1 -1
  45. package/dist/mcp.js +69 -1
  46. package/dist/mcp.js.map +1 -1
  47. package/dist/preflight.d.ts.map +1 -1
  48. package/dist/preflight.js +10 -0
  49. package/dist/preflight.js.map +1 -1
  50. package/dist/provider-add.d.ts.map +1 -1
  51. package/dist/provider-add.js +167 -13
  52. package/dist/provider-add.js.map +1 -1
  53. package/dist/provider-audit.d.ts +6 -0
  54. package/dist/provider-audit.d.ts.map +1 -1
  55. package/dist/provider-audit.js +63 -15
  56. package/dist/provider-audit.js.map +1 -1
  57. package/dist/templates/agents.d.ts.map +1 -1
  58. package/dist/templates/agents.js +17 -4
  59. package/dist/templates/agents.js.map +1 -1
  60. package/dist/templates/base.d.ts.map +1 -1
  61. package/dist/templates/base.js +10 -4
  62. package/dist/templates/base.js.map +1 -1
  63. package/dist/templates/index.d.ts +1 -1
  64. package/dist/templates/index.d.ts.map +1 -1
  65. package/dist/templates/index.js +8 -1
  66. package/dist/templates/index.js.map +1 -1
  67. package/dist/templates/server.d.ts +3 -0
  68. package/dist/templates/server.d.ts.map +1 -1
  69. package/dist/templates/server.js +55 -1
  70. package/dist/templates/server.js.map +1 -1
  71. package/dist/templates/shared.js +1 -1
  72. package/package.json +2 -2
  73. package/skills/app-structure/SKILL.md +22 -4
  74. package/src/analysis/source-index.ts +395 -0
  75. package/src/analysis/workspace.ts +180 -0
  76. package/src/app-map-schema.ts +28 -0
  77. package/src/app-map.ts +1705 -0
  78. package/src/check.ts +27 -4
  79. package/src/db.ts +232 -14
  80. package/src/explain.ts +786 -0
  81. package/src/index.ts +113 -2
  82. package/src/inspect.ts +70 -2
  83. package/src/lib.ts +36 -0
  84. package/src/make.ts +90 -2
  85. package/src/mcp.ts +107 -1
  86. package/src/preflight.ts +14 -0
  87. package/src/provider-add.ts +211 -12
  88. package/src/provider-audit.ts +127 -22
  89. package/src/templates/agents.ts +17 -4
  90. package/src/templates/base.ts +22 -4
  91. package/src/templates/index.ts +18 -1
  92. package/src/templates/server.ts +58 -1
  93. package/src/templates/shared.ts +1 -1
@@ -0,0 +1,395 @@
1
+ import ts from "typescript";
2
+ import { type AnalysisWorkspace, resolveLocalSourceFile } from "./workspace.js";
3
+
4
+ export type AnalysisSourceLocation = {
5
+ file: string;
6
+ exportName?: string;
7
+ line?: number;
8
+ column?: number;
9
+ };
10
+
11
+ export type ResolvedSourceReference = {
12
+ file: string;
13
+ exportName: string;
14
+ declaration?: ts.Declaration;
15
+ };
16
+
17
+ export type LocalImportReference = {
18
+ file: string;
19
+ importPath: string;
20
+ resolvedFile: string;
21
+ kind: "type" | "value";
22
+ line: number;
23
+ column: number;
24
+ };
25
+
26
+ type ImportBinding =
27
+ | { kind: "named"; file: string; exportName: string }
28
+ | { kind: "namespace"; file: string };
29
+
30
+ type FileIndex = {
31
+ declarations: Map<string, ts.Declaration>;
32
+ imports: Map<string, ImportBinding>;
33
+ reexports: Map<string, { file: string; exportName: string }>;
34
+ exportStars: string[];
35
+ };
36
+
37
+ export type SourceIndex = {
38
+ resolveName(
39
+ file: string,
40
+ name: string,
41
+ ): Promise<ResolvedSourceReference | undefined>;
42
+ resolveExpression(
43
+ file: string,
44
+ expression: ts.Expression,
45
+ ): Promise<ResolvedSourceReference | undefined>;
46
+ resolveExport(
47
+ file: string,
48
+ exportName: string,
49
+ ): Promise<ResolvedSourceReference | undefined>;
50
+ sourceLocation(
51
+ file: string,
52
+ node: ts.Node,
53
+ exportName?: string,
54
+ ): Promise<AnalysisSourceLocation>;
55
+ };
56
+
57
+ export function createSourceIndex(workspace: AnalysisWorkspace): SourceIndex {
58
+ const indexes = new Map<string, FileIndex>();
59
+
60
+ const readIndex = async (file: string): Promise<FileIndex> => {
61
+ const cached = indexes.get(file);
62
+ if (cached) return cached;
63
+
64
+ const sourceFile = await workspace.readSourceFile(file);
65
+ const index: FileIndex = {
66
+ declarations: new Map(),
67
+ imports: new Map(),
68
+ reexports: new Map(),
69
+ exportStars: [],
70
+ };
71
+ indexes.set(file, index);
72
+
73
+ for (const statement of sourceFile.statements) {
74
+ if (ts.isImportDeclaration(statement)) {
75
+ const source = moduleText(statement.moduleSpecifier);
76
+ const target = source
77
+ ? resolveLocalSourceFile(workspace, file, source)
78
+ : undefined;
79
+ if (!target || !statement.importClause) continue;
80
+
81
+ if (statement.importClause.name) {
82
+ index.imports.set(statement.importClause.name.text, {
83
+ kind: "named",
84
+ file: target,
85
+ exportName: "default",
86
+ });
87
+ }
88
+
89
+ const bindings = statement.importClause.namedBindings;
90
+ if (bindings && ts.isNamespaceImport(bindings)) {
91
+ index.imports.set(bindings.name.text, {
92
+ kind: "namespace",
93
+ file: target,
94
+ });
95
+ } else if (bindings && ts.isNamedImports(bindings)) {
96
+ for (const element of bindings.elements) {
97
+ index.imports.set(element.name.text, {
98
+ kind: "named",
99
+ file: target,
100
+ exportName: element.propertyName?.text ?? element.name.text,
101
+ });
102
+ }
103
+ }
104
+ continue;
105
+ }
106
+
107
+ if (ts.isExportDeclaration(statement)) {
108
+ const source = statement.moduleSpecifier
109
+ ? moduleText(statement.moduleSpecifier)
110
+ : undefined;
111
+ const target = source
112
+ ? resolveLocalSourceFile(workspace, file, source)
113
+ : undefined;
114
+ if (!target) continue;
115
+
116
+ if (!statement.exportClause) {
117
+ index.exportStars.push(target);
118
+ continue;
119
+ }
120
+
121
+ if (ts.isNamedExports(statement.exportClause)) {
122
+ for (const element of statement.exportClause.elements) {
123
+ index.reexports.set(element.name.text, {
124
+ file: target,
125
+ exportName: element.propertyName?.text ?? element.name.text,
126
+ });
127
+ }
128
+ }
129
+ continue;
130
+ }
131
+
132
+ if (ts.isVariableStatement(statement) && hasExportModifier(statement)) {
133
+ for (const declaration of statement.declarationList.declarations) {
134
+ if (ts.isIdentifier(declaration.name)) {
135
+ index.declarations.set(declaration.name.text, declaration);
136
+ }
137
+ }
138
+ continue;
139
+ }
140
+
141
+ if (
142
+ (ts.isFunctionDeclaration(statement) ||
143
+ ts.isClassDeclaration(statement) ||
144
+ ts.isInterfaceDeclaration(statement) ||
145
+ ts.isTypeAliasDeclaration(statement) ||
146
+ ts.isEnumDeclaration(statement)) &&
147
+ hasExportModifier(statement) &&
148
+ statement.name
149
+ ) {
150
+ index.declarations.set(statement.name.text, statement);
151
+ }
152
+ }
153
+
154
+ return index;
155
+ };
156
+
157
+ const resolveExport = async (
158
+ file: string,
159
+ exportName: string,
160
+ visited = new Set<string>(),
161
+ ): Promise<ResolvedSourceReference | undefined> => {
162
+ const key = `${file}#${exportName}`;
163
+ if (visited.has(key)) return undefined;
164
+ visited.add(key);
165
+
166
+ const index = await readIndex(file);
167
+ const declaration = index.declarations.get(exportName);
168
+ if (declaration) return { file, exportName, declaration };
169
+
170
+ const reexport = index.reexports.get(exportName);
171
+ if (reexport) {
172
+ return (
173
+ (await resolveExport(reexport.file, reexport.exportName, visited)) ?? {
174
+ file: reexport.file,
175
+ exportName: reexport.exportName,
176
+ }
177
+ );
178
+ }
179
+
180
+ for (const target of index.exportStars) {
181
+ const resolved = await resolveExport(target, exportName, visited);
182
+ if (resolved) return resolved;
183
+ }
184
+
185
+ return undefined;
186
+ };
187
+
188
+ return {
189
+ async resolveName(file, name) {
190
+ const index = await readIndex(file);
191
+ const local = index.declarations.get(name);
192
+ if (local) {
193
+ return { file, exportName: name, declaration: local };
194
+ }
195
+
196
+ const binding = index.imports.get(name);
197
+ if (binding?.kind !== "named") return undefined;
198
+ return (
199
+ (await resolveExport(binding.file, binding.exportName)) ?? {
200
+ file: binding.file,
201
+ exportName: binding.exportName,
202
+ }
203
+ );
204
+ },
205
+ async resolveExpression(file, rawExpression) {
206
+ const expression = unwrapExpression(rawExpression);
207
+ const index = await readIndex(file);
208
+ if (ts.isIdentifier(expression)) {
209
+ const local = index.declarations.get(expression.text);
210
+ if (local) {
211
+ return { file, exportName: expression.text, declaration: local };
212
+ }
213
+
214
+ const binding = index.imports.get(expression.text);
215
+ if (binding?.kind !== "named") return undefined;
216
+ return (
217
+ (await resolveExport(binding.file, binding.exportName)) ?? {
218
+ file: binding.file,
219
+ exportName: binding.exportName,
220
+ }
221
+ );
222
+ }
223
+
224
+ if (
225
+ ts.isPropertyAccessExpression(expression) &&
226
+ ts.isIdentifier(expression.expression)
227
+ ) {
228
+ const binding = index.imports.get(expression.expression.text);
229
+ if (binding?.kind !== "namespace") return undefined;
230
+ return (
231
+ (await resolveExport(binding.file, expression.name.text)) ?? {
232
+ file: binding.file,
233
+ exportName: expression.name.text,
234
+ }
235
+ );
236
+ }
237
+
238
+ return undefined;
239
+ },
240
+ resolveExport(file, exportName) {
241
+ return resolveExport(file, exportName);
242
+ },
243
+ async sourceLocation(file, node, exportName) {
244
+ const sourceFile = await workspace.readSourceFile(file);
245
+ const position = sourceFile.getLineAndCharacterOfPosition(
246
+ node.getStart(),
247
+ );
248
+ return {
249
+ file,
250
+ ...(exportName ? { exportName } : {}),
251
+ line: position.line + 1,
252
+ column: position.character + 1,
253
+ };
254
+ },
255
+ };
256
+ }
257
+
258
+ export function exportedVariableDeclarations(
259
+ sourceFile: ts.SourceFile,
260
+ ): Array<{ exportName: string; declaration: ts.VariableDeclaration }> {
261
+ const declarations: Array<{
262
+ exportName: string;
263
+ declaration: ts.VariableDeclaration;
264
+ }> = [];
265
+
266
+ for (const statement of sourceFile.statements) {
267
+ if (!ts.isVariableStatement(statement) || !hasExportModifier(statement)) {
268
+ continue;
269
+ }
270
+ for (const declaration of statement.declarationList.declarations) {
271
+ if (ts.isIdentifier(declaration.name)) {
272
+ declarations.push({
273
+ exportName: declaration.name.text,
274
+ declaration,
275
+ });
276
+ }
277
+ }
278
+ }
279
+
280
+ return declarations;
281
+ }
282
+
283
+ export function literalText(node: ts.Node | undefined): string | undefined {
284
+ return node &&
285
+ (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node))
286
+ ? node.text
287
+ : undefined;
288
+ }
289
+
290
+ export function propertyAssignment(
291
+ object: ts.ObjectLiteralExpression,
292
+ name: string,
293
+ ): ts.PropertyAssignment | undefined {
294
+ return object.properties.find(
295
+ (property): property is ts.PropertyAssignment =>
296
+ ts.isPropertyAssignment(property) && propertyName(property.name) === name,
297
+ );
298
+ }
299
+
300
+ export function propertyName(node: ts.PropertyName): string | undefined {
301
+ if (ts.isIdentifier(node) || ts.isStringLiteral(node)) return node.text;
302
+ return undefined;
303
+ }
304
+
305
+ export function visitCalls(
306
+ node: ts.Node,
307
+ visit: (call: ts.CallExpression) => void,
308
+ ): void {
309
+ const walk = (current: ts.Node) => {
310
+ if (ts.isCallExpression(current)) visit(current);
311
+ current.forEachChild(walk);
312
+ };
313
+ walk(node);
314
+ }
315
+
316
+ export async function localImportReferences(
317
+ workspace: AnalysisWorkspace,
318
+ file: string,
319
+ ): Promise<LocalImportReference[]> {
320
+ const sourceFile = await workspace.readSourceFile(file);
321
+ const references: LocalImportReference[] = [];
322
+
323
+ for (const statement of sourceFile.statements) {
324
+ if (
325
+ !ts.isImportDeclaration(statement) &&
326
+ !ts.isExportDeclaration(statement)
327
+ ) {
328
+ continue;
329
+ }
330
+ if (!statement.moduleSpecifier) continue;
331
+
332
+ const importPath = moduleText(statement.moduleSpecifier);
333
+ const resolvedFile = importPath
334
+ ? resolveLocalSourceFile(workspace, file, importPath)
335
+ : undefined;
336
+ if (!importPath || !resolvedFile) continue;
337
+
338
+ const position = sourceFile.getLineAndCharacterOfPosition(
339
+ statement.getStart(sourceFile),
340
+ );
341
+ const typeOnly = ts.isImportDeclaration(statement)
342
+ ? Boolean(
343
+ statement.importClause?.isTypeOnly ||
344
+ (statement.importClause?.namedBindings &&
345
+ ts.isNamedImports(statement.importClause.namedBindings) &&
346
+ statement.importClause.namedBindings.elements.length > 0 &&
347
+ statement.importClause.namedBindings.elements.every(
348
+ (element) => element.isTypeOnly,
349
+ )),
350
+ )
351
+ : statement.isTypeOnly ||
352
+ (statement.exportClause &&
353
+ ts.isNamedExports(statement.exportClause) &&
354
+ statement.exportClause.elements.length > 0 &&
355
+ statement.exportClause.elements.every(
356
+ (element) => element.isTypeOnly,
357
+ ));
358
+ references.push({
359
+ file,
360
+ importPath,
361
+ resolvedFile,
362
+ kind: typeOnly ? "type" : "value",
363
+ line: position.line + 1,
364
+ column: position.character + 1,
365
+ });
366
+ }
367
+
368
+ return references;
369
+ }
370
+
371
+ function moduleText(node: ts.Expression): string | undefined {
372
+ return ts.isStringLiteral(node) ? node.text : undefined;
373
+ }
374
+
375
+ function hasExportModifier(node: ts.Node): boolean {
376
+ return Boolean(
377
+ ts.canHaveModifiers(node) &&
378
+ ts
379
+ .getModifiers(node)
380
+ ?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword),
381
+ );
382
+ }
383
+
384
+ function unwrapExpression(expression: ts.Expression): ts.Expression {
385
+ let current = expression;
386
+ while (
387
+ ts.isAsExpression(current) ||
388
+ ts.isSatisfiesExpression(current) ||
389
+ ts.isParenthesizedExpression(current) ||
390
+ ts.isNonNullExpression(current)
391
+ ) {
392
+ current = current.expression;
393
+ }
394
+ return current;
395
+ }
@@ -0,0 +1,180 @@
1
+ import { readdir, readFile, stat } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import ts from "typescript";
4
+ import {
5
+ directoryPath,
6
+ loadBeignetConfig,
7
+ normalizePath,
8
+ type ResolvedBeignetConfig,
9
+ } from "../config.js";
10
+
11
+ const ignoredDirectoryNames = new Set([
12
+ ".git",
13
+ ".next",
14
+ ".turbo",
15
+ "coverage",
16
+ "dist",
17
+ "node_modules",
18
+ ]);
19
+
20
+ export type AnalysisWorkspace = {
21
+ targetDir: string;
22
+ files: string[];
23
+ fileSet: Set<string>;
24
+ config: ResolvedBeignetConfig;
25
+ atAliasRoot: string;
26
+ readSource(file: string): Promise<string>;
27
+ readSourceFile(file: string): Promise<ts.SourceFile>;
28
+ };
29
+
30
+ export async function createAnalysisWorkspace(
31
+ cwd = process.cwd(),
32
+ ): Promise<AnalysisWorkspace> {
33
+ const targetDir = path.resolve(cwd);
34
+ await assertDirectory(targetDir);
35
+ const files = await listProjectFiles(targetDir);
36
+ const fileSet = new Set(files);
37
+ const config = await loadBeignetConfig(targetDir, files);
38
+ const atAliasRoot = resolveAtAliasRoot(targetDir);
39
+ const sources = new Map<string, string>();
40
+ const sourceFiles = new Map<string, ts.SourceFile>();
41
+
42
+ const readSource = async (file: string): Promise<string> => {
43
+ const cached = sources.get(file);
44
+ if (cached !== undefined) return cached;
45
+
46
+ const source = await readFile(path.join(targetDir, file), "utf8");
47
+ sources.set(file, source);
48
+ return source;
49
+ };
50
+
51
+ return {
52
+ targetDir,
53
+ files,
54
+ fileSet,
55
+ config,
56
+ atAliasRoot,
57
+ readSource,
58
+ async readSourceFile(file) {
59
+ const cached = sourceFiles.get(file);
60
+ if (cached) return cached;
61
+
62
+ const sourceFile = ts.createSourceFile(
63
+ file,
64
+ await readSource(file),
65
+ ts.ScriptTarget.Latest,
66
+ true,
67
+ scriptKind(file),
68
+ );
69
+ sourceFiles.set(file, sourceFile);
70
+ return sourceFile;
71
+ },
72
+ };
73
+ }
74
+
75
+ export async function assertDirectory(targetDir: string): Promise<void> {
76
+ try {
77
+ const stats = await stat(targetDir);
78
+ if (!stats.isDirectory()) {
79
+ throw new Error(`${targetDir} is not a directory.`);
80
+ }
81
+ } catch (error) {
82
+ if (error instanceof Error && error.message.includes("not a directory")) {
83
+ throw error;
84
+ }
85
+ throw new Error(`Directory ${targetDir} does not exist.`);
86
+ }
87
+ }
88
+
89
+ export async function listProjectFiles(targetDir: string): Promise<string[]> {
90
+ const files: string[] = [];
91
+
92
+ async function visit(relativeDir: string): Promise<void> {
93
+ const absoluteDir = path.join(targetDir, relativeDir);
94
+ const entries = await readdir(absoluteDir, { withFileTypes: true });
95
+
96
+ for (const entry of entries) {
97
+ if (ignoredDirectoryNames.has(entry.name)) continue;
98
+
99
+ const relativePath = normalizePath(path.join(relativeDir, entry.name));
100
+ if (entry.isDirectory()) {
101
+ await visit(relativePath);
102
+ } else if (entry.isFile()) {
103
+ files.push(relativePath);
104
+ }
105
+ }
106
+ }
107
+
108
+ await visit("");
109
+ return files.sort();
110
+ }
111
+
112
+ export function resolveLocalSourceFile(
113
+ workspace: Pick<AnalysisWorkspace, "atAliasRoot" | "fileSet">,
114
+ importerFile: string,
115
+ importPath: string,
116
+ ): string | undefined {
117
+ let candidate: string;
118
+ if (importPath.startsWith("@/")) {
119
+ candidate = path.posix.join(
120
+ workspace.atAliasRoot,
121
+ importPath.slice("@/".length),
122
+ );
123
+ } else if (importPath.startsWith(".")) {
124
+ candidate = normalizePath(
125
+ path.join(path.dirname(importerFile), importPath),
126
+ );
127
+ } else {
128
+ return undefined;
129
+ }
130
+
131
+ const withoutExtension = candidate.replace(/\.(c|m)?tsx?$/, "");
132
+ const candidates = [
133
+ candidate,
134
+ `${withoutExtension}.ts`,
135
+ `${withoutExtension}.tsx`,
136
+ `${withoutExtension}.mts`,
137
+ `${withoutExtension}.cts`,
138
+ `${withoutExtension}/index.ts`,
139
+ `${withoutExtension}/index.tsx`,
140
+ `${withoutExtension}/index.mts`,
141
+ `${withoutExtension}/index.cts`,
142
+ ];
143
+
144
+ return candidates.find((file) => workspace.fileSet.has(file));
145
+ }
146
+
147
+ function resolveAtAliasRoot(targetDir: string): string {
148
+ const parsed = ts.getParsedCommandLineOfConfigFile(
149
+ path.join(targetDir, "tsconfig.json"),
150
+ {},
151
+ {
152
+ ...ts.sys,
153
+ onUnRecoverableConfigFileDiagnostic: () => {},
154
+ },
155
+ );
156
+ const aliasTarget = parsed?.options.paths?.["@/*"]?.[0];
157
+ if (!aliasTarget) return "";
158
+
159
+ const targetWithoutWildcard = aliasTarget.replace(/[/\\]?\*$/, "");
160
+ const baseUrl = parsed.options.baseUrl ?? targetDir;
161
+ const relativeTarget = normalizePath(
162
+ path.relative(targetDir, path.resolve(baseUrl, targetWithoutWildcard)),
163
+ );
164
+ if (
165
+ relativeTarget === "" ||
166
+ relativeTarget === "." ||
167
+ relativeTarget === ".." ||
168
+ relativeTarget.startsWith("../")
169
+ ) {
170
+ return "";
171
+ }
172
+ return directoryPath(relativeTarget);
173
+ }
174
+
175
+ function scriptKind(file: string): ts.ScriptKind {
176
+ if (file.endsWith(".tsx")) return ts.ScriptKind.TSX;
177
+ if (file.endsWith(".jsx")) return ts.ScriptKind.JSX;
178
+ if (file.endsWith(".js") || file.endsWith(".mjs")) return ts.ScriptKind.JS;
179
+ return ts.ScriptKind.TS;
180
+ }
@@ -0,0 +1,28 @@
1
+ /** Node kinds emitted by the version 1 Beignet app-map schema. */
2
+ export const appMapNodeKinds = [
3
+ "feature",
4
+ "contract",
5
+ "route",
6
+ "route-group",
7
+ "use-case",
8
+ "policy",
9
+ "ability",
10
+ "event",
11
+ "listener",
12
+ "job",
13
+ "schedule",
14
+ "task",
15
+ "notification",
16
+ "upload",
17
+ "agent-capability",
18
+ "registry",
19
+ "port",
20
+ "provider",
21
+ "table",
22
+ "openapi",
23
+ "entrypoint",
24
+ "test",
25
+ ] as const;
26
+
27
+ /** One node-kind value from the version 1 Beignet app-map schema. */
28
+ export type AppMapNodeKind = (typeof appMapNodeKinds)[number];