@codeyam/codeyam-cli 0.1.0-staging.8778565 → 0.1.0-staging.8b51541

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 (152) hide show
  1. package/analyzer-template/.build-info.json +7 -7
  2. package/analyzer-template/log.txt +3 -3
  3. package/analyzer-template/packages/database/src/lib/kysely/tables/editorScenariosTable.ts +56 -0
  4. package/analyzer-template/packages/database/src/lib/loadEntities.ts +0 -6
  5. package/analyzer-template/packages/database/src/lib/updateCommitMetadata.ts +0 -65
  6. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.d.ts +3 -0
  7. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.d.ts.map +1 -1
  8. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.js +56 -0
  9. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.js.map +1 -1
  10. package/analyzer-template/packages/github/dist/database/src/lib/loadEntities.d.ts.map +1 -1
  11. package/analyzer-template/packages/github/dist/database/src/lib/loadEntities.js +0 -6
  12. package/analyzer-template/packages/github/dist/database/src/lib/loadEntities.js.map +1 -1
  13. package/analyzer-template/packages/github/dist/database/src/lib/updateCommitMetadata.d.ts.map +1 -1
  14. package/analyzer-template/packages/github/dist/database/src/lib/updateCommitMetadata.js +0 -25
  15. package/analyzer-template/packages/github/dist/database/src/lib/updateCommitMetadata.js.map +1 -1
  16. package/codeyam-cli/src/commands/__tests__/editor.stepDispatch.test.js +45 -0
  17. package/codeyam-cli/src/commands/__tests__/editor.stepDispatch.test.js.map +1 -0
  18. package/codeyam-cli/src/commands/__tests__/init.gitignore.test.js +101 -47
  19. package/codeyam-cli/src/commands/__tests__/init.gitignore.test.js.map +1 -1
  20. package/codeyam-cli/src/commands/editor.js +799 -211
  21. package/codeyam-cli/src/commands/editor.js.map +1 -1
  22. package/codeyam-cli/src/commands/init.js +68 -34
  23. package/codeyam-cli/src/commands/init.js.map +1 -1
  24. package/codeyam-cli/src/utils/__tests__/analyzerFinalization.test.js +173 -0
  25. package/codeyam-cli/src/utils/__tests__/analyzerFinalization.test.js.map +1 -0
  26. package/codeyam-cli/src/utils/__tests__/editorApi.test.js +18 -8
  27. package/codeyam-cli/src/utils/__tests__/editorApi.test.js.map +1 -1
  28. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js +353 -1
  29. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js.map +1 -1
  30. package/codeyam-cli/src/utils/__tests__/editorLoaderHelpers.test.js +128 -1
  31. package/codeyam-cli/src/utils/__tests__/editorLoaderHelpers.test.js.map +1 -1
  32. package/codeyam-cli/src/utils/__tests__/editorPreview.test.js +88 -1
  33. package/codeyam-cli/src/utils/__tests__/editorPreview.test.js.map +1 -1
  34. package/codeyam-cli/src/utils/__tests__/editorProxySession.test.js +47 -1
  35. package/codeyam-cli/src/utils/__tests__/editorProxySession.test.js.map +1 -1
  36. package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js +785 -1
  37. package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js.map +1 -1
  38. package/codeyam-cli/src/utils/__tests__/entityChangeStatus.test.js +63 -1
  39. package/codeyam-cli/src/utils/__tests__/entityChangeStatus.test.js.map +1 -1
  40. package/codeyam-cli/src/utils/__tests__/parseRegisterArg.test.js +30 -2
  41. package/codeyam-cli/src/utils/__tests__/parseRegisterArg.test.js.map +1 -1
  42. package/codeyam-cli/src/utils/__tests__/scenarioCoverage.test.js +227 -0
  43. package/codeyam-cli/src/utils/__tests__/scenarioCoverage.test.js.map +1 -0
  44. package/codeyam-cli/src/utils/__tests__/scenariosManifest.test.js +426 -218
  45. package/codeyam-cli/src/utils/__tests__/scenariosManifest.test.js.map +1 -1
  46. package/codeyam-cli/src/utils/__tests__/setupClaudeCodeSettings.test.js +1 -0
  47. package/codeyam-cli/src/utils/__tests__/setupClaudeCodeSettings.test.js.map +1 -1
  48. package/codeyam-cli/src/utils/analyzer.js +9 -0
  49. package/codeyam-cli/src/utils/analyzer.js.map +1 -1
  50. package/codeyam-cli/src/utils/analyzerFinalization.js +100 -0
  51. package/codeyam-cli/src/utils/analyzerFinalization.js.map +1 -0
  52. package/codeyam-cli/src/utils/backgroundServer.js +2 -8
  53. package/codeyam-cli/src/utils/backgroundServer.js.map +1 -1
  54. package/codeyam-cli/src/utils/database.js +37 -2
  55. package/codeyam-cli/src/utils/database.js.map +1 -1
  56. package/codeyam-cli/src/utils/editorApi.js +11 -5
  57. package/codeyam-cli/src/utils/editorApi.js.map +1 -1
  58. package/codeyam-cli/src/utils/editorAudit.js +51 -0
  59. package/codeyam-cli/src/utils/editorAudit.js.map +1 -1
  60. package/codeyam-cli/src/utils/editorLoaderHelpers.js +32 -0
  61. package/codeyam-cli/src/utils/editorLoaderHelpers.js.map +1 -1
  62. package/codeyam-cli/src/utils/editorPreview.js +31 -0
  63. package/codeyam-cli/src/utils/editorPreview.js.map +1 -1
  64. package/codeyam-cli/src/utils/editorScenarios.js +261 -0
  65. package/codeyam-cli/src/utils/editorScenarios.js.map +1 -1
  66. package/codeyam-cli/src/utils/entityChangeStatus.js +4 -2
  67. package/codeyam-cli/src/utils/entityChangeStatus.js.map +1 -1
  68. package/codeyam-cli/src/utils/entityChangeStatus.server.js +34 -0
  69. package/codeyam-cli/src/utils/entityChangeStatus.server.js.map +1 -1
  70. package/codeyam-cli/src/utils/parseRegisterArg.js.map +1 -1
  71. package/codeyam-cli/src/utils/progress.js +2 -2
  72. package/codeyam-cli/src/utils/progress.js.map +1 -1
  73. package/codeyam-cli/src/utils/scenarioCoverage.js +75 -0
  74. package/codeyam-cli/src/utils/scenarioCoverage.js.map +1 -0
  75. package/codeyam-cli/src/utils/scenariosManifest.js +204 -75
  76. package/codeyam-cli/src/utils/scenariosManifest.js.map +1 -1
  77. package/codeyam-cli/src/utils/setupClaudeCodeSettings.js +1 -0
  78. package/codeyam-cli/src/utils/setupClaudeCodeSettings.js.map +1 -1
  79. package/codeyam-cli/src/utils/simulationGateMiddleware.js +8 -1
  80. package/codeyam-cli/src/utils/simulationGateMiddleware.js.map +1 -1
  81. package/codeyam-cli/src/utils/slugUtils.js +25 -0
  82. package/codeyam-cli/src/utils/slugUtils.js.map +1 -0
  83. package/codeyam-cli/src/utils/syncMocksMiddleware.js +2 -2
  84. package/codeyam-cli/src/utils/syncMocksMiddleware.js.map +1 -1
  85. package/codeyam-cli/src/webserver/__tests__/clientErrors.test.js +40 -0
  86. package/codeyam-cli/src/webserver/__tests__/clientErrors.test.js.map +1 -0
  87. package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js +157 -0
  88. package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js.map +1 -1
  89. package/codeyam-cli/src/webserver/__tests__/idleDetector.test.js +146 -0
  90. package/codeyam-cli/src/webserver/__tests__/idleDetector.test.js.map +1 -0
  91. package/codeyam-cli/src/webserver/app/lib/clientErrors.js +65 -0
  92. package/codeyam-cli/src/webserver/app/lib/clientErrors.js.map +1 -0
  93. package/codeyam-cli/src/webserver/app/lib/git.js +3 -2
  94. package/codeyam-cli/src/webserver/app/lib/git.js.map +1 -1
  95. package/codeyam-cli/src/webserver/build/client/assets/{ScenarioViewer-0DY_NKil.js → ScenarioViewer-TSD3C211.js} +1 -1
  96. package/codeyam-cli/src/webserver/build/client/assets/api.editor-scenario-coverage-l0sNRNKZ.js +1 -0
  97. package/codeyam-cli/src/webserver/build/client/assets/api.editor-session-l0sNRNKZ.js +1 -0
  98. package/codeyam-cli/src/webserver/build/client/assets/{dev.empty-Csi0_PMl.js → dev.empty-Ii3inc0_.js} +1 -1
  99. package/codeyam-cli/src/webserver/build/client/assets/editor-16o0AIFV.js +15 -0
  100. package/codeyam-cli/src/webserver/build/client/assets/editorPreview-7Uga8I59.js +41 -0
  101. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha._-BF4oLwaE.js → entity._sha._-DwCV5__E.js} +1 -1
  102. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha.scenarios._scenarioId.dev-C7YX6r3H.js → entity._sha.scenarios._scenarioId.dev-BwKcai0j.js} +1 -1
  103. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.fullscreen-CHMiAog3.js +6 -0
  104. package/codeyam-cli/src/webserver/build/client/assets/globals-CQPR0pFR.css +1 -0
  105. package/codeyam-cli/src/webserver/build/client/assets/manifest-76e7b62c.js +1 -0
  106. package/codeyam-cli/src/webserver/build/client/assets/memory-9gnxSZlb.js +101 -0
  107. package/codeyam-cli/src/webserver/build/client/assets/{root-ClvYBUSA.js → root-DBjt6o04.js} +3 -3
  108. package/codeyam-cli/src/webserver/build/client/assets/useCustomSizes-C-_hOl_g.js +1 -0
  109. package/codeyam-cli/src/webserver/build/client/sound-test.html +98 -0
  110. package/codeyam-cli/src/webserver/build/server/assets/index-DsZjKspK.js +1 -0
  111. package/codeyam-cli/src/webserver/build/server/assets/init-DdqKD2p4.js +10 -0
  112. package/codeyam-cli/src/webserver/build/server/assets/server-build-CKKeWtVK.js +444 -0
  113. package/codeyam-cli/src/webserver/build/server/index.js +1 -1
  114. package/codeyam-cli/src/webserver/build-info.json +5 -5
  115. package/codeyam-cli/src/webserver/editorProxy.js +99 -7
  116. package/codeyam-cli/src/webserver/editorProxy.js.map +1 -1
  117. package/codeyam-cli/src/webserver/idleDetector.js +73 -0
  118. package/codeyam-cli/src/webserver/idleDetector.js.map +1 -0
  119. package/codeyam-cli/src/webserver/public/sound-test.html +98 -0
  120. package/codeyam-cli/src/webserver/server.js +46 -4
  121. package/codeyam-cli/src/webserver/server.js.map +1 -1
  122. package/codeyam-cli/src/webserver/terminalServer.js +68 -29
  123. package/codeyam-cli/src/webserver/terminalServer.js.map +1 -1
  124. package/codeyam-cli/templates/chrome-extension-react/README.md +46 -0
  125. package/codeyam-cli/templates/chrome-extension-react/package.json +1 -0
  126. package/codeyam-cli/templates/codeyam-editor-claude.md +84 -5
  127. package/codeyam-cli/templates/editor-step-hook.py +14 -8
  128. package/codeyam-cli/templates/expo-react-native/README.md +41 -0
  129. package/codeyam-cli/templates/expo-react-native/package.json +1 -0
  130. package/codeyam-cli/templates/nextjs-prisma-sqlite/DATABASE.md +14 -0
  131. package/codeyam-cli/templates/nextjs-prisma-sqlite/README.md +53 -0
  132. package/codeyam-cli/templates/nextjs-prisma-sqlite/package.json +1 -0
  133. package/codeyam-cli/templates/nextjs-prisma-supabase/README.md +52 -0
  134. package/codeyam-cli/templates/nextjs-prisma-supabase/package.json +1 -0
  135. package/codeyam-cli/templates/skills/codeyam-dev-mode/SKILL.md +1 -1
  136. package/codeyam-cli/templates/skills/codeyam-editor/SKILL.md +14 -10
  137. package/package.json +1 -1
  138. package/packages/database/src/lib/kysely/tables/editorScenariosTable.js +56 -0
  139. package/packages/database/src/lib/kysely/tables/editorScenariosTable.js.map +1 -1
  140. package/packages/database/src/lib/loadEntities.js +0 -6
  141. package/packages/database/src/lib/loadEntities.js.map +1 -1
  142. package/packages/database/src/lib/updateCommitMetadata.js +0 -25
  143. package/packages/database/src/lib/updateCommitMetadata.js.map +1 -1
  144. package/codeyam-cli/src/webserver/build/client/assets/editor-DgN1LTTt.js +0 -10
  145. package/codeyam-cli/src/webserver/build/client/assets/editorPreview-BLQMSKZa.js +0 -41
  146. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.fullscreen-CF164ouH.js +0 -6
  147. package/codeyam-cli/src/webserver/build/client/assets/globals-BkWJ_UNc.css +0 -1
  148. package/codeyam-cli/src/webserver/build/client/assets/manifest-c26eb85b.js +0 -1
  149. package/codeyam-cli/src/webserver/build/client/assets/memory-Bl2rpw8u.js +0 -96
  150. package/codeyam-cli/src/webserver/build/client/assets/useCustomSizes-CrAK28Bc.js +0 -1
  151. package/codeyam-cli/src/webserver/build/server/assets/index-DflIr5SD.js +0 -1
  152. package/codeyam-cli/src/webserver/build/server/assets/server-build-OhKy839M.js +0 -416
