@codeyam/codeyam-cli 0.1.0-staging.323686 → 0.1.0-staging.483fdc2

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 (171) hide show
  1. package/analyzer-template/.build-info.json +7 -7
  2. package/analyzer-template/log.txt +3 -3
  3. package/analyzer-template/package.json +2 -2
  4. package/analyzer-template/packages/ai/index.ts +6 -1
  5. package/analyzer-template/packages/ai/src/lib/analyzeScope.ts +39 -17
  6. package/analyzer-template/packages/ai/src/lib/astScopes/astScopeAnalyzer.ts +67 -9
  7. package/analyzer-template/packages/ai/src/lib/astScopes/processExpression.ts +308 -50
  8. package/analyzer-template/packages/ai/src/lib/astScopes/types.ts +15 -6
  9. package/analyzer-template/packages/ai/src/lib/dataStructure/ScopeDataStructure.ts +664 -242
  10. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/BatchSchemaProcessor.ts +16 -3
  11. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/ScopeTreeManager.ts +6 -4
  12. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.ts +20 -1
  13. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/cleanNonObjectFunctions.ts +35 -13
  14. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/convertTypeAnnotationsToValues.ts +160 -0
  15. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/deduplicateFunctionSchemas.ts +40 -30
  16. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/fillInSchemaGapsAndUnknowns.ts +289 -83
  17. package/analyzer-template/packages/ai/src/lib/generateEntityScenarioData.ts +269 -1
  18. package/analyzer-template/packages/ai/src/lib/generateEntityScenarios.ts +9 -5
  19. package/analyzer-template/packages/ai/src/lib/generateExecutionFlows.ts +11 -3
  20. package/analyzer-template/packages/ai/src/lib/generateExecutionFlowsFromConditionalEffects.ts +1 -1
  21. package/analyzer-template/packages/ai/src/lib/generateExecutionFlowsFromConditionals.ts +297 -7
  22. package/analyzer-template/packages/ai/src/lib/generateExecutionFlowsFromJsxUsages.ts +1 -1
  23. package/analyzer-template/packages/ai/src/lib/mergeStatements.ts +90 -96
  24. package/analyzer-template/packages/ai/src/lib/promptGenerators/gatherAttributesMap.ts +10 -7
  25. package/analyzer-template/packages/ai/src/lib/resolvePathToControllable.ts +25 -13
  26. package/analyzer-template/packages/ai/src/lib/worker/SerializableDataStructure.ts +4 -3
  27. package/analyzer-template/packages/analyze/src/lib/FileAnalyzer.ts +65 -59
  28. package/analyzer-template/packages/analyze/src/lib/ProjectAnalyzer.ts +113 -26
  29. package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getAllDeclaredEntityNodes.ts +19 -0
  30. package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getAllEntityNodes.ts +19 -0
  31. package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getAllExports.ts +11 -0
  32. package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getImportsAnalysis.ts +8 -0
  33. package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getResolvedModule.ts +49 -1
  34. package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getSourceFilesForAllImports.ts +2 -1
  35. package/analyzer-template/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.ts +20 -6
  36. package/analyzer-template/packages/analyze/src/lib/files/analyze/analyzeEntities.ts +14 -4
  37. package/analyzer-template/packages/analyze/src/lib/files/analyze/gatherEntityMap.ts +4 -2
  38. package/analyzer-template/packages/analyze/src/lib/files/analyze/validateDependencyAnalyses.ts +0 -3
  39. package/analyzer-template/packages/analyze/src/lib/files/analyzeRemixRoute.ts +4 -5
  40. package/analyzer-template/packages/analyze/src/lib/files/getImportedExports.ts +14 -12
  41. package/analyzer-template/packages/analyze/src/lib/files/scenarios/enrichArrayTypesFromChildSignatures.ts +57 -13
  42. package/analyzer-template/packages/analyze/src/lib/files/scenarios/gatherDataForMocks.ts +29 -0
  43. package/analyzer-template/packages/analyze/src/lib/files/scenarios/generateDataStructure.ts +35 -4
  44. package/analyzer-template/packages/analyze/src/lib/files/scenarios/generateExecutionFlows.ts +117 -9
  45. package/analyzer-template/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.ts +199 -17
  46. package/analyzer-template/packages/analyze/src/lib/files/scenarios/propagateArrayItemSchemas.ts +474 -0
  47. package/analyzer-template/packages/analyze/src/lib/files/setImportedExports.ts +2 -1
  48. package/analyzer-template/packages/analyze/src/lib/utils/getFileByPath.ts +19 -0
  49. package/analyzer-template/packages/aws/package.json +1 -1
  50. package/analyzer-template/packages/github/dist/types/src/types/ScenariosDataStructure.d.ts +5 -5
  51. package/analyzer-template/packages/github/dist/types/src/types/ScenariosDataStructure.d.ts.map +1 -1
  52. package/analyzer-template/packages/github/dist/types/src/types/ScopeAnalysis.d.ts +6 -1
  53. package/analyzer-template/packages/github/dist/types/src/types/ScopeAnalysis.d.ts.map +1 -1
  54. package/analyzer-template/packages/github/package.json +1 -1
  55. package/analyzer-template/packages/types/src/types/ScenariosDataStructure.ts +6 -5
  56. package/analyzer-template/packages/types/src/types/ScopeAnalysis.ts +6 -1
  57. package/analyzer-template/packages/utils/dist/types/src/types/ScenariosDataStructure.d.ts +5 -5
  58. package/analyzer-template/packages/utils/dist/types/src/types/ScenariosDataStructure.d.ts.map +1 -1
  59. package/analyzer-template/packages/utils/dist/types/src/types/ScopeAnalysis.d.ts +6 -1
  60. package/analyzer-template/packages/utils/dist/types/src/types/ScopeAnalysis.d.ts.map +1 -1
  61. package/analyzer-template/project/constructMockCode.ts +54 -9
  62. package/analyzer-template/project/writeMockDataTsx.ts +73 -2
  63. package/background/src/lib/virtualized/project/constructMockCode.js +45 -3
  64. package/background/src/lib/virtualized/project/constructMockCode.js.map +1 -1
  65. package/background/src/lib/virtualized/project/writeMockDataTsx.js +71 -2
  66. package/background/src/lib/virtualized/project/writeMockDataTsx.js.map +1 -1
  67. package/codeyam-cli/scripts/apply-setup.js +146 -0
  68. package/codeyam-cli/scripts/apply-setup.js.map +1 -1
  69. package/codeyam-cli/src/commands/debug.js +7 -5
  70. package/codeyam-cli/src/commands/debug.js.map +1 -1
  71. package/codeyam-cli/src/utils/install-skills.js +22 -0
  72. package/codeyam-cli/src/utils/install-skills.js.map +1 -1
  73. package/codeyam-cli/src/utils/reviewedRules.js +92 -0
  74. package/codeyam-cli/src/utils/reviewedRules.js.map +1 -0
  75. package/codeyam-cli/src/webserver/build/client/assets/globals-CX9f-5xM.css +1 -0
  76. package/codeyam-cli/src/webserver/build/client/assets/{manifest-7522edd4.js → manifest-bba56ec1.js} +1 -1
  77. package/codeyam-cli/src/webserver/build/client/assets/memory-DuTFSyJ2.js +92 -0
  78. package/codeyam-cli/src/webserver/build/client/assets/{root-eVAaavTS.js → root-DTfSQARG.js} +6 -6
  79. package/codeyam-cli/src/webserver/build/server/assets/{index-DVzYx8PN.js → index-TD1f-DHV.js} +1 -1
  80. package/codeyam-cli/src/webserver/build/server/assets/server-build-BQ-1XyEa.js +258 -0
  81. package/codeyam-cli/src/webserver/build/server/index.js +1 -1
  82. package/codeyam-cli/src/webserver/build-info.json +5 -5
  83. package/codeyam-cli/templates/codeyam:memory.md +174 -233
  84. package/codeyam-cli/templates/codeyam:new-rule.md +41 -2
  85. package/codeyam-cli/templates/rule-reflection-hook.py +161 -0
  86. package/codeyam-cli/templates/rules-instructions.md +126 -0
  87. package/package.json +1 -1
  88. package/packages/ai/index.js +2 -1
  89. package/packages/ai/index.js.map +1 -1
  90. package/packages/ai/src/lib/analyzeScope.js +29 -12
  91. package/packages/ai/src/lib/analyzeScope.js.map +1 -1
  92. package/packages/ai/src/lib/astScopes/astScopeAnalyzer.js +54 -8
  93. package/packages/ai/src/lib/astScopes/astScopeAnalyzer.js.map +1 -1
  94. package/packages/ai/src/lib/astScopes/processExpression.js +239 -43
  95. package/packages/ai/src/lib/astScopes/processExpression.js.map +1 -1
  96. package/packages/ai/src/lib/dataStructure/ScopeDataStructure.js +503 -165
  97. package/packages/ai/src/lib/dataStructure/ScopeDataStructure.js.map +1 -1
  98. package/packages/ai/src/lib/dataStructure/helpers/BatchSchemaProcessor.js +13 -3
  99. package/packages/ai/src/lib/dataStructure/helpers/BatchSchemaProcessor.js.map +1 -1
  100. package/packages/ai/src/lib/dataStructure/helpers/ScopeTreeManager.js +6 -4
  101. package/packages/ai/src/lib/dataStructure/helpers/ScopeTreeManager.js.map +1 -1
  102. package/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.js +22 -1
  103. package/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.js.map +1 -1
  104. package/packages/ai/src/lib/dataStructure/helpers/cleanNonObjectFunctions.js +34 -9
  105. package/packages/ai/src/lib/dataStructure/helpers/cleanNonObjectFunctions.js.map +1 -1
  106. package/packages/ai/src/lib/dataStructure/helpers/convertTypeAnnotationsToValues.js +159 -0
  107. package/packages/ai/src/lib/dataStructure/helpers/convertTypeAnnotationsToValues.js.map +1 -0
  108. package/packages/ai/src/lib/dataStructure/helpers/deduplicateFunctionSchemas.js +37 -20
  109. package/packages/ai/src/lib/dataStructure/helpers/deduplicateFunctionSchemas.js.map +1 -1
  110. package/packages/ai/src/lib/dataStructure/helpers/fillInSchemaGapsAndUnknowns.js +237 -73
  111. package/packages/ai/src/lib/dataStructure/helpers/fillInSchemaGapsAndUnknowns.js.map +1 -1
  112. package/packages/ai/src/lib/generateEntityScenarioData.js +195 -1
  113. package/packages/ai/src/lib/generateEntityScenarioData.js.map +1 -1
  114. package/packages/ai/src/lib/generateEntityScenarios.js +7 -1
  115. package/packages/ai/src/lib/generateEntityScenarios.js.map +1 -1
  116. package/packages/ai/src/lib/generateExecutionFlows.js +10 -2
  117. package/packages/ai/src/lib/generateExecutionFlows.js.map +1 -1
  118. package/packages/ai/src/lib/generateExecutionFlowsFromConditionals.js +209 -3
  119. package/packages/ai/src/lib/generateExecutionFlowsFromConditionals.js.map +1 -1
  120. package/packages/ai/src/lib/mergeStatements.js +70 -51
  121. package/packages/ai/src/lib/mergeStatements.js.map +1 -1
  122. package/packages/ai/src/lib/promptGenerators/gatherAttributesMap.js +10 -4
  123. package/packages/ai/src/lib/promptGenerators/gatherAttributesMap.js.map +1 -1
  124. package/packages/ai/src/lib/resolvePathToControllable.js +24 -14
  125. package/packages/ai/src/lib/resolvePathToControllable.js.map +1 -1
  126. package/packages/ai/src/lib/worker/SerializableDataStructure.js.map +1 -1
  127. package/packages/analyze/src/lib/FileAnalyzer.js +60 -36
  128. package/packages/analyze/src/lib/FileAnalyzer.js.map +1 -1
  129. package/packages/analyze/src/lib/ProjectAnalyzer.js +96 -26
  130. package/packages/analyze/src/lib/ProjectAnalyzer.js.map +1 -1
  131. package/packages/analyze/src/lib/asts/sourceFiles/getAllDeclaredEntityNodes.js +14 -0
  132. package/packages/analyze/src/lib/asts/sourceFiles/getAllDeclaredEntityNodes.js.map +1 -1
  133. package/packages/analyze/src/lib/asts/sourceFiles/getAllEntityNodes.js +14 -0
  134. package/packages/analyze/src/lib/asts/sourceFiles/getAllEntityNodes.js.map +1 -1
  135. package/packages/analyze/src/lib/asts/sourceFiles/getAllExports.js +6 -0
  136. package/packages/analyze/src/lib/asts/sourceFiles/getAllExports.js.map +1 -1
  137. package/packages/analyze/src/lib/asts/sourceFiles/getImportsAnalysis.js +6 -0
  138. package/packages/analyze/src/lib/asts/sourceFiles/getImportsAnalysis.js.map +1 -1
  139. package/packages/analyze/src/lib/asts/sourceFiles/getResolvedModule.js +39 -1
  140. package/packages/analyze/src/lib/asts/sourceFiles/getResolvedModule.js.map +1 -1
  141. package/packages/analyze/src/lib/asts/sourceFiles/getSourceFilesForAllImports.js +2 -1
  142. package/packages/analyze/src/lib/asts/sourceFiles/getSourceFilesForAllImports.js.map +1 -1
  143. package/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.js +13 -5
  144. package/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.js.map +1 -1
  145. package/packages/analyze/src/lib/files/analyze/analyzeEntities.js +14 -4
  146. package/packages/analyze/src/lib/files/analyze/analyzeEntities.js.map +1 -1
  147. package/packages/analyze/src/lib/files/analyze/gatherEntityMap.js +2 -1
  148. package/packages/analyze/src/lib/files/analyze/gatherEntityMap.js.map +1 -1
  149. package/packages/analyze/src/lib/files/analyze/validateDependencyAnalyses.js +0 -3
  150. package/packages/analyze/src/lib/files/analyze/validateDependencyAnalyses.js.map +1 -1
  151. package/packages/analyze/src/lib/files/analyzeRemixRoute.js +3 -2
  152. package/packages/analyze/src/lib/files/analyzeRemixRoute.js.map +1 -1
  153. package/packages/analyze/src/lib/files/getImportedExports.js +11 -7
  154. package/packages/analyze/src/lib/files/getImportedExports.js.map +1 -1
  155. package/packages/analyze/src/lib/files/scenarios/enrichArrayTypesFromChildSignatures.js +52 -10
  156. package/packages/analyze/src/lib/files/scenarios/enrichArrayTypesFromChildSignatures.js.map +1 -1
  157. package/packages/analyze/src/lib/files/scenarios/gatherDataForMocks.js +25 -8
  158. package/packages/analyze/src/lib/files/scenarios/gatherDataForMocks.js.map +1 -1
  159. package/packages/analyze/src/lib/files/scenarios/generateDataStructure.js +34 -4
  160. package/packages/analyze/src/lib/files/scenarios/generateDataStructure.js.map +1 -1
  161. package/packages/analyze/src/lib/files/scenarios/generateExecutionFlows.js +56 -8
  162. package/packages/analyze/src/lib/files/scenarios/generateExecutionFlows.js.map +1 -1
  163. package/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.js +168 -9
  164. package/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.js.map +1 -1
  165. package/packages/analyze/src/lib/files/setImportedExports.js +2 -1
  166. package/packages/analyze/src/lib/files/setImportedExports.js.map +1 -1
  167. package/packages/analyze/src/lib/utils/getFileByPath.js +12 -0
  168. package/packages/analyze/src/lib/utils/getFileByPath.js.map +1 -0
  169. package/codeyam-cli/src/webserver/build/client/assets/globals-D3yhhV8x.css +0 -1
  170. package/codeyam-cli/src/webserver/build/client/assets/memory-yxFcrxBX.js +0 -92
  171. package/codeyam-cli/src/webserver/build/server/assets/server-build-4Cr0uToj.js +0 -257
