@codeyam/codeyam-cli 0.1.0-staging.e090cb3 → 0.1.0-staging.e2d4438
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 +1 -1
- package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/convertDotNotation.ts +29 -18
- package/analyzer-template/packages/ai/src/lib/dataStructureChunking.ts +15 -6
- package/analyzer-template/packages/ai/src/lib/generateExecutionFlows.ts +96 -0
- package/analyzer-template/packages/analyze/src/lib/files/scenarios/gatherDataForMocks.ts +50 -25
- package/analyzer-template/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.ts +153 -76
- package/analyzer-template/packages/utils/dist/utils/src/lib/fs/rsyncCopy.d.ts.map +1 -1
- package/analyzer-template/packages/utils/dist/utils/src/lib/fs/rsyncCopy.js +93 -2
- package/analyzer-template/packages/utils/dist/utils/src/lib/fs/rsyncCopy.js.map +1 -1
- package/analyzer-template/packages/utils/src/lib/fs/rsyncCopy.ts +108 -2
- package/analyzer-template/project/constructMockCode.ts +2 -2
- package/analyzer-template/project/writeScenarioComponents.ts +14 -0
- package/background/src/lib/virtualized/project/constructMockCode.js +2 -2
- package/background/src/lib/virtualized/project/constructMockCode.js.map +1 -1
- package/background/src/lib/virtualized/project/writeScenarioComponents.js +10 -0
- package/background/src/lib/virtualized/project/writeScenarioComponents.js.map +1 -1
- package/codeyam-cli/src/commands/analyze.js +2 -2
- package/codeyam-cli/src/commands/analyze.js.map +1 -1
- package/codeyam-cli/src/commands/default.js +1 -1
- package/codeyam-cli/src/commands/default.js.map +1 -1
- package/codeyam-cli/src/commands/memory.js +63 -20
- package/codeyam-cli/src/commands/memory.js.map +1 -1
- package/codeyam-cli/src/commands/verify.js +2 -2
- package/codeyam-cli/src/commands/verify.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/npmVersionCheck.test.js +179 -0
- package/codeyam-cli/src/utils/__tests__/npmVersionCheck.test.js.map +1 -0
- package/codeyam-cli/src/utils/backgroundServer.js +90 -23
- package/codeyam-cli/src/utils/backgroundServer.js.map +1 -1
- package/codeyam-cli/src/utils/npmVersionCheck.js +76 -0
- package/codeyam-cli/src/utils/npmVersionCheck.js.map +1 -0
- package/codeyam-cli/src/utils/ruleReflection/__tests__/integration/helpers/assertRules.js +1 -1
- package/codeyam-cli/src/utils/ruleReflection/__tests__/integration/helpers/assertRules.js.map +1 -1
- package/codeyam-cli/src/utils/ruleReflection/__tests__/integration/helpers/setupTempProject.js +0 -1
- package/codeyam-cli/src/utils/ruleReflection/__tests__/integration/helpers/setupTempProject.js.map +1 -1
- package/codeyam-cli/src/utils/ruleReflection/__tests__/integration/ruleReflectionE2E.test.js +2 -4
- package/codeyam-cli/src/utils/ruleReflection/__tests__/integration/ruleReflectionE2E.test.js.map +1 -1
- package/codeyam-cli/src/utils/ruleReflection/__tests__/promptBuilder.test.js +2 -1
- package/codeyam-cli/src/utils/ruleReflection/__tests__/promptBuilder.test.js.map +1 -1
- package/codeyam-cli/src/utils/ruleReflection/contextBuilder.js +1 -1
- package/codeyam-cli/src/utils/ruleReflection/contextBuilder.js.map +1 -1
- package/codeyam-cli/src/utils/versionInfo.js +25 -0
- package/codeyam-cli/src/utils/versionInfo.js.map +1 -1
- package/codeyam-cli/src/webserver/__tests__/dependency-smoke.test.js +66 -0
- package/codeyam-cli/src/webserver/__tests__/dependency-smoke.test.js.map +1 -0
- package/codeyam-cli/src/webserver/app/lib/dbNotifier.js.map +1 -1
- package/codeyam-cli/src/webserver/backgroundServer.js +26 -0
- package/codeyam-cli/src/webserver/backgroundServer.js.map +1 -1
- package/codeyam-cli/src/webserver/bootstrap.js +11 -0
- package/codeyam-cli/src/webserver/bootstrap.js.map +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{CopyButton-CA3JxPb7.js → CopyButton-jNYXRRNI.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{EntityItem-B86KKU7e.js → EntityItem-bwuHPyTa.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{EntityTypeBadge-B5ctlSYt.js → EntityTypeBadge-CvzqMxcu.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{EntityTypeIcon-BqY8gDAW.js → EntityTypeIcon-BH0XDim7.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{InlineSpinner-ClaLpuOo.js → InlineSpinner-EhOseatT.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{InteractivePreview-BDhPilK7.js → InteractivePreview-yjIHlOGa.js} +2 -2
- package/codeyam-cli/src/webserver/build/client/assets/{LibraryFunctionPreview-VeqEBv9v.js → LibraryFunctionPreview-Cq5o8jL4.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{LoadingDots-Bs7Nn1Jr.js → LoadingDots-BvMu2i-g.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{LogViewer-Bm3PmcCz.js → LogViewer-kgBTLoJD.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{ReportIssueModal-CgMEzchJ.js → ReportIssueModal-BzPgx-xO.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{SafeScreenshot-Gq3Ocjo6.js → SafeScreenshot-CwZrv-Ok.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{ScenarioViewer-CBui0id_.js → ScenarioViewer-BX2Ny2Qj.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{TruncatedFilePath-CiwXDxLh.js → TruncatedFilePath-CDpEprKa.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{_index-B3TDXxnk.js → _index-BRx8ZGZo.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{activity.(_tab)-BtBFH820.js → activity.(_tab)-4S4yPfFw.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/agent-transcripts-DHKuQSmR.js +17 -0
- package/codeyam-cli/src/webserver/build/client/assets/{book-open-PttOB2SF.js → book-open-D4IPYH_y.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{chevron-down-TJp6ofnp.js → chevron-down-CG65viiV.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{chunk-JZWAC4HX-JE9ZIoBl.js → chunk-JZWAC4HX-DB3aFuEO.js} +9 -9
- package/codeyam-cli/src/webserver/build/client/assets/{circle-check-CXhHQYrI.js → circle-check-igfMr5DY.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{copy-6y9ALfGT.js → copy-Coc4o_8c.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{createLucideIcon-Ca9fAY46.js → createLucideIcon-D1zB-pYc.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{dev.empty-C0epRiVn.js → dev.empty-JTAjQ54M.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha._-BVnB8a9L.js → entity._sha._-B0h9AqE6.js} +2 -2
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha.scenarios._scenarioId.fullscreen-CBoafmVs.js → entity._sha.scenarios._scenarioId.fullscreen-DjLxr2JB.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha_.create-scenario-DGgZjdFg.js → entity._sha_.create-scenario-CtYowLOt.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha_.edit._scenarioId-38yPijoD.js → entity._sha_.edit._scenarioId-PePWg17F.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{entry.client-BSHEfydn.js → entry.client-I-Wo99C_.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{fileTableUtils-DCPhhSMo.js → fileTableUtils-9sMMAiWJ.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{files-0N0YJQv7.js → files-Co65J0s3.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{git-DXnyr8uP.js → git-BdHOxVfg.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/globals-BSZfYCkU.css +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{index-ChN9-fAY.js → index-CUM5iXwc.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{index-CcsFv748.js → index-_417gcQW.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{labs-BLJ7HxOC.js → labs-BK0C1H1T.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{loader-circle-CTqLEAGU.js → loader-circle-TzRHMVog.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{manifest-b171b9d3.js → manifest-040dab1c.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/memory-UIDVz141.js +92 -0
- package/codeyam-cli/src/webserver/build/client/assets/{pause-D6vreykR.js → pause-hjzB7t2z.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/root-D1WadSdf.js +62 -0
- package/codeyam-cli/src/webserver/build/client/assets/{search-B8VUL8nl.js → search-DcAwD_Ln.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{settings-BejnUJ6R.js → settings-CclxrcPK.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{simulations-CPoAg7Zo.js → simulations-DVNJVQgD.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{terminal-BrCP7uQo.js → terminal-DbEAHMbA.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{triangle-alert-BZz2NjYa.js → triangle-alert-CAD5b1o_.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{useCustomSizes-DNwUduNu.js → useCustomSizes-BqgrAzs3.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{useLastLogLine-COky1GVF.js → useLastLogLine-DAFqfEDH.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{useReportContext-CpZgwliL.js → useReportContext-DZlYx2c4.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{useToast-Bv9JFvUO.js → useToast-ihdMtlf6.js} +1 -1
- package/codeyam-cli/src/webserver/build/server/assets/{index-8Fv-lH1-.js → index-B3dE0r28.js} +1 -1
- package/codeyam-cli/src/webserver/build/server/assets/server-build-DYbfdxa3.js +273 -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 +5 -1
- package/codeyam-cli/templates/rule-reflection-hook.py +1 -1
- package/codeyam-cli/templates/rules-instructions.md +1 -1
- package/package.json +8 -8
- package/packages/ai/src/lib/dataStructure/helpers/convertDotNotation.js +24 -16
- package/packages/ai/src/lib/dataStructure/helpers/convertDotNotation.js.map +1 -1
- package/packages/ai/src/lib/dataStructureChunking.js +9 -5
- package/packages/ai/src/lib/dataStructureChunking.js.map +1 -1
- package/packages/ai/src/lib/generateExecutionFlows.js +81 -0
- package/packages/ai/src/lib/generateExecutionFlows.js.map +1 -1
- package/packages/analyze/src/lib/files/scenarios/gatherDataForMocks.js +42 -13
- package/packages/analyze/src/lib/files/scenarios/gatherDataForMocks.js.map +1 -1
- package/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.js +123 -67
- package/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.js.map +1 -1
- package/packages/utils/src/lib/fs/rsyncCopy.js +93 -2
- package/packages/utils/src/lib/fs/rsyncCopy.js.map +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/agent-transcripts-CN61MOMa.js +0 -11
- package/codeyam-cli/src/webserver/build/client/assets/globals-CKT08Djd.css +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/memory-CCQd4aZA.js +0 -78
- package/codeyam-cli/src/webserver/build/client/assets/root-CHhiHoo_.js +0 -62
- package/codeyam-cli/src/webserver/build/server/assets/server-build-Akn3iYFP.js +0 -257
package/analyzer-template/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.ts
CHANGED
|
@@ -210,6 +210,23 @@ export default function mergeInDependentDataStructure({
|
|
|
210
210
|
equivalentPostfixes: Record<string, string>;
|
|
211
211
|
}[] = [];
|
|
212
212
|
|
|
213
|
+
// O(1) index for findOrCreateEquivalentSchemaPathsEntry.
|
|
214
|
+
// Maps "(rootPath)::(normalizedFuncName)" → the entry containing that root.
|
|
215
|
+
// This replaces the O(E) linear search that was causing O(E²) gather performance.
|
|
216
|
+
const espIndex = new Map<string, (typeof equivalentSchemaPaths)[0]>();
|
|
217
|
+
const espIndexKey = (path: string, functionName: string | undefined) => {
|
|
218
|
+
const normalized = cleanFunctionName(functionName);
|
|
219
|
+
const funcKey =
|
|
220
|
+
normalized === rootScopeName ? '__self__' : normalized || '__self__';
|
|
221
|
+
return `${path}::${funcKey}`;
|
|
222
|
+
};
|
|
223
|
+
const updateEspIndex = (entry: (typeof equivalentSchemaPaths)[0]) => {
|
|
224
|
+
for (const root of entry.equivalentRoots) {
|
|
225
|
+
const funcName = root.function?.name ?? rootScopeName;
|
|
226
|
+
espIndex.set(espIndexKey(root.schemaRootPath, funcName), entry);
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
|
|
213
230
|
// Pre-build a lookup map from cleaned function name to dependency for O(1) lookups.
|
|
214
231
|
// This avoids O(n) linear search in findRelevantDependency which was causing O(n²) performance.
|
|
215
232
|
const dependencyByCleanedName = new Map<
|
|
@@ -324,6 +341,54 @@ export default function mergeInDependentDataStructure({
|
|
|
324
341
|
) => {
|
|
325
342
|
if (!sourceAndUsageEquivalencies) return;
|
|
326
343
|
|
|
344
|
+
// Pre-computed normalized schema index cache.
|
|
345
|
+
// Avoids repeated splitOutsideParenthesesAndArrays calls and function-name
|
|
346
|
+
// normalization for the same schema paths across multiple equivalency iterations.
|
|
347
|
+
// The normalization depends on `functionName` (constant per gatherAllEquivalentSchemaPaths call),
|
|
348
|
+
// so this cache is scoped to this call.
|
|
349
|
+
type NormalizedEntry = { path: string; parts: string[] };
|
|
350
|
+
const normalizedSchemaCache = new Map<
|
|
351
|
+
object,
|
|
352
|
+
{
|
|
353
|
+
byFirstPart: Map<string, NormalizedEntry[]>;
|
|
354
|
+
}
|
|
355
|
+
>();
|
|
356
|
+
const getSchemaIndex = (
|
|
357
|
+
schema: Record<string, string> | undefined,
|
|
358
|
+
): { byFirstPart: Map<string, NormalizedEntry[]> } => {
|
|
359
|
+
if (!schema) return { byFirstPart: new Map() };
|
|
360
|
+
const cached = normalizedSchemaCache.get(schema);
|
|
361
|
+
if (cached) return cached;
|
|
362
|
+
const byFirstPart = new Map<string, NormalizedEntry[]>();
|
|
363
|
+
for (const path in schema) {
|
|
364
|
+
let parts = splitOutsideParenthesesAndArrays(path);
|
|
365
|
+
if (parts[0].startsWith(functionName)) {
|
|
366
|
+
const baseName = cleanFunctionName(parts[0]);
|
|
367
|
+
if (!functionsWithMultipleTypeParams.has(baseName)) {
|
|
368
|
+
parts =
|
|
369
|
+
parts[1] === 'functionCallReturnValue'
|
|
370
|
+
? ['returnValue', ...parts.slice(2)]
|
|
371
|
+
: parts.slice(1);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
const entry: NormalizedEntry = { path, parts };
|
|
375
|
+
// Index by the base of the first part (before any function call args)
|
|
376
|
+
const firstPart = parts[0] ?? '';
|
|
377
|
+
const parenIdx = firstPart.indexOf('(');
|
|
378
|
+
const firstPartBase =
|
|
379
|
+
parenIdx >= 0 ? firstPart.slice(0, parenIdx) : firstPart;
|
|
380
|
+
let bucket = byFirstPart.get(firstPartBase);
|
|
381
|
+
if (!bucket) {
|
|
382
|
+
bucket = [];
|
|
383
|
+
byFirstPart.set(firstPartBase, bucket);
|
|
384
|
+
}
|
|
385
|
+
bucket.push(entry);
|
|
386
|
+
}
|
|
387
|
+
const result = { byFirstPart };
|
|
388
|
+
normalizedSchemaCache.set(schema, result);
|
|
389
|
+
return result;
|
|
390
|
+
};
|
|
391
|
+
|
|
327
392
|
const findOrCreateEquivalentSchemaPathsEntry = (
|
|
328
393
|
allPaths: { path: string; functionName?: string }[],
|
|
329
394
|
) => {
|
|
@@ -354,62 +419,52 @@ export default function mergeInDependentDataStructure({
|
|
|
354
419
|
}
|
|
355
420
|
}
|
|
356
421
|
|
|
422
|
+
// Use espIndex Map for O(1) lookup instead of O(E) linear search.
|
|
423
|
+
// Falls back to linear search only when Map hit has a signature index conflict.
|
|
357
424
|
for (const pathInfo of allPaths) {
|
|
358
|
-
if (
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
// Get all signature indices in the existing entry (grouped by function)
|
|
376
|
-
const existingIndicesByFunction = new Map<string, Set<number>>();
|
|
377
|
-
for (const er of esp.equivalentRoots) {
|
|
378
|
-
const funcKey = er.function
|
|
379
|
-
? `${er.function.name}::${er.function.filePath}`
|
|
380
|
-
: '__self__';
|
|
381
|
-
const idx = extractSignatureIndex(er.schemaRootPath);
|
|
382
|
-
if (idx !== undefined) {
|
|
383
|
-
if (!existingIndicesByFunction.has(funcKey)) {
|
|
384
|
-
existingIndicesByFunction.set(funcKey, new Set());
|
|
385
|
-
}
|
|
386
|
-
existingIndicesByFunction.get(funcKey)!.add(idx);
|
|
387
|
-
}
|
|
425
|
+
if (equivalentSchemaPathsEntry) break;
|
|
426
|
+
const candidate = espIndex.get(
|
|
427
|
+
espIndexKey(pathInfo.path, pathInfo.functionName),
|
|
428
|
+
);
|
|
429
|
+
if (!candidate) continue;
|
|
430
|
+
|
|
431
|
+
// Verify no signature index conflict with the candidate entry
|
|
432
|
+
if (newRootSignatureIndices.size > 0) {
|
|
433
|
+
const existingIndicesByFunction = new Map<string, Set<number>>();
|
|
434
|
+
for (const er of candidate.equivalentRoots) {
|
|
435
|
+
const funcKey = er.function
|
|
436
|
+
? `${er.function.name}::${er.function.filePath}`
|
|
437
|
+
: '__self__';
|
|
438
|
+
const idx = extractSignatureIndex(er.schemaRootPath);
|
|
439
|
+
if (idx !== undefined) {
|
|
440
|
+
if (!existingIndicesByFunction.has(funcKey)) {
|
|
441
|
+
existingIndicesByFunction.set(funcKey, new Set());
|
|
388
442
|
}
|
|
443
|
+
existingIndicesByFunction.get(funcKey)!.add(idx);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
389
446
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
// Conflict: entry has indices like [1] but we want to add [2]
|
|
403
|
-
return false;
|
|
404
|
-
}
|
|
405
|
-
}
|
|
447
|
+
let hasConflict = false;
|
|
448
|
+
for (const newRoot of equivalentRoots) {
|
|
449
|
+
const funcKey = newRoot.function
|
|
450
|
+
? `${newRoot.function.name}::${newRoot.function.filePath}`
|
|
451
|
+
: '__self__';
|
|
452
|
+
const newIdx = extractSignatureIndex(newRoot.schemaRootPath);
|
|
453
|
+
if (newIdx !== undefined) {
|
|
454
|
+
const existingIndices = existingIndicesByFunction.get(funcKey);
|
|
455
|
+
if (existingIndices && existingIndices.size > 0) {
|
|
456
|
+
if (!existingIndices.has(newIdx)) {
|
|
457
|
+
hasConflict = true;
|
|
458
|
+
break;
|
|
406
459
|
}
|
|
407
460
|
}
|
|
408
461
|
}
|
|
462
|
+
}
|
|
409
463
|
|
|
410
|
-
|
|
411
|
-
});
|
|
464
|
+
if (hasConflict) continue;
|
|
412
465
|
}
|
|
466
|
+
|
|
467
|
+
equivalentSchemaPathsEntry = candidate;
|
|
413
468
|
}
|
|
414
469
|
|
|
415
470
|
if (!equivalentSchemaPathsEntry) {
|
|
@@ -467,6 +522,9 @@ export default function mergeInDependentDataStructure({
|
|
|
467
522
|
return true;
|
|
468
523
|
});
|
|
469
524
|
|
|
525
|
+
// Keep the espIndex in sync after adding/deduplicating roots
|
|
526
|
+
updateEspIndex(equivalentSchemaPathsEntry);
|
|
527
|
+
|
|
470
528
|
return equivalentSchemaPathsEntry;
|
|
471
529
|
};
|
|
472
530
|
|
|
@@ -535,6 +593,8 @@ export default function mergeInDependentDataStructure({
|
|
|
535
593
|
);
|
|
536
594
|
|
|
537
595
|
const derivedBasePaths: { path: string; functionName?: string }[] = [];
|
|
596
|
+
const allPathSet = new Set(allPaths.map((p) => p.path));
|
|
597
|
+
const derivedBasePathSet = new Set<string>();
|
|
538
598
|
|
|
539
599
|
// For each child path, find its equivalent parent path and derive bases
|
|
540
600
|
for (const childPathInfo of childPaths) {
|
|
@@ -611,26 +671,28 @@ export default function mergeInDependentDataStructure({
|
|
|
611
671
|
!childHasArrayIterator &&
|
|
612
672
|
!childBaseIsGenericSignature
|
|
613
673
|
) {
|
|
614
|
-
// Add child base if not already present
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
674
|
+
// Add child base if not already present (O(1) Set lookup)
|
|
675
|
+
if (
|
|
676
|
+
!allPathSet.has(childBase) &&
|
|
677
|
+
!derivedBasePathSet.has(childBase)
|
|
678
|
+
) {
|
|
619
679
|
derivedBasePaths.push({
|
|
620
680
|
path: childBase,
|
|
621
681
|
functionName: childPathInfo.functionName,
|
|
622
682
|
});
|
|
683
|
+
derivedBasePathSet.add(childBase);
|
|
623
684
|
}
|
|
624
685
|
|
|
625
|
-
// Add parent base if not already present
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
686
|
+
// Add parent base if not already present (O(1) Set lookup)
|
|
687
|
+
if (
|
|
688
|
+
!allPathSet.has(parentBase) &&
|
|
689
|
+
!derivedBasePathSet.has(parentBase)
|
|
690
|
+
) {
|
|
630
691
|
derivedBasePaths.push({
|
|
631
692
|
path: parentBase,
|
|
632
693
|
functionName: parentPathInfo.functionName,
|
|
633
694
|
});
|
|
695
|
+
derivedBasePathSet.add(parentBase);
|
|
634
696
|
}
|
|
635
697
|
}
|
|
636
698
|
}
|
|
@@ -708,21 +770,20 @@ export default function mergeInDependentDataStructure({
|
|
|
708
770
|
}
|
|
709
771
|
|
|
710
772
|
for (const schema of schemas) {
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
773
|
+
// Use pre-computed index to only iterate schema entries whose
|
|
774
|
+
// normalized first part matches pathParts[0], instead of all entries.
|
|
775
|
+
const schemaIndex = getSchemaIndex(schema);
|
|
776
|
+
const lookupPart = pathParts[0] ?? '';
|
|
777
|
+
const lookupParenIdx = lookupPart.indexOf('(');
|
|
778
|
+
const lookupBase =
|
|
779
|
+
lookupParenIdx >= 0
|
|
780
|
+
? lookupPart.slice(0, lookupParenIdx)
|
|
781
|
+
: lookupPart;
|
|
782
|
+
const candidates = schemaIndex.byFirstPart.get(lookupBase) || [];
|
|
783
|
+
for (const {
|
|
784
|
+
path: schemaPath,
|
|
785
|
+
parts: schemaPathParts,
|
|
786
|
+
} of candidates) {
|
|
726
787
|
if (schemaPathParts.length < pathParts.length) continue;
|
|
727
788
|
|
|
728
789
|
// Check if all path parts match (allowing function call variants)
|
|
@@ -850,10 +911,17 @@ export default function mergeInDependentDataStructure({
|
|
|
850
911
|
const entry = findOrCreateEquivalentSchemaPathsEntry([
|
|
851
912
|
{ path: translatedBasePath, functionName: functionName },
|
|
852
913
|
]);
|
|
853
|
-
|
|
914
|
+
const newRoot = {
|
|
854
915
|
schemaRootPath: translatedBasePath,
|
|
855
916
|
function: findRelevantDependency(functionName),
|
|
856
|
-
}
|
|
917
|
+
};
|
|
918
|
+
entry.equivalentRoots.push(newRoot);
|
|
919
|
+
// Update index for the newly added root
|
|
920
|
+
const newRootFuncName = newRoot.function?.name ?? rootScopeName;
|
|
921
|
+
espIndex.set(
|
|
922
|
+
espIndexKey(newRoot.schemaRootPath, newRootFuncName),
|
|
923
|
+
entry,
|
|
924
|
+
);
|
|
857
925
|
|
|
858
926
|
const basePathParts = splitOutsideParenthesesAndArrays(basePath);
|
|
859
927
|
for (const schemaPath in dataStructure.returnValueSchema) {
|
|
@@ -1113,6 +1181,10 @@ export default function mergeInDependentDataStructure({
|
|
|
1113
1181
|
|
|
1114
1182
|
equivalentSchemaPaths = mergeAllEquivalentSchemaPaths();
|
|
1115
1183
|
|
|
1184
|
+
// Collect schemas that need cleaning — batch the calls for the end instead of
|
|
1185
|
+
// calling cleanSchema inside the inner root loop (which was O(roots * schemaSize)).
|
|
1186
|
+
const schemasToClean = new Set<{ [key: string]: string }>();
|
|
1187
|
+
|
|
1116
1188
|
for (const esp of equivalentSchemaPaths) {
|
|
1117
1189
|
// Pre-compute which postfixes have children to avoid O(n²) lookups in the inner loop.
|
|
1118
1190
|
// A postfix "has children" if there are other postfixes that extend it.
|
|
@@ -1290,10 +1362,15 @@ export default function mergeInDependentDataStructure({
|
|
|
1290
1362
|
schema[newSchemaPath] = postfixValue;
|
|
1291
1363
|
}
|
|
1292
1364
|
|
|
1293
|
-
|
|
1365
|
+
schemasToClean.add(schema);
|
|
1294
1366
|
}
|
|
1295
1367
|
}
|
|
1296
1368
|
|
|
1369
|
+
// Batch-clean all modified schemas once (instead of once per root per ESP entry)
|
|
1370
|
+
for (const schema of schemasToClean) {
|
|
1371
|
+
cleanSchema(schema, { stage: 'afterMergePostfix' });
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1297
1374
|
// Propagate equivalency-derived attributes to generic function call variants.
|
|
1298
1375
|
// When attributes are traced via equivalencies (e.g., fileComparisons from buildDataMap.signature[2]),
|
|
1299
1376
|
// they get written to non-generic paths (returnValue.data.x or funcName().functionCallReturnValue.data.x).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rsyncCopy.d.ts","sourceRoot":"","sources":["../../../../../src/lib/fs/rsyncCopy.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"rsyncCopy.d.ts","sourceRoot":"","sources":["../../../../../src/lib/fs/rsyncCopy.ts"],"names":[],"mappings":"AAqFA,wBAA8B,SAAS,CAAC,EACtC,UAAU,EACV,eAAe,EACf,QAAa,EACb,YAAoB,EACpB,MAAc,EACd,SAAc,GACf,EAAE;IACD,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB,GAAG,OAAO,CAAC,IAAI,CAAC,CAsEhB"}
|
|
@@ -1,5 +1,97 @@
|
|
|
1
|
-
import { spawn } from 'child_process';
|
|
1
|
+
import { spawn, execSync } from 'child_process';
|
|
2
|
+
import { existsSync, readdirSync, rmSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
/**
|
|
5
|
+
* Try to use APFS copy-on-write clones on macOS for near-instant directory copies.
|
|
6
|
+
* Falls back to rsync on non-macOS or if the clone fails.
|
|
7
|
+
*
|
|
8
|
+
* Returns true if the clone succeeded (caller can skip rsync).
|
|
9
|
+
*/
|
|
10
|
+
function tryApfsClone({ sourcePath, destinationPath, excludes, silent, }) {
|
|
11
|
+
if (process.platform !== 'darwin')
|
|
12
|
+
return false;
|
|
13
|
+
// APFS clone requires the destination to not exist.
|
|
14
|
+
// If it exists and is empty, remove it so we can clone into it.
|
|
15
|
+
if (existsSync(destinationPath)) {
|
|
16
|
+
try {
|
|
17
|
+
const contents = readdirSync(destinationPath);
|
|
18
|
+
if (contents.length > 0) {
|
|
19
|
+
// Destination is non-empty — can't use clone, fall back to rsync
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
rmSync(destinationPath, { recursive: true });
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
// cp -c -R: APFS copy-on-write clone (faster than rsync, avoids data copy)
|
|
30
|
+
execSync(`cp -c -R "${sourcePath}" "${destinationPath}"`, {
|
|
31
|
+
stdio: 'pipe',
|
|
32
|
+
timeout: 300000, // 5 min safety timeout
|
|
33
|
+
});
|
|
34
|
+
// Remove excluded items from the clone
|
|
35
|
+
for (const exclude of excludes) {
|
|
36
|
+
if (exclude.includes('*')) {
|
|
37
|
+
// Glob pattern — use shell expansion
|
|
38
|
+
try {
|
|
39
|
+
execSync(`rm -rf "${join(destinationPath, exclude)}"`, {
|
|
40
|
+
stdio: 'pipe',
|
|
41
|
+
shell: '/bin/sh',
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// Glob matched nothing — fine
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
const excludePath = join(destinationPath, exclude);
|
|
50
|
+
if (existsSync(excludePath)) {
|
|
51
|
+
rmSync(excludePath, { recursive: true, force: true });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (!silent) {
|
|
56
|
+
console.log(`Directory cloned (APFS CoW) from ${sourcePath} to ${destinationPath}`);
|
|
57
|
+
}
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// Clone failed (cross-volume, non-APFS, etc.) — fall back to rsync
|
|
62
|
+
// Clean up any partial clone
|
|
63
|
+
if (existsSync(destinationPath)) {
|
|
64
|
+
try {
|
|
65
|
+
rmSync(destinationPath, { recursive: true });
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
// Best effort cleanup
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
2
74
|
export default async function rsyncCopy({ sourcePath, destinationPath, excludes = [], keepExisting = false, silent = false, extraArgs = [], }) {
|
|
75
|
+
const startTime = Date.now();
|
|
76
|
+
// On macOS, try APFS copy-on-write clone first (near-instant).
|
|
77
|
+
// Skip when extraArgs are provided since those are rsync-specific flags
|
|
78
|
+
// that the clone path can't honor.
|
|
79
|
+
if (!keepExisting && extraArgs.length === 0) {
|
|
80
|
+
const cloned = tryApfsClone({
|
|
81
|
+
sourcePath,
|
|
82
|
+
destinationPath,
|
|
83
|
+
excludes,
|
|
84
|
+
silent,
|
|
85
|
+
});
|
|
86
|
+
if (cloned) {
|
|
87
|
+
if (!silent) {
|
|
88
|
+
const duration = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
89
|
+
console.log(`Directory synced from ${sourcePath} to ${destinationPath} [Time: ${duration}s]`);
|
|
90
|
+
}
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// Fall back to rsync
|
|
3
95
|
return new Promise((resolve, reject) => {
|
|
4
96
|
const source = sourcePath.endsWith('/') ? sourcePath : `${sourcePath}/`;
|
|
5
97
|
const dest = destinationPath.endsWith('/')
|
|
@@ -16,7 +108,6 @@ export default async function rsyncCopy({ sourcePath, destinationPath, excludes
|
|
|
16
108
|
rsyncArgs.push(`--exclude=${exclude}`);
|
|
17
109
|
}
|
|
18
110
|
rsyncArgs.push(source, dest);
|
|
19
|
-
const startTime = Date.now();
|
|
20
111
|
const rsyncProcess = spawn('rsync', rsyncArgs);
|
|
21
112
|
rsyncProcess.on('exit', (code) => {
|
|
22
113
|
if (code === 0) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rsyncCopy.js","sourceRoot":"","sources":["../../../../../src/lib/fs/rsyncCopy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"rsyncCopy.js","sourceRoot":"","sources":["../../../../../src/lib/fs/rsyncCopy.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,87 @@
|
|
|
1
|
-
import { spawn } from 'child_process';
|
|
1
|
+
import { spawn, execSync } from 'child_process';
|
|
2
|
+
import { existsSync, readdirSync, rmSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Try to use APFS copy-on-write clones on macOS for near-instant directory copies.
|
|
7
|
+
* Falls back to rsync on non-macOS or if the clone fails.
|
|
8
|
+
*
|
|
9
|
+
* Returns true if the clone succeeded (caller can skip rsync).
|
|
10
|
+
*/
|
|
11
|
+
function tryApfsClone({
|
|
12
|
+
sourcePath,
|
|
13
|
+
destinationPath,
|
|
14
|
+
excludes,
|
|
15
|
+
silent,
|
|
16
|
+
}: {
|
|
17
|
+
sourcePath: string;
|
|
18
|
+
destinationPath: string;
|
|
19
|
+
excludes: string[];
|
|
20
|
+
silent: boolean;
|
|
21
|
+
}): boolean {
|
|
22
|
+
if (process.platform !== 'darwin') return false;
|
|
23
|
+
|
|
24
|
+
// APFS clone requires the destination to not exist.
|
|
25
|
+
// If it exists and is empty, remove it so we can clone into it.
|
|
26
|
+
if (existsSync(destinationPath)) {
|
|
27
|
+
try {
|
|
28
|
+
const contents = readdirSync(destinationPath);
|
|
29
|
+
if (contents.length > 0) {
|
|
30
|
+
// Destination is non-empty — can't use clone, fall back to rsync
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
rmSync(destinationPath, { recursive: true });
|
|
34
|
+
} catch {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
// cp -c -R: APFS copy-on-write clone (faster than rsync, avoids data copy)
|
|
41
|
+
execSync(`cp -c -R "${sourcePath}" "${destinationPath}"`, {
|
|
42
|
+
stdio: 'pipe',
|
|
43
|
+
timeout: 300_000, // 5 min safety timeout
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Remove excluded items from the clone
|
|
47
|
+
for (const exclude of excludes) {
|
|
48
|
+
if (exclude.includes('*')) {
|
|
49
|
+
// Glob pattern — use shell expansion
|
|
50
|
+
try {
|
|
51
|
+
execSync(`rm -rf "${join(destinationPath, exclude)}"`, {
|
|
52
|
+
stdio: 'pipe',
|
|
53
|
+
shell: '/bin/sh',
|
|
54
|
+
});
|
|
55
|
+
} catch {
|
|
56
|
+
// Glob matched nothing — fine
|
|
57
|
+
}
|
|
58
|
+
} else {
|
|
59
|
+
const excludePath = join(destinationPath, exclude);
|
|
60
|
+
if (existsSync(excludePath)) {
|
|
61
|
+
rmSync(excludePath, { recursive: true, force: true });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (!silent) {
|
|
67
|
+
console.log(
|
|
68
|
+
`Directory cloned (APFS CoW) from ${sourcePath} to ${destinationPath}`,
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
return true;
|
|
72
|
+
} catch {
|
|
73
|
+
// Clone failed (cross-volume, non-APFS, etc.) — fall back to rsync
|
|
74
|
+
// Clean up any partial clone
|
|
75
|
+
if (existsSync(destinationPath)) {
|
|
76
|
+
try {
|
|
77
|
+
rmSync(destinationPath, { recursive: true });
|
|
78
|
+
} catch {
|
|
79
|
+
// Best effort cleanup
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
2
85
|
|
|
3
86
|
export default async function rsyncCopy({
|
|
4
87
|
sourcePath,
|
|
@@ -15,6 +98,30 @@ export default async function rsyncCopy({
|
|
|
15
98
|
silent?: boolean;
|
|
16
99
|
extraArgs?: string[];
|
|
17
100
|
}): Promise<void> {
|
|
101
|
+
const startTime = Date.now();
|
|
102
|
+
|
|
103
|
+
// On macOS, try APFS copy-on-write clone first (near-instant).
|
|
104
|
+
// Skip when extraArgs are provided since those are rsync-specific flags
|
|
105
|
+
// that the clone path can't honor.
|
|
106
|
+
if (!keepExisting && extraArgs.length === 0) {
|
|
107
|
+
const cloned = tryApfsClone({
|
|
108
|
+
sourcePath,
|
|
109
|
+
destinationPath,
|
|
110
|
+
excludes,
|
|
111
|
+
silent,
|
|
112
|
+
});
|
|
113
|
+
if (cloned) {
|
|
114
|
+
if (!silent) {
|
|
115
|
+
const duration = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
116
|
+
console.log(
|
|
117
|
+
`Directory synced from ${sourcePath} to ${destinationPath} [Time: ${duration}s]`,
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Fall back to rsync
|
|
18
125
|
return new Promise((resolve, reject) => {
|
|
19
126
|
const source = sourcePath.endsWith('/') ? sourcePath : `${sourcePath}/`;
|
|
20
127
|
const dest = destinationPath.endsWith('/')
|
|
@@ -37,7 +144,6 @@ export default async function rsyncCopy({
|
|
|
37
144
|
|
|
38
145
|
rsyncArgs.push(source, dest);
|
|
39
146
|
|
|
40
|
-
const startTime = Date.now();
|
|
41
147
|
const rsyncProcess = spawn('rsync', rsyncArgs);
|
|
42
148
|
|
|
43
149
|
rsyncProcess.on('exit', (code) => {
|
|
@@ -342,7 +342,7 @@ export default function constructMockCode(
|
|
|
342
342
|
let foundEntityWithSignature = false;
|
|
343
343
|
let signatureSchema: DataStructure['signatureSchema'] | undefined;
|
|
344
344
|
|
|
345
|
-
for (const filePath in dependencySchemas) {
|
|
345
|
+
entitySearch: for (const filePath in dependencySchemas) {
|
|
346
346
|
for (const entityName in dependencySchemas[filePath]) {
|
|
347
347
|
// Match entity by base name (without generics/args)
|
|
348
348
|
const entityBaseName = entityName.split(/[<(]/)[0];
|
|
@@ -386,7 +386,7 @@ export default function constructMockCode(
|
|
|
386
386
|
// However, we still need to remove duplicate function calls that create invalid syntax
|
|
387
387
|
removeDuplicateFunctionCalls(relevantReturnValueSchema);
|
|
388
388
|
dataStructureValue = relevantReturnValueSchema?.[dataStructurePath];
|
|
389
|
-
break;
|
|
389
|
+
break entitySearch;
|
|
390
390
|
}
|
|
391
391
|
}
|
|
392
392
|
}
|
|
@@ -2733,6 +2733,20 @@ ${exportKeyword}const ${functionName} = new Proxy(() => scenarios().data()?.["${
|
|
|
2733
2733
|
debugLog(
|
|
2734
2734
|
`[REMAINING LOOP] import ${remainingImportIndex}/${remainingImportPaths.length}: ${importPath}`,
|
|
2735
2735
|
);
|
|
2736
|
+
|
|
2737
|
+
// Skip imports that point to generated CodeYam files (same skip logic as
|
|
2738
|
+
// rewriteRelativeModuleImports). Without this, MockData files from earlier
|
|
2739
|
+
// captures that get discovered by the TypeScript compiler would be treated as
|
|
2740
|
+
// regular imports, creating transitive copies with stale content.
|
|
2741
|
+
const scenarioFilePattern = /[a-f0-9]{64}_\w+_[A-Z]\w*$/;
|
|
2742
|
+
const mockDataPattern = /__codeyamMocks__\//;
|
|
2743
|
+
if (
|
|
2744
|
+
scenarioFilePattern.test(importPath) ||
|
|
2745
|
+
mockDataPattern.test(importPath)
|
|
2746
|
+
) {
|
|
2747
|
+
continue;
|
|
2748
|
+
}
|
|
2749
|
+
|
|
2736
2750
|
// Resolve the import path to a project file path
|
|
2737
2751
|
const resolvedFilePath = resolveImportPath(importPath, file.path, project);
|
|
2738
2752
|
|
|
@@ -257,7 +257,7 @@ options) {
|
|
|
257
257
|
let dataStructureValue;
|
|
258
258
|
let foundEntityWithSignature = false;
|
|
259
259
|
let signatureSchema;
|
|
260
|
-
for (const filePath in dependencySchemas) {
|
|
260
|
+
entitySearch: for (const filePath in dependencySchemas) {
|
|
261
261
|
for (const entityName in dependencySchemas[filePath]) {
|
|
262
262
|
// Match entity by base name (without generics/args)
|
|
263
263
|
const entityBaseName = entityName.split(/[<(]/)[0];
|
|
@@ -289,7 +289,7 @@ options) {
|
|
|
289
289
|
// However, we still need to remove duplicate function calls that create invalid syntax
|
|
290
290
|
removeDuplicateFunctionCalls(relevantReturnValueSchema);
|
|
291
291
|
dataStructureValue = relevantReturnValueSchema?.[dataStructurePath];
|
|
292
|
-
break;
|
|
292
|
+
break entitySearch;
|
|
293
293
|
}
|
|
294
294
|
}
|
|
295
295
|
}
|