@beignet/cli 0.0.47 → 0.0.49

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 (144) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/README.md +126 -42
  3. package/dist/analysis/contract-factories.d.ts +17 -0
  4. package/dist/analysis/contract-factories.d.ts.map +1 -0
  5. package/dist/analysis/contract-factories.js +176 -0
  6. package/dist/analysis/contract-factories.js.map +1 -0
  7. package/dist/analysis/layers.d.ts +5 -0
  8. package/dist/analysis/layers.d.ts.map +1 -0
  9. package/dist/analysis/layers.js +172 -0
  10. package/dist/analysis/layers.js.map +1 -0
  11. package/dist/analysis/port-wiring.d.ts +23 -0
  12. package/dist/analysis/port-wiring.d.ts.map +1 -0
  13. package/dist/analysis/port-wiring.js +379 -0
  14. package/dist/analysis/port-wiring.js.map +1 -0
  15. package/dist/analysis/source-index.d.ts +5 -0
  16. package/dist/analysis/source-index.d.ts.map +1 -1
  17. package/dist/analysis/source-index.js +171 -42
  18. package/dist/analysis/source-index.js.map +1 -1
  19. package/dist/analysis/workspace.d.ts +10 -4
  20. package/dist/analysis/workspace.d.ts.map +1 -1
  21. package/dist/analysis/workspace.js +66 -6
  22. package/dist/analysis/workspace.js.map +1 -1
  23. package/dist/app-map.d.ts.map +1 -1
  24. package/dist/app-map.js +311 -92
  25. package/dist/app-map.js.map +1 -1
  26. package/dist/check.d.ts +8 -1
  27. package/dist/check.d.ts.map +1 -1
  28. package/dist/check.js +55 -9
  29. package/dist/check.js.map +1 -1
  30. package/dist/db.d.ts +38 -0
  31. package/dist/db.d.ts.map +1 -1
  32. package/dist/db.js +52 -0
  33. package/dist/db.js.map +1 -1
  34. package/dist/explain.js +4 -4
  35. package/dist/explain.js.map +1 -1
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +73 -6
  38. package/dist/index.js.map +1 -1
  39. package/dist/inspect.d.ts.map +1 -1
  40. package/dist/inspect.js +454 -170
  41. package/dist/inspect.js.map +1 -1
  42. package/dist/lib.d.ts +2 -2
  43. package/dist/lib.d.ts.map +1 -1
  44. package/dist/lib.js +1 -1
  45. package/dist/lib.js.map +1 -1
  46. package/dist/lint.d.ts.map +1 -1
  47. package/dist/lint.js +309 -487
  48. package/dist/lint.js.map +1 -1
  49. package/dist/make/inbox.d.ts.map +1 -1
  50. package/dist/make/inbox.js +12 -5
  51. package/dist/make/inbox.js.map +1 -1
  52. package/dist/make/payments.js +3 -3
  53. package/dist/make/payments.js.map +1 -1
  54. package/dist/make/tenancy.js +1 -1
  55. package/dist/make/tenancy.js.map +1 -1
  56. package/dist/make.d.ts.map +1 -1
  57. package/dist/make.js +144 -8
  58. package/dist/make.js.map +1 -1
  59. package/dist/mcp.d.ts +1 -1
  60. package/dist/mcp.d.ts.map +1 -1
  61. package/dist/mcp.js +56 -19
  62. package/dist/mcp.js.map +1 -1
  63. package/dist/preflight.d.ts +11 -3
  64. package/dist/preflight.d.ts.map +1 -1
  65. package/dist/preflight.js +139 -16
  66. package/dist/preflight.js.map +1 -1
  67. package/dist/provider-add.d.ts.map +1 -1
  68. package/dist/provider-add.js +149 -9
  69. package/dist/provider-add.js.map +1 -1
  70. package/dist/provider-audit.d.ts +13 -0
  71. package/dist/provider-audit.d.ts.map +1 -1
  72. package/dist/provider-audit.js +122 -8
  73. package/dist/provider-audit.js.map +1 -1
  74. package/dist/registry-edits.d.ts +7 -0
  75. package/dist/registry-edits.d.ts.map +1 -1
  76. package/dist/registry-edits.js +237 -53
  77. package/dist/registry-edits.js.map +1 -1
  78. package/dist/templates/agents.d.ts.map +1 -1
  79. package/dist/templates/agents.js +29 -7
  80. package/dist/templates/agents.js.map +1 -1
  81. package/dist/templates/base.d.ts +1 -0
  82. package/dist/templates/base.d.ts.map +1 -1
  83. package/dist/templates/base.js +20 -6
  84. package/dist/templates/base.js.map +1 -1
  85. package/dist/templates/db/index.d.ts +1 -0
  86. package/dist/templates/db/index.d.ts.map +1 -1
  87. package/dist/templates/db/index.js.map +1 -1
  88. package/dist/templates/db/migration-status.d.ts +9 -0
  89. package/dist/templates/db/migration-status.d.ts.map +1 -0
  90. package/dist/templates/db/migration-status.js +117 -0
  91. package/dist/templates/db/migration-status.js.map +1 -0
  92. package/dist/templates/db/mysql.d.ts.map +1 -1
  93. package/dist/templates/db/mysql.js +23 -0
  94. package/dist/templates/db/mysql.js.map +1 -1
  95. package/dist/templates/db/postgres.d.ts.map +1 -1
  96. package/dist/templates/db/postgres.js +21 -0
  97. package/dist/templates/db/postgres.js.map +1 -1
  98. package/dist/templates/db/sqlite.d.ts.map +1 -1
  99. package/dist/templates/db/sqlite.js +53 -0
  100. package/dist/templates/db/sqlite.js.map +1 -1
  101. package/dist/templates/index.d.ts.map +1 -1
  102. package/dist/templates/index.js +5 -0
  103. package/dist/templates/index.js.map +1 -1
  104. package/dist/templates/server.d.ts.map +1 -1
  105. package/dist/templates/server.js +2 -0
  106. package/dist/templates/server.js.map +1 -1
  107. package/dist/templates/shared.d.ts +1 -0
  108. package/dist/templates/shared.d.ts.map +1 -1
  109. package/dist/templates/shared.js +4 -2
  110. package/dist/templates/shared.js.map +1 -1
  111. package/package.json +7 -6
  112. package/skills/app-structure/SKILL.md +56 -11
  113. package/src/analysis/contract-factories.ts +256 -0
  114. package/src/analysis/layers.ts +266 -0
  115. package/src/analysis/port-wiring.ts +506 -0
  116. package/src/analysis/source-index.ts +225 -57
  117. package/src/analysis/workspace.ts +115 -6
  118. package/src/app-map.ts +418 -109
  119. package/src/check.ts +86 -10
  120. package/src/db.ts +97 -0
  121. package/src/explain.ts +4 -4
  122. package/src/index.ts +108 -7
  123. package/src/inspect.ts +659 -216
  124. package/src/lib.ts +9 -1
  125. package/src/lint.ts +393 -643
  126. package/src/make/inbox.ts +12 -5
  127. package/src/make/payments.ts +3 -3
  128. package/src/make/tenancy.ts +1 -1
  129. package/src/make.ts +225 -8
  130. package/src/mcp.ts +73 -21
  131. package/src/preflight.ts +195 -16
  132. package/src/provider-add.ts +193 -9
  133. package/src/provider-audit.ts +210 -18
  134. package/src/registry-edits.ts +313 -63
  135. package/src/templates/agents.ts +29 -7
  136. package/src/templates/base.ts +21 -4
  137. package/src/templates/db/index.ts +1 -0
  138. package/src/templates/db/migration-status.ts +126 -0
  139. package/src/templates/db/mysql.ts +23 -0
  140. package/src/templates/db/postgres.ts +21 -0
  141. package/src/templates/db/sqlite.ts +53 -0
  142. package/src/templates/index.ts +5 -0
  143. package/src/templates/server.ts +2 -0
  144. package/src/templates/shared.ts +4 -2
