@codeyam/codeyam-cli 0.1.0-staging.a890816 → 0.1.0-staging.ae0de75

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 (106) hide show
  1. package/analyzer-template/.build-info.json +6 -6
  2. package/analyzer-template/log.txt +3 -3
  3. package/analyzer-template/package.json +6 -6
  4. package/analyzer-template/packages/ai/package.json +1 -1
  5. package/analyzer-template/packages/aws/package.json +1 -1
  6. package/analyzer-template/packages/database/package.json +3 -3
  7. package/analyzer-template/packages/github/package.json +1 -1
  8. package/codeyam-cli/src/commands/editor.js +892 -90
  9. package/codeyam-cli/src/commands/editor.js.map +1 -1
  10. package/codeyam-cli/src/commands/init.js +6 -1
  11. package/codeyam-cli/src/commands/init.js.map +1 -1
  12. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js +246 -0
  13. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js.map +1 -0
  14. package/codeyam-cli/src/utils/__tests__/editorDevServer.test.js +126 -0
  15. package/codeyam-cli/src/utils/__tests__/editorDevServer.test.js.map +1 -0
  16. package/codeyam-cli/src/utils/__tests__/editorJournal.test.js +295 -0
  17. package/codeyam-cli/src/utils/__tests__/editorJournal.test.js.map +1 -0
  18. package/codeyam-cli/src/utils/__tests__/editorMockState.test.js +270 -0
  19. package/codeyam-cli/src/utils/__tests__/editorMockState.test.js.map +1 -0
  20. package/codeyam-cli/src/utils/__tests__/editorPreloadHelpers.test.js +100 -0
  21. package/codeyam-cli/src/utils/__tests__/editorPreloadHelpers.test.js.map +1 -0
  22. package/codeyam-cli/src/utils/__tests__/editorPreview.test.js +147 -0
  23. package/codeyam-cli/src/utils/__tests__/editorPreview.test.js.map +1 -0
  24. package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js +76 -0
  25. package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js.map +1 -0
  26. package/codeyam-cli/src/utils/__tests__/git.editor.test.js +134 -0
  27. package/codeyam-cli/src/utils/__tests__/git.editor.test.js.map +1 -0
  28. package/codeyam-cli/src/utils/__tests__/project.test.js +65 -0
  29. package/codeyam-cli/src/utils/__tests__/project.test.js.map +1 -0
  30. package/codeyam-cli/src/utils/__tests__/scenarioMarkers.test.js +121 -0
  31. package/codeyam-cli/src/utils/__tests__/scenarioMarkers.test.js.map +1 -0
  32. package/codeyam-cli/src/utils/buildFlags.js +4 -0
  33. package/codeyam-cli/src/utils/buildFlags.js.map +1 -0
  34. package/codeyam-cli/src/utils/editorAudit.js +82 -0
  35. package/codeyam-cli/src/utils/editorAudit.js.map +1 -0
  36. package/codeyam-cli/src/utils/editorDevServer.js +98 -0
  37. package/codeyam-cli/src/utils/editorDevServer.js.map +1 -0
  38. package/codeyam-cli/src/utils/editorJournal.js +137 -0
  39. package/codeyam-cli/src/utils/editorJournal.js.map +1 -0
  40. package/codeyam-cli/src/utils/editorMockState.js +248 -0
  41. package/codeyam-cli/src/utils/editorMockState.js.map +1 -0
  42. package/codeyam-cli/src/utils/editorPreloadHelpers.js +64 -0
  43. package/codeyam-cli/src/utils/editorPreloadHelpers.js.map +1 -0
  44. package/codeyam-cli/src/utils/editorPreview.js +66 -0
  45. package/codeyam-cli/src/utils/editorPreview.js.map +1 -0
  46. package/codeyam-cli/src/utils/editorScenarios.js +56 -0
  47. package/codeyam-cli/src/utils/editorScenarios.js.map +1 -0
  48. package/codeyam-cli/src/utils/git.js +51 -0
  49. package/codeyam-cli/src/utils/git.js.map +1 -1
  50. package/codeyam-cli/src/utils/install-skills.js +28 -17
  51. package/codeyam-cli/src/utils/install-skills.js.map +1 -1
  52. package/codeyam-cli/src/utils/project.js +15 -5
  53. package/codeyam-cli/src/utils/project.js.map +1 -1
  54. package/codeyam-cli/src/utils/scenarioMarkers.js +134 -0
  55. package/codeyam-cli/src/utils/scenarioMarkers.js.map +1 -0
  56. package/codeyam-cli/src/utils/testRunner.js +1 -1
  57. package/codeyam-cli/src/utils/testRunner.js.map +1 -1
  58. package/codeyam-cli/src/webserver/build/client/assets/Terminal-wkqC0AQk.js +41 -0
  59. package/codeyam-cli/src/webserver/build/client/assets/api.editor-audit-l0sNRNKZ.js +1 -0
  60. package/codeyam-cli/src/webserver/build/client/assets/api.editor-load-commit-l0sNRNKZ.js +1 -0
  61. package/codeyam-cli/src/webserver/build/client/assets/editor-CdjF_fX6.js +8 -0
  62. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.dev-D8ILZMR0.js +6 -0
  63. package/codeyam-cli/src/webserver/build/client/assets/globals-B17TBSS6.css +1 -0
  64. package/codeyam-cli/src/webserver/build/client/assets/manifest-b8fd6b07.js +1 -0
  65. package/codeyam-cli/src/webserver/build/client/assets/{root-DiRdBreB.js → root-DUKqhFlb.js} +7 -7
  66. package/codeyam-cli/src/webserver/build/client/assets/xterm-BqvuqXEL.js +27 -0
  67. package/codeyam-cli/src/webserver/build/server/assets/{index-BzAbACSx.js → index-BLhjL9Xi.js} +1 -1
  68. package/codeyam-cli/src/webserver/build/server/assets/server-build-DyMuI5mU.js +363 -0
  69. package/codeyam-cli/src/webserver/build/server/index.js +1 -1
  70. package/codeyam-cli/src/webserver/build-info.json +5 -5
  71. package/codeyam-cli/src/webserver/editorProxy.js +182 -14
  72. package/codeyam-cli/src/webserver/editorProxy.js.map +1 -1
  73. package/codeyam-cli/src/webserver/scripts/codeyam-preload.mjs +175 -0
  74. package/codeyam-cli/src/webserver/server.js +61 -12
  75. package/codeyam-cli/src/webserver/server.js.map +1 -1
  76. package/codeyam-cli/src/webserver/terminalServer.js +29 -103
  77. package/codeyam-cli/src/webserver/terminalServer.js.map +1 -1
  78. package/codeyam-cli/templates/editor-step-hook.py +6 -4
  79. package/codeyam-cli/templates/{codeyam-dev-mode.md → skills/codeyam-dev-mode/SKILL.md} +1 -1
  80. package/codeyam-cli/templates/{codeyam-editor.md → skills/codeyam-editor/SKILL.md} +5 -4
  81. package/codeyam-cli/templates/{codeyam-memory.md → skills/codeyam-memory/SKILL.md} +215 -0
  82. package/codeyam-cli/templates/skills/codeyam-memory/scripts/holistic-analysis/deprecated-prompt.md +100 -0
  83. package/codeyam-cli/templates/skills/codeyam-memory/scripts/holistic-analysis/detect-deprecated-patterns.sh +108 -0
  84. package/codeyam-cli/templates/skills/codeyam-memory/scripts/holistic-analysis/find-exports.sh +69 -0
  85. package/codeyam-cli/templates/skills/codeyam-memory/scripts/holistic-analysis/misleading-api-prompt.md +117 -0
  86. package/codeyam-cli/templates/skills/codeyam-memory/scripts/session-mining/analyze-prompt.md +46 -0
  87. package/codeyam-cli/templates/skills/codeyam-memory/scripts/session-mining/cleanup.sh +12 -0
  88. package/codeyam-cli/templates/skills/codeyam-memory/scripts/session-mining/filter.jq +45 -0
  89. package/codeyam-cli/templates/skills/codeyam-memory/scripts/session-mining/preprocess.sh +139 -0
  90. package/package.json +2 -2
  91. package/scripts/npm-post-install.cjs +12 -0
  92. package/codeyam-cli/src/webserver/build/client/assets/Terminal-CcG8YTLx.js +0 -41
  93. package/codeyam-cli/src/webserver/build/client/assets/editor-W_IGJ2Kd.js +0 -7
  94. package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.dev-D6SEzMCu.js +0 -6
  95. package/codeyam-cli/src/webserver/build/client/assets/globals-BZB_H1w2.css +0 -1
  96. package/codeyam-cli/src/webserver/build/client/assets/manifest-8daa4147.js +0 -1
  97. package/codeyam-cli/src/webserver/build/client/assets/xterm-DMSzMhqy.js +0 -9
  98. package/codeyam-cli/src/webserver/build/server/assets/server-build-OdUocH6P.js +0 -362
  99. package/scripts/finalize-analyzer.cjs +0 -13
  100. /package/codeyam-cli/templates/{codeyam-diagnose.md → commands/codeyam-diagnose.md} +0 -0
  101. /package/codeyam-cli/templates/{codeyam-debug.md → skills/codeyam-debug/SKILL.md} +0 -0
  102. /package/codeyam-cli/templates/{codeyam-new-rule.md → skills/codeyam-new-rule/SKILL.md} +0 -0
  103. /package/codeyam-cli/templates/{codeyam-setup.md → skills/codeyam-setup/SKILL.md} +0 -0
  104. /package/codeyam-cli/templates/{codeyam-sim.md → skills/codeyam-sim/SKILL.md} +0 -0
  105. /package/codeyam-cli/templates/{codeyam-test.md → skills/codeyam-test/SKILL.md} +0 -0
  106. /package/codeyam-cli/templates/{codeyam-verify.md → skills/codeyam-verify/SKILL.md} +0 -0
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Journal data transformation utilities for the editor.
3
+ *
4
+ * Extracted from inline logic in EditorJournal.tsx, api.editor-journal-entry.ts,
5
+ * and api.editor-journal-update.ts.
6
+ */
7
+ /**
8
+ * Group journal entries by date, reversing so newest entries come first.
9
+ * Returns a Map preserving insertion order (newest date first).
10
+ */
11
+ export function groupEntriesByDate(entries) {
12
+ const grouped = new Map();
13
+ for (const entry of [...entries].reverse()) {
14
+ const existing = grouped.get(entry.date) || [];
15
+ existing.push(entry);
16
+ grouped.set(entry.date, existing);
17
+ }
18
+ return grouped;
19
+ }
20
+ /**
21
+ * Group scenario screenshots by component name.
22
+ * Names with " - " are split on the first dash; names without are grouped as "App".
23
+ * Sorted with "App" first, then alphabetical.
24
+ */
25
+ export function groupScreenshotsByComponent(screenshots) {
26
+ const groups = new Map();
27
+ for (const ss of screenshots) {
28
+ const dashIdx = ss.name.indexOf(' - ');
29
+ const group = dashIdx !== -1 ? ss.name.slice(0, dashIdx) : 'App';
30
+ const existing = groups.get(group) || [];
31
+ existing.push(ss);
32
+ groups.set(group, existing);
33
+ }
34
+ return [...groups.entries()].sort(([a], [b]) => {
35
+ if (a === 'App')
36
+ return -1;
37
+ if (b === 'App')
38
+ return 1;
39
+ return a.localeCompare(b);
40
+ });
41
+ }
42
+ /**
43
+ * Generate a sanitized filename for a journal screenshot.
44
+ * Format: `{ISO timestamp with colons replaced}_{sanitized name}.png`
45
+ */
46
+ export function generateJournalScreenshotFilename(name, timestamp) {
47
+ const safeName = name.replace(/[^a-zA-Z0-9_\-]/g, '_');
48
+ const isoTimestamp = timestamp
49
+ .toISOString()
50
+ .replace(/:/g, '-')
51
+ .replace(/\.\d+Z$/, '');
52
+ return `${isoTimestamp}_${safeName}.png`;
53
+ }
54
+ /**
55
+ * Build markdown lines array for a journal entry.
56
+ * Returns the joined string ready to append to a daily markdown file.
57
+ */
58
+ export function buildJournalMarkdown(params) {
59
+ const { title, timeStr, type, description, allScenarioNames, screenshot, scenarioScreenshots, commitSha, commitMessage, } = params;
60
+ const mdLines = [
61
+ '',
62
+ '---',
63
+ '',
64
+ `### ${title}`,
65
+ `**Time:** ${timeStr}`,
66
+ `**Type:** ${type}`,
67
+ ];
68
+ if (allScenarioNames.length > 0) {
69
+ mdLines.push(`**Scenarios:** ${allScenarioNames.join(', ')}`);
70
+ }
71
+ mdLines.push('');
72
+ mdLines.push(description);
73
+ if (screenshot) {
74
+ mdLines.push('');
75
+ mdLines.push(`![${title}](${screenshot})`);
76
+ }
77
+ if (scenarioScreenshots.length > 0) {
78
+ mdLines.push('');
79
+ mdLines.push('**Scenario Screenshots:**');
80
+ for (const ss of scenarioScreenshots) {
81
+ mdLines.push('');
82
+ mdLines.push(`![${ss.name}](${ss.path})`);
83
+ }
84
+ }
85
+ if (commitSha && commitMessage) {
86
+ mdLines.push('');
87
+ mdLines.push(`**Commit:** \`${commitSha}\` — ${commitMessage}`);
88
+ }
89
+ mdLines.push('');
90
+ return mdLines.join('\n');
91
+ }
92
+ /**
93
+ * Find a journal entry's index by its time field.
94
+ * Returns -1 if not found.
95
+ */
96
+ export function findJournalEntryByTime(entries, time) {
97
+ return entries.findIndex((e) => e.time === time);
98
+ }
99
+ /**
100
+ * Apply non-undefined updates to a journal entry.
101
+ * Returns the mutated entry (same reference).
102
+ */
103
+ export function applyJournalEntryUpdates(entry, updates) {
104
+ if (updates.commitSha !== undefined) {
105
+ entry.commitSha = updates.commitSha;
106
+ }
107
+ if (updates.commitMessage !== undefined) {
108
+ entry.commitMessage = updates.commitMessage;
109
+ }
110
+ if (updates.description !== undefined) {
111
+ entry.description = updates.description;
112
+ }
113
+ if (updates.scenarios !== undefined) {
114
+ entry.scenarios = updates.scenarios;
115
+ }
116
+ if (updates.scenarioScreenshots !== undefined) {
117
+ entry.scenarioScreenshots = updates.scenarioScreenshots;
118
+ }
119
+ return entry;
120
+ }
121
+ /**
122
+ * Insert a commit line into markdown content before the next section separator (---).
123
+ * Finds the entry by its title marker (### Title), then inserts before the next `\n---\n`.
124
+ * Returns the modified content, or null if the title was not found.
125
+ */
126
+ export function insertCommitIntoMarkdown(content, title, commitSha, commitMessage) {
127
+ const commitLine = `\n**Commit:** \`${commitSha}\` — ${commitMessage || 'no message'}\n`;
128
+ const titleMarker = `### ${title}`;
129
+ const titlePos = content.lastIndexOf(titleMarker);
130
+ if (titlePos === -1) {
131
+ return null;
132
+ }
133
+ const nextSection = content.indexOf('\n---\n', titlePos + 1);
134
+ const insertPos = nextSection !== -1 ? nextSection : content.length;
135
+ return content.slice(0, insertPos) + commitLine + content.slice(insertPos);
136
+ }
137
+ //# sourceMappingURL=editorJournal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"editorJournal.js","sourceRoot":"","sources":["../../../../src/utils/editorJournal.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,248 @@
1
+ /**
2
+ * Stateful mock state manager for the editor proxy.
3
+ *
4
+ * Replaces the static findMatchingRoute() with method-aware, stateful matching.
5
+ * Supports legacy key-based format, route-keyed format, method-prefixed routes,
6
+ * parameterized routes, and mutable state for CRUD operations.
7
+ */
8
+ import crypto from 'crypto';
9
+ function hashData(data) {
10
+ return crypto.createHash('sha256').update(JSON.stringify(data)).digest('hex');
11
+ }
12
+ /**
13
+ * Parse a route key like "POST /api/drinks/:id" into method, path pattern, and regex.
14
+ */
15
+ function parseRouteKey(key) {
16
+ const methodMatch = key.match(/^(GET|POST|PUT|DELETE|PATCH)\s+(\/\S+)$/);
17
+ if (methodMatch) {
18
+ return { method: methodMatch[1], pathPattern: methodMatch[2] };
19
+ }
20
+ // Path-only — treat as GET
21
+ return { method: null, pathPattern: key };
22
+ }
23
+ /**
24
+ * Build a regex and extract param names from a path pattern like /api/drinks/:id.
25
+ */
26
+ function buildPathRegex(pattern) {
27
+ const paramNames = [];
28
+ const regexStr = pattern.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, (_, name) => {
29
+ paramNames.push(name);
30
+ return '([^/]+)';
31
+ });
32
+ return { regex: new RegExp(`^${regexStr}$`), paramNames };
33
+ }
34
+ /**
35
+ * Find the state path that corresponds to a given pathname.
36
+ * For "/api/drinks/1", checks if "/api/drinks" is a state key.
37
+ */
38
+ function findStateBasePath(pathname, stateKeys) {
39
+ // Exact match first
40
+ if (stateKeys.includes(pathname))
41
+ return pathname;
42
+ // Try parent path (e.g., /api/drinks/1 → /api/drinks)
43
+ const lastSlash = pathname.lastIndexOf('/');
44
+ if (lastSlash > 0) {
45
+ const parent = pathname.substring(0, lastSlash);
46
+ if (stateKeys.includes(parent))
47
+ return parent;
48
+ }
49
+ return null;
50
+ }
51
+ export function createMockStateManager() {
52
+ let routes = [];
53
+ let mutableState = {};
54
+ let initialStateSnapshot = null;
55
+ let scenarioHash = null;
56
+ let hasState = false;
57
+ let scenarioData = null;
58
+ function parseRoutes(data) {
59
+ const parsed = [];
60
+ const routesObj = data.routes;
61
+ if (routesObj && typeof routesObj === 'object') {
62
+ for (const [key, value] of Object.entries(routesObj)) {
63
+ const { method, pathPattern } = parseRouteKey(key);
64
+ const { regex, paramNames } = buildPathRegex(pathPattern);
65
+ const entry = typeof value === 'object' && value !== null
66
+ ? value
67
+ : { body: value };
68
+ parsed.push({
69
+ method,
70
+ pathPattern,
71
+ pathRegex: regex,
72
+ paramNames,
73
+ response: {
74
+ body: entry.body,
75
+ status: typeof entry.status === 'number' ? entry.status : 200,
76
+ },
77
+ });
78
+ }
79
+ }
80
+ return parsed;
81
+ }
82
+ function initializeState(data) {
83
+ const stateObj = data.state;
84
+ if (stateObj && typeof stateObj === 'object') {
85
+ hasState = true;
86
+ mutableState = {};
87
+ for (const [key, value] of Object.entries(stateObj)) {
88
+ mutableState[key] = Array.isArray(value)
89
+ ? JSON.parse(JSON.stringify(value))
90
+ : [];
91
+ }
92
+ initialStateSnapshot = JSON.stringify(stateObj);
93
+ }
94
+ else {
95
+ hasState = false;
96
+ mutableState = {};
97
+ initialStateSnapshot = null;
98
+ }
99
+ }
100
+ return {
101
+ loadScenario(data) {
102
+ const newHash = hashData(data);
103
+ // Smart reload: if same data, preserve mutations
104
+ if (scenarioHash && newHash === scenarioHash) {
105
+ return;
106
+ }
107
+ scenarioHash = newHash;
108
+ scenarioData = data;
109
+ routes = parseRoutes(data);
110
+ initializeState(data);
111
+ },
112
+ matchRequest(method, pathname, body) {
113
+ if (!scenarioData && routes.length === 0 && !hasState) {
114
+ return null;
115
+ }
116
+ const stateKeys = Object.keys(mutableState);
117
+ // --- Stateful matching ---
118
+ if (hasState) {
119
+ const stateBasePath = findStateBasePath(pathname, stateKeys);
120
+ if (stateBasePath && method === 'GET' && stateBasePath === pathname) {
121
+ // Return mutable state array
122
+ return { body: mutableState[stateBasePath], status: 200 };
123
+ }
124
+ // For mutations, find a matching route first, then apply state changes
125
+ if (stateBasePath) {
126
+ const matchedRoute = matchRoute(method, pathname);
127
+ if (method === 'POST' && stateBasePath === pathname && matchedRoute) {
128
+ const arr = mutableState[stateBasePath];
129
+ const newItem = typeof body === 'object' && body !== null
130
+ ? { ...body }
131
+ : {};
132
+ if (!('id' in newItem)) {
133
+ const maxId = arr.reduce((max, item) => {
134
+ const id = typeof item.id === 'number' ? item.id : 0;
135
+ return Math.max(max, id);
136
+ }, 0);
137
+ newItem.id = maxId + 1;
138
+ }
139
+ arr.push(newItem);
140
+ return {
141
+ body: newItem,
142
+ status: matchedRoute.response.status,
143
+ };
144
+ }
145
+ if (method === 'DELETE' && matchedRoute && matchedRoute.params) {
146
+ const idParam = matchedRoute.params.id;
147
+ const arr = mutableState[stateBasePath];
148
+ const idx = arr.findIndex((item) => String(item.id) === String(idParam));
149
+ if (idx === -1) {
150
+ return { body: { error: 'Not found' }, status: 404 };
151
+ }
152
+ arr.splice(idx, 1);
153
+ return { body: null, status: matchedRoute.response.status };
154
+ }
155
+ if (method === 'PUT' && matchedRoute && matchedRoute.params) {
156
+ const idParam = matchedRoute.params.id;
157
+ const arr = mutableState[stateBasePath];
158
+ const idx = arr.findIndex((item) => String(item.id) === String(idParam));
159
+ if (idx === -1) {
160
+ return { body: { error: 'Not found' }, status: 404 };
161
+ }
162
+ const replacement = typeof body === 'object' && body !== null
163
+ ? { ...body }
164
+ : arr[idx];
165
+ arr[idx] = replacement;
166
+ return { body: replacement, status: matchedRoute.response.status };
167
+ }
168
+ }
169
+ }
170
+ // --- Non-stateful route matching ---
171
+ const matched = matchRoute(method, pathname);
172
+ if (matched) {
173
+ return {
174
+ body: matched.response.body ?? null,
175
+ status: matched.response.status,
176
+ params: matched.params,
177
+ };
178
+ }
179
+ // --- Legacy format ---
180
+ if (scenarioData && method === 'GET') {
181
+ const apiMatch = pathname.match(/^\/api\/(.+)$/);
182
+ if (apiMatch) {
183
+ const key = apiMatch[1];
184
+ if (key in scenarioData && key !== 'routes' && key !== 'state') {
185
+ return { body: scenarioData[key], status: 200 };
186
+ }
187
+ }
188
+ }
189
+ return null;
190
+ },
191
+ resetState() {
192
+ if (initialStateSnapshot) {
193
+ const restored = JSON.parse(initialStateSnapshot);
194
+ mutableState = {};
195
+ for (const [key, value] of Object.entries(restored)) {
196
+ mutableState[key] = Array.isArray(value)
197
+ ? JSON.parse(JSON.stringify(value))
198
+ : [];
199
+ }
200
+ }
201
+ },
202
+ getState() {
203
+ return { ...mutableState };
204
+ },
205
+ };
206
+ function matchRoute(method, pathname) {
207
+ // Priority 1: Exact method-prefixed match
208
+ for (const route of routes) {
209
+ if (route.method !== null &&
210
+ route.method === method &&
211
+ route.paramNames.length === 0) {
212
+ const match = route.pathRegex.exec(pathname);
213
+ if (match) {
214
+ return { response: route.response };
215
+ }
216
+ }
217
+ }
218
+ // Priority 2: Method-less (GET only)
219
+ if (method === 'GET') {
220
+ for (const route of routes) {
221
+ if (route.method === null && route.paramNames.length === 0) {
222
+ const match = route.pathRegex.exec(pathname);
223
+ if (match) {
224
+ return { response: route.response };
225
+ }
226
+ }
227
+ }
228
+ }
229
+ // Priority 3: Parameterized routes
230
+ for (const route of routes) {
231
+ if (route.paramNames.length > 0) {
232
+ const expectedMethod = route.method ?? 'GET';
233
+ if (expectedMethod !== method)
234
+ continue;
235
+ const match = route.pathRegex.exec(pathname);
236
+ if (match) {
237
+ const params = {};
238
+ for (let i = 0; i < route.paramNames.length; i++) {
239
+ params[route.paramNames[i]] = match[i + 1];
240
+ }
241
+ return { response: route.response, params };
242
+ }
243
+ }
244
+ }
245
+ return null;
246
+ }
247
+ }
248
+ //# sourceMappingURL=editorMockState.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"editorMockState.js","sourceRoot":"","sources":["../../../../src/utils/editorMockState.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Pure redirect functions for the Node.js preload module.
3
+ *
4
+ * Extracted so they can be unit-tested without loading the actual preload
5
+ * (which patches global fetch and http).
6
+ */
7
+ /**
8
+ * If `url` points to the dev server, return a redirected URL going through the proxy.
9
+ * Otherwise return the original URL unchanged.
10
+ */
11
+ export function redirectUrl(url, devServerUrl, proxyUrl) {
12
+ let parsed;
13
+ try {
14
+ parsed = new URL(url);
15
+ }
16
+ catch {
17
+ // Relative URL or invalid — pass through
18
+ return url;
19
+ }
20
+ const dev = new URL(devServerUrl);
21
+ const proxy = new URL(proxyUrl);
22
+ if (parsed.hostname === dev.hostname && parsed.port === dev.port) {
23
+ parsed.hostname = proxy.hostname;
24
+ parsed.port = proxy.port;
25
+ parsed.protocol = proxy.protocol;
26
+ return parsed.toString();
27
+ }
28
+ return url;
29
+ }
30
+ /**
31
+ * If http.request options target the dev server, patch them to go through the proxy.
32
+ * Returns the original options object if no redirect is needed.
33
+ */
34
+ export function redirectOptions(options, devServerUrl, proxyUrl) {
35
+ const dev = new URL(devServerUrl);
36
+ const proxy = new URL(proxyUrl);
37
+ const devPort = parseInt(dev.port, 10);
38
+ const proxyPort = parseInt(proxy.port, 10);
39
+ // Match on hostname + port
40
+ if (options.hostname &&
41
+ parseInt(String(options.port), 10) === devPort &&
42
+ options.hostname === dev.hostname) {
43
+ return {
44
+ ...options,
45
+ hostname: proxy.hostname,
46
+ port: proxyPort,
47
+ headers: options.headers
48
+ ? { ...options.headers, host: `${proxy.hostname}:${proxyPort}` }
49
+ : options.headers,
50
+ };
51
+ }
52
+ // Match on host field (hostname:port combined)
53
+ if (options.host) {
54
+ const expectedHost = `${dev.hostname}:${devPort}`;
55
+ if (options.host === expectedHost) {
56
+ return {
57
+ ...options,
58
+ host: `${proxy.hostname}:${proxyPort}`,
59
+ };
60
+ }
61
+ }
62
+ return options;
63
+ }
64
+ //# sourceMappingURL=editorPreloadHelpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"editorPreloadHelpers.js","sourceRoot":"","sources":["../../../../src/utils/editorPreloadHelpers.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;GAGG;AACH,MAAM,UAAU,WAAW,CACzB,GAAW,EACX,YAAoB,EACpB,QAAgB;IAEhB,IAAI,MAAW,CAAC;IAChB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,yCAAyC;QACzC,OAAO,GAAG,CAAC;IACb,CAAC;IAED,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,YAAY,CAAC,CAAC;IAClC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC;IAEhC,IAAI,MAAM,CAAC,QAAQ,KAAK,GAAG,CAAC,QAAQ,IAAI,MAAM,CAAC,IAAI,KAAK,GAAG,CAAC,IAAI,EAAE,CAAC;QACjE,MAAM,CAAC,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;QACjC,MAAM,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;QACzB,MAAM,CAAC,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;QACjC,OAAO,MAAM,CAAC,QAAQ,EAAE,CAAC;IAC3B,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC;AAYD;;;GAGG;AACH,MAAM,UAAU,eAAe,CAC7B,OAAuB,EACvB,YAAoB,EACpB,QAAgB;IAEhB,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,YAAY,CAAC,CAAC;IAClC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC;IAChC,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACvC,MAAM,SAAS,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAE3C,2BAA2B;IAC3B,IACE,OAAO,CAAC,QAAQ;QAChB,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,KAAK,OAAO;QAC9C,OAAO,CAAC,QAAQ,KAAK,GAAG,CAAC,QAAQ,EACjC,CAAC;QACD,OAAO;YACL,GAAG,OAAO;YACV,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,OAAO,CAAC,OAAO;gBACtB,CAAC,CAAC,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC,QAAQ,IAAI,SAAS,EAAE,EAAE;gBAChE,CAAC,CAAC,OAAO,CAAC,OAAO;SACpB,CAAC;IACJ,CAAC;IAED,+CAA+C;IAC/C,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACjB,MAAM,YAAY,GAAG,GAAG,GAAG,CAAC,QAAQ,IAAI,OAAO,EAAE,CAAC;QAClD,IAAI,OAAO,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;YAClC,OAAO;gBACL,GAAG,OAAO;gBACV,IAAI,EAAE,GAAG,KAAK,CAAC,QAAQ,IAAI,SAAS,EAAE;aACvC,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Preview URL construction and viewport scaling for the editor.
3
+ *
4
+ * Extracted from inline logic in editor.tsx (previewUrl useMemo and previewScale useMemo).
5
+ */
6
+ import { generateScenarioSlug } from "./editorScenarios.js";
7
+ /**
8
+ * Resolve the preview URL based on current editor state.
9
+ *
10
+ * Priority:
11
+ * 1. Analyzed scenario with interactive server URL
12
+ * 2. Active scenario with path-based URL → combine with proxy/dev server
13
+ * 3. Active scenario with full URL → use directly
14
+ * 4. Zoom component with scenario → __codeyam__ isolation URL
15
+ * 5. Base proxy/dev server URL
16
+ */
17
+ export function buildPreviewUrl(params) {
18
+ const { activeAnalyzedScenario, analyzedPreviewUrl, activeScenarioId, scenarios, proxyUrl, devServerUrl, zoomComponent, } = params;
19
+ // When viewing an analyzed scenario, use the interactive server URL
20
+ if (activeAnalyzedScenario && analyzedPreviewUrl) {
21
+ return analyzedPreviewUrl;
22
+ }
23
+ // If analyzed scenario is active but no URL, fall through to check other sources
24
+ // but if nothing else matches either, return null
25
+ if (activeAnalyzedScenario && !analyzedPreviewUrl) {
26
+ return null;
27
+ }
28
+ // Component isolation scenarios store a path (e.g. /codeyam-isolate/DrinkCard?s=Default)
29
+ if (activeScenarioId) {
30
+ const scenario = scenarios.find((s) => s.id === activeScenarioId);
31
+ if (scenario?.url) {
32
+ const base = proxyUrl || devServerUrl;
33
+ if (!base)
34
+ return null;
35
+ if (scenario.url.startsWith('/')) {
36
+ return `${base}${scenario.url}`;
37
+ }
38
+ return scenario.url;
39
+ }
40
+ }
41
+ const baseUrl = proxyUrl || devServerUrl;
42
+ if (!baseUrl)
43
+ return null;
44
+ if (zoomComponent && activeScenarioId) {
45
+ const scenario = scenarios.find((s) => s.id === activeScenarioId);
46
+ const scenarioSlug = scenario
47
+ ? generateScenarioSlug(scenario.name)
48
+ : 'Default';
49
+ return `${baseUrl}/__codeyam__/${zoomComponent}/${scenarioSlug}`;
50
+ }
51
+ return baseUrl;
52
+ }
53
+ /**
54
+ * Compute CSS scale factor to fit a viewport in a container.
55
+ * Returns 1 if the target fits, otherwise the smallest scale that fits both dimensions.
56
+ */
57
+ export function calculatePreviewScale(containerSize, targetSize) {
58
+ const targetW = targetSize.width;
59
+ const targetH = targetSize.height ?? 900;
60
+ const availW = containerSize.width;
61
+ const availH = containerSize.height;
62
+ if (targetW <= availW && targetH <= availH)
63
+ return 1;
64
+ return Math.min(availW / targetW, availH / targetH);
65
+ }
66
+ //# sourceMappingURL=editorPreview.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"editorPreview.js","sourceRoot":"","sources":["../../../../src/utils/editorPreview.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAkBzD;;;;;;;;;GASG;AACH,MAAM,UAAU,eAAe,CAAC,MAA6B;IAC3D,MAAM,EACJ,sBAAsB,EACtB,kBAAkB,EAClB,gBAAgB,EAChB,SAAS,EACT,QAAQ,EACR,YAAY,EACZ,aAAa,GACd,GAAG,MAAM,CAAC;IAEX,oEAAoE;IACpE,IAAI,sBAAsB,IAAI,kBAAkB,EAAE,CAAC;QACjD,OAAO,kBAAkB,CAAC;IAC5B,CAAC;IAED,iFAAiF;IACjF,kDAAkD;IAClD,IAAI,sBAAsB,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAClD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,yFAAyF;IACzF,IAAI,gBAAgB,EAAE,CAAC;QACrB,MAAM,QAAQ,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,gBAAgB,CAAC,CAAC;QAClE,IAAI,QAAQ,EAAE,GAAG,EAAE,CAAC;YAClB,MAAM,IAAI,GAAG,QAAQ,IAAI,YAAY,CAAC;YACtC,IAAI,CAAC,IAAI;gBAAE,OAAO,IAAI,CAAC;YACvB,IAAI,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBACjC,OAAO,GAAG,IAAI,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC;YAClC,CAAC;YACD,OAAO,QAAQ,CAAC,GAAG,CAAC;QACtB,CAAC;IACH,CAAC;IAED,MAAM,OAAO,GAAG,QAAQ,IAAI,YAAY,CAAC;IACzC,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAE1B,IAAI,aAAa,IAAI,gBAAgB,EAAE,CAAC;QACtC,MAAM,QAAQ,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,gBAAgB,CAAC,CAAC;QAClE,MAAM,YAAY,GAAG,QAAQ;YAC3B,CAAC,CAAC,oBAAoB,CAAC,QAAQ,CAAC,IAAI,CAAC;YACrC,CAAC,CAAC,SAAS,CAAC;QACd,OAAO,GAAG,OAAO,gBAAgB,aAAa,IAAI,YAAY,EAAE,CAAC;IACnE,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CACnC,aAAgD,EAChD,UAAoD;IAEpD,MAAM,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC;IACjC,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,IAAI,GAAG,CAAC;IACzC,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC;IACnC,MAAM,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC;IACpC,IAAI,OAAO,IAAI,MAAM,IAAI,OAAO,IAAI,MAAM;QAAE,OAAO,CAAC,CAAC;IACrD,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC;AACtD,CAAC"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Shared scenario utilities used across editor routes and components.
3
+ *
4
+ * Extracted from inline logic in editor.tsx, api.editor-journal-entry.ts,
5
+ * and api.editor-register-scenario.ts.
6
+ */
7
+ /**
8
+ * Generic Map-based deduplication that keeps the last item for each key.
9
+ * Items are iterated in order, so later entries overwrite earlier ones.
10
+ * Map preserves insertion order of first appearance.
11
+ */
12
+ export function deduplicateByName(items, keyFn) {
13
+ const byKey = new Map();
14
+ for (const item of items) {
15
+ byKey.set(keyFn(item), item);
16
+ }
17
+ return [...byKey.values()];
18
+ }
19
+ /**
20
+ * Convert a scenario name to a URL-safe slug.
21
+ * Replaces any run of non-alphanumeric, non-underscore characters with a single underscore.
22
+ */
23
+ export function generateScenarioSlug(name) {
24
+ return name.replace(/[^a-zA-Z0-9_]+/g, '_');
25
+ }
26
+ /**
27
+ * Convert an ISO 8601 timestamp (e.g. "2026-02-28T19:00:00.000Z") to
28
+ * SQLite-compatible format (e.g. "2026-02-28 19:00:00").
29
+ *
30
+ * SQLite's CURRENT_TIMESTAMP uses space-separated "YYYY-MM-DD HH:MM:SS" format,
31
+ * so we need this conversion for date comparisons.
32
+ */
33
+ export function convertIsoToSqliteTimestamp(iso) {
34
+ return iso.replace('T', ' ').replace(/\.\d{3}Z$/, '');
35
+ }
36
+ /**
37
+ * Resolve which URL to use for Playwright screenshot capture.
38
+ *
39
+ * Priority:
40
+ * 1. Path-based URL + proxy → combine (component isolation with API interception)
41
+ * 2. Full URL → use directly (legacy)
42
+ * 3. No URL → proxy root or dev server root (page scenarios)
43
+ */
44
+ export function determineCaptureUrl(url, proxyUrl, devServerUrl) {
45
+ const isPath = url && url.startsWith('/');
46
+ if (isPath && proxyUrl) {
47
+ return `${proxyUrl}${url}`;
48
+ }
49
+ else if (url && !isPath) {
50
+ return url;
51
+ }
52
+ else {
53
+ return proxyUrl || devServerUrl || null;
54
+ }
55
+ }
56
+ //# sourceMappingURL=editorScenarios.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"editorScenarios.js","sourceRoot":"","sources":["../../../../src/utils/editorScenarios.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAC/B,KAAU,EACV,KAA0B;IAE1B,MAAM,KAAK,GAAG,IAAI,GAAG,EAAa,CAAC;IACnC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC;IAC/B,CAAC;IACD,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;AAC7B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,OAAO,IAAI,CAAC,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,CAAC;AAC9C,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,2BAA2B,CAAC,GAAW;IACrD,OAAO,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;AACxD,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CACjC,GAA8B,EAC9B,QAAuB,EACvB,YAA2B;IAE3B,MAAM,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;IAE1C,IAAI,MAAM,IAAI,QAAQ,EAAE,CAAC;QACvB,OAAO,GAAG,QAAQ,GAAG,GAAG,EAAE,CAAC;IAC7B,CAAC;SAAM,IAAI,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QAC1B,OAAO,GAAG,CAAC;IACb,CAAC;SAAM,CAAC;QACN,OAAO,QAAQ,IAAI,YAAY,IAAI,IAAI,CAAC;IAC1C,CAAC;AACH,CAAC"}
@@ -128,4 +128,55 @@ export function gitCommit(rootPath, message) {
128
128
  });
129
129
  return getHeadSha(rootPath);
130
130
  }
131
+ /**
132
+ * Validate that a string looks like a valid git commit SHA (7-40 hex chars).
133
+ * Used to prevent command injection before passing to git commands.
134
+ */
135
+ export function validateCommitSha(sha) {
136
+ return /^[0-9a-f]{7,40}$/i.test(sha);
137
+ }
138
+ /**
139
+ * Verify that a commit exists in the repository using `git cat-file -t`.
140
+ */
141
+ export function verifyCommitExists(rootPath, sha) {
142
+ try {
143
+ execSync(`git cat-file -t ${sha}`, {
144
+ cwd: rootPath,
145
+ encoding: 'utf8',
146
+ stdio: 'pipe',
147
+ });
148
+ return true;
149
+ }
150
+ catch {
151
+ return false;
152
+ }
153
+ }
154
+ /**
155
+ * Stash uncommitted changes with a message.
156
+ * Returns `{ stashed: true }` if changes were stashed, `{ stashed: false }` if clean.
157
+ */
158
+ export function gitStash(rootPath, message) {
159
+ try {
160
+ const output = execSync(`git stash push -m ${JSON.stringify(message)}`, {
161
+ cwd: rootPath,
162
+ encoding: 'utf8',
163
+ stdio: 'pipe',
164
+ });
165
+ return { stashed: !output.includes('No local changes') };
166
+ }
167
+ catch {
168
+ return { stashed: false };
169
+ }
170
+ }
171
+ /**
172
+ * Checkout a specific git ref (commit SHA, branch name, etc.).
173
+ * Throws on failure.
174
+ */
175
+ export function gitCheckout(rootPath, ref) {
176
+ execSync(`git checkout ${ref}`, {
177
+ cwd: rootPath,
178
+ encoding: 'utf8',
179
+ stdio: 'pipe',
180
+ });
181
+ }
131
182
  //# sourceMappingURL=git.js.map