@codeyam/codeyam-cli 0.1.0-staging.e090cb3 → 0.1.0-staging.eb21b2f
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__/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-CCgBKWy4.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-390cb8fa.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{memory-CCQd4aZA.js → memory-CzZySbBE.js} +17 -17
- 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-DnbDhvTU.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-CXfuiwt3.js} +1 -1
- package/codeyam-cli/src/webserver/build/server/assets/server-build-BSvme_Ao.js +259 -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/root-CHhiHoo_.js +0 -62
- package/codeyam-cli/src/webserver/build/server/assets/server-build-Akn3iYFP.js +0 -257
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
|
-
"buildTimestamp": "2026-02-
|
|
3
|
-
"buildTime":
|
|
4
|
-
"gitCommit": "
|
|
2
|
+
"buildTimestamp": "2026-02-14T10:09:30.576Z",
|
|
3
|
+
"buildTime": 1771063770576,
|
|
4
|
+
"gitCommit": "eb21b2f5ac2124ce27c80ffc92fb7f8aad27e383",
|
|
5
5
|
"nodeVersion": "v20.20.0",
|
|
6
|
-
"contentHash": "
|
|
7
|
-
"buildNumber":
|
|
8
|
-
"semanticVersion": "0.1.
|
|
9
|
-
"version": "0.1.
|
|
6
|
+
"contentHash": "7f2a19190511996b687bdd764ab6297993590ece837b6bbdec8bda87b904ab50",
|
|
7
|
+
"buildNumber": 625,
|
|
8
|
+
"semanticVersion": "0.1.625",
|
|
9
|
+
"version": "0.1.625 (2026-02-14T10:09+7f2a191)"
|
|
10
10
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
|
-
[2/
|
|
3
|
-
[2/
|
|
2
|
+
[2/14/2026, 10:09:30 AM] > codeyam-combo@1.0.0 mergeDependencies
|
|
3
|
+
[2/14/2026, 10:09:30 AM] > node ./scripts/mergePackageJsonFiles.cjs
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
[2/
|
|
6
|
+
[2/14/2026, 10:09:30 AM] Merged dependencies into root package.json
|
|
7
7
|
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"@aws-sdk/client-cloudwatch-logs": "^3.980.0",
|
|
11
|
-
"@aws-sdk/client-cloudfront": "^3.
|
|
11
|
+
"@aws-sdk/client-cloudfront": "^3.985.0",
|
|
12
12
|
"@aws-sdk/client-codebuild": "^3.948.0",
|
|
13
13
|
"@aws-sdk/client-dynamodb": "^3.956.0",
|
|
14
14
|
"@aws-sdk/client-ec2": "^3.899.0",
|
|
@@ -14,35 +14,46 @@ const isStandaloneIndex = (s?: string) => !!s && STANDALONE_INDEX_RE.test(s);
|
|
|
14
14
|
// The regex matches any path ending with .length that has [] somewhere before it
|
|
15
15
|
const DYNAMIC_LENGTH_RE = /\[\].*\.length$/;
|
|
16
16
|
|
|
17
|
-
//
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
// Cache for type string analysis to avoid repeated split/filter operations.
|
|
18
|
+
// These functions are called multiple times per path segment across thousands of paths.
|
|
19
|
+
const typeAnalysisCache = new Map<
|
|
20
|
+
string,
|
|
21
|
+
{ isSkippable: boolean; baseType: string; isNullable: boolean }
|
|
22
|
+
>();
|
|
23
|
+
|
|
24
|
+
function getTypeAnalysis(t: string) {
|
|
25
|
+
const cached = typeAnalysisCache.get(t);
|
|
26
|
+
if (cached) return cached;
|
|
23
27
|
const parts = t.split('|').map((s) => s.trim());
|
|
24
28
|
const base = parts.filter((s) => s !== 'undefined' && s !== 'null');
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
base[0] === '
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
const result = {
|
|
30
|
+
isSkippable:
|
|
31
|
+
base.length === 1 &&
|
|
32
|
+
(base[0] === 'object' ||
|
|
33
|
+
base[0] === 'array' ||
|
|
34
|
+
base[0] === 'function' ||
|
|
35
|
+
base[0] === 'unknown'),
|
|
36
|
+
baseType: base[0],
|
|
37
|
+
isNullable: parts.includes('undefined') || parts.includes('null'),
|
|
38
|
+
};
|
|
39
|
+
typeAnalysisCache.set(t, result);
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Treat these as structural placeholders (don't commit them as concrete leaves)
|
|
44
|
+
function isSkippableLeafType(t: string) {
|
|
45
|
+
return getTypeAnalysis(t).isSkippable;
|
|
32
46
|
}
|
|
33
47
|
|
|
34
48
|
// Extract the base structural type from a potentially nullable type string.
|
|
35
49
|
// e.g., 'object | undefined' → 'object', 'array | null' → 'array'
|
|
36
50
|
function getBaseSkippableType(t: string): string {
|
|
37
|
-
|
|
38
|
-
const base = parts.filter((s) => s !== 'undefined' && s !== 'null');
|
|
39
|
-
return base[0];
|
|
51
|
+
return getTypeAnalysis(t).baseType;
|
|
40
52
|
}
|
|
41
53
|
|
|
42
54
|
// Check if a type string has nullable annotations (| undefined or | null)
|
|
43
55
|
function isNullableType(t: string): boolean {
|
|
44
|
-
|
|
45
|
-
return parts.includes('undefined') || parts.includes('null');
|
|
56
|
+
return getTypeAnalysis(t).isNullable;
|
|
46
57
|
}
|
|
47
58
|
|
|
48
59
|
// Matches paths containing [][] — e.g., "items[][]" or "items[][].text"
|
|
@@ -4,10 +4,10 @@ import type { ExecutionFlow, ScenariosDataStructure } from '~codeyam/types';
|
|
|
4
4
|
type RequiredValue = NonNullable<ExecutionFlow['requiredValues']>[number];
|
|
5
5
|
|
|
6
6
|
const DEFAULT_MAX_CHUNK_SIZE = 10_000; // ~10K chars per chunk
|
|
7
|
+
const DEFAULT_MAX_KEYS_PER_CHUNK = 20;
|
|
7
8
|
|
|
8
9
|
export interface ChunkOptions {
|
|
9
10
|
maxChunkSize?: number;
|
|
10
|
-
/** @deprecated Use maxChunkSize instead. Kept for backward compatibility in tests. */
|
|
11
11
|
maxKeysPerChunk?: number;
|
|
12
12
|
}
|
|
13
13
|
|
|
@@ -33,16 +33,19 @@ export function chunkDataStructure(
|
|
|
33
33
|
return [dataForMocks];
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
const {
|
|
36
|
+
const {
|
|
37
|
+
maxChunkSize = DEFAULT_MAX_CHUNK_SIZE,
|
|
38
|
+
maxKeysPerChunk = DEFAULT_MAX_KEYS_PER_CHUNK,
|
|
39
|
+
} = options;
|
|
37
40
|
const keys = Object.keys(dataForMocks);
|
|
38
41
|
|
|
39
42
|
// Calculate total size to see if chunking is needed
|
|
40
43
|
const totalSize = JSON.stringify(dataForMocks).length;
|
|
41
|
-
if (totalSize <= maxChunkSize) {
|
|
44
|
+
if (totalSize <= maxChunkSize && keys.length <= maxKeysPerChunk) {
|
|
42
45
|
return [dataForMocks];
|
|
43
46
|
}
|
|
44
47
|
|
|
45
|
-
// Greedily pack keys into chunks by cumulative size
|
|
48
|
+
// Greedily pack keys into chunks by cumulative size and key count
|
|
46
49
|
const chunks: Array<ScenariosDataStructure['dataForMocks']> = [];
|
|
47
50
|
let currentChunk: Record<string, unknown> = {};
|
|
48
51
|
let currentSize = 0;
|
|
@@ -52,8 +55,14 @@ export function chunkDataStructure(
|
|
|
52
55
|
(dataForMocks as Record<string, unknown>)[key],
|
|
53
56
|
).length;
|
|
54
57
|
|
|
55
|
-
|
|
56
|
-
|
|
58
|
+
const currentKeyCount = Object.keys(currentChunk).length;
|
|
59
|
+
|
|
60
|
+
// If adding this key would exceed size or key count limit AND chunk isn't empty, start a new chunk
|
|
61
|
+
if (
|
|
62
|
+
currentSize > 0 &&
|
|
63
|
+
(currentSize + keySize > maxChunkSize ||
|
|
64
|
+
currentKeyCount >= maxKeysPerChunk)
|
|
65
|
+
) {
|
|
57
66
|
chunks.push(currentChunk as ScenariosDataStructure['dataForMocks']);
|
|
58
67
|
currentChunk = {};
|
|
59
68
|
currentSize = 0;
|
|
@@ -32,6 +32,30 @@ import resolvePathToControllable from './resolvePathToControllable';
|
|
|
32
32
|
import { AI } from '~codeyam/ai';
|
|
33
33
|
import type { JsxRenderingUsage, ConditionalUsage } from './astScopes/types';
|
|
34
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Patterns that indicate a child component is a modal/overlay/blocking element.
|
|
37
|
+
* Same patterns used in generateExecutionFlowsFromConditionalEffects.ts for
|
|
38
|
+
* state variable names — here applied to child component names.
|
|
39
|
+
*/
|
|
40
|
+
const BLOCKING_CHILD_PATTERNS = [
|
|
41
|
+
/modal/i,
|
|
42
|
+
/overlay/i,
|
|
43
|
+
/dialog/i,
|
|
44
|
+
/popup/i,
|
|
45
|
+
/drawer/i,
|
|
46
|
+
/sheet/i,
|
|
47
|
+
/lightbox/i,
|
|
48
|
+
/backdrop/i,
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Check if a child component name suggests it's a blocking/modal element.
|
|
53
|
+
* Exported for testing.
|
|
54
|
+
*/
|
|
55
|
+
export function isBlockingChildComponent(componentName: string): boolean {
|
|
56
|
+
return BLOCKING_CHILD_PATTERNS.some((pattern) => pattern.test(componentName));
|
|
57
|
+
}
|
|
58
|
+
|
|
35
59
|
interface GenerateExecutionFlowsArgs {
|
|
36
60
|
entity: Pick<Entity, 'sha' | 'name' | 'filePath' | 'code' | 'metadata'>;
|
|
37
61
|
mergedDataStructure: Omit<DataStructure, 'equivalentSignatureVariables'>;
|
|
@@ -273,6 +297,11 @@ export default function generateExecutionFlows({
|
|
|
273
297
|
calculateFlowPriority(flow, gatingPathToChildren);
|
|
274
298
|
}
|
|
275
299
|
|
|
300
|
+
// Assign exclusiveGroup for truthy/falsy flow pairs that control child components.
|
|
301
|
+
// When the same gating path produces both a truthy flow (show component) and a
|
|
302
|
+
// falsy flow (hide component), they are mutually exclusive and should be grouped.
|
|
303
|
+
assignExclusiveGroupsForGatingPairs(allFlows);
|
|
304
|
+
|
|
276
305
|
// Sort flows by priority (highest first)
|
|
277
306
|
allFlows.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
|
|
278
307
|
|
|
@@ -361,6 +390,60 @@ function buildGatingPathToChildrenMap(
|
|
|
361
390
|
return gatingPathToChildren;
|
|
362
391
|
}
|
|
363
392
|
|
|
393
|
+
/**
|
|
394
|
+
* Assign exclusiveGroup for truthy/falsy flow pairs that control child components.
|
|
395
|
+
*
|
|
396
|
+
* When the same attribute path produces both a truthy and falsy flow (e.g.,
|
|
397
|
+
* {diffView && <DiffModal/>}), they are mutually exclusive — only one can be
|
|
398
|
+
* active at a time. Grouping them ensures the scenario generator treats them
|
|
399
|
+
* as alternative states rather than combining them.
|
|
400
|
+
*/
|
|
401
|
+
function assignExclusiveGroupsForGatingPairs(flows: ExecutionFlow[]): void {
|
|
402
|
+
// Build a map: attributePath -> flows with truthy or falsy comparison for that path
|
|
403
|
+
const pathToFlows = new Map<
|
|
404
|
+
string,
|
|
405
|
+
{ truthy: ExecutionFlow[]; falsy: ExecutionFlow[] }
|
|
406
|
+
>();
|
|
407
|
+
|
|
408
|
+
for (const flow of flows) {
|
|
409
|
+
// Only consider flows that control child components
|
|
410
|
+
if (!flow.childComponentsControlled?.length) continue;
|
|
411
|
+
|
|
412
|
+
for (const rv of flow.requiredValues) {
|
|
413
|
+
if (rv.comparison === 'truthy' || rv.comparison === 'falsy') {
|
|
414
|
+
if (!pathToFlows.has(rv.attributePath)) {
|
|
415
|
+
pathToFlows.set(rv.attributePath, { truthy: [], falsy: [] });
|
|
416
|
+
}
|
|
417
|
+
pathToFlows.get(rv.attributePath)![rv.comparison].push(flow);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// Also check falsy flows that DON'T have childComponentsControlled —
|
|
423
|
+
// they still pair with truthy flows that do
|
|
424
|
+
for (const flow of flows) {
|
|
425
|
+
if (flow.childComponentsControlled?.length) continue; // Already processed
|
|
426
|
+
|
|
427
|
+
for (const rv of flow.requiredValues) {
|
|
428
|
+
if (rv.comparison === 'falsy' && pathToFlows.has(rv.attributePath)) {
|
|
429
|
+
pathToFlows.get(rv.attributePath)!.falsy.push(flow);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// Assign exclusiveGroup where both truthy and falsy flows exist
|
|
435
|
+
for (const [attributePath, { truthy, falsy }] of pathToFlows) {
|
|
436
|
+
if (truthy.length > 0 && falsy.length > 0) {
|
|
437
|
+
const groupName = `gating:${attributePath}`;
|
|
438
|
+
for (const flow of [...truthy, ...falsy]) {
|
|
439
|
+
if (!flow.exclusiveGroup) {
|
|
440
|
+
flow.exclusiveGroup = groupName;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
364
447
|
/**
|
|
365
448
|
* Calculate priority and childComponentsControlled for a flow.
|
|
366
449
|
*
|
|
@@ -415,6 +498,19 @@ function calculateFlowPriority(
|
|
|
415
498
|
if (flow.impact !== 'high') {
|
|
416
499
|
flow.impact = 'high';
|
|
417
500
|
}
|
|
501
|
+
|
|
502
|
+
// Check if any controlled child component is a modal/dialog/overlay.
|
|
503
|
+
// Only truthy flows (which SHOW the component) should be marked as blocking.
|
|
504
|
+
// Falsy flows (which HIDE the component) should not be blocking.
|
|
505
|
+
const isTruthyFlow = flow.requiredValues.some(
|
|
506
|
+
(rv) => rv.comparison === 'truthy' || rv.comparison === 'equals',
|
|
507
|
+
);
|
|
508
|
+
if (
|
|
509
|
+
isTruthyFlow &&
|
|
510
|
+
Array.from(childComponentsControlled).some(isBlockingChildComponent)
|
|
511
|
+
) {
|
|
512
|
+
flow.blocksOtherFlows = true;
|
|
513
|
+
}
|
|
418
514
|
}
|
|
419
515
|
|
|
420
516
|
// Calculate priority score
|
|
@@ -353,8 +353,6 @@ function processCall(
|
|
|
353
353
|
// Preprocess to filter signatures and extract functionCallReturnValue
|
|
354
354
|
let preprocessedSchema = preprocessSchemaForMocks(mergedSchema);
|
|
355
355
|
|
|
356
|
-
const functionNames: string[] = [];
|
|
357
|
-
|
|
358
356
|
const isFunctionNameWithoutFunction = (
|
|
359
357
|
path: string,
|
|
360
358
|
functionName: string,
|
|
@@ -439,31 +437,58 @@ function processCall(
|
|
|
439
437
|
}
|
|
440
438
|
}
|
|
441
439
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
}
|
|
440
|
+
// Two-pass function name filtering to avoid O(k²) retroactive deletion.
|
|
441
|
+
// Pass 1: Discover all unique function names from paths ending with ')'
|
|
442
|
+
const functionNameSet = new Set<string>();
|
|
443
|
+
const schemaKeys = Object.keys(preprocessedSchema);
|
|
444
|
+
for (const path of schemaKeys) {
|
|
445
|
+
if (path.endsWith(')')) {
|
|
446
|
+
const pathParts = splitOutsideParenthesesAndArrays(path);
|
|
447
|
+
const functionName = joinParenthesesAndArrays([
|
|
448
|
+
...pathParts.slice(0, -1),
|
|
449
|
+
pathParts[pathParts.length - 1].split('(')[0],
|
|
450
|
+
]);
|
|
451
|
+
functionNameSet.add(functionName);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
457
454
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
455
|
+
// Index function names by first segment for O(1) bucket lookup instead of
|
|
456
|
+
// checking all function names for every path. Most paths only need to check
|
|
457
|
+
// 1-2 function names instead of all F.
|
|
458
|
+
const fnByFirstSegment = new Map<string, string[]>();
|
|
459
|
+
for (const fn of functionNameSet) {
|
|
460
|
+
const dotIdx = fn.indexOf('.');
|
|
461
|
+
const firstSeg = dotIdx >= 0 ? fn.slice(0, dotIdx) : fn;
|
|
462
|
+
let bucket = fnByFirstSegment.get(firstSeg);
|
|
463
|
+
if (!bucket) {
|
|
464
|
+
bucket = [];
|
|
465
|
+
fnByFirstSegment.set(firstSeg, bucket);
|
|
466
|
+
}
|
|
467
|
+
bucket.push(fn);
|
|
468
|
+
}
|
|
461
469
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
470
|
+
// Pass 2: Filter paths that match any discovered function name prefix.
|
|
471
|
+
// Use indexed lookup: extract path's first segment, check only matching function names.
|
|
472
|
+
const relevantMergedDependencySchema: Record<string, string> = {};
|
|
473
|
+
for (const path of schemaKeys) {
|
|
474
|
+
// Extract first segment of path (before first '.', '(', or '<')
|
|
475
|
+
const dotIdx = path.indexOf('.');
|
|
476
|
+
const parenIdx = path.indexOf('(');
|
|
477
|
+
const angleIdx = path.indexOf('<');
|
|
478
|
+
let minIdx = path.length;
|
|
479
|
+
if (dotIdx >= 0 && dotIdx < minIdx) minIdx = dotIdx;
|
|
480
|
+
if (parenIdx >= 0 && parenIdx < minIdx) minIdx = parenIdx;
|
|
481
|
+
if (angleIdx >= 0 && angleIdx < minIdx) minIdx = angleIdx;
|
|
482
|
+
const firstSeg = path.slice(0, minIdx);
|
|
483
|
+
|
|
484
|
+
const candidates = fnByFirstSegment.get(firstSeg);
|
|
485
|
+
if (
|
|
486
|
+
!candidates ||
|
|
487
|
+
!candidates.some((fn) => isFunctionNameWithoutFunction(path, fn))
|
|
488
|
+
) {
|
|
489
|
+
relevantMergedDependencySchema[path] = preprocessedSchema[path];
|
|
490
|
+
}
|
|
491
|
+
}
|
|
467
492
|
|
|
468
493
|
const filledSchema = fillInDirectSchemaGapsAndUnknowns({
|
|
469
494
|
scopeName: importedExport.name,
|