@@ -0,0 +1,506 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import ts from "typescript";
4
+ import type { ResolvedBeignetConfig } from "../config.js";
5
+ import type { AnalysisWorkspace } from "./workspace.js";
6
+
7
+ type PortAnalysisWorkspace = Pick<
8
+ AnalysisWorkspace,
9
+ "config" | "fileSet" | "readSourceFile"
10
+ >;
11
+
12
+ export type PortWiringAnalysis = {
13
+ portsFileExists: boolean;
14
+ portWiringFileExists: boolean;
15
+ properties: Map<string, ts.PropertySignature>;
16
+ declared: Set<string>;
17
+ bound: Set<string>;
18
+ deferred: Set<string>;
19
+ appPortsInferredFromWiring: boolean;
20
+ declarationIndeterminate: boolean;
21
+ indeterminate: boolean;
22
+ };
23
+
24
+ export async function analyzePortWiring(
25
+ workspace: PortAnalysisWorkspace,
26
+ ): Promise<PortWiringAnalysis> {
27
+ const result: PortWiringAnalysis = {
28
+ portsFileExists: workspace.fileSet.has(workspace.config.paths.ports),
29
+ portWiringFileExists: workspace.fileSet.has(
30
+ workspace.config.paths.portWiring,
31
+ ),
32
+ properties: new Map(),
33
+ declared: new Set(),
34
+ bound: new Set(),
35
+ deferred: new Set(),
36
+ appPortsInferredFromWiring: false,
37
+ declarationIndeterminate: false,
38
+ indeterminate: false,
39
+ };
40
+
41
+ let inferredAppPortsName: string | undefined;
42
+ if (result.portsFileExists) {
43
+ const sourceFile = await workspace.readSourceFile(
44
+ workspace.config.paths.ports,
45
+ );
46
+ const declarations = collectTypeDeclarations(sourceFile);
47
+ inferredAppPortsName = inferredTypeQueryName("AppPorts", declarations);
48
+ const collection = inferredAppPortsName
49
+ ? { properties: [], indeterminate: false }
50
+ : collectNamedTypeProperties("AppPorts", declarations, true);
51
+ result.declarationIndeterminate = collection.indeterminate;
52
+ for (const property of collection.properties) {
53
+ const name = propertyName(property.name);
54
+ if (name && !result.properties.has(name)) {
55
+ result.properties.set(name, property);
56
+ result.declared.add(name);
57
+ }
58
+ }
59
+ }
60
+
61
+ if (!result.portWiringFileExists) {
62
+ if (inferredAppPortsName) result.declarationIndeterminate = true;
63
+ return result;
64
+ }
65
+ const sourceFile = await workspace.readSourceFile(
66
+ workspace.config.paths.portWiring,
67
+ );
68
+ const values = collectVariableInitializers(sourceFile);
69
+ const definitions = portDefinitionCalls(sourceFile);
70
+ if (definitions.indeterminate) result.indeterminate = true;
71
+
72
+ for (const call of definitions.calls) {
73
+ const objectArgument = call.arguments
74
+ .map((argument) => resolveLocalExpression(argument, values))
75
+ .find(ts.isObjectLiteralExpression);
76
+ if (!objectArgument) {
77
+ if (call.arguments.length > 0) result.indeterminate = true;
78
+ continue;
79
+ }
80
+
81
+ const bound = objectPropertyAnalysis(objectArgument, "bound");
82
+ const deferred = objectPropertyAnalysis(objectArgument, "deferred");
83
+ if (
84
+ !bound.property &&
85
+ !bound.indeterminate &&
86
+ !deferred.property &&
87
+ !deferred.indeterminate
88
+ ) {
89
+ if (addObjectPropertyNames(objectArgument, result.bound)) {
90
+ result.indeterminate = true;
91
+ }
92
+ continue;
93
+ }
94
+ if (bound.indeterminate || deferred.indeterminate) {
95
+ result.indeterminate = true;
96
+ }
97
+
98
+ const boundValue = bound.property
99
+ ? resolveLocalExpression(propertyInitializer(bound.property), values)
100
+ : undefined;
101
+ if (boundValue && ts.isObjectLiteralExpression(boundValue)) {
102
+ if (addObjectPropertyNames(boundValue, result.bound)) {
103
+ result.indeterminate = true;
104
+ }
105
+ } else if (bound.property) {
106
+ result.indeterminate = true;
107
+ }
108
+
109
+ const deferredValue = deferred.property
110
+ ? resolveLocalExpression(propertyInitializer(deferred.property), values)
111
+ : undefined;
112
+ if (deferredValue && ts.isArrayLiteralExpression(deferredValue)) {
113
+ for (const element of deferredValue.elements) {
114
+ const value = literalText(resolveLocalExpression(element, values));
115
+ if (value) result.deferred.add(value);
116
+ else result.indeterminate = true;
117
+ }
118
+ } else if (deferred.property) {
119
+ result.indeterminate = true;
120
+ }
121
+ }
122
+
123
+ if (inferredAppPortsName) {
124
+ if (
125
+ !definitions.indeterminate &&
126
+ definitions.name === inferredAppPortsName
127
+ ) {
128
+ result.appPortsInferredFromWiring = true;
129
+ for (const name of result.bound) result.declared.add(name);
130
+ for (const name of result.deferred) result.declared.add(name);
131
+ } else {
132
+ result.declarationIndeterminate = true;
133
+ }
134
+ }
135
+
136
+ return result;
137
+ }
138
+
139
+ function portDefinitionCalls(sourceFile: ts.SourceFile): {
140
+ calls: ts.CallExpression[];
141
+ name?: string;
142
+ indeterminate: boolean;
143
+ } {
144
+ const exportedNames = locallyExportedNames(sourceFile);
145
+ const candidates: {
146
+ name?: string;
147
+ exported: boolean;
148
+ calls: ts.CallExpression[];
149
+ }[] = [];
150
+
151
+ for (const statement of sourceFile.statements) {
152
+ if (!ts.isVariableStatement(statement)) continue;
153
+ const statementExported = Boolean(
154
+ statement.modifiers?.some(
155
+ (modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword,
156
+ ),
157
+ );
158
+ for (const declaration of statement.declarationList.declarations) {
159
+ if (!declaration.initializer) continue;
160
+ const calls = definePortsCalls(declaration.initializer);
161
+ if (calls.length === 0) continue;
162
+ const name = ts.isIdentifier(declaration.name)
163
+ ? declaration.name.text
164
+ : undefined;
165
+ candidates.push({
166
+ ...(name ? { name } : {}),
167
+ exported: statementExported || (name ? exportedNames.has(name) : false),
168
+ calls,
169
+ });
170
+ }
171
+ }
172
+
173
+ const canonical = candidates.filter(
174
+ (candidate) => candidate.name === "initialPorts" && candidate.exported,
175
+ );
176
+ const exported = candidates.filter((candidate) => candidate.exported);
177
+ const selected =
178
+ canonical.length > 0
179
+ ? canonical
180
+ : exported.length > 0
181
+ ? exported
182
+ : candidates;
183
+
184
+ if (selected.length === 1 && selected[0].calls.length === 1) {
185
+ return {
186
+ calls: selected[0].calls,
187
+ ...(selected[0].name ? { name: selected[0].name } : {}),
188
+ indeterminate: false,
189
+ };
190
+ }
191
+ if (selected.length > 0) return { calls: [], indeterminate: true };
192
+
193
+ const fallback = definePortsCalls(sourceFile);
194
+ return fallback.length === 1
195
+ ? { calls: fallback, indeterminate: false }
196
+ : { calls: [], indeterminate: fallback.length > 0 };
197
+ }
198
+
199
+ function locallyExportedNames(sourceFile: ts.SourceFile): Set<string> {
200
+ const names = new Set<string>();
201
+ for (const statement of sourceFile.statements) {
202
+ if (
203
+ !ts.isExportDeclaration(statement) ||
204
+ !statement.exportClause ||
205
+ !ts.isNamedExports(statement.exportClause) ||
206
+ statement.moduleSpecifier
207
+ ) {
208
+ continue;
209
+ }
210
+ for (const element of statement.exportClause.elements) {
211
+ names.add(element.propertyName?.text ?? element.name.text);
212
+ }
213
+ }
214
+ return names;
215
+ }
216
+
217
+ function definePortsCalls(node: ts.Node): ts.CallExpression[] {
218
+ const calls: ts.CallExpression[] = [];
219
+ visitCalls(node, (call) => {
220
+ if (callName(call) === "definePorts" && call.arguments.length > 0) {
221
+ calls.push(call);
222
+ }
223
+ });
224
+ return calls;
225
+ }
226
+
227
+ export async function analyzePortWiringFiles(options: {
228
+ targetDir: string;
229
+ files: readonly string[];
230
+ config: ResolvedBeignetConfig;
231
+ }): Promise<PortWiringAnalysis> {
232
+ const sourceFiles = new Map<string, ts.SourceFile>();
233
+ return analyzePortWiring({
234
+ config: options.config,
235
+ fileSet: new Set(options.files),
236
+ async readSourceFile(file) {
237
+ const cached = sourceFiles.get(file);
238
+ if (cached) return cached;
239
+ const sourceFile = ts.createSourceFile(
240
+ file,
241
+ await readFile(path.join(options.targetDir, file), "utf8"),
242
+ ts.ScriptTarget.Latest,
243
+ true,
244
+ file.endsWith(".tsx") ? ts.ScriptKind.TSX : ts.ScriptKind.TS,
245
+ );
246
+ sourceFiles.set(file, sourceFile);
247
+ return sourceFile;
248
+ },
249
+ });
250
+ }
251
+
252
+ type TypeDeclaration = ts.TypeAliasDeclaration | ts.InterfaceDeclaration;
253
+ type TypeDeclarations = Map<string, TypeDeclaration[]>;
254
+ type PropertyCollection = {
255
+ properties: ts.PropertySignature[];
256
+ indeterminate: boolean;
257
+ };
258
+
259
+ function collectTypeDeclarations(sourceFile: ts.SourceFile): TypeDeclarations {
260
+ const declarations: TypeDeclarations = new Map();
261
+ for (const statement of sourceFile.statements) {
262
+ if (
263
+ ts.isTypeAliasDeclaration(statement) ||
264
+ ts.isInterfaceDeclaration(statement)
265
+ ) {
266
+ const name = statement.name.text;
267
+ declarations.set(name, [...(declarations.get(name) ?? []), statement]);
268
+ }
269
+ }
270
+ return declarations;
271
+ }
272
+
273
+ function collectNamedTypeProperties(
274
+ name: string,
275
+ declarations: TypeDeclarations,
276
+ root = false,
277
+ visited = new Set<string>(),
278
+ ): PropertyCollection {
279
+ if (visited.has(name)) return { properties: [], indeterminate: false };
280
+ visited.add(name);
281
+ const namedDeclarations = declarations.get(name);
282
+ if (!namedDeclarations) {
283
+ return { properties: [], indeterminate: !root };
284
+ }
285
+
286
+ const collection: PropertyCollection = {
287
+ properties: [],
288
+ indeterminate: false,
289
+ };
290
+ for (const declaration of namedDeclarations) {
291
+ if (ts.isInterfaceDeclaration(declaration)) {
292
+ collection.properties.push(
293
+ ...declaration.members.filter(ts.isPropertySignature),
294
+ );
295
+ for (const clause of declaration.heritageClauses ?? []) {
296
+ for (const type of clause.types) {
297
+ if (!ts.isIdentifier(type.expression)) {
298
+ collection.indeterminate = true;
299
+ continue;
300
+ }
301
+ const inherited = collectNamedTypeProperties(
302
+ type.expression.text,
303
+ declarations,
304
+ false,
305
+ visited,
306
+ );
307
+ collection.properties.push(...inherited.properties);
308
+ collection.indeterminate ||= inherited.indeterminate;
309
+ }
310
+ }
311
+ continue;
312
+ }
313
+
314
+ const aliased = collectTypeNodeProperties(
315
+ declaration.type,
316
+ declarations,
317
+ visited,
318
+ );
319
+ collection.properties.push(...aliased.properties);
320
+ collection.indeterminate ||= aliased.indeterminate;
321
+ }
322
+ return collection;
323
+ }
324
+
325
+ function collectTypeNodeProperties(
326
+ type: ts.TypeNode,
327
+ declarations: TypeDeclarations,
328
+ visited: Set<string>,
329
+ ): PropertyCollection {
330
+ if (ts.isTypeLiteralNode(type)) {
331
+ return {
332
+ properties: type.members.filter(ts.isPropertySignature),
333
+ indeterminate: false,
334
+ };
335
+ }
336
+ if (ts.isIntersectionTypeNode(type)) {
337
+ const members = type.types.map((member) =>
338
+ collectTypeNodeProperties(member, declarations, visited),
339
+ );
340
+ return {
341
+ properties: members.flatMap((member) => member.properties),
342
+ indeterminate: members.some((member) => member.indeterminate),
343
+ };
344
+ }
345
+ if (ts.isTypeReferenceNode(type) && ts.isIdentifier(type.typeName)) {
346
+ return collectNamedTypeProperties(
347
+ type.typeName.text,
348
+ declarations,
349
+ false,
350
+ visited,
351
+ );
352
+ }
353
+ return { properties: [], indeterminate: true };
354
+ }
355
+
356
+ function inferredTypeQueryName(
357
+ name: string,
358
+ declarations: TypeDeclarations,
359
+ ): string | undefined {
360
+ const namedDeclarations = declarations.get(name);
361
+ if (
362
+ namedDeclarations?.length !== 1 ||
363
+ !ts.isTypeAliasDeclaration(namedDeclarations[0]) ||
364
+ !ts.isTypeQueryNode(namedDeclarations[0].type) ||
365
+ !ts.isIdentifier(namedDeclarations[0].type.exprName)
366
+ ) {
367
+ return undefined;
368
+ }
369
+ return namedDeclarations[0].type.exprName.text;
370
+ }
371
+
372
+ function collectVariableInitializers(
373
+ sourceFile: ts.SourceFile,
374
+ ): Map<string, ts.Expression> {
375
+ const values = new Map<string, ts.Expression>();
376
+ for (const statement of sourceFile.statements) {
377
+ if (!ts.isVariableStatement(statement)) continue;
378
+ for (const declaration of statement.declarationList.declarations) {
379
+ if (ts.isIdentifier(declaration.name) && declaration.initializer) {
380
+ values.set(declaration.name.text, declaration.initializer);
381
+ }
382
+ }
383
+ }
384
+ return values;
385
+ }
386
+
387
+ function resolveLocalExpression(
388
+ expression: ts.Expression,
389
+ values: Map<string, ts.Expression>,
390
+ visited = new Set<string>(),
391
+ ): ts.Expression {
392
+ const current = unwrapExpression(expression);
393
+ if (!ts.isIdentifier(current) || visited.has(current.text)) return current;
394
+ const target = values.get(current.text);
395
+ if (!target) return current;
396
+ visited.add(current.text);
397
+ return resolveLocalExpression(target, values, visited);
398
+ }
399
+
400
+ function unwrapExpression(expression: ts.Expression): ts.Expression {
401
+ let current = expression;
402
+ while (
403
+ ts.isParenthesizedExpression(current) ||
404
+ ts.isAsExpression(current) ||
405
+ ts.isSatisfiesExpression(current) ||
406
+ ts.isTypeAssertionExpression(current) ||
407
+ ts.isNonNullExpression(current)
408
+ ) {
409
+ current = current.expression;
410
+ }
411
+ return current;
412
+ }
413
+
414
+ function addObjectPropertyNames(
415
+ object: ts.ObjectLiteralExpression,
416
+ target: Set<string>,
417
+ ): boolean {
418
+ let indeterminate = false;
419
+ for (const property of object.properties) {
420
+ if (ts.isSpreadAssignment(property)) {
421
+ target.clear();
422
+ indeterminate = true;
423
+ continue;
424
+ }
425
+ const name = property.name ? propertyName(property.name) : undefined;
426
+ if (name) target.add(name);
427
+ else {
428
+ target.clear();
429
+ indeterminate = true;
430
+ }
431
+ }
432
+ return indeterminate;
433
+ }
434
+
435
+ function objectPropertyAnalysis(
436
+ object: ts.ObjectLiteralExpression,
437
+ name: string,
438
+ ): {
439
+ property?: ts.PropertyAssignment | ts.ShorthandPropertyAssignment;
440
+ indeterminate: boolean;
441
+ } {
442
+ let property:
443
+ | ts.PropertyAssignment
444
+ | ts.ShorthandPropertyAssignment
445
+ | undefined;
446
+ let indeterminate = false;
447
+ for (const member of object.properties) {
448
+ if (ts.isSpreadAssignment(member) || !member.name) {
449
+ property = undefined;
450
+ indeterminate = true;
451
+ continue;
452
+ }
453
+ const key = propertyName(member.name);
454
+ if (!key) {
455
+ property = undefined;
456
+ indeterminate = true;
457
+ continue;
458
+ }
459
+ if (key !== name) continue;
460
+ if (
461
+ ts.isPropertyAssignment(member) ||
462
+ ts.isShorthandPropertyAssignment(member)
463
+ ) {
464
+ property = member;
465
+ indeterminate = false;
466
+ } else {
467
+ property = undefined;
468
+ indeterminate = true;
469
+ }
470
+ }
471
+ return { ...(property ? { property } : {}), indeterminate };
472
+ }
473
+
474
+ function propertyInitializer(
475
+ property: ts.PropertyAssignment | ts.ShorthandPropertyAssignment,
476
+ ): ts.Expression {
477
+ return ts.isPropertyAssignment(property)
478
+ ? property.initializer
479
+ : property.name;
480
+ }
481
+
482
+ function callName(call: ts.CallExpression): string | undefined {
483
+ const expression = call.expression;
484
+ if (ts.isIdentifier(expression)) return expression.text;
485
+ if (ts.isPropertyAccessExpression(expression)) return expression.name.text;
486
+ if (ts.isCallExpression(expression)) return callName(expression);
487
+ return undefined;
488
+ }
489
+
490
+ function visitCalls(node: ts.Node, visit: (call: ts.CallExpression) => void) {
491
+ const walker = (child: ts.Node): void => {
492
+ if (ts.isCallExpression(child)) visit(child);
493
+ ts.forEachChild(child, walker);
494
+ };
495
+ walker(node);
496
+ }
497
+
498
+ function propertyName(name: ts.PropertyName): string | undefined {
499
+ if (ts.isIdentifier(name) || ts.isStringLiteralLike(name)) return name.text;
500
+ return undefined;
501
+ }
502
+
503
+ function literalText(expression: ts.Expression): string | undefined {
504
+ if (ts.isStringLiteralLike(expression)) return expression.text;
505
+ return undefined;
506
+ }