@codeyam/codeyam-cli 0.1.16 → 0.1.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/analyzer-template/.build-info.json +6 -6
  2. package/analyzer-template/log.txt +3 -3
  3. package/codeyam-cli/src/commands/editor.js +187 -7
  4. package/codeyam-cli/src/commands/editor.js.map +1 -1
  5. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js +310 -9
  6. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js.map +1 -1
  7. package/codeyam-cli/src/utils/__tests__/editorCaptureScenarioSeeding.test.js +137 -0
  8. package/codeyam-cli/src/utils/__tests__/editorCaptureScenarioSeeding.test.js.map +1 -0
  9. package/codeyam-cli/src/utils/__tests__/editorEntityHelpers.test.js +66 -0
  10. package/codeyam-cli/src/utils/__tests__/editorEntityHelpers.test.js.map +1 -1
  11. package/codeyam-cli/src/utils/__tests__/editorScenarioSwitch.test.js +70 -0
  12. package/codeyam-cli/src/utils/__tests__/editorScenarioSwitch.test.js.map +1 -1
  13. package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js +51 -1
  14. package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js.map +1 -1
  15. package/codeyam-cli/src/utils/__tests__/entityChangeStatus.test.js +233 -1
  16. package/codeyam-cli/src/utils/__tests__/entityChangeStatus.test.js.map +1 -1
  17. package/codeyam-cli/src/utils/__tests__/glossaryAdd.test.js +177 -0
  18. package/codeyam-cli/src/utils/__tests__/glossaryAdd.test.js.map +1 -0
  19. package/codeyam-cli/src/utils/editorAudit.js +71 -25
  20. package/codeyam-cli/src/utils/editorAudit.js.map +1 -1
  21. package/codeyam-cli/src/utils/editorRecapture.js +109 -0
  22. package/codeyam-cli/src/utils/editorRecapture.js.map +1 -0
  23. package/codeyam-cli/src/utils/editorScenarioSwitch.js +24 -2
  24. package/codeyam-cli/src/utils/editorScenarioSwitch.js.map +1 -1
  25. package/codeyam-cli/src/utils/editorScenarios.js +28 -1
  26. package/codeyam-cli/src/utils/editorScenarios.js.map +1 -1
  27. package/codeyam-cli/src/utils/entityChangeStatus.js +31 -3
  28. package/codeyam-cli/src/utils/entityChangeStatus.js.map +1 -1
  29. package/codeyam-cli/src/utils/glossaryAdd.js +74 -0
  30. package/codeyam-cli/src/utils/glossaryAdd.js.map +1 -0
  31. package/codeyam-cli/src/utils/scenariosManifest.js +22 -0
  32. package/codeyam-cli/src/utils/scenariosManifest.js.map +1 -1
  33. package/codeyam-cli/src/webserver/build/client/assets/api.editor-recapture-stale-l0sNRNKZ.js +1 -0
  34. package/codeyam-cli/src/webserver/build/client/assets/{editor.entity.(_sha)-DN5ouXAl.js → editor.entity.(_sha)-Bnx7yUP0.js} +1 -1
  35. package/codeyam-cli/src/webserver/build/client/assets/manifest-b9d4d267.js +1 -0
  36. package/codeyam-cli/src/webserver/build/server/assets/{analysisRunner-D_1MSYeW.js → analysisRunner-CGwTN3V2.js} +1 -1
  37. package/codeyam-cli/src/webserver/build/server/assets/{index-ckWaCf_v.js → index-D4meMKy3.js} +1 -1
  38. package/codeyam-cli/src/webserver/build/server/assets/{init-ld124R4Z.js → init-odGJ_c2-.js} +1 -1
  39. package/codeyam-cli/src/webserver/build/server/assets/{server-build-DzzNZGv_.js → server-build-TmPfF7pT.js} +125 -124
  40. package/codeyam-cli/src/webserver/build/server/index.js +1 -1
  41. package/codeyam-cli/src/webserver/build-info.json +5 -5
  42. package/package.json +1 -1
  43. package/codeyam-cli/src/webserver/build/client/assets/manifest-389033be.js +0 -1