@@ -1,246 +1,454 @@
1
1
  import * as fs from 'fs';
2
2
  import * as path from 'path';
3
3
  import * as os from 'os';
4
- import { readManifest, writeManifest, addScenarioToManifest, removeScenarioFromManifest, buildManifestFromRows, syncManifestToDatabase, } from "../scenariosManifest.js";
4
+ import { scanScenarioFiles, writeScenarioMetadata, deleteScenarioFiles, syncScenarioFilesToDatabase, backfillScenarioMetadata, migrateScenarioFormats, } from "../scenariosManifest.js";
5
5
  function makeTmpDir() {
6
6
  return fs.mkdtempSync(path.join(os.tmpdir(), 'manifest-test-'));
7
7
  }
8
- function makeEntry(overrides = {}) {
8
+ function makeMetadata(overrides = {}) {
9
9
  return {
10
- id: 'test-id-1',
11
10
  name: 'Test Scenario',
12
11
  description: null,
13
12
  componentName: null,
14
13
  componentPath: null,
15
14
  url: '/',
16
- mockDataFile: 'editor-scenarios/test-id-1.json',
17
- screenshotFile: null,
15
+ type: 'application',
16
+ screenshotPath: null,
17
+ viewportWidth: null,
18
+ viewportHeight: null,
18
19
  createdAt: '2024-01-01T00:00:00.000Z',
19
20
  updatedAt: '2024-01-01T00:00:00.000Z',
20
21
  ...overrides,
21
22
  };
22
23
  }
23
- describe('scenariosManifest', () => {
24
- describe('readManifest / writeManifest round-trip', () => {
25
- it('should write and read back a manifest', () => {
26
- const tmp = makeTmpDir();
27
- const manifest = {
28
- version: 1,
29
- updatedAt: '2024-01-01T00:00:00.000Z',
30
- scenarios: [makeEntry()],
31
- };
32
- writeManifest(tmp, manifest);
33
- const result = readManifest(tmp);
34
- expect(result).toEqual(manifest);
35
- });
36
- it('should return null for missing file', () => {
37
- const tmp = makeTmpDir();
38
- expect(readManifest(tmp)).toBeNull();
39
- });
24
+ function writeScenarioFile(tmp, id, data, metadata) {
25
+ const dir = path.join(tmp, '.codeyam', 'editor-scenarios');
26
+ fs.mkdirSync(dir, { recursive: true });
27
+ const content = metadata ? { _metadata: metadata, ...data } : data;
28
+ fs.writeFileSync(path.join(dir, `${id}.json`), JSON.stringify(content, null, 2));
29
+ }
30
+ // ─── scanScenarioFiles ────────────────────────────────────────────
31
+ describe('scanScenarioFiles', () => {
32
+ it('should find scenario files with _metadata', () => {
33
+ const tmp = makeTmpDir();
34
+ writeScenarioFile(tmp, 'abc-123', { seed: {} }, makeMetadata({ name: 'Home Page' }));
35
+ writeScenarioFile(tmp, 'def-456', { seed: {} }, makeMetadata({ name: 'About Page' }));
36
+ const results = scanScenarioFiles(tmp);
37
+ expect(results).toHaveLength(2);
38
+ const names = results.map((r) => r.metadata.name).sort();
39
+ expect(names).toEqual(['About Page', 'Home Page']);
40
40
  });
41
- describe('addScenarioToManifest', () => {
42
- it('should create manifest if none exists', () => {
43
- const tmp = makeTmpDir();
44
- const entry = makeEntry();
45
- addScenarioToManifest(tmp, entry);
46
- const manifest = readManifest(tmp);
47
- expect(manifest).not.toBeNull();
48
- expect(manifest.scenarios).toHaveLength(1);
49
- expect(manifest.scenarios[0].id).toBe('test-id-1');
50
- });
51
- it('should append to existing manifest', () => {
52
- const tmp = makeTmpDir();
53
- addScenarioToManifest(tmp, makeEntry({ id: 'a', name: 'A' }));
54
- addScenarioToManifest(tmp, makeEntry({ id: 'b', name: 'B' }));
55
- const manifest = readManifest(tmp);
56
- expect(manifest.scenarios).toHaveLength(2);
57
- });
58
- it('should deduplicate by id (update existing)', () => {
59
- const tmp = makeTmpDir();
60
- addScenarioToManifest(tmp, makeEntry({ id: 'a', name: 'Original' }));
61
- addScenarioToManifest(tmp, makeEntry({ id: 'a', name: 'Updated' }));
62
- const manifest = readManifest(tmp);
63
- expect(manifest.scenarios).toHaveLength(1);
64
- expect(manifest.scenarios[0].name).toBe('Updated');
65
- });
41
+ it('should skip files without _metadata', () => {
42
+ const tmp = makeTmpDir();
43
+ writeScenarioFile(tmp, 'with-meta', { seed: {} }, makeMetadata());
44
+ writeScenarioFile(tmp, 'no-meta', { seed: {} }); // no metadata
45
+ const results = scanScenarioFiles(tmp);
46
+ expect(results).toHaveLength(1);
47
+ expect(results[0].id).toBe('with-meta');
66
48
  });
67
- describe('removeScenarioFromManifest', () => {
68
- it('should remove a scenario by id', () => {
69
- const tmp = makeTmpDir();
70
- addScenarioToManifest(tmp, makeEntry({ id: 'a', name: 'A' }));
71
- addScenarioToManifest(tmp, makeEntry({ id: 'b', name: 'B' }));
72
- removeScenarioFromManifest(tmp, 'a');
73
- const manifest = readManifest(tmp);
74
- expect(manifest.scenarios).toHaveLength(1);
75
- expect(manifest.scenarios[0].id).toBe('b');
76
- });
77
- it('should handle removing from nonexistent manifest', () => {
78
- const tmp = makeTmpDir();
79
- // Should not throw
80
- removeScenarioFromManifest(tmp, 'nonexistent');
81
- });
82
- it('should handle removing nonexistent id', () => {
83
- const tmp = makeTmpDir();
84
- addScenarioToManifest(tmp, makeEntry({ id: 'a', name: 'A' }));
85
- removeScenarioFromManifest(tmp, 'nonexistent');
86
- const manifest = readManifest(tmp);
87
- expect(manifest.scenarios).toHaveLength(1);
88
- });
49
+ it('should skip .seed.json files', () => {
50
+ const tmp = makeTmpDir();
51
+ const dir = path.join(tmp, '.codeyam', 'editor-scenarios');
52
+ fs.mkdirSync(dir, { recursive: true });
53
+ writeScenarioFile(tmp, 'abc', { seed: {} }, makeMetadata());
54
+ // Write a .seed.json file that has _metadata (shouldn't happen but be safe)
55
+ fs.writeFileSync(path.join(dir, 'abc.seed.json'), JSON.stringify({ _metadata: makeMetadata() }));
56
+ const results = scanScenarioFiles(tmp);
57
+ expect(results).toHaveLength(1);
58
+ expect(results[0].id).toBe('abc');
89
59
  });
90
- describe('buildManifestFromRows', () => {
91
- it('should build manifest from database-like rows', () => {
92
- const rows = [
93
- {
94
- id: 'row-1',
95
- name: 'Scenario 1',
96
- description: 'A scenario',
97
- component_name: 'Button',
98
- component_path: 'app/components/Button.tsx',
99
- url: '/isolated-components/Button?s=Default',
100
- screenshot_path: 'screenshots/row-1.png',
101
- created_at: '2024-01-01 00:00:00',
102
- updated_at: '2024-01-02 00:00:00',
103
- },
104
- {
105
- id: 'row-2',
106
- name: 'Scenario 2',
107
- description: null,
108
- component_name: null,
109
- component_path: null,
110
- url: '/',
111
- screenshot_path: null,
112
- created_at: '2024-01-03 00:00:00',
113
- updated_at: '2024-01-03 00:00:00',
114
- },
115
- ];
116
- const manifest = buildManifestFromRows(rows);
117
- expect(manifest.version).toBe(1);
118
- expect(manifest.scenarios).toHaveLength(2);
119
- expect(manifest.scenarios[0]).toEqual({
120
- id: 'row-1',
121
- name: 'Scenario 1',
122
- description: 'A scenario',
123
- componentName: 'Button',
124
- componentPath: 'app/components/Button.tsx',
125
- url: '/isolated-components/Button?s=Default',
126
- mockDataFile: 'editor-scenarios/row-1.json',
127
- screenshotFile: 'editor-scenarios/screenshots/row-1.png',
128
- createdAt: '2024-01-01 00:00:00',
129
- updatedAt: '2024-01-02 00:00:00',
130
- });
131
- expect(manifest.scenarios[1].screenshotFile).toBeNull();
132
- });
133
- it('should handle empty rows', () => {
134
- const manifest = buildManifestFromRows([]);
135
- expect(manifest.scenarios).toEqual([]);
136
- });
60
+ it('should skip client-errors.json', () => {
61
+ const tmp = makeTmpDir();
62
+ const dir = path.join(tmp, '.codeyam', 'editor-scenarios');
63
+ fs.mkdirSync(dir, { recursive: true });
64
+ writeScenarioFile(tmp, 'abc', { seed: {} }, makeMetadata());
65
+ fs.writeFileSync(path.join(dir, 'client-errors.json'), JSON.stringify({ _metadata: makeMetadata() }));
66
+ const results = scanScenarioFiles(tmp);
67
+ expect(results).toHaveLength(1);
137
68
  });
138
- describe('syncManifestToDatabase', () => {
139
- it('should insert missing scenarios', async () => {
140
- const tmp = makeTmpDir();
141
- const entry = makeEntry({ id: 'new-1', name: 'New Scenario' });
142
- addScenarioToManifest(tmp, entry);
143
- const existingRows = [];
144
- const insertedRows = [];
145
- const updatedRows = [];
146
- const result = await syncManifestToDatabase(tmp, 'project-1', existingRows, (row) => {
147
- insertedRows.push(row);
148
- }, (id, row) => {
149
- updatedRows.push({ id, ...row });
150
- });
151
- expect(result.inserted).toBe(1);
152
- expect(result.updated).toBe(0);
153
- expect(result.skipped).toBe(0);
154
- expect(insertedRows).toHaveLength(1);
155
- expect(insertedRows[0].id).toBe('new-1');
156
- expect(insertedRows[0].project_id).toBe('project-1');
157
- });
158
- it('should skip scenarios already in DB with same updatedAt', async () => {
159
- const tmp = makeTmpDir();
160
- const entry = makeEntry({
161
- id: 'existing-1',
162
- name: 'Existing',
163
- updatedAt: '2024-01-01T00:00:00.000Z',
164
- });
165
- addScenarioToManifest(tmp, entry);
166
- const existingRows = [
167
- { id: 'existing-1', updated_at: '2024-01-01T00:00:00.000Z' },
168
- ];
169
- const insertedRows = [];
170
- const updatedRows = [];
171
- const result = await syncManifestToDatabase(tmp, 'project-1', existingRows, (row) => {
172
- insertedRows.push(row);
173
- }, (id, row) => {
174
- updatedRows.push({ id, ...row });
175
- });
176
- expect(result.inserted).toBe(0);
177
- expect(result.updated).toBe(0);
178
- expect(result.skipped).toBe(1);
179
- });
180
- it('should update scenarios newer than DB row', async () => {
181
- const tmp = makeTmpDir();
182
- const entry = makeEntry({
183
- id: 'existing-1',
184
- name: 'Updated Name',
185
- updatedAt: '2024-02-01T00:00:00.000Z',
186
- });
187
- addScenarioToManifest(tmp, entry);
188
- const existingRows = [
189
- { id: 'existing-1', updated_at: '2024-01-01T00:00:00.000Z' },
190
- ];
191
- const insertedRows = [];
192
- const updatedRows = [];
193
- const result = await syncManifestToDatabase(tmp, 'project-1', existingRows, (row) => {
194
- insertedRows.push(row);
195
- }, (id, row) => {
196
- updatedRows.push({ id, ...row });
197
- });
198
- expect(result.inserted).toBe(0);
199
- expect(result.updated).toBe(1);
200
- expect(result.skipped).toBe(0);
201
- expect(updatedRows[0].name).toBe('Updated Name');
69
+ it('should return empty array for missing directory', () => {
70
+ const tmp = makeTmpDir();
71
+ expect(scanScenarioFiles(tmp)).toEqual([]);
72
+ });
73
+ it('should extract id from filename', () => {
74
+ const tmp = makeTmpDir();
75
+ writeScenarioFile(tmp, 'my-uuid-here', { seed: {} }, makeMetadata());
76
+ const results = scanScenarioFiles(tmp);
77
+ expect(results[0].id).toBe('my-uuid-here');
78
+ });
79
+ });
80
+ // ─── writeScenarioMetadata ────────────────────────────────────────
81
+ describe('writeScenarioMetadata', () => {
82
+ it('should add _metadata to existing scenario file', () => {
83
+ const tmp = makeTmpDir();
84
+ writeScenarioFile(tmp, 'abc', { type: 'application', seed: { users: [] } });
85
+ writeScenarioMetadata(tmp, 'abc', makeMetadata({ name: 'Updated' }));
86
+ const filePath = path.join(tmp, '.codeyam', 'editor-scenarios', 'abc.json');
87
+ const content = JSON.parse(fs.readFileSync(filePath, 'utf8'));
88
+ expect(content._metadata.name).toBe('Updated');
89
+ expect(content.type).toBe('application'); // original data preserved
90
+ expect(content.seed).toEqual({ users: [] }); // original data preserved
91
+ });
92
+ it('should update existing _metadata', () => {
93
+ const tmp = makeTmpDir();
94
+ writeScenarioFile(tmp, 'abc', { seed: {} }, makeMetadata({ name: 'Old' }));
95
+ writeScenarioMetadata(tmp, 'abc', makeMetadata({ name: 'New' }));
96
+ const filePath = path.join(tmp, '.codeyam', 'editor-scenarios', 'abc.json');
97
+ const content = JSON.parse(fs.readFileSync(filePath, 'utf8'));
98
+ expect(content._metadata.name).toBe('New');
99
+ });
100
+ it('should not create file if it does not exist', () => {
101
+ const tmp = makeTmpDir();
102
+ writeScenarioMetadata(tmp, 'nonexistent', makeMetadata());
103
+ const filePath = path.join(tmp, '.codeyam', 'editor-scenarios', 'nonexistent.json');
104
+ expect(fs.existsSync(filePath)).toBe(false);
105
+ });
106
+ });
107
+ // ─── deleteScenarioFiles ──────────────────────────────────────────
108
+ describe('deleteScenarioFiles', () => {
109
+ it('should delete both .json and .seed.json', () => {
110
+ const tmp = makeTmpDir();
111
+ const dir = path.join(tmp, '.codeyam', 'editor-scenarios');
112
+ fs.mkdirSync(dir, { recursive: true });
113
+ fs.writeFileSync(path.join(dir, 'abc.json'), '{}');
114
+ fs.writeFileSync(path.join(dir, 'abc.seed.json'), '{}');
115
+ deleteScenarioFiles(tmp, 'abc');
116
+ expect(fs.existsSync(path.join(dir, 'abc.json'))).toBe(false);
117
+ expect(fs.existsSync(path.join(dir, 'abc.seed.json'))).toBe(false);
118
+ });
119
+ it('should not throw for missing files', () => {
120
+ const tmp = makeTmpDir();
121
+ expect(() => deleteScenarioFiles(tmp, 'nonexistent')).not.toThrow();
122
+ });
123
+ });
124
+ // ─── backfillScenarioMetadata ─────────────────────────────────────
125
+ describe('backfillScenarioMetadata', () => {
126
+ it('should add _metadata to files that lack it', () => {
127
+ const tmp = makeTmpDir();
128
+ // Write a file WITHOUT _metadata (legacy)
129
+ writeScenarioFile(tmp, 'abc-123', {
130
+ type: 'application',
131
+ seed: { users: [] },
202
132
  });
203
- it('should handle empty manifest', async () => {
204
- const tmp = makeTmpDir();
205
- writeManifest(tmp, { version: 1, updatedAt: '', scenarios: [] });
206
- const result = await syncManifestToDatabase(tmp, 'project-1', [], () => { }, () => { });
207
- expect(result.inserted).toBe(0);
208
- expect(result.updated).toBe(0);
209
- expect(result.skipped).toBe(0);
133
+ const updated = backfillScenarioMetadata(tmp, [
134
+ {
135
+ id: 'abc-123',
136
+ name: 'Home Page',
137
+ description: 'Default view',
138
+ component_name: null,
139
+ component_path: null,
140
+ url: '/',
141
+ type: 'application',
142
+ created_at: '2024-01-01T00:00:00.000Z',
143
+ updated_at: '2024-01-02T00:00:00.000Z',
144
+ },
145
+ ]);
146
+ expect(updated).toBe(1);
147
+ // Verify _metadata was written
148
+ const filePath = path.join(tmp, '.codeyam', 'editor-scenarios', 'abc-123.json');
149
+ const content = JSON.parse(fs.readFileSync(filePath, 'utf8'));
150
+ expect(content._metadata.name).toBe('Home Page');
151
+ expect(content._metadata.url).toBe('/');
152
+ // Original data preserved
153
+ expect(content.type).toBe('application');
154
+ expect(content.seed).toEqual({ users: [] });
155
+ });
156
+ it('should skip files that already have _metadata', () => {
157
+ const tmp = makeTmpDir();
158
+ writeScenarioFile(tmp, 'abc-123', { seed: {} }, makeMetadata({ name: 'Existing' }));
159
+ const updated = backfillScenarioMetadata(tmp, [
160
+ {
161
+ id: 'abc-123',
162
+ name: 'From DB',
163
+ description: null,
164
+ component_name: null,
165
+ component_path: null,
166
+ url: '/',
167
+ type: 'application',
168
+ created_at: '2024-01-01T00:00:00.000Z',
169
+ updated_at: '2024-01-01T00:00:00.000Z',
170
+ },
171
+ ]);
172
+ expect(updated).toBe(0);
173
+ // Verify original _metadata preserved
174
+ const filePath = path.join(tmp, '.codeyam', 'editor-scenarios', 'abc-123.json');
175
+ const content = JSON.parse(fs.readFileSync(filePath, 'utf8'));
176
+ expect(content._metadata.name).toBe('Existing');
177
+ });
178
+ it('should skip DB rows with no matching file', () => {
179
+ const tmp = makeTmpDir();
180
+ const dir = path.join(tmp, '.codeyam', 'editor-scenarios');
181
+ fs.mkdirSync(dir, { recursive: true });
182
+ const updated = backfillScenarioMetadata(tmp, [
183
+ {
184
+ id: 'nonexistent',
185
+ name: 'Ghost',
186
+ description: null,
187
+ component_name: null,
188
+ component_path: null,
189
+ url: '/',
190
+ type: null,
191
+ created_at: '2024-01-01T00:00:00.000Z',
192
+ updated_at: '2024-01-01T00:00:00.000Z',
193
+ },
194
+ ]);
195
+ expect(updated).toBe(0);
196
+ });
197
+ it('should handle mix of files with and without _metadata', () => {
198
+ const tmp = makeTmpDir();
199
+ writeScenarioFile(tmp, 'has-meta', { seed: {} }, makeMetadata());
200
+ writeScenarioFile(tmp, 'no-meta', { type: 'application', seed: {} });
201
+ const updated = backfillScenarioMetadata(tmp, [
202
+ {
203
+ id: 'has-meta',
204
+ name: 'A',
205
+ description: null,
206
+ component_name: null,
207
+ component_path: null,
208
+ url: '/',
209
+ type: null,
210
+ created_at: '2024-01-01T00:00:00.000Z',
211
+ updated_at: '2024-01-01T00:00:00.000Z',
212
+ },
213
+ {
214
+ id: 'no-meta',
215
+ name: 'B',
216
+ description: null,
217
+ component_name: null,
218
+ component_path: null,
219
+ url: '/about',
220
+ type: 'application',
221
+ created_at: '2024-01-01T00:00:00.000Z',
222
+ updated_at: '2024-01-01T00:00:00.000Z',
223
+ },
224
+ ]);
225
+ expect(updated).toBe(1); // Only no-meta was updated
226
+ });
227
+ });
228
+ // ─── syncScenarioFilesToDatabase ──────────────────────────────────
229
+ describe('syncScenarioFilesToDatabase', () => {
230
+ it('should insert scenarios from _metadata files', async () => {
231
+ const tmp = makeTmpDir();
232
+ writeScenarioFile(tmp, 'new-1', { seed: {} }, makeMetadata({ name: 'New Scenario' }));
233
+ const insertedRows = [];
234
+ const result = await syncScenarioFilesToDatabase(tmp, 'project-1', [], (row) => {
235
+ insertedRows.push(row);
236
+ }, () => { });
237
+ expect(result.inserted).toBe(1);
238
+ expect(insertedRows[0].id).toBe('new-1');
239
+ expect(insertedRows[0].name).toBe('New Scenario');
240
+ expect(insertedRows[0].project_id).toBe('project-1');
241
+ expect(insertedRows[0].type).toBe('application');
242
+ });
243
+ it('should sync type, screenshot_path, and viewport from _metadata', async () => {
244
+ const tmp = makeTmpDir();
245
+ writeScenarioFile(tmp, 'full-1', { seed: {} }, makeMetadata({
246
+ name: 'Full Scenario',
247
+ type: 'user',
248
+ screenshotPath: 'screenshots/full-1.png',
249
+ viewportWidth: 1280,
250
+ viewportHeight: 720,
251
+ }));
252
+ const insertedRows = [];
253
+ await syncScenarioFilesToDatabase(tmp, 'project-1', [], (row) => {
254
+ insertedRows.push(row);
255
+ }, () => { });
256
+ expect(insertedRows[0].type).toBe('user');
257
+ expect(insertedRows[0].screenshot_path).toBe('screenshots/full-1.png');
258
+ expect(insertedRows[0].viewport_width).toBe(1280);
259
+ expect(insertedRows[0].viewport_height).toBe(720);
260
+ });
261
+ it('should skip scenarios already in DB with same updatedAt', async () => {
262
+ const tmp = makeTmpDir();
263
+ writeScenarioFile(tmp, 'existing-1', { seed: {} }, makeMetadata({ updatedAt: '2024-01-01T00:00:00.000Z' }));
264
+ const result = await syncScenarioFilesToDatabase(tmp, 'project-1', [{ id: 'existing-1', updated_at: '2024-01-01T00:00:00.000Z' }], () => { }, () => { });
265
+ expect(result.skipped).toBe(1);
266
+ expect(result.inserted).toBe(0);
267
+ expect(result.updated).toBe(0);
268
+ });
269
+ it('should update scenarios newer than DB row', async () => {
270
+ const tmp = makeTmpDir();
271
+ writeScenarioFile(tmp, 'existing-1', { seed: {} }, makeMetadata({ name: 'Updated', updatedAt: '2024-02-01T00:00:00.000Z' }));
272
+ const updatedRows = [];
273
+ const result = await syncScenarioFilesToDatabase(tmp, 'project-1', [{ id: 'existing-1', updated_at: '2024-01-01T00:00:00.000Z' }], () => { }, (id, row) => {
274
+ updatedRows.push({ id, ...row });
210
275
  });
211
- it('should handle no manifest file', async () => {
212
- const tmp = makeTmpDir();
213
- const result = await syncManifestToDatabase(tmp, 'project-1', [], () => { }, () => { });
214
- expect(result.inserted).toBe(0);
215
- expect(result.updated).toBe(0);
216
- expect(result.skipped).toBe(0);
276
+ expect(result.updated).toBe(1);
277
+ expect(updatedRows[0].name).toBe('Updated');
278
+ });
279
+ it('should handle empty directory', async () => {
280
+ const tmp = makeTmpDir();
281
+ const dir = path.join(tmp, '.codeyam', 'editor-scenarios');
282
+ fs.mkdirSync(dir, { recursive: true });
283
+ const result = await syncScenarioFilesToDatabase(tmp, 'project-1', [], () => { }, () => { });
284
+ expect(result.inserted).toBe(0);
285
+ });
286
+ it('should handle mixed insert/update/skip', async () => {
287
+ const tmp = makeTmpDir();
288
+ writeScenarioFile(tmp, 'new-1', { seed: {} }, makeMetadata({ name: 'New', updatedAt: '2024-01-01T00:00:00.000Z' }));
289
+ writeScenarioFile(tmp, 'updated-1', { seed: {} }, makeMetadata({ name: 'Updated', updatedAt: '2024-02-01T00:00:00.000Z' }));
290
+ writeScenarioFile(tmp, 'current-1', { seed: {} }, makeMetadata({ name: 'Current', updatedAt: '2024-01-01T00:00:00.000Z' }));
291
+ const result = await syncScenarioFilesToDatabase(tmp, 'project-1', [
292
+ { id: 'updated-1', updated_at: '2024-01-01T00:00:00.000Z' },
293
+ { id: 'current-1', updated_at: '2024-01-01T00:00:00.000Z' },
294
+ ], () => { }, () => { });
295
+ expect(result.inserted).toBe(1);
296
+ expect(result.updated).toBe(1);
297
+ expect(result.skipped).toBe(1);
298
+ });
299
+ });
300
+ // ─── migrateScenarioFormats ──────────────────────────────────────
301
+ describe('migrateScenarioFormats', () => {
302
+ function writeConfigWithSizes(tmp, screenSizes, defaultScreenSize) {
303
+ const dir = path.join(tmp, '.codeyam');
304
+ fs.mkdirSync(dir, { recursive: true });
305
+ fs.writeFileSync(path.join(dir, 'config.json'), JSON.stringify({
306
+ ...(defaultScreenSize ? { defaultScreenSize } : {}),
307
+ ...(Object.keys(screenSizes).length > 0 ? { screenSizes } : {}),
308
+ }));
309
+ }
310
+ it('should resolve null viewport using project defaultScreenSize', () => {
311
+ const tmp = makeTmpDir();
312
+ writeConfigWithSizes(tmp, { Desktop: { width: 1440, height: 900 } }, { name: 'Desktop', width: 1440, height: 900 });
313
+ writeScenarioFile(tmp, 'null-vp', { seed: {} }, makeMetadata({
314
+ name: 'Home Page',
315
+ viewportWidth: null,
316
+ viewportHeight: null,
317
+ }));
318
+ const result = migrateScenarioFormats(tmp);
319
+ expect(result.fixed).toBe(1);
320
+ const filePath = path.join(tmp, '.codeyam', 'editor-scenarios', 'null-vp.json');
321
+ const content = JSON.parse(fs.readFileSync(filePath, 'utf8'));
322
+ expect(content._metadata.viewportWidth).toBe(1440);
323
+ expect(content._metadata.viewportHeight).toBe(900);
324
+ });
325
+ it('should wrap single dimension into dimensions array', () => {
326
+ const tmp = makeTmpDir();
327
+ writeConfigWithSizes(tmp, { Mobile: { width: 375, height: 667 } });
328
+ writeScenarioFile(tmp, 'single-dim', { seed: {} }, makeMetadata({
329
+ name: 'Mobile View',
330
+ dimension: 'Mobile',
331
+ viewportWidth: 375,
332
+ viewportHeight: 667,
333
+ }));
334
+ const result = migrateScenarioFormats(tmp);
335
+ expect(result.fixed).toBe(1);
336
+ const filePath = path.join(tmp, '.codeyam', 'editor-scenarios', 'single-dim.json');
337
+ const content = JSON.parse(fs.readFileSync(filePath, 'utf8'));
338
+ expect(content._metadata.dimensions).toEqual(['Mobile']);
339
+ });
340
+ it('should build screenshotPaths from single screenshotPath + dimension', () => {
341
+ const tmp = makeTmpDir();
342
+ writeConfigWithSizes(tmp, { Desktop: { width: 1440, height: 900 } });
343
+ writeScenarioFile(tmp, 'single-ss', { seed: {} }, makeMetadata({
344
+ name: 'Dashboard',
345
+ dimension: 'Desktop',
346
+ screenshotPath: 'screenshots/single-ss.png',
347
+ viewportWidth: 1440,
348
+ viewportHeight: 900,
349
+ }));
350
+ const result = migrateScenarioFormats(tmp);
351
+ expect(result.fixed).toBe(1);
352
+ const filePath = path.join(tmp, '.codeyam', 'editor-scenarios', 'single-ss.json');
353
+ const content = JSON.parse(fs.readFileSync(filePath, 'utf8'));
354
+ expect(content._metadata.screenshotPaths).toEqual({
355
+ Desktop: 'screenshots/single-ss.png',
217
356
  });
218
- it('should handle mixed insert/update/skip', async () => {
219
- const tmp = makeTmpDir();
220
- addScenarioToManifest(tmp, makeEntry({
221
- id: 'new-1',
222
- name: 'New',
223
- updatedAt: '2024-01-01T00:00:00.000Z',
224
- }));
225
- addScenarioToManifest(tmp, makeEntry({
226
- id: 'updated-1',
227
- name: 'Updated',
228
- updatedAt: '2024-02-01T00:00:00.000Z',
229
- }));
230
- addScenarioToManifest(tmp, makeEntry({
231
- id: 'current-1',
232
- name: 'Current',
233
- updatedAt: '2024-01-01T00:00:00.000Z',
234
- }));
235
- const existingRows = [
236
- { id: 'updated-1', updated_at: '2024-01-01T00:00:00.000Z' },
237
- { id: 'current-1', updated_at: '2024-01-01T00:00:00.000Z' },
238
- ];
239
- const result = await syncManifestToDatabase(tmp, 'project-1', existingRows, () => { }, () => { });
240
- expect(result.inserted).toBe(1);
241
- expect(result.updated).toBe(1);
242
- expect(result.skipped).toBe(1);
357
+ });
358
+ it('should skip files that are already in the correct format', () => {
359
+ const tmp = makeTmpDir();
360
+ writeConfigWithSizes(tmp, { Desktop: { width: 1440, height: 900 } });
361
+ writeScenarioFile(tmp, 'good-fmt', { seed: {} }, makeMetadata({
362
+ name: 'Good Scenario',
363
+ viewportWidth: 1440,
364
+ viewportHeight: 900,
365
+ dimension: 'Desktop',
366
+ dimensions: ['Desktop'],
367
+ screenshotPaths: { Desktop: 'screenshots/good-fmt.png' },
368
+ screenshotPath: 'screenshots/good-fmt.png',
369
+ }));
370
+ const result = migrateScenarioFormats(tmp);
371
+ expect(result.fixed).toBe(0);
372
+ expect(result.scanned).toBe(1);
373
+ });
374
+ it('should fix multiple issues in a single file', () => {
375
+ const tmp = makeTmpDir();
376
+ writeConfigWithSizes(tmp, { Tablet: { width: 768, height: 1024 } }, { name: 'Tablet', width: 768, height: 1024 });
377
+ // File has null viewport, dimension set but no dimensions array, screenshotPath but no map
378
+ writeScenarioFile(tmp, 'multi-fix', { seed: {} }, makeMetadata({
379
+ name: 'Tablet View',
380
+ dimension: 'Tablet',
381
+ viewportWidth: null,
382
+ viewportHeight: null,
383
+ screenshotPath: 'screenshots/multi-fix.png',
384
+ }));
385
+ const result = migrateScenarioFormats(tmp);
386
+ expect(result.fixed).toBe(1);
387
+ const filePath = path.join(tmp, '.codeyam', 'editor-scenarios', 'multi-fix.json');
388
+ const content = JSON.parse(fs.readFileSync(filePath, 'utf8'));
389
+ // Null viewport resolved via dimension name lookup
390
+ expect(content._metadata.viewportWidth).toBe(768);
391
+ expect(content._metadata.viewportHeight).toBe(1024);
392
+ // dimensions array created from single dimension
393
+ expect(content._metadata.dimensions).toEqual(['Tablet']);
394
+ // screenshotPaths map created from single screenshotPath
395
+ expect(content._metadata.screenshotPaths).toEqual({
396
+ Tablet: 'screenshots/multi-fix.png',
243
397
  });
244
398
  });
399
+ it('should use hardcoded fallback (1280x720) when no project config exists', () => {
400
+ const tmp = makeTmpDir();
401
+ // No config.json at all
402
+ writeScenarioFile(tmp, 'no-config', { seed: {} }, makeMetadata({
403
+ name: 'Bare Scenario',
404
+ viewportWidth: null,
405
+ viewportHeight: null,
406
+ }));
407
+ const result = migrateScenarioFormats(tmp);
408
+ expect(result.fixed).toBe(1);
409
+ const filePath = path.join(tmp, '.codeyam', 'editor-scenarios', 'no-config.json');
410
+ const content = JSON.parse(fs.readFileSync(filePath, 'utf8'));
411
+ expect(content._metadata.viewportWidth).toBe(1280);
412
+ expect(content._metadata.viewportHeight).toBe(720);
413
+ });
414
+ it('should handle empty scenarios directory', () => {
415
+ const tmp = makeTmpDir();
416
+ const dir = path.join(tmp, '.codeyam', 'editor-scenarios');
417
+ fs.mkdirSync(dir, { recursive: true });
418
+ const result = migrateScenarioFormats(tmp);
419
+ expect(result.scanned).toBe(0);
420
+ expect(result.fixed).toBe(0);
421
+ });
422
+ it('should handle missing scenarios directory', () => {
423
+ const tmp = makeTmpDir();
424
+ const result = migrateScenarioFormats(tmp);
425
+ expect(result.scanned).toBe(0);
426
+ expect(result.fixed).toBe(0);
427
+ });
428
+ it('should preserve all non-metadata data in the file', () => {
429
+ const tmp = makeTmpDir();
430
+ writeConfigWithSizes(tmp, {}, { name: 'Desktop', width: 1440, height: 900 });
431
+ // Write file with extra data alongside _metadata
432
+ const dir = path.join(tmp, '.codeyam', 'editor-scenarios');
433
+ fs.mkdirSync(dir, { recursive: true });
434
+ const fileContent = {
435
+ _metadata: makeMetadata({
436
+ name: 'With Data',
437
+ viewportWidth: null,
438
+ viewportHeight: null,
439
+ }),
440
+ type: 'application',
441
+ seed: { users: [{ id: 1, name: 'Alice' }] },
442
+ session: { cookieValue: 'abc123' },
443
+ };
444
+ fs.writeFileSync(path.join(dir, 'with-data.json'), JSON.stringify(fileContent, null, 2));
445
+ migrateScenarioFormats(tmp);
446
+ const content = JSON.parse(fs.readFileSync(path.join(dir, 'with-data.json'), 'utf8'));
447
+ expect(content.type).toBe('application');
448
+ expect(content.seed).toEqual({ users: [{ id: 1, name: 'Alice' }] });
449
+ expect(content.session).toEqual({ cookieValue: 'abc123' });
450
+ // And metadata was fixed
451
+ expect(content._metadata.viewportWidth).toBe(1440);
452
+ });
245
453
  });
246
454
  //# sourceMappingURL=scenariosManifest.test.js.map