@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.
- package/analyzer-template/.build-info.json +7 -7
- package/analyzer-template/log.txt +3 -3
- package/analyzer-template/package.json +2 -2
- package/analyzer-template/packages/ai/index.ts +6 -1
- package/analyzer-template/packages/ai/src/lib/analyzeScope.ts +39 -17
- package/analyzer-template/packages/ai/src/lib/astScopes/astScopeAnalyzer.ts +67 -9
- package/analyzer-template/packages/ai/src/lib/astScopes/processExpression.ts +308 -50
- package/analyzer-template/packages/ai/src/lib/astScopes/types.ts +15 -6
- package/analyzer-template/packages/ai/src/lib/dataStructure/ScopeDataStructure.ts +664 -242
- package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/BatchSchemaProcessor.ts +16 -3
- package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/ScopeTreeManager.ts +6 -4
- package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.ts +20 -1
- package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/cleanNonObjectFunctions.ts +35 -13
- package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/convertTypeAnnotationsToValues.ts +160 -0
- package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/deduplicateFunctionSchemas.ts +40 -30
- package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/fillInSchemaGapsAndUnknowns.ts +289 -83
- package/analyzer-template/packages/ai/src/lib/generateEntityScenarioData.ts +269 -1
- package/analyzer-template/packages/ai/src/lib/generateEntityScenarios.ts +9 -5
- package/analyzer-template/packages/ai/src/lib/generateExecutionFlows.ts +11 -3
- package/analyzer-template/packages/ai/src/lib/generateExecutionFlowsFromConditionalEffects.ts +1 -1
- package/analyzer-template/packages/ai/src/lib/generateExecutionFlowsFromConditionals.ts +297 -7
- package/analyzer-template/packages/ai/src/lib/generateExecutionFlowsFromJsxUsages.ts +1 -1
- package/analyzer-template/packages/ai/src/lib/mergeStatements.ts +90 -96
- package/analyzer-template/packages/ai/src/lib/promptGenerators/gatherAttributesMap.ts +10 -7
- package/analyzer-template/packages/ai/src/lib/resolvePathToControllable.ts +25 -13
- package/analyzer-template/packages/ai/src/lib/worker/SerializableDataStructure.ts +4 -3
- package/analyzer-template/packages/analyze/src/lib/FileAnalyzer.ts +65 -59
- package/analyzer-template/packages/analyze/src/lib/ProjectAnalyzer.ts +113 -26
- package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getAllDeclaredEntityNodes.ts +19 -0
- package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getAllEntityNodes.ts +19 -0
- package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getAllExports.ts +11 -0
- package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getImportsAnalysis.ts +8 -0
- package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getResolvedModule.ts +49 -1
- package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getSourceFilesForAllImports.ts +2 -1
- package/analyzer-template/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.ts +20 -6
- package/analyzer-template/packages/analyze/src/lib/files/analyze/analyzeEntities.ts +14 -4
- package/analyzer-template/packages/analyze/src/lib/files/analyze/gatherEntityMap.ts +4 -2
- package/analyzer-template/packages/analyze/src/lib/files/analyze/validateDependencyAnalyses.ts +0 -3
- package/analyzer-template/packages/analyze/src/lib/files/analyzeRemixRoute.ts +4 -5
- package/analyzer-template/packages/analyze/src/lib/files/getImportedExports.ts +14 -12
- package/analyzer-template/packages/analyze/src/lib/files/scenarios/enrichArrayTypesFromChildSignatures.ts +57 -13
- package/analyzer-template/packages/analyze/src/lib/files/scenarios/gatherDataForMocks.ts +29 -0
- package/analyzer-template/packages/analyze/src/lib/files/scenarios/generateDataStructure.ts +35 -4
- package/analyzer-template/packages/analyze/src/lib/files/scenarios/generateExecutionFlows.ts +117 -9
- package/analyzer-template/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.ts +199 -17
- package/analyzer-template/packages/analyze/src/lib/files/scenarios/propagateArrayItemSchemas.ts +474 -0
- package/analyzer-template/packages/analyze/src/lib/files/setImportedExports.ts +2 -1
- package/analyzer-template/packages/analyze/src/lib/utils/getFileByPath.ts +19 -0
- package/analyzer-template/packages/aws/package.json +1 -1
- package/analyzer-template/packages/github/dist/types/src/types/ScenariosDataStructure.d.ts +5 -5
- package/analyzer-template/packages/github/dist/types/src/types/ScenariosDataStructure.d.ts.map +1 -1
- package/analyzer-template/packages/github/dist/types/src/types/ScopeAnalysis.d.ts +6 -1
- package/analyzer-template/packages/github/dist/types/src/types/ScopeAnalysis.d.ts.map +1 -1
- package/analyzer-template/packages/github/package.json +1 -1
- package/analyzer-template/packages/types/src/types/ScenariosDataStructure.ts +6 -5
- package/analyzer-template/packages/types/src/types/ScopeAnalysis.ts +6 -1
- package/analyzer-template/packages/utils/dist/types/src/types/ScenariosDataStructure.d.ts +5 -5
- package/analyzer-template/packages/utils/dist/types/src/types/ScenariosDataStructure.d.ts.map +1 -1
- package/analyzer-template/packages/utils/dist/types/src/types/ScopeAnalysis.d.ts +6 -1
- package/analyzer-template/packages/utils/dist/types/src/types/ScopeAnalysis.d.ts.map +1 -1
- package/analyzer-template/project/constructMockCode.ts +54 -9
- package/analyzer-template/project/writeMockDataTsx.ts +73 -2
- package/background/src/lib/virtualized/project/constructMockCode.js +45 -3
- package/background/src/lib/virtualized/project/constructMockCode.js.map +1 -1
- package/background/src/lib/virtualized/project/writeMockDataTsx.js +71 -2
- package/background/src/lib/virtualized/project/writeMockDataTsx.js.map +1 -1
- package/codeyam-cli/scripts/apply-setup.js +146 -0
- package/codeyam-cli/scripts/apply-setup.js.map +1 -1
- package/codeyam-cli/src/commands/debug.js +7 -5
- package/codeyam-cli/src/commands/debug.js.map +1 -1
- package/codeyam-cli/src/utils/install-skills.js +22 -0
- package/codeyam-cli/src/utils/install-skills.js.map +1 -1
- package/codeyam-cli/src/utils/reviewedRules.js +92 -0
- package/codeyam-cli/src/utils/reviewedRules.js.map +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/globals-CX9f-5xM.css +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{manifest-7522edd4.js → manifest-bba56ec1.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/memory-DuTFSyJ2.js +92 -0
- package/codeyam-cli/src/webserver/build/client/assets/{root-eVAaavTS.js → root-DTfSQARG.js} +6 -6
- package/codeyam-cli/src/webserver/build/server/assets/{index-DVzYx8PN.js → index-TD1f-DHV.js} +1 -1
- package/codeyam-cli/src/webserver/build/server/assets/server-build-BQ-1XyEa.js +258 -0
- package/codeyam-cli/src/webserver/build/server/index.js +1 -1
- package/codeyam-cli/src/webserver/build-info.json +5 -5
- package/codeyam-cli/templates/codeyam:memory.md +174 -233
- package/codeyam-cli/templates/codeyam:new-rule.md +41 -2
- package/codeyam-cli/templates/rule-reflection-hook.py +161 -0
- package/codeyam-cli/templates/rules-instructions.md +126 -0
- package/package.json +1 -1
- package/packages/ai/index.js +2 -1
- package/packages/ai/index.js.map +1 -1
- package/packages/ai/src/lib/analyzeScope.js +29 -12
- package/packages/ai/src/lib/analyzeScope.js.map +1 -1
- package/packages/ai/src/lib/astScopes/astScopeAnalyzer.js +54 -8
- package/packages/ai/src/lib/astScopes/astScopeAnalyzer.js.map +1 -1
- package/packages/ai/src/lib/astScopes/processExpression.js +239 -43
- package/packages/ai/src/lib/astScopes/processExpression.js.map +1 -1
- package/packages/ai/src/lib/dataStructure/ScopeDataStructure.js +503 -165
- package/packages/ai/src/lib/dataStructure/ScopeDataStructure.js.map +1 -1
- package/packages/ai/src/lib/dataStructure/helpers/BatchSchemaProcessor.js +13 -3
- package/packages/ai/src/lib/dataStructure/helpers/BatchSchemaProcessor.js.map +1 -1
- package/packages/ai/src/lib/dataStructure/helpers/ScopeTreeManager.js +6 -4
- package/packages/ai/src/lib/dataStructure/helpers/ScopeTreeManager.js.map +1 -1
- package/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.js +22 -1
- package/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.js.map +1 -1
- package/packages/ai/src/lib/dataStructure/helpers/cleanNonObjectFunctions.js +34 -9
- package/packages/ai/src/lib/dataStructure/helpers/cleanNonObjectFunctions.js.map +1 -1
- package/packages/ai/src/lib/dataStructure/helpers/convertTypeAnnotationsToValues.js +159 -0
- package/packages/ai/src/lib/dataStructure/helpers/convertTypeAnnotationsToValues.js.map +1 -0
- package/packages/ai/src/lib/dataStructure/helpers/deduplicateFunctionSchemas.js +37 -20
- package/packages/ai/src/lib/dataStructure/helpers/deduplicateFunctionSchemas.js.map +1 -1
- package/packages/ai/src/lib/dataStructure/helpers/fillInSchemaGapsAndUnknowns.js +237 -73
- package/packages/ai/src/lib/dataStructure/helpers/fillInSchemaGapsAndUnknowns.js.map +1 -1
- package/packages/ai/src/lib/generateEntityScenarioData.js +195 -1
- package/packages/ai/src/lib/generateEntityScenarioData.js.map +1 -1
- package/packages/ai/src/lib/generateEntityScenarios.js +7 -1
- package/packages/ai/src/lib/generateEntityScenarios.js.map +1 -1
- package/packages/ai/src/lib/generateExecutionFlows.js +10 -2
- package/packages/ai/src/lib/generateExecutionFlows.js.map +1 -1
- package/packages/ai/src/lib/generateExecutionFlowsFromConditionals.js +209 -3
- package/packages/ai/src/lib/generateExecutionFlowsFromConditionals.js.map +1 -1
- package/packages/ai/src/lib/mergeStatements.js +70 -51
- package/packages/ai/src/lib/mergeStatements.js.map +1 -1
- package/packages/ai/src/lib/promptGenerators/gatherAttributesMap.js +10 -4
- package/packages/ai/src/lib/promptGenerators/gatherAttributesMap.js.map +1 -1
- package/packages/ai/src/lib/resolvePathToControllable.js +24 -14
- package/packages/ai/src/lib/resolvePathToControllable.js.map +1 -1
- package/packages/ai/src/lib/worker/SerializableDataStructure.js.map +1 -1
- package/packages/analyze/src/lib/FileAnalyzer.js +60 -36
- package/packages/analyze/src/lib/FileAnalyzer.js.map +1 -1
- package/packages/analyze/src/lib/ProjectAnalyzer.js +96 -26
- package/packages/analyze/src/lib/ProjectAnalyzer.js.map +1 -1
- package/packages/analyze/src/lib/asts/sourceFiles/getAllDeclaredEntityNodes.js +14 -0
- package/packages/analyze/src/lib/asts/sourceFiles/getAllDeclaredEntityNodes.js.map +1 -1
- package/packages/analyze/src/lib/asts/sourceFiles/getAllEntityNodes.js +14 -0
- package/packages/analyze/src/lib/asts/sourceFiles/getAllEntityNodes.js.map +1 -1
- package/packages/analyze/src/lib/asts/sourceFiles/getAllExports.js +6 -0
- package/packages/analyze/src/lib/asts/sourceFiles/getAllExports.js.map +1 -1
- package/packages/analyze/src/lib/asts/sourceFiles/getImportsAnalysis.js +6 -0
- package/packages/analyze/src/lib/asts/sourceFiles/getImportsAnalysis.js.map +1 -1
- package/packages/analyze/src/lib/asts/sourceFiles/getResolvedModule.js +39 -1
- package/packages/analyze/src/lib/asts/sourceFiles/getResolvedModule.js.map +1 -1
- package/packages/analyze/src/lib/asts/sourceFiles/getSourceFilesForAllImports.js +2 -1
- package/packages/analyze/src/lib/asts/sourceFiles/getSourceFilesForAllImports.js.map +1 -1
- package/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.js +13 -5
- package/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.js.map +1 -1
- package/packages/analyze/src/lib/files/analyze/analyzeEntities.js +14 -4
- package/packages/analyze/src/lib/files/analyze/analyzeEntities.js.map +1 -1
- package/packages/analyze/src/lib/files/analyze/gatherEntityMap.js +2 -1
- package/packages/analyze/src/lib/files/analyze/gatherEntityMap.js.map +1 -1
- package/packages/analyze/src/lib/files/analyze/validateDependencyAnalyses.js +0 -3
- package/packages/analyze/src/lib/files/analyze/validateDependencyAnalyses.js.map +1 -1
- package/packages/analyze/src/lib/files/analyzeRemixRoute.js +3 -2
- package/packages/analyze/src/lib/files/analyzeRemixRoute.js.map +1 -1
- package/packages/analyze/src/lib/files/getImportedExports.js +11 -7
- package/packages/analyze/src/lib/files/getImportedExports.js.map +1 -1
- package/packages/analyze/src/lib/files/scenarios/enrichArrayTypesFromChildSignatures.js +52 -10
- package/packages/analyze/src/lib/files/scenarios/enrichArrayTypesFromChildSignatures.js.map +1 -1
- package/packages/analyze/src/lib/files/scenarios/gatherDataForMocks.js +25 -8
- package/packages/analyze/src/lib/files/scenarios/gatherDataForMocks.js.map +1 -1
- package/packages/analyze/src/lib/files/scenarios/generateDataStructure.js +34 -4
- package/packages/analyze/src/lib/files/scenarios/generateDataStructure.js.map +1 -1
- package/packages/analyze/src/lib/files/scenarios/generateExecutionFlows.js +56 -8
- package/packages/analyze/src/lib/files/scenarios/generateExecutionFlows.js.map +1 -1
- package/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.js +168 -9
- package/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.js.map +1 -1
- package/packages/analyze/src/lib/files/setImportedExports.js +2 -1
- package/packages/analyze/src/lib/files/setImportedExports.js.map +1 -1
- package/packages/analyze/src/lib/utils/getFileByPath.js +12 -0
- package/packages/analyze/src/lib/utils/getFileByPath.js.map +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/globals-D3yhhV8x.css +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/memory-yxFcrxBX.js +0 -92
- package/codeyam-cli/src/webserver/build/server/assets/server-build-4Cr0uToj.js +0 -257
package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/fillInSchemaGapsAndUnknowns.ts
CHANGED
|
@@ -205,16 +205,88 @@ export default function fillInSchemaGapsAndUnknowns(
|
|
|
205
205
|
}
|
|
206
206
|
}
|
|
207
207
|
|
|
208
|
+
/**
|
|
209
|
+
* Pre-built indexes that can be reused across multiple calls to fillInDirectSchemaGapsAndUnknowns.
|
|
210
|
+
* Build once with buildSchemaIndexes(), then pass to all calls processing the same schema.
|
|
211
|
+
*/
|
|
212
|
+
export interface SchemaIndexes {
|
|
213
|
+
functionKeysMapping: Record<string, string[]>;
|
|
214
|
+
prefixMaxDepth: Map<string, number>;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Build indexes for a schema that can be reused across multiple calls.
|
|
219
|
+
* This avoids rebuilding indexes (~1-2 seconds for 18k keys) on every call.
|
|
220
|
+
*
|
|
221
|
+
* @param schema - The schema to build indexes from (typically the full dependency schema)
|
|
222
|
+
* @returns Indexes that can be passed to fillInDirectSchemaGapsAndUnknowns
|
|
223
|
+
*/
|
|
224
|
+
export function buildSchemaIndexes(
|
|
225
|
+
schema: Record<string, string>,
|
|
226
|
+
): SchemaIndexes {
|
|
227
|
+
// Build functionKeysMapping + prefixMaxDepth in a single pass
|
|
228
|
+
// Avoid repeated split/join work by caching parts and building prefixes incrementally.
|
|
229
|
+
const functionKeysMapping: Record<string, string[]> = {};
|
|
230
|
+
const prefixMaxDepth = new Map<string, number>();
|
|
231
|
+
const partsCache = new Map<string, string[]>();
|
|
232
|
+
|
|
233
|
+
for (const key in schema) {
|
|
234
|
+
const parts = getCachedParts(partsCache, key);
|
|
235
|
+
const lastPart = parts[parts.length - 1];
|
|
236
|
+
const prefixes = buildPrefixParts(parts);
|
|
237
|
+
|
|
238
|
+
if (
|
|
239
|
+
key.endsWith(')') ||
|
|
240
|
+
schema[key] === 'function' ||
|
|
241
|
+
lastPart === 'length'
|
|
242
|
+
) {
|
|
243
|
+
const allButLastPart = parts.length > 1 ? prefixes[parts.length - 2] : '';
|
|
244
|
+
functionKeysMapping[allButLastPart] ||= [];
|
|
245
|
+
functionKeysMapping[allButLastPart].push(lastPart);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
for (let i = 0; i < prefixes.length; i++) {
|
|
249
|
+
const prefix = prefixes[i];
|
|
250
|
+
const existingMax = prefixMaxDepth.get(prefix) ?? 0;
|
|
251
|
+
if (parts.length > existingMax) {
|
|
252
|
+
prefixMaxDepth.set(prefix, parts.length);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return { functionKeysMapping, prefixMaxDepth };
|
|
258
|
+
}
|
|
259
|
+
|
|
208
260
|
export function fillInDirectSchemaGapsAndUnknowns({
|
|
209
261
|
scopeName,
|
|
210
262
|
schema,
|
|
211
263
|
mergedSchema,
|
|
212
264
|
attempts = 0,
|
|
265
|
+
// Pre-built indexes from buildSchemaIndexes() - use for cross-call caching
|
|
266
|
+
prebuiltIndexes,
|
|
267
|
+
// Internal: pre-computed indexes to avoid rebuilding on recursion
|
|
268
|
+
_functionKeysMapping,
|
|
269
|
+
_prefixMaxDepth,
|
|
270
|
+
_partsCache,
|
|
271
|
+
_prefixPartsCache,
|
|
272
|
+
_knownTypeCache,
|
|
273
|
+
_guessTypeCache,
|
|
274
|
+
_nameHintCache,
|
|
213
275
|
}: {
|
|
214
276
|
scopeName?: string;
|
|
215
277
|
schema: Record<string, string>;
|
|
216
278
|
mergedSchema?: Record<string, string>;
|
|
217
279
|
attempts?: number;
|
|
280
|
+
/** Pre-built indexes from buildSchemaIndexes() - pass to reuse across multiple calls */
|
|
281
|
+
prebuiltIndexes?: SchemaIndexes;
|
|
282
|
+
// Internal: pre-computed indexes to avoid rebuilding on recursion (used by recursive calls)
|
|
283
|
+
_functionKeysMapping?: Record<string, string[]>;
|
|
284
|
+
_prefixMaxDepth?: Map<string, number>;
|
|
285
|
+
_partsCache?: Map<string, string[]>;
|
|
286
|
+
_prefixPartsCache?: Map<string, string[]>;
|
|
287
|
+
_knownTypeCache?: Map<string, string | undefined>;
|
|
288
|
+
_guessTypeCache?: Map<string, string | undefined>;
|
|
289
|
+
_nameHintCache?: Map<string, string | undefined>;
|
|
218
290
|
}) {
|
|
219
291
|
try {
|
|
220
292
|
const existingSchema = (path: any) => {
|
|
@@ -225,105 +297,204 @@ export function fillInDirectSchemaGapsAndUnknowns({
|
|
|
225
297
|
}
|
|
226
298
|
};
|
|
227
299
|
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
300
|
+
const partsCache = _partsCache ?? new Map<string, string[]>();
|
|
301
|
+
const prefixPartsCache = _prefixPartsCache ?? new Map<string, string[]>();
|
|
302
|
+
const knownTypeCache =
|
|
303
|
+
_knownTypeCache ?? new Map<string, string | undefined>();
|
|
304
|
+
const guessTypeCache =
|
|
305
|
+
_guessTypeCache ?? new Map<string, string | undefined>();
|
|
306
|
+
const nameHintCache =
|
|
307
|
+
_nameHintCache ?? new Map<string, string | undefined>();
|
|
308
|
+
|
|
309
|
+
const getParts = (path: string) => getCachedParts(partsCache, path);
|
|
310
|
+
const getPrefixParts = (path: string) => {
|
|
311
|
+
const cached = prefixPartsCache.get(path);
|
|
312
|
+
if (cached) return cached;
|
|
313
|
+
const prefixes = buildPrefixParts(getParts(path));
|
|
314
|
+
prefixPartsCache.set(path, prefixes);
|
|
315
|
+
return prefixes;
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
// Index resolution priority:
|
|
319
|
+
// 1. _functionKeysMapping/_prefixMaxDepth (from recursive calls within this invocation)
|
|
320
|
+
// 2. prebuiltIndexes (from cross-call caching - built once, reused across multiple calls)
|
|
321
|
+
// 3. Build new indexes (fallback - expensive for large schemas)
|
|
322
|
+
const functionKeysMapping =
|
|
323
|
+
_functionKeysMapping ??
|
|
324
|
+
prebuiltIndexes?.functionKeysMapping ??
|
|
325
|
+
(() => {
|
|
326
|
+
const mapping: Record<string, string[]> = {};
|
|
327
|
+
for (const key in schema) {
|
|
328
|
+
const parts = getParts(key);
|
|
238
329
|
const lastPart = parts[parts.length - 1];
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
330
|
+
if (
|
|
331
|
+
key.endsWith(')') ||
|
|
332
|
+
schema[key] === 'function' ||
|
|
333
|
+
lastPart === 'length'
|
|
334
|
+
) {
|
|
335
|
+
const prefixes = getPrefixParts(key);
|
|
336
|
+
const allButLastPart =
|
|
337
|
+
parts.length > 1 ? prefixes[parts.length - 2] : '';
|
|
338
|
+
mapping[allButLastPart] ||= [];
|
|
339
|
+
mapping[allButLastPart].push(lastPart);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return mapping;
|
|
343
|
+
})();
|
|
344
|
+
|
|
345
|
+
// Build prefix index for O(1) lookups in checkIfObjectOrFunction
|
|
346
|
+
// Maps each prefix to the max depth of keys that have this prefix
|
|
347
|
+
// This replaces O(n) scans with O(1) lookups, fixing the O(n²) bottleneck
|
|
348
|
+
let prefixMaxDepth =
|
|
349
|
+
_prefixMaxDepth ?? prebuiltIndexes?.prefixMaxDepth ?? null;
|
|
350
|
+
if (!prefixMaxDepth) {
|
|
351
|
+
prefixMaxDepth = new Map<string, number>();
|
|
352
|
+
const targetSchema = mergedSchema ?? schema;
|
|
353
|
+
for (const key in targetSchema) {
|
|
354
|
+
const keyParts = getParts(key);
|
|
355
|
+
const prefixes = buildPrefixParts(keyParts);
|
|
356
|
+
// Record each prefix of this key with its max depth
|
|
357
|
+
for (let i = 0; i < prefixes.length; i++) {
|
|
358
|
+
const prefix = prefixes[i];
|
|
359
|
+
const existingMax = prefixMaxDepth.get(prefix) ?? 0;
|
|
360
|
+
if (keyParts.length > existingMax) {
|
|
361
|
+
prefixMaxDepth.set(prefix, keyParts.length);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
245
366
|
|
|
246
|
-
|
|
247
|
-
const
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
367
|
+
// O(1) replacement for checkIfObjectOrFunction
|
|
368
|
+
const checkIfObjectOrFunctionFast = (path: string): string | undefined => {
|
|
369
|
+
const maxDepth = prefixMaxDepth!.get(path);
|
|
370
|
+
|
|
371
|
+
if (maxDepth === undefined) return undefined; // No key starts with this path
|
|
372
|
+
|
|
373
|
+
const pathParts = getParts(path);
|
|
374
|
+
// Check if path's last part ends with ')' → function
|
|
375
|
+
const lastPart = pathParts[pathParts.length - 1];
|
|
376
|
+
if (lastPart?.endsWith(')')) {
|
|
377
|
+
return 'function';
|
|
253
378
|
}
|
|
254
379
|
|
|
255
|
-
|
|
256
|
-
|
|
380
|
+
// Check if there are deeper keys
|
|
381
|
+
if (maxDepth > pathParts.length) {
|
|
382
|
+
return 'object';
|
|
257
383
|
}
|
|
258
384
|
|
|
259
|
-
|
|
260
|
-
|
|
385
|
+
return undefined;
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
let changeMade = false;
|
|
389
|
+
const getKnownType = (path: string) => {
|
|
390
|
+
if (knownTypeCache.has(path)) return knownTypeCache.get(path);
|
|
391
|
+
const known = checkIfKnownType(path, functionKeysMapping);
|
|
392
|
+
knownTypeCache.set(path, known);
|
|
393
|
+
return known;
|
|
394
|
+
};
|
|
395
|
+
const guessTypeFromName = (lastPart: string, defaultType: string) => {
|
|
396
|
+
if (nameHintCache.has(lastPart)) {
|
|
397
|
+
return nameHintCache.get(lastPart) ?? defaultType;
|
|
398
|
+
}
|
|
261
399
|
|
|
400
|
+
let hint: string | undefined;
|
|
262
401
|
if (isLikelyBoolean(lastPart)) {
|
|
263
|
-
|
|
402
|
+
hint = 'boolean';
|
|
264
403
|
} else if (isLikelyDate(lastPart)) {
|
|
265
|
-
|
|
404
|
+
hint = 'date';
|
|
266
405
|
} else if (isLikelyAction(lastPart)) {
|
|
267
|
-
|
|
406
|
+
hint = 'function';
|
|
268
407
|
} else if (pluralize.isPlural(lastPart)) {
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
408
|
+
// Default to number instead of array - safer for JSX rendering
|
|
409
|
+
// Arrays cause "Objects are not valid as a React child" errors when rendered directly
|
|
410
|
+
hint = 'number';
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
nameHintCache.set(lastPart, hint);
|
|
414
|
+
return hint ?? defaultType;
|
|
415
|
+
};
|
|
416
|
+
const guessTypeForPath = (path: string, defaultType = 'string') => {
|
|
417
|
+
const cacheKey = `${path}|${defaultType}`;
|
|
418
|
+
if (guessTypeCache.has(cacheKey)) {
|
|
419
|
+
return guessTypeCache.get(cacheKey);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
let knownType = getKnownType(path);
|
|
423
|
+
if (!knownType) {
|
|
424
|
+
// Use optimized O(1) prefix lookup instead of O(n) scan
|
|
425
|
+
knownType = checkIfObjectOrFunctionFast(path);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
if (knownType) {
|
|
429
|
+
guessTypeCache.set(cacheKey, knownType);
|
|
430
|
+
return knownType;
|
|
272
431
|
}
|
|
432
|
+
|
|
433
|
+
const keyParts = getParts(path);
|
|
434
|
+
const lastPart = keyParts[keyParts.length - 1];
|
|
435
|
+
const guessed = guessTypeFromName(lastPart, defaultType);
|
|
436
|
+
guessTypeCache.set(cacheKey, guessed);
|
|
437
|
+
return guessed;
|
|
273
438
|
};
|
|
274
439
|
|
|
275
440
|
for (const key in schema) {
|
|
276
441
|
if (!schema[key]) schema[key] = 'unknown';
|
|
277
442
|
if (schema[key].includes(' | unknown')) {
|
|
278
|
-
|
|
279
|
-
|
|
443
|
+
const cleaned = schema[key].replace(' | unknown', '');
|
|
444
|
+
if (schema[key] !== cleaned) {
|
|
445
|
+
schema[key] = cleaned;
|
|
446
|
+
changeMade = true;
|
|
447
|
+
}
|
|
280
448
|
} else if (schema[key].startsWith('unknown | ')) {
|
|
281
449
|
// Handle "unknown | undefined" -> "string | undefined"
|
|
282
450
|
const remainingType = schema[key].substring('unknown | '.length);
|
|
283
451
|
const newType = guessTypeForPath(key, 'string');
|
|
284
452
|
if (newType) {
|
|
285
|
-
|
|
286
|
-
|
|
453
|
+
const replacement = `${newType} | ${remainingType}`;
|
|
454
|
+
if (schema[key] !== replacement) {
|
|
455
|
+
schema[key] = replacement;
|
|
456
|
+
changeMade = true;
|
|
457
|
+
}
|
|
287
458
|
}
|
|
288
459
|
} else if (schema[key] === 'unknown') {
|
|
289
460
|
const newType = guessTypeForPath(key, 'string');
|
|
290
|
-
if (newType) {
|
|
461
|
+
if (newType && schema[key] !== newType) {
|
|
291
462
|
schema[key] = newType;
|
|
292
463
|
changeMade = true;
|
|
293
464
|
}
|
|
294
465
|
}
|
|
295
466
|
|
|
296
|
-
const keyParts =
|
|
467
|
+
const keyParts = getParts(key);
|
|
468
|
+
const prefixParts = getPrefixParts(key);
|
|
297
469
|
for (let i = 0; i < keyParts.length; i++) {
|
|
298
|
-
|
|
299
|
-
const lastSubPathPart =
|
|
300
|
-
|
|
301
|
-
const
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
changeMade = true;
|
|
470
|
+
if (i === 0) continue;
|
|
471
|
+
const lastSubPathPart = keyParts[i];
|
|
472
|
+
const previousSubPath = prefixParts[i - 1];
|
|
473
|
+
const isSignatureIndex =
|
|
474
|
+
lastSubPathPart.includes('signature[') &&
|
|
475
|
+
SIGNATURE_INDEX_REGEX.test(lastSubPathPart);
|
|
476
|
+
|
|
477
|
+
if (lastSubPathPart.includes('[')) {
|
|
478
|
+
const cleaned = cleanOutBoundary(lastSubPathPart);
|
|
479
|
+
if (cleaned.match(/\[\d*\]/) && !isSignatureIndex) {
|
|
480
|
+
// Fix 39: Don't overwrite explicit 'object' types with 'array'
|
|
481
|
+
// This handles spurious [] paths from components like JsonNode that handle
|
|
482
|
+
// both arrays and objects. When metadata is explicitly typed as 'object',
|
|
483
|
+
// paths like metadata[] (from dynamic iteration) should not change it.
|
|
484
|
+
if (
|
|
485
|
+
schema[previousSubPath] !== 'array' &&
|
|
486
|
+
schema[previousSubPath] !== 'object'
|
|
487
|
+
) {
|
|
488
|
+
schema[previousSubPath] = 'array';
|
|
489
|
+
changeMade = true;
|
|
490
|
+
}
|
|
320
491
|
}
|
|
321
492
|
}
|
|
322
493
|
|
|
323
494
|
const isFunction =
|
|
324
495
|
lastSubPathPart.endsWith(')') ||
|
|
325
496
|
lastSubPathPart === 'functionCallReturnValue' ||
|
|
326
|
-
|
|
497
|
+
isSignatureIndex;
|
|
327
498
|
|
|
328
499
|
if (
|
|
329
500
|
!isFunction &&
|
|
@@ -331,8 +502,7 @@ export function fillInDirectSchemaGapsAndUnknowns({
|
|
|
331
502
|
schema[previousSubPath],
|
|
332
503
|
)
|
|
333
504
|
) {
|
|
334
|
-
const newValue =
|
|
335
|
-
checkIfKnownType(previousSubPath, functionKeysMapping) ?? 'object';
|
|
505
|
+
const newValue = getKnownType(previousSubPath) ?? 'object';
|
|
336
506
|
|
|
337
507
|
if (
|
|
338
508
|
!schema[previousSubPath] ||
|
|
@@ -347,8 +517,7 @@ export function fillInDirectSchemaGapsAndUnknowns({
|
|
|
347
517
|
if (isFunction && !existingSchema(previousSubPath)) {
|
|
348
518
|
schema[previousSubPath] = previousSubPath.endsWith(')')
|
|
349
519
|
? 'function'
|
|
350
|
-
: (
|
|
351
|
-
'object');
|
|
520
|
+
: (getKnownType(previousSubPath) ?? 'object');
|
|
352
521
|
changeMade = true;
|
|
353
522
|
}
|
|
354
523
|
|
|
@@ -358,10 +527,12 @@ export function fillInDirectSchemaGapsAndUnknowns({
|
|
|
358
527
|
const functionReturnValuePath = `${previousSubPath}.functionCallReturnValue`;
|
|
359
528
|
|
|
360
529
|
// Extract the method name from lastSubPathPart (e.g., "then()" -> "then")
|
|
361
|
-
const
|
|
362
|
-
const
|
|
363
|
-
|
|
364
|
-
|
|
530
|
+
const parenIndex = lastSubPathPart.indexOf('(');
|
|
531
|
+
const methodName =
|
|
532
|
+
parenIndex === -1
|
|
533
|
+
? lastSubPathPart
|
|
534
|
+
: lastSubPathPart.slice(0, parenIndex);
|
|
535
|
+
const isPromiseMethod = PROMISE_METHODS.has(methodName);
|
|
365
536
|
const isPreviousAsync = schema[previousSubPath] === 'async-function';
|
|
366
537
|
|
|
367
538
|
// Don't set to 'function' if this is a promise method on an async function
|
|
@@ -386,6 +557,15 @@ export function fillInDirectSchemaGapsAndUnknowns({
|
|
|
386
557
|
schema,
|
|
387
558
|
mergedSchema,
|
|
388
559
|
attempts: ++attempts,
|
|
560
|
+
prebuiltIndexes,
|
|
561
|
+
// Pass pre-computed indexes to avoid rebuilding on each recursive call
|
|
562
|
+
_functionKeysMapping: functionKeysMapping,
|
|
563
|
+
_prefixMaxDepth: prefixMaxDepth,
|
|
564
|
+
_partsCache: partsCache,
|
|
565
|
+
_prefixPartsCache: prefixPartsCache,
|
|
566
|
+
_knownTypeCache: knownTypeCache,
|
|
567
|
+
_guessTypeCache: guessTypeCache,
|
|
568
|
+
_nameHintCache: nameHintCache,
|
|
389
569
|
});
|
|
390
570
|
}
|
|
391
571
|
|
|
@@ -395,9 +575,10 @@ export function fillInDirectSchemaGapsAndUnknowns({
|
|
|
395
575
|
// However, if the parent could be an 'object' or other type, keep .length as it may be
|
|
396
576
|
// a custom property.
|
|
397
577
|
for (const key of Object.keys(schema)) {
|
|
398
|
-
const parts =
|
|
578
|
+
const parts = getParts(key);
|
|
399
579
|
if (parts[parts.length - 1] === 'length') {
|
|
400
|
-
const
|
|
580
|
+
const prefixes = getPrefixParts(key);
|
|
581
|
+
const parentPath = parts.length > 1 ? prefixes[parts.length - 2] : '';
|
|
401
582
|
const parentType = schema[parentPath];
|
|
402
583
|
if (parentType) {
|
|
403
584
|
// Split union types and filter out nullable parts
|
|
@@ -434,6 +615,35 @@ export function fillInDirectSchemaGapsAndUnknowns({
|
|
|
434
615
|
}
|
|
435
616
|
}
|
|
436
617
|
|
|
618
|
+
const SIGNATURE_INDEX_REGEX = /signature\[\d+\]/;
|
|
619
|
+
const PROMISE_METHODS = new Set(['then', 'catch', 'finally']);
|
|
620
|
+
|
|
621
|
+
function getCachedParts(cache: Map<string, string[]>, path: string): string[] {
|
|
622
|
+
const cached = cache.get(path);
|
|
623
|
+
if (cached) return cached;
|
|
624
|
+
const parts = splitOutsideParenthesesAndArrays(path);
|
|
625
|
+
cache.set(path, parts);
|
|
626
|
+
return parts;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
function buildPrefixParts(parts: string[]): string[] {
|
|
630
|
+
if (parts.length === 0) return [];
|
|
631
|
+
const prefixes = new Array(parts.length);
|
|
632
|
+
let prefix = '';
|
|
633
|
+
for (let i = 0; i < parts.length; i++) {
|
|
634
|
+
const part = parts[i];
|
|
635
|
+
if (i === 0) {
|
|
636
|
+
prefix = part;
|
|
637
|
+
} else if (part.startsWith('[') || part.startsWith('(')) {
|
|
638
|
+
prefix += part;
|
|
639
|
+
} else {
|
|
640
|
+
prefix += `.${part}`;
|
|
641
|
+
}
|
|
642
|
+
prefixes[i] = prefix;
|
|
643
|
+
}
|
|
644
|
+
return prefixes;
|
|
645
|
+
}
|
|
646
|
+
|
|
437
647
|
function checkIfObjectOrFunction(path: string, schema: ScopeNode['schema']) {
|
|
438
648
|
const pathParts = splitOutsideParenthesesAndArrays(path);
|
|
439
649
|
for (const structureKey in schema) {
|
|
@@ -506,20 +716,20 @@ function checkIfKnownType(
|
|
|
506
716
|
let hasStrongArrayEvidence = false;
|
|
507
717
|
let hasOnlyAmbiguousArrayMethods = true;
|
|
508
718
|
let hasLengthAccess = false;
|
|
509
|
-
let hasArrayStringAmbiguousMethod = false;
|
|
510
719
|
let hasIncludesOrIndexOfWithVariable = false;
|
|
511
720
|
|
|
512
|
-
|
|
513
|
-
|
|
721
|
+
const entries = functionAndAttributesKeysMapping[key];
|
|
722
|
+
if (!entries || entries.length === 0) return;
|
|
723
|
+
|
|
724
|
+
for (const functionOrAttributeName of entries) {
|
|
514
725
|
// Extract the method name without arguments for checking against ambiguous list
|
|
515
726
|
const methodName = functionOrAttributeName.split('(')[0].trim();
|
|
516
727
|
|
|
728
|
+
const knownType = knownMethodCalls(functionOrAttributeName);
|
|
517
729
|
const couldBeArray =
|
|
518
|
-
|
|
519
|
-
functionOrAttributeName === 'length';
|
|
730
|
+
knownType.includes('array') || functionOrAttributeName === 'length';
|
|
520
731
|
const couldBeString =
|
|
521
|
-
|
|
522
|
-
functionOrAttributeName === 'length';
|
|
732
|
+
knownType.includes('string') || functionOrAttributeName === 'length';
|
|
523
733
|
|
|
524
734
|
// Track .length access
|
|
525
735
|
if (functionOrAttributeName === 'length') {
|
|
@@ -527,10 +737,6 @@ function checkIfKnownType(
|
|
|
527
737
|
}
|
|
528
738
|
|
|
529
739
|
// Track methods that exist on both arrays and strings (like includes, indexOf)
|
|
530
|
-
if (couldBeArray && couldBeString && functionOrAttributeName !== 'length') {
|
|
531
|
-
hasArrayStringAmbiguousMethod = true;
|
|
532
|
-
}
|
|
533
|
-
|
|
534
740
|
// Check if .includes() or .indexOf() is called with a variable argument (not a string literal).
|
|
535
741
|
// Pattern: arr.includes(item) -> likely array (checking if item exists)
|
|
536
742
|
// Pattern: str.includes('substring') -> likely string (checking for substring)
|