@@ -1873,7 +1873,12 @@ describe('editorAudit', () => {
1873
1873
  .execute();
1874
1874
  const result = await queryIncompleteEntities(db, projectId, null);
1875
1875
  expect(result).toEqual([
1876
- { entitySha: 'sha-chips', name: 'CollectionChips', scenarioCount: 2 },
1876
+ {
1877
+ entitySha: 'sha-chips',
1878
+ name: 'CollectionChips',
1879
+ scenarioCount: 2,
1880
+ preExisting: false,
1881
+ },
1877
1882
  ]);
1878
1883
  });
1879
1884
  it('should only return entities without analyses, not those with analyses', async () => {
@@ -1930,10 +1935,15 @@ describe('editorAudit', () => {
1930
1935
  .execute();
1931
1936
  const result = await queryIncompleteEntities(db, projectId, null);
1932
1937
  expect(result).toEqual([
1933
- { entitySha: 'sha-picker', name: 'CollectionPicker', scenarioCount: 1 },
1938
+ {
1939
+ entitySha: 'sha-picker',
1940
+ name: 'CollectionPicker',
1941
+ scenarioCount: 1,
1942
+ preExisting: false,
1943
+ },
1934
1944
  ]);
1935
1945
  });
1936
- it('should scope to session when featureStartedAt is provided', async () => {
1946
+ it('should return both pre-session and in-session entities with preExisting flags', async () => {
1937
1947
  // Entity without analysis, scenario created BEFORE session
1938
1948
  await db
1939
1949
  .insertInto('entities')
@@ -1977,12 +1987,24 @@ describe('editorAudit', () => {
1977
1987
  })
1978
1988
  .execute();
1979
1989
  const result = await queryIncompleteEntities(db, projectId, '2026-03-16T23:07:12.698Z');
1980
- // Only NewComponent should be flagged (created in session)
1981
- expect(result).toEqual([
1982
- { entitySha: 'sha-new', name: 'NewComponent', scenarioCount: 1 },
1983
- ]);
1990
+ // Both should be returned OldComponent is preExisting, NewComponent is not
1991
+ expect(result).toEqual(expect.arrayContaining([
1992
+ {
1993
+ entitySha: 'sha-old',
1994
+ name: 'OldComponent',
1995
+ scenarioCount: 1,
1996
+ preExisting: true,
1997
+ },
1998
+ {
1999
+ entitySha: 'sha-new',
2000
+ name: 'NewComponent',
2001
+ scenarioCount: 1,
2002
+ preExisting: false,
2003
+ },
2004
+ ]));
2005
+ expect(result).toHaveLength(2);
1984
2006
  });
1985
- it('should include scenarios updated in session even if created before', async () => {
2007
+ it('should flag preExisting: false when scenario was updated in session even if created before', async () => {
1986
2008
  await db
1987
2009
  .insertInto('entities')
1988
2010
  .values({
@@ -2010,6 +2032,7 @@ describe('editorAudit', () => {
2010
2032
  entitySha: 'sha-updated',
2011
2033
  name: 'UpdatedComponent',
2012
2034
  scenarioCount: 1,
2035
+ preExisting: false,
2013
2036
  },
2014
2037
  ]);
2015
2038
  });
@@ -2077,6 +2100,60 @@ describe('editorAudit', () => {
2077
2100
  const result = await queryIncompleteEntities(db, projectId, null);
2078
2101
  expect(result).toEqual([]);
2079
2102
  });
2103
+ it('should flag entity when sibling has analyses but different filePath (extracted component)', async () => {
2104
+ // Old entity version WITH analysis at ORIGINAL file path
2105
+ await db
2106
+ .insertInto('entities')
2107
+ .values({
2108
+ sha: 'sha-card-v1',
2109
+ name: 'TaskCard',
2110
+ entity_type: 'visual',
2111
+ file_path: 'app/page.tsx',
2112
+ })
2113
+ .execute();
2114
+ await db
2115
+ .insertInto('analyses')
2116
+ .values({
2117
+ id: 'a-card-v1',
2118
+ entity_sha: 'sha-card-v1',
2119
+ entity_name: 'TaskCard',
2120
+ project_id: projectId,
2121
+ })
2122
+ .execute();
2123
+ // New entity version WITHOUT analysis at EXTRACTED file path
2124
+ await db
2125
+ .insertInto('entities')
2126
+ .values({
2127
+ sha: 'sha-card-v2',
2128
+ name: 'TaskCard',
2129
+ entity_type: 'visual',
2130
+ file_path: 'app/components/TaskCard.tsx',
2131
+ })
2132
+ .execute();
2133
+ // Scenario points to the new version (synced by syncScenarioEntityShas)
2134
+ await db
2135
+ .insertInto('editor_scenarios')
2136
+ .values({
2137
+ id: 'sc-card',
2138
+ project_id: projectId,
2139
+ name: 'TaskCard - Default',
2140
+ component_name: 'TaskCard',
2141
+ entity_sha: 'sha-card-v2',
2142
+ created_at: '2026-03-16 23:00:00',
2143
+ })
2144
+ .execute();
2145
+ // SHOULD flag as incomplete — sibling has analyses but at a different filePath,
2146
+ // so getAllEntities() won't inherit (it matches by name+filePath)
2147
+ const result = await queryIncompleteEntities(db, projectId, null);
2148
+ expect(result).toEqual([
2149
+ {
2150
+ entitySha: 'sha-card-v2',
2151
+ name: 'TaskCard',
2152
+ scenarioCount: 1,
2153
+ preExisting: false,
2154
+ },
2155
+ ]);
2156
+ });
2080
2157
  it('should still flag entities when no sibling version has analyses', async () => {
2081
2158
  // Only one version, no analyses
2082
2159
  await db
@@ -2106,6 +2183,7 @@ describe('editorAudit', () => {
2106
2183
  entitySha: 'sha-icon',
2107
2184
  name: 'ExternalLinkIcon',
2108
2185
  scenarioCount: 1,
2186
+ preExisting: false,
2109
2187
  },
2110
2188
  ]);
2111
2189
  });
@@ -2124,7 +2202,124 @@ describe('editorAudit', () => {
2124
2202
  .execute();
2125
2203
  const result = await queryIncompleteEntities(db, projectId, null);
2126
2204
  expect(result).toEqual([
2127
- { entitySha: 'sha-ghost', name: 'GhostComponent', scenarioCount: 1 },
2205
+ {
2206
+ entitySha: 'sha-ghost',
2207
+ name: 'GhostComponent',
2208
+ scenarioCount: 1,
2209
+ preExisting: false,
2210
+ },
2211
+ ]);
2212
+ });
2213
+ it('should detect incomplete entity whose scenario predates the session', async () => {
2214
+ // Entity with no analyses, scenario created BEFORE session
2215
+ await db
2216
+ .insertInto('entities')
2217
+ .values({
2218
+ sha: 'sha-preexisting',
2219
+ name: 'PreExistingComponent',
2220
+ entity_type: 'visual',
2221
+ file_path: 'src/PreExistingComponent.tsx',
2222
+ })
2223
+ .execute();
2224
+ await db
2225
+ .insertInto('editor_scenarios')
2226
+ .values({
2227
+ id: 'sc-preexisting',
2228
+ project_id: projectId,
2229
+ name: 'PreExistingComponent - Default',
2230
+ component_name: 'PreExistingComponent',
2231
+ entity_sha: 'sha-preexisting',
2232
+ created_at: '2026-03-16 20:00:00',
2233
+ updated_at: '2026-03-16 20:00:00',
2234
+ })
2235
+ .execute();
2236
+ // Session started well after scenario was created/updated
2237
+ const result = await queryIncompleteEntities(db, projectId, '2026-03-16T23:07:12.698Z');
2238
+ // Should still be detected — the old time filter would have excluded it
2239
+ expect(result).toEqual([
2240
+ {
2241
+ entitySha: 'sha-preexisting',
2242
+ name: 'PreExistingComponent',
2243
+ scenarioCount: 1,
2244
+ preExisting: true,
2245
+ },
2246
+ ]);
2247
+ });
2248
+ it('should flag preExisting: true when all scenarios predate the session', async () => {
2249
+ await db
2250
+ .insertInto('entities')
2251
+ .values({
2252
+ sha: 'sha-old-entity',
2253
+ name: 'OldEntity',
2254
+ entity_type: 'visual',
2255
+ file_path: 'src/OldEntity.tsx',
2256
+ })
2257
+ .execute();
2258
+ // Two scenarios, both before session
2259
+ await db
2260
+ .insertInto('editor_scenarios')
2261
+ .values({
2262
+ id: 'sc-old-1',
2263
+ project_id: projectId,
2264
+ name: 'OldEntity - Default',
2265
+ component_name: 'OldEntity',
2266
+ entity_sha: 'sha-old-entity',
2267
+ created_at: '2026-03-16 19:00:00',
2268
+ updated_at: '2026-03-16 19:00:00',
2269
+ })
2270
+ .execute();
2271
+ await db
2272
+ .insertInto('editor_scenarios')
2273
+ .values({
2274
+ id: 'sc-old-2',
2275
+ project_id: projectId,
2276
+ name: 'OldEntity - Hover',
2277
+ component_name: 'OldEntity',
2278
+ entity_sha: 'sha-old-entity',
2279
+ created_at: '2026-03-16 19:30:00',
2280
+ updated_at: '2026-03-16 19:30:00',
2281
+ })
2282
+ .execute();
2283
+ const result = await queryIncompleteEntities(db, projectId, '2026-03-16T23:07:12.698Z');
2284
+ expect(result).toEqual([
2285
+ {
2286
+ entitySha: 'sha-old-entity',
2287
+ name: 'OldEntity',
2288
+ scenarioCount: 2,
2289
+ preExisting: true,
2290
+ },
2291
+ ]);
2292
+ });
2293
+ it('should flag preExisting: false when scenario is from the current session', async () => {
2294
+ await db
2295
+ .insertInto('entities')
2296
+ .values({
2297
+ sha: 'sha-session-entity',
2298
+ name: 'SessionEntity',
2299
+ entity_type: 'visual',
2300
+ file_path: 'src/SessionEntity.tsx',
2301
+ })
2302
+ .execute();
2303
+ await db
2304
+ .insertInto('editor_scenarios')
2305
+ .values({
2306
+ id: 'sc-session',
2307
+ project_id: projectId,
2308
+ name: 'SessionEntity - Default',
2309
+ component_name: 'SessionEntity',
2310
+ entity_sha: 'sha-session-entity',
2311
+ created_at: '2026-03-16 23:30:00',
2312
+ updated_at: '2026-03-16 23:30:00',
2313
+ })
2314
+ .execute();
2315
+ const result = await queryIncompleteEntities(db, projectId, '2026-03-16T23:07:12.698Z');
2316
+ expect(result).toEqual([
2317
+ {
2318
+ entitySha: 'sha-session-entity',
2319
+ name: 'SessionEntity',
2320
+ scenarioCount: 1,
2321
+ preExisting: false,
2322
+ },
2128
2323
  ]);
2129
2324
  });
2130
2325
  });
@@ -2375,5 +2570,111 @@ describe('editorAudit', () => {
2375
2570
  expect(result.size).toBe(0);
2376
2571
  });
2377
2572
  });
2573
+ // ── computeAudit: impacted components with stale scenarios ──────────
2574
+ describe('computeAudit — impacted components with stale scenarios', () => {
2575
+ it('should mark impacted component as needs_recapture when it has total scenarios but none in session', () => {
2576
+ // Library page has 3 scenarios from Feature 1 (totalScenarioCounts),
2577
+ // 0 in the current session (scenarioCounts), and is "impacted" in entityChangeStatus.
2578
+ // It should NOT be marked "missing" — it needs recapture, not new scenarios.
2579
+ const result = computeAudit({
2580
+ components: [
2581
+ {
2582
+ name: 'Library',
2583
+ filePath: 'app/library/page.tsx',
2584
+ returnType: 'JSX.Element',
2585
+ },
2586
+ {
2587
+ name: 'ArticleTable',
2588
+ filePath: 'app/components/ArticleTable.tsx',
2589
+ returnType: 'JSX.Element',
2590
+ },
2591
+ ],
2592
+ functions: [],
2593
+ scenarioCounts: { ArticleTable: 2 },
2594
+ testFileExistence: {},
2595
+ totalScenarioCounts: { Library: 3 },
2596
+ entityChangeStatus: {
2597
+ Library: { status: 'impacted' },
2598
+ ArticleTable: { status: 'edited' },
2599
+ },
2600
+ });
2601
+ // Library: impacted + has total scenarios but 0 in session → needs_recapture
2602
+ expect(result.components[0].status).toBe('needs_recapture');
2603
+ expect(result.components[0].scenarioCount).toBe(3);
2604
+ // ArticleTable: edited + has session scenarios → ok
2605
+ expect(result.components[1].status).toBe('ok');
2606
+ // needs_recapture should NOT count as missing
2607
+ expect(result.summary.componentsMissing).toBe(0);
2608
+ expect(result.summary.componentsNeedingRecapture).toBe(1);
2609
+ // should NOT fail the audit (scenariosNeedingRecapture handles it)
2610
+ expect(result.summary.allPassing).toBe(true);
2611
+ });
2612
+ it('should still mark component as missing when impacted but has zero total scenarios', () => {
2613
+ // New page added to glossary but never had scenarios — truly missing
2614
+ const result = computeAudit({
2615
+ components: [
2616
+ {
2617
+ name: 'NewPage',
2618
+ filePath: 'app/new/page.tsx',
2619
+ returnType: 'JSX.Element',
2620
+ },
2621
+ ],
2622
+ functions: [],
2623
+ scenarioCounts: {},
2624
+ testFileExistence: {},
2625
+ totalScenarioCounts: {},
2626
+ entityChangeStatus: {
2627
+ NewPage: { status: 'impacted' },
2628
+ },
2629
+ });
2630
+ expect(result.components[0].status).toBe('missing');
2631
+ expect(result.summary.componentsMissing).toBe(1);
2632
+ });
2633
+ it('should use needs_recapture for edited entities with existing scenarios from prior sessions', () => {
2634
+ // Edited entities that already have scenarios from prior sessions
2635
+ // need recapture, not re-registration. The code changed, but the
2636
+ // scenarios exist — they just need fresh screenshots.
2637
+ const result = computeAudit({
2638
+ components: [
2639
+ {
2640
+ name: 'EditedComp',
2641
+ filePath: 'app/components/Edited.tsx',
2642
+ returnType: 'JSX.Element',
2643
+ },
2644
+ ],
2645
+ functions: [],
2646
+ scenarioCounts: {},
2647
+ testFileExistence: {},
2648
+ totalScenarioCounts: { EditedComp: 2 },
2649
+ entityChangeStatus: {
2650
+ EditedComp: { status: 'edited' },
2651
+ },
2652
+ });
2653
+ expect(result.components[0].status).toBe('needs_recapture');
2654
+ expect(result.summary.componentsMissing).toBe(0);
2655
+ expect(result.summary.componentsNeedingRecapture).toBe(1);
2656
+ });
2657
+ it('should still mark as missing when new entity has zero total scenarios', () => {
2658
+ // Truly new component with no scenarios ever — needs scenarios created
2659
+ const result = computeAudit({
2660
+ components: [
2661
+ {
2662
+ name: 'BrandNew',
2663
+ filePath: 'app/components/BrandNew.tsx',
2664
+ returnType: 'JSX.Element',
2665
+ },
2666
+ ],
2667
+ functions: [],
2668
+ scenarioCounts: {},
2669
+ testFileExistence: {},
2670
+ totalScenarioCounts: {},
2671
+ entityChangeStatus: {
2672
+ BrandNew: { status: 'new' },
2673
+ },
2674
+ });
2675
+ expect(result.components[0].status).toBe('missing');
2676
+ expect(result.summary.componentsMissing).toBe(1);
2677
+ });
2678
+ });
2378
2679
  });
2379
2680
  //# sourceMappingURL=editorAudit.test.js.map