@@ -41,6 +41,7 @@ export interface BatchProcessorStats {
41
41
 
42
42
  export class BatchSchemaProcessor {
43
43
  private workQueue: WorkItem[] = [];
44
+ private queueIndex = 0;
44
45
  private stats: BatchProcessorStats = {
45
46
  itemsProcessed: 0,
46
47
  itemsSkipped: 0, // Kept for API compatibility, always 0
@@ -71,17 +72,28 @@ export class BatchSchemaProcessor {
71
72
  * Check if there are more items to process.
72
73
  */
73
74
  hasWork(): boolean {
74
- return this.workQueue.length > 0;
75
+ return this.queueIndex < this.workQueue.length;
75
76
  }
76
77
 
77
78
  /**
78
79
  * Get the next work item from the queue.
79
80
  */
80
81
  getNextWork(): WorkItem | undefined {
81
- const item = this.workQueue.shift();
82
+ if (this.queueIndex >= this.workQueue.length) {
83
+ return undefined;
84
+ }
85
+
86
+ const item = this.workQueue[this.queueIndex++];
82
87
  if (item) {
83
88
  this.stats.itemsProcessed++;
84
89
  }
90
+
91
+ // Once we've consumed the queue, reset to avoid unbounded growth
92
+ if (this.queueIndex >= this.workQueue.length) {
93
+ this.workQueue = [];
94
+ this.queueIndex = 0;
95
+ }
96
+
85
97
  return item;
86
98
  }
87
99
 
@@ -122,6 +134,7 @@ export class BatchSchemaProcessor {
122
134
  */
123
135
  reset(): void {
124
136
  this.workQueue = [];
137
+ this.queueIndex = 0;
125
138
  this.stats = {
126
139
  itemsProcessed: 0,
127
140
  itemsSkipped: 0,
@@ -134,6 +147,6 @@ export class BatchSchemaProcessor {
134
147
  * Get current queue size (for debugging/monitoring).
135
148
  */
136
149
  getQueueSize(): number {
137
- return this.workQueue.length;
150
+ return this.workQueue.length - this.queueIndex;
138
151
  }
139
152
  }
@@ -117,9 +117,10 @@ export class ScopeTreeManager {
117
117
  */
118
118
  findNode(name: string): ScopeTreeNode | undefined {
119
119
  const queue: ScopeTreeNode[] = [this.tree];
120
+ let index = 0;
120
121
 
121
- while (queue.length > 0) {
122
- const node = queue.shift()!;
122
+ while (index < queue.length) {
123
+ const node = queue[index++]!;
123
124
  if (node.name === name) {
124
125
  return node;
125
126
  }
@@ -135,9 +136,10 @@ export class ScopeTreeManager {
135
136
  getAllNames(): string[] {
136
137
  const names: string[] = [];
137
138
  const queue: ScopeTreeNode[] = [this.tree];
139
+ let index = 0;
138
140
 
139
- while (queue.length > 0) {
140
- const node = queue.shift()!;
141
+ while (index < queue.length) {
142
+ const node = queue[index++]!;
141
143
  names.push(node.name);
142
144
  queue.push(...node.children);
143
145
  }
@@ -35,6 +35,24 @@ const getParts = (path: string): string[] => {
35
35
  return parts;
36
36
  };
37
37
 
38
+ function buildPrefixParts(parts: string[]): string[] {
39
+ if (parts.length === 0) return [];
40
+ const prefixes = new Array(parts.length);
41
+ let current = '';
42
+ for (let i = 0; i < parts.length; i++) {
43
+ const part = parts[i];
44
+ if (i === 0) {
45
+ current = part;
46
+ } else if (part.startsWith('[') || part.startsWith('(')) {
47
+ current += part;
48
+ } else {
49
+ current += `.${part}`;
50
+ }
51
+ prefixes[i] = current;
52
+ }
53
+ return prefixes;
54
+ }
55
+
38
56
  /**
39
57
  * Clear the module-level cache.
40
58
  * Call this between entity analyses to prevent unbounded memory growth.
@@ -133,6 +151,7 @@ function scrub(
133
151
 
134
152
  for (const keyPath of sortedKeys) {
135
153
  const keyParts = getParts(keyPath);
154
+ const prefixParts = buildPrefixParts(keyParts);
136
155
 
137
156
  if (
138
157
  IGNORE_CLASSES_AND_OBJECTS.find(
@@ -145,7 +164,7 @@ function scrub(
145
164
 
146
165
  // walk back through the prefixes: foo.bar.baz.qux → foo.bar.baz → foo.bar …
147
166
  for (let cut = keyParts.length - 1; cut > 0; --cut) {
148
- const prefix = joinParenthesesAndArrays(keyParts.slice(0, cut));
167
+ const prefix = prefixParts[cut - 1];
149
168
  const rootKind = rootTable.get(prefix);
150
169
  if (!rootKind) continue;
151
170
 
@@ -178,17 +178,36 @@ export function clearCleanNonObjectFunctionsCache(): {
178
178
  // Maps parent prefix -> array of last parts (method names without parens)
179
179
  type PrefixIndex = Map<string, string[]>;
180
180
 
181
+ function buildPrefixPartsWithRoot(parts: string[]): string[] {
182
+ const prefixes = new Array(parts.length + 1);
183
+ prefixes[0] = '';
184
+ let current = '';
185
+ for (let i = 0; i < parts.length; i++) {
186
+ const part = parts[i];
187
+ if (i === 0) {
188
+ current = part;
189
+ } else if (part.startsWith('[') || part.startsWith('(')) {
190
+ current += part;
191
+ } else {
192
+ current += `.${part}`;
193
+ }
194
+ prefixes[i + 1] = current;
195
+ }
196
+ return prefixes;
197
+ }
198
+
181
199
  function buildPrefixIndex(allPaths: string[]): PrefixIndex {
182
200
  const index: PrefixIndex = new Map();
183
201
 
184
202
  for (const path of allPaths) {
185
203
  const parts = cachedSplit(path);
186
204
  if (parts.length < 1) continue;
205
+ const prefixes = buildPrefixPartsWithRoot(parts);
187
206
 
188
207
  // For each possible prefix length, add the last part to that prefix's entry
189
208
  // This allows lookup at any depth
190
209
  for (let prefixLen = 0; prefixLen < parts.length; prefixLen++) {
191
- const prefix = joinParenthesesAndArrays(parts.slice(0, prefixLen));
210
+ const prefix = prefixes[prefixLen];
192
211
  const lastPart = parts[prefixLen]?.split('(')?.[0];
193
212
  if (lastPart) {
194
213
  let existing = index.get(prefix);
@@ -1085,29 +1104,32 @@ function clearAttributes(
1085
1104
  knownBasePaths.add(path);
1086
1105
  }
1087
1106
 
1107
+ // OPTIMIZATION: Instead of O(keys × basePaths), use O(keys × keyDepth) where keyDepth is small
1108
+ // For each key, find all its prefixes and check if any is a known basePath (O(1) Set lookup)
1088
1109
  for (const key in mapping) {
1089
1110
  const keyParts = cachedSplit(key);
1090
- for (const basePath of knownBasePaths) {
1091
- const basePathParts = cachedSplit(basePath);
1092
- if (keyParts.length <= basePathParts.length) continue;
1093
- if (
1094
- joinParenthesesAndArrays(keyParts.slice(0, basePathParts.length)) !==
1095
- basePath
1096
- )
1097
- continue;
1111
+ if (keyParts.length <= 1) continue; // No possible parent basePath
1112
+
1113
+ // Check each prefix of the key to see if it's a known basePath
1114
+ // Start from shortest prefix (most likely to match) for early exit
1115
+ for (let prefixLen = 1; prefixLen < keyParts.length; prefixLen++) {
1116
+ const prefix = joinParenthesesAndArrays(keyParts.slice(0, prefixLen));
1117
+ if (!knownBasePaths.has(prefix)) continue;
1118
+
1119
+ // Found a matching basePath - now check the conditions
1120
+ const basePath = prefix;
1121
+
1098
1122
  // Preserve functionCallReturnValue for non-string types, UNLESS
1099
1123
  // the method is a primitive-returning method (like some, every, includes, indexOf)
1100
1124
  // whose return values are not mockable
1101
- const methodPart = keyParts[basePathParts.length];
1125
+ const methodPart = keyParts[prefixLen];
1102
1126
  const methodName = methodPart?.split('(')[0];
1103
1127
  const isPrimitiveReturning = primitiveReturningMethodsSet.has(
1104
1128
  methodName ?? '',
1105
1129
  );
1106
1130
  if (
1107
1131
  mapping[basePath] !== 'string' &&
1108
- keyParts[basePathParts.length + 1]?.startsWith(
1109
- 'functionCallReturnValue',
1110
- ) &&
1132
+ keyParts[prefixLen + 1]?.startsWith('functionCallReturnValue') &&
1111
1133
  !isPrimitiveReturning
1112
1134
  )
1113
1135
  continue;
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Converts type annotation strings that appear as values in mockData to actual values.
3
+ *
4
+ * The LLM sometimes echoes the schema type annotation as the actual value.
5
+ * For example, if the schema says { filePath: "string | undefined" },
6
+ * the LLM might return { filePath: "string | undefined" } instead of
7
+ * generating an actual value like { filePath: "/path/to/file.tsx" }.
8
+ *
9
+ * This causes runtime errors when the code tries to use these "values"
10
+ * as actual data (e.g., calling .length on the string "string | undefined").
11
+ *
12
+ * This function detects type annotation strings and converts them to
13
+ * appropriate default values:
14
+ * - "string" → ""
15
+ * - "number" → 0
16
+ * - "boolean" → false
17
+ * - "function" → () => {}
18
+ * - "Date" → new Date()
19
+ * - "T | undefined" → undefined
20
+ * - "T | null" → null
21
+ * - "string[]" → []
22
+ */
23
+
24
+ // Pattern to detect if a string looks like a TypeScript type annotation
25
+ // This matches common type patterns:
26
+ // - Primitive types: string, number, boolean, function, Date, object, any, unknown
27
+ // - Union types: string | undefined, number | null, T | null | undefined
28
+ // - Array types: string[], number[], T[]
29
+ // - Optional types: T?
30
+ const TYPE_ANNOTATION_PATTERN =
31
+ /^(string|number|boolean|function|Date|object|any|unknown|void|never|null|undefined|array)(\s*\|\s*(string|number|boolean|function|Date|object|any|unknown|void|never|null|undefined|array))*(\[\])?$/i;
32
+
33
+ /**
34
+ * Check if a string value looks like a TypeScript type annotation.
35
+ */
36
+ function isTypeAnnotation(value: string): boolean {
37
+ if (typeof value !== 'string') return false;
38
+
39
+ const trimmed = value.trim();
40
+
41
+ // Quick check: type annotations are typically short
42
+ if (trimmed.length > 100) return false;
43
+
44
+ // Check against the pattern
45
+ if (TYPE_ANNOTATION_PATTERN.test(trimmed)) return true;
46
+
47
+ // Also check for common patterns that the regex might miss
48
+ const commonTypePatterns = [
49
+ 'string',
50
+ 'number',
51
+ 'boolean',
52
+ 'function',
53
+ 'object',
54
+ 'array',
55
+ 'Date',
56
+ 'any',
57
+ 'unknown',
58
+ 'void',
59
+ 'never',
60
+ 'null',
61
+ 'undefined',
62
+ 'string | undefined',
63
+ 'string | null',
64
+ 'string | null | undefined',
65
+ 'number | undefined',
66
+ 'number | null',
67
+ 'number | null | undefined',
68
+ 'boolean | undefined',
69
+ 'boolean | null',
70
+ 'boolean | null | undefined',
71
+ 'Date | undefined',
72
+ 'Date | null',
73
+ 'function | undefined',
74
+ 'object | undefined',
75
+ 'object | null',
76
+ 'string[]',
77
+ 'number[]',
78
+ 'boolean[]',
79
+ 'any[]',
80
+ 'string | number',
81
+ 'string | number | undefined',
82
+ ];
83
+
84
+ return commonTypePatterns.includes(trimmed);
85
+ }
86
+
87
+ /**
88
+ * Convert a type annotation string to an appropriate default value.
89
+ */
90
+ function typeAnnotationToValue(typeAnnotation: string): unknown {
91
+ const trimmed = typeAnnotation.trim().toLowerCase();
92
+
93
+ // Handle undefined types first (they take precedence)
94
+ if (trimmed.includes('| undefined') || trimmed.includes('undefined |')) {
95
+ return undefined;
96
+ }
97
+
98
+ // Handle null types
99
+ if (trimmed.includes('| null') || trimmed.includes('null |')) {
100
+ return null;
101
+ }
102
+
103
+ // Handle array types
104
+ if (trimmed.endsWith('[]') || trimmed === 'array') {
105
+ return [];
106
+ }
107
+
108
+ // Handle primitive types
109
+ if (trimmed === 'string') return '';
110
+ if (trimmed === 'number') return 0;
111
+ if (trimmed === 'boolean') return false;
112
+ if (trimmed === 'function') return () => {};
113
+ if (trimmed === 'date') return new Date();
114
+ if (trimmed === 'object') return {};
115
+ if (trimmed === 'null') return null;
116
+ if (trimmed === 'undefined') return undefined;
117
+ if (trimmed === 'any' || trimmed === 'unknown') return undefined;
118
+ if (trimmed === 'void' || trimmed === 'never') return undefined;
119
+
120
+ // Handle union types - prefer the first concrete type
121
+ if (trimmed.includes(' | ')) {
122
+ const parts = trimmed.split(' | ').map((p) => p.trim());
123
+ for (const part of parts) {
124
+ if (part === 'string') return '';
125
+ if (part === 'number') return 0;
126
+ if (part === 'boolean') return false;
127
+ }
128
+ }
129
+
130
+ // Fallback: return undefined for unknown type patterns
131
+ return undefined;
132
+ }
133
+
134
+ /**
135
+ * Recursively convert type annotation strings to values in a mockData object.
136
+ * Mutates the object in place.
137
+ */
138
+ export default function convertTypeAnnotationsToValues(
139
+ mockData: Record<string, unknown>,
140
+ ): void {
141
+ for (const [key, value] of Object.entries(mockData)) {
142
+ if (typeof value === 'string' && isTypeAnnotation(value)) {
143
+ mockData[key] = typeAnnotationToValue(value);
144
+ } else if (Array.isArray(value)) {
145
+ // Recursively process array elements
146
+ for (const item of value) {
147
+ if (item !== null && typeof item === 'object' && !Array.isArray(item)) {
148
+ convertTypeAnnotationsToValues(item as Record<string, unknown>);
149
+ }
150
+ }
151
+ } else if (
152
+ value !== null &&
153
+ typeof value === 'object' &&
154
+ !Array.isArray(value)
155
+ ) {
156
+ // Recursively process nested objects
157
+ convertTypeAnnotationsToValues(value as Record<string, unknown>);
158
+ }
159
+ }
160
+ }
@@ -77,42 +77,52 @@ export default function deduplicateFunctionSchemas(
77
77
 
78
78
  // If multiple variants with arguments exist, find the one with the most robust structure
79
79
  if (withArgs.length > 1) {
80
- // Count the number of related schema keys for each variant
81
- const variantDepths = withArgs.map((variant) => {
82
- // Find the end of the function call (the closing parenthesis)
80
+ // Pre-compute function call prefixes for all variants
81
+ const variantPrefixes = withArgs.map((variant) => {
83
82
  const closingParenIndex = variant.lastIndexOf(')');
84
- const functionCallPrefix =
85
- closingParenIndex > -1
86
- ? variant.slice(0, closingParenIndex + 1)
87
- : variant;
88
-
89
- const relatedKeys = Object.keys(schema).filter((k) => {
90
- // Check if this key is a child of the variant (starts with the function call)
91
- return (
92
- k.startsWith(functionCallPrefix + '.') ||
93
- k.startsWith(functionCallPrefix + '[')
94
- );
95
- });
96
-
97
83
  return {
98
84
  key: variant,
99
- depth: relatedKeys.length,
100
- maxDepth: Math.max(
101
- 0,
102
- ...relatedKeys.map((k) => {
103
- // Count the nesting depth by counting dots and brackets after the variant
104
- const remaining = k.slice(functionCallPrefix.length);
105
- return (
106
- (remaining.match(/\./g) || []).length +
107
- (remaining.match(/\[/g) || []).length
108
- );
109
- }),
110
- ),
85
+ prefix:
86
+ closingParenIndex > -1
87
+ ? variant.slice(0, closingParenIndex + 1)
88
+ : variant,
89
+ depth: 0,
90
+ maxDepth: 0,
111
91
  };
112
92
  });
113
93
 
94
+ // Single pass through schema keys to count related keys for ALL variants at once.
95
+ // This is O(schemaSize × variants) but done in one pass instead of
96
+ // O(variants × schemaSize) with separate passes per variant.
97
+ const schemaKeys = Object.keys(schema);
98
+ for (const k of schemaKeys) {
99
+ for (const variantInfo of variantPrefixes) {
100
+ // Check if key starts with prefix followed by . or [
101
+ const prefixLen = variantInfo.prefix.length;
102
+ if (k.length > prefixLen) {
103
+ const nextChar = k[prefixLen];
104
+ if (
105
+ (nextChar === '.' || nextChar === '[') &&
106
+ k.startsWith(variantInfo.prefix)
107
+ ) {
108
+ variantInfo.depth++;
109
+ // Count nesting depth by counting . and [ characters (faster than regex)
110
+ const remaining = k.slice(prefixLen);
111
+ let nestingDepth = 0;
112
+ for (let i = 0; i < remaining.length; i++) {
113
+ const c = remaining[i];
114
+ if (c === '.' || c === '[') nestingDepth++;
115
+ }
116
+ if (nestingDepth > variantInfo.maxDepth) {
117
+ variantInfo.maxDepth = nestingDepth;
118
+ }
119
+ }
120
+ }
121
+ }
122
+ }
123
+
114
124
  // Sort by depth (number of related keys) and then by max depth (nesting level)
115
- variantDepths.sort((a, b) => {
125
+ variantPrefixes.sort((a, b) => {
116
126
  if (b.depth !== a.depth) {
117
127
  return b.depth - a.depth;
118
128
  }
@@ -120,7 +130,7 @@ export default function deduplicateFunctionSchemas(
120
130
  });
121
131
 
122
132
  // Keep the one with the most robust structure, remove others
123
- const keepVariant = variantDepths[0].key;
133
+ const keepVariant = variantPrefixes[0].key;
124
134
  withArgs.forEach((variant) => {
125
135
  if (variant !== keepVariant) {
126
136
  keysToRemove.add(variant);