swagger-parser-mcp-server 2.3.3 → 3.0.0

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 (127) hide show
  1. package/README.md +81 -120
  2. package/dist/index.d.ts +0 -3
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +266 -102
  5. package/dist/index.js.map +1 -1
  6. package/dist/schemas.d.ts +2 -283
  7. package/dist/schemas.d.ts.map +1 -1
  8. package/dist/schemas.js +0 -108
  9. package/dist/schemas.js.map +1 -1
  10. package/dist/utils/swaggerCache.d.ts +0 -4
  11. package/dist/utils/swaggerCache.d.ts.map +1 -1
  12. package/dist/utils/swaggerCache.js +0 -8
  13. package/dist/utils/swaggerCache.js.map +1 -1
  14. package/package.json +3 -11
  15. package/dist/application/snapshot/createSnapshotRuntime.d.ts +0 -2
  16. package/dist/application/snapshot/createSnapshotRuntime.d.ts.map +0 -1
  17. package/dist/application/snapshot/createSnapshotRuntime.js +0 -2
  18. package/dist/application/snapshot/createSnapshotRuntime.js.map +0 -1
  19. package/dist/application/snapshot/snapshotCaptureService.d.ts +0 -90
  20. package/dist/application/snapshot/snapshotCaptureService.d.ts.map +0 -1
  21. package/dist/application/snapshot/snapshotCaptureService.js +0 -394
  22. package/dist/application/snapshot/snapshotCaptureService.js.map +0 -1
  23. package/dist/application/snapshot/snapshotRepository.d.ts +0 -77
  24. package/dist/application/snapshot/snapshotRepository.d.ts.map +0 -1
  25. package/dist/application/snapshot/snapshotRepository.js +0 -2
  26. package/dist/application/snapshot/snapshotRepository.js.map +0 -1
  27. package/dist/domain/canonical/canonicalSnapshot.d.ts +0 -61
  28. package/dist/domain/canonical/canonicalSnapshot.d.ts.map +0 -1
  29. package/dist/domain/canonical/canonicalSnapshot.js +0 -300
  30. package/dist/domain/canonical/canonicalSnapshot.js.map +0 -1
  31. package/dist/domain/contracts/runtimeEnvironmentContract.d.ts +0 -21
  32. package/dist/domain/contracts/runtimeEnvironmentContract.d.ts.map +0 -1
  33. package/dist/domain/contracts/runtimeEnvironmentContract.js +0 -50
  34. package/dist/domain/contracts/runtimeEnvironmentContract.js.map +0 -1
  35. package/dist/domain/contracts/snapshotDiffContract.d.ts +0 -270
  36. package/dist/domain/contracts/snapshotDiffContract.d.ts.map +0 -1
  37. package/dist/domain/contracts/snapshotDiffContract.js +0 -99
  38. package/dist/domain/contracts/snapshotDiffContract.js.map +0 -1
  39. package/dist/domain/diff/endpointDiffClassifier.d.ts +0 -78
  40. package/dist/domain/diff/endpointDiffClassifier.d.ts.map +0 -1
  41. package/dist/domain/diff/endpointDiffClassifier.js +0 -317
  42. package/dist/domain/diff/endpointDiffClassifier.js.map +0 -1
  43. package/dist/http.d.ts +0 -3
  44. package/dist/http.d.ts.map +0 -1
  45. package/dist/http.js +0 -52
  46. package/dist/http.js.map +0 -1
  47. package/dist/infrastructure/postgres/migrationRunner.d.ts +0 -14
  48. package/dist/infrastructure/postgres/migrationRunner.d.ts.map +0 -1
  49. package/dist/infrastructure/postgres/migrationRunner.js +0 -161
  50. package/dist/infrastructure/postgres/migrationRunner.js.map +0 -1
  51. package/dist/infrastructure/postgres/migrations/001_snapshot_schema.sql +0 -29
  52. package/dist/infrastructure/postgres/migrations/002_snapshot_change_history.sql +0 -32
  53. package/dist/infrastructure/postgres/postgresSnapshotRepository.d.ts +0 -25
  54. package/dist/infrastructure/postgres/postgresSnapshotRepository.d.ts.map +0 -1
  55. package/dist/infrastructure/postgres/postgresSnapshotRepository.js +0 -323
  56. package/dist/infrastructure/postgres/postgresSnapshotRepository.js.map +0 -1
  57. package/dist/infrastructure/postgres/runMigrations.d.ts +0 -3
  58. package/dist/infrastructure/postgres/runMigrations.d.ts.map +0 -1
  59. package/dist/infrastructure/postgres/runMigrations.js +0 -33
  60. package/dist/infrastructure/postgres/runMigrations.js.map +0 -1
  61. package/dist/infrastructure/runtime/createSnapshotRuntime.d.ts +0 -17
  62. package/dist/infrastructure/runtime/createSnapshotRuntime.d.ts.map +0 -1
  63. package/dist/infrastructure/runtime/createSnapshotRuntime.js +0 -38
  64. package/dist/infrastructure/runtime/createSnapshotRuntime.js.map +0 -1
  65. package/dist/tests/canonicalSnapshot.test.d.ts +0 -2
  66. package/dist/tests/canonicalSnapshot.test.d.ts.map +0 -1
  67. package/dist/tests/canonicalSnapshot.test.js +0 -425
  68. package/dist/tests/canonicalSnapshot.test.js.map +0 -1
  69. package/dist/tests/endpointDiffClassifier.test.d.ts +0 -2
  70. package/dist/tests/endpointDiffClassifier.test.d.ts.map +0 -1
  71. package/dist/tests/endpointDiffClassifier.test.js +0 -633
  72. package/dist/tests/endpointDiffClassifier.test.js.map +0 -1
  73. package/dist/tests/httpSnapshotTransport.test.d.ts +0 -2
  74. package/dist/tests/httpSnapshotTransport.test.d.ts.map +0 -1
  75. package/dist/tests/httpSnapshotTransport.test.js +0 -356
  76. package/dist/tests/httpSnapshotTransport.test.js.map +0 -1
  77. package/dist/tests/indexLifecycle.test.d.ts +0 -2
  78. package/dist/tests/indexLifecycle.test.d.ts.map +0 -1
  79. package/dist/tests/indexLifecycle.test.js +0 -44
  80. package/dist/tests/indexLifecycle.test.js.map +0 -1
  81. package/dist/tests/mcpSnapshotTools.test.d.ts +0 -2
  82. package/dist/tests/mcpSnapshotTools.test.d.ts.map +0 -1
  83. package/dist/tests/mcpSnapshotTools.test.js +0 -316
  84. package/dist/tests/mcpSnapshotTools.test.js.map +0 -1
  85. package/dist/tests/postgresMigrationSmoke.test.d.ts +0 -2
  86. package/dist/tests/postgresMigrationSmoke.test.d.ts.map +0 -1
  87. package/dist/tests/postgresMigrationSmoke.test.js +0 -187
  88. package/dist/tests/postgresMigrationSmoke.test.js.map +0 -1
  89. package/dist/tests/realPostgresTestSchema.d.ts +0 -10
  90. package/dist/tests/realPostgresTestSchema.d.ts.map +0 -1
  91. package/dist/tests/realPostgresTestSchema.js +0 -73
  92. package/dist/tests/realPostgresTestSchema.js.map +0 -1
  93. package/dist/tests/snapshotCapturePipeline.test.d.ts +0 -2
  94. package/dist/tests/snapshotCapturePipeline.test.d.ts.map +0 -1
  95. package/dist/tests/snapshotCapturePipeline.test.js +0 -475
  96. package/dist/tests/snapshotCapturePipeline.test.js.map +0 -1
  97. package/dist/tests/snapshotDiffContract.test.d.ts +0 -2
  98. package/dist/tests/snapshotDiffContract.test.d.ts.map +0 -1
  99. package/dist/tests/snapshotDiffContract.test.js +0 -156
  100. package/dist/tests/snapshotDiffContract.test.js.map +0 -1
  101. package/dist/tests/snapshotPersistence.real.test.d.ts +0 -2
  102. package/dist/tests/snapshotPersistence.real.test.d.ts.map +0 -1
  103. package/dist/tests/snapshotPersistence.real.test.js +0 -310
  104. package/dist/tests/snapshotPersistence.real.test.js.map +0 -1
  105. package/dist/tests/webServerRuntime.test.d.ts +0 -2
  106. package/dist/tests/webServerRuntime.test.d.ts.map +0 -1
  107. package/dist/tests/webServerRuntime.test.js +0 -123
  108. package/dist/tests/webServerRuntime.test.js.map +0 -1
  109. package/dist/transport/createSnapshotToolRuntime.d.ts +0 -7
  110. package/dist/transport/createSnapshotToolRuntime.d.ts.map +0 -1
  111. package/dist/transport/createSnapshotToolRuntime.js +0 -40
  112. package/dist/transport/createSnapshotToolRuntime.js.map +0 -1
  113. package/dist/transport/httpSnapshotServer.d.ts +0 -11
  114. package/dist/transport/httpSnapshotServer.d.ts.map +0 -1
  115. package/dist/transport/httpSnapshotServer.js +0 -216
  116. package/dist/transport/httpSnapshotServer.js.map +0 -1
  117. package/dist/transport/mcpToolRouter.d.ts +0 -81
  118. package/dist/transport/mcpToolRouter.d.ts.map +0 -1
  119. package/dist/transport/mcpToolRouter.js +0 -416
  120. package/dist/transport/mcpToolRouter.js.map +0 -1
  121. package/dist/transport/webServerRuntime.d.ts +0 -17
  122. package/dist/transport/webServerRuntime.d.ts.map +0 -1
  123. package/dist/transport/webServerRuntime.js +0 -73
  124. package/dist/transport/webServerRuntime.js.map +0 -1
  125. package/dist/web/dashboard.css +0 -411
  126. package/dist/web/dashboard.html +0 -141
  127. package/dist/web/dashboard.js +0 -540
@@ -1,540 +0,0 @@
1
- const API_BASE = '/api/snapshots';
2
- const HISTORY_LIMIT = 50;
3
-
4
- const elements = {
5
- alert: document.getElementById('status-alert'),
6
- sourceCaption: document.getElementById('source-caption'),
7
- refreshButton: document.getElementById('refresh-snapshots-button'),
8
- captureButton: document.getElementById('capture-button'),
9
- compareForm: document.getElementById('compare-form'),
10
- baselineSelect: document.getElementById('baseline-select'),
11
- targetSelect: document.getElementById('target-select'),
12
- snapshotList: document.getElementById('snapshot-list'),
13
- metricTotal: document.getElementById('metric-total'),
14
- metricBreaking: document.getElementById('metric-breaking'),
15
- metricNonBreaking: document.getElementById('metric-non-breaking'),
16
- metricInfo: document.getElementById('metric-info'),
17
- diffSummaryLine: document.getElementById('diff-summary-line'),
18
- changeTableBody: document.getElementById('change-table-body'),
19
- historyCaption: document.getElementById('history-caption'),
20
- historyFilterForm: document.getElementById('history-filter-form'),
21
- historyPathInput: document.getElementById('history-path-input'),
22
- historyMethodSelect: document.getElementById('history-method-select'),
23
- historyClearButton: document.getElementById('history-clear-button'),
24
- historyList: document.getElementById('history-list'),
25
- detailHeading: document.getElementById('detail-heading'),
26
- detailEvents: document.getElementById('detail-events'),
27
- detailBefore: document.getElementById('detail-before'),
28
- detailAfter: document.getElementById('detail-after'),
29
- };
30
-
31
- if (
32
- !elements.alert ||
33
- !elements.sourceCaption ||
34
- !elements.refreshButton ||
35
- !elements.captureButton ||
36
- !elements.compareForm ||
37
- !elements.baselineSelect ||
38
- !elements.targetSelect ||
39
- !elements.snapshotList ||
40
- !elements.metricTotal ||
41
- !elements.metricBreaking ||
42
- !elements.metricNonBreaking ||
43
- !elements.metricInfo ||
44
- !elements.diffSummaryLine ||
45
- !elements.changeTableBody ||
46
- !elements.historyCaption ||
47
- !elements.historyFilterForm ||
48
- !elements.historyPathInput ||
49
- !elements.historyMethodSelect ||
50
- !elements.historyClearButton ||
51
- !elements.historyList ||
52
- !elements.detailHeading ||
53
- !elements.detailEvents ||
54
- !elements.detailBefore ||
55
- !elements.detailAfter
56
- ) {
57
- throw new Error('Dashboard markup is missing required elements.');
58
- }
59
-
60
- const state = {
61
- projectKey: '',
62
- sourceUrl: '',
63
- snapshots: [],
64
- diffResult: null,
65
- historyEvents: [],
66
- historyFilter: {
67
- path: null,
68
- method: null,
69
- },
70
- };
71
-
72
- function formatTimestamp(isoDate) {
73
- const parsed = new Date(isoDate);
74
- return Number.isNaN(parsed.getTime()) ? isoDate : parsed.toLocaleString();
75
- }
76
-
77
- function setAlert(message, kind = 'info') {
78
- if (!message) {
79
- elements.alert.hidden = true;
80
- elements.alert.textContent = '';
81
- elements.alert.classList.remove('error');
82
- return;
83
- }
84
-
85
- elements.alert.hidden = false;
86
- elements.alert.textContent = message;
87
- elements.alert.classList.toggle('error', kind === 'error');
88
- }
89
-
90
- async function requestJson(path, options) {
91
- const response = await fetch(path, options);
92
- let payload = null;
93
- try {
94
- payload = await response.json();
95
- } catch {
96
- payload = null;
97
- }
98
-
99
- if (!response.ok) {
100
- const errorMessage =
101
- payload && typeof payload.error === 'string' ? payload.error : `Request failed: ${response.status}`;
102
- throw new Error(errorMessage);
103
- }
104
-
105
- return payload;
106
- }
107
-
108
- function renderSnapshotSelects() {
109
- const previousBaseline = elements.baselineSelect.value;
110
- const previousTarget = elements.targetSelect.value;
111
- const options = state.snapshots
112
- .map(snapshot => `<option value="${snapshot.id}">${formatTimestamp(snapshot.capturedAt)} · ${snapshot.id}</option>`)
113
- .join('');
114
-
115
- elements.baselineSelect.innerHTML = options;
116
- elements.targetSelect.innerHTML = options;
117
-
118
- const hasPreviousBaseline = state.snapshots.some(snapshot => snapshot.id === previousBaseline);
119
- const hasPreviousTarget = state.snapshots.some(snapshot => snapshot.id === previousTarget);
120
-
121
- if (hasPreviousBaseline) {
122
- elements.baselineSelect.value = previousBaseline;
123
- }
124
- if (hasPreviousTarget) {
125
- elements.targetSelect.value = previousTarget;
126
- }
127
-
128
- if (!elements.baselineSelect.value && state.snapshots.length >= 2) {
129
- elements.baselineSelect.value = state.snapshots[1].id;
130
- }
131
-
132
- if (!elements.targetSelect.value && state.snapshots.length >= 1) {
133
- elements.targetSelect.value = state.snapshots[0].id;
134
- }
135
-
136
- if (elements.baselineSelect.value === elements.targetSelect.value && state.snapshots.length >= 2) {
137
- elements.baselineSelect.value = state.snapshots[1].id;
138
- elements.targetSelect.value = state.snapshots[0].id;
139
- }
140
- }
141
-
142
- function renderSnapshotList() {
143
- if (state.snapshots.length === 0) {
144
- elements.snapshotList.innerHTML = '<p class="caption">No snapshots captured yet.</p>';
145
- return;
146
- }
147
-
148
- const list = document.createElement('div');
149
- list.className = 'snapshot-list';
150
-
151
- for (const snapshot of state.snapshots) {
152
- const button = document.createElement('button');
153
- button.type = 'button';
154
- button.className = 'snapshot-item';
155
- button.innerHTML = `
156
- <time datetime="${snapshot.capturedAt}">${formatTimestamp(snapshot.capturedAt)}</time>
157
- <strong>${snapshot.id}</strong>
158
- `;
159
- button.addEventListener('click', () => {
160
- elements.targetSelect.value = snapshot.id;
161
- if (elements.baselineSelect.value === snapshot.id && state.snapshots.length > 1) {
162
- const fallback = state.snapshots.find(item => item.id !== snapshot.id);
163
- if (fallback) {
164
- elements.baselineSelect.value = fallback.id;
165
- }
166
- }
167
- setAlert(`Target snapshot selected: ${snapshot.id}`);
168
- });
169
- list.appendChild(button);
170
- }
171
-
172
- elements.snapshotList.innerHTML = '';
173
- elements.snapshotList.appendChild(list);
174
- }
175
-
176
- function renderChangeHistory() {
177
- elements.historyPathInput.value = state.historyFilter.path ?? '';
178
- elements.historyMethodSelect.value = state.historyFilter.method ?? '';
179
-
180
- if (state.historyFilter.path && state.historyFilter.method) {
181
- elements.historyCaption.textContent = `Timeline for ${state.historyFilter.method.toUpperCase()} ${state.historyFilter.path}`;
182
- } else {
183
- elements.historyCaption.textContent = 'Latest endpoint changes.';
184
- }
185
-
186
- if (state.historyEvents.length === 0) {
187
- elements.historyList.innerHTML = '<p class="caption">No change history events.</p>';
188
- return;
189
- }
190
-
191
- const list = document.createElement('div');
192
- list.className = 'history-list';
193
-
194
- for (const event of state.historyEvents) {
195
- const button = document.createElement('button');
196
- button.type = 'button';
197
- button.className = 'history-item';
198
- button.innerHTML = `
199
- <div class="history-item-top">
200
- <strong>${event.method.toUpperCase()} ${event.path}</strong>
201
- <span class="${classificationClassName(event.classification)}">${event.classification}</span>
202
- </div>
203
- <div class="history-item-meta">
204
- <span>${event.changeReason}</span>
205
- <time datetime="${event.changedAt}">${formatTimestamp(event.changedAt)}</time>
206
- </div>
207
- <div class="caption">${event.baselineSnapshotId} -> ${event.targetSnapshotId}</div>
208
- `;
209
- button.addEventListener('click', () => {
210
- setLoading(true);
211
- void fetchSnapshots()
212
- .then(() => {
213
- elements.baselineSelect.value = event.baselineSnapshotId;
214
- elements.targetSelect.value = event.targetSnapshotId;
215
- return compareSnapshots();
216
- })
217
- .then(() => loadEndpointDetail(event.path, event.method))
218
- .then(() => {
219
- setAlert(`History event loaded: ${event.method.toUpperCase()} ${event.path}`);
220
- })
221
- .catch(error => {
222
- const message = error instanceof Error ? error.message : String(error);
223
- setAlert(message, 'error');
224
- })
225
- .finally(() => {
226
- setLoading(false);
227
- });
228
- });
229
- list.appendChild(button);
230
- }
231
-
232
- elements.historyList.innerHTML = '';
233
- elements.historyList.appendChild(list);
234
- }
235
-
236
- function classificationClassName(value) {
237
- if (value === 'breaking') {
238
- return 'classification breaking';
239
- }
240
- if (value === 'non-breaking') {
241
- return 'classification non-breaking';
242
- }
243
- return 'classification info';
244
- }
245
-
246
- function resetDetail() {
247
- elements.detailHeading.textContent = 'Pick one endpoint from the diff table.';
248
- elements.detailEvents.innerHTML = '';
249
- elements.detailBefore.textContent = 'No selection';
250
- elements.detailAfter.textContent = 'No selection';
251
- }
252
-
253
- function renderDiffSummary() {
254
- if (!state.diffResult) {
255
- elements.metricTotal.textContent = '-';
256
- elements.metricBreaking.textContent = '-';
257
- elements.metricNonBreaking.textContent = '-';
258
- elements.metricInfo.textContent = '-';
259
- elements.diffSummaryLine.textContent = 'No diff selected.';
260
- elements.changeTableBody.innerHTML = '<tr><td colspan="3" class="caption">No change rows.</td></tr>';
261
- resetDetail();
262
- return;
263
- }
264
-
265
- const { summary, counts, changes, warnings } = state.diffResult;
266
- elements.metricTotal.textContent = String(counts.total);
267
- elements.metricBreaking.textContent = String(counts.breaking);
268
- elements.metricNonBreaking.textContent = String(counts.nonBreaking);
269
- elements.metricInfo.textContent = String(counts.info);
270
- elements.diffSummaryLine.textContent = `Compared ${summary.baselineSnapshotId} -> ${summary.targetSnapshotId} at ${formatTimestamp(
271
- summary.comparedAt
272
- )}`;
273
-
274
- if (warnings.length > 0) {
275
- setAlert(warnings.map(warning => warning.message).join(' | '));
276
- }
277
-
278
- if (changes.length === 0) {
279
- elements.changeTableBody.innerHTML = '<tr><td colspan="3" class="caption">No endpoint changes.</td></tr>';
280
- return;
281
- }
282
-
283
- elements.changeTableBody.innerHTML = '';
284
- for (const change of changes) {
285
- const row = document.createElement('tr');
286
- const endpointCell = document.createElement('td');
287
- const endpointButton = document.createElement('button');
288
- endpointButton.type = 'button';
289
- endpointButton.className = 'button secondary';
290
- endpointButton.textContent = `${change.method.toUpperCase()} ${change.url}`;
291
- endpointButton.addEventListener('click', () => {
292
- void loadEndpointDetail(change.url, change.method);
293
- });
294
- endpointCell.appendChild(endpointButton);
295
-
296
- const classificationCell = document.createElement('td');
297
- classificationCell.className = classificationClassName(change.classification);
298
- classificationCell.textContent = change.classification;
299
-
300
- const reasonCell = document.createElement('td');
301
- reasonCell.textContent = change.changeReason;
302
-
303
- row.appendChild(endpointCell);
304
- row.appendChild(classificationCell);
305
- row.appendChild(reasonCell);
306
- elements.changeTableBody.appendChild(row);
307
- }
308
- }
309
-
310
- function renderEndpointDetail(detail) {
311
- elements.detailHeading.textContent = `${detail.method.toUpperCase()} ${detail.path} · ${detail.changeReason ?? 'no_change'}`;
312
- elements.detailEvents.innerHTML = '';
313
-
314
- if (detail.details.length === 0) {
315
- const empty = document.createElement('p');
316
- empty.className = 'caption';
317
- empty.textContent = 'No detail events.';
318
- elements.detailEvents.appendChild(empty);
319
- } else {
320
- for (const event of detail.details) {
321
- const item = document.createElement('div');
322
- item.className = 'event-row';
323
- item.innerHTML = `
324
- <strong class="${classificationClassName(event.classification)}">${event.classification}</strong>
325
- <div>${event.message}</div>
326
- `;
327
- elements.detailEvents.appendChild(item);
328
- }
329
- }
330
-
331
- elements.detailBefore.textContent = JSON.stringify(detail.before, null, 2) ?? 'null';
332
- elements.detailAfter.textContent = JSON.stringify(detail.after, null, 2) ?? 'null';
333
- }
334
-
335
- async function fetchSnapshots() {
336
- const result = await requestJson(`${API_BASE}?limit=200`);
337
- state.projectKey = result.projectKey;
338
- state.sourceUrl = result.sourceUrl;
339
- state.snapshots = result.snapshots;
340
- elements.sourceCaption.textContent = `${result.projectKey} · ${result.sourceUrl}`;
341
- renderSnapshotSelects();
342
- renderSnapshotList();
343
- }
344
-
345
- function normalizeHistoryFilter(filter = {}) {
346
- const path = typeof filter.path === 'string' && filter.path.trim().length > 0 ? filter.path.trim() : undefined;
347
- const method =
348
- typeof filter.method === 'string' && filter.method.trim().length > 0
349
- ? filter.method.trim().toLowerCase()
350
- : undefined;
351
-
352
- if (!path && !method) {
353
- return {};
354
- }
355
-
356
- return {
357
- path,
358
- method,
359
- };
360
- }
361
-
362
- function readHistoryFilterInputs() {
363
- return normalizeHistoryFilter({
364
- path: elements.historyPathInput.value,
365
- method: elements.historyMethodSelect.value,
366
- });
367
- }
368
-
369
- async function loadChangeHistory(filter = {}) {
370
- const normalizedFilter = normalizeHistoryFilter(filter);
371
- const params = new URLSearchParams({
372
- limit: String(HISTORY_LIMIT),
373
- });
374
-
375
- if (normalizedFilter.path) {
376
- params.set('path', normalizedFilter.path);
377
- }
378
- if (normalizedFilter.method) {
379
- params.set('method', normalizedFilter.method);
380
- }
381
-
382
- const result = await requestJson(`${API_BASE}/change-history?${params.toString()}`);
383
- state.historyEvents = result.events;
384
- state.historyFilter = result.filter;
385
- renderChangeHistory();
386
- }
387
-
388
- async function compareSnapshots() {
389
- const baselineSnapshotId = elements.baselineSelect.value;
390
- const targetSnapshotId = elements.targetSelect.value;
391
-
392
- if (!baselineSnapshotId || !targetSnapshotId) {
393
- setAlert('Choose baseline and target snapshot first.', 'error');
394
- return;
395
- }
396
-
397
- if (baselineSnapshotId === targetSnapshotId) {
398
- setAlert('Baseline and target must be different.', 'error');
399
- return;
400
- }
401
-
402
- const diffResult = await requestJson(
403
- `${API_BASE}/diff?baselineSnapshotId=${encodeURIComponent(baselineSnapshotId)}&targetSnapshotId=${encodeURIComponent(
404
- targetSnapshotId
405
- )}`
406
- );
407
-
408
- state.diffResult = diffResult;
409
- renderDiffSummary();
410
- }
411
-
412
- async function loadEndpointDetail(endpointPath, endpointMethod) {
413
- const baselineSnapshotId = elements.baselineSelect.value;
414
- const targetSnapshotId = elements.targetSelect.value;
415
- const detail = await requestJson(
416
- `${API_BASE}/endpoint-detail?baselineSnapshotId=${encodeURIComponent(
417
- baselineSnapshotId
418
- )}&targetSnapshotId=${encodeURIComponent(targetSnapshotId)}&path=${encodeURIComponent(
419
- endpointPath
420
- )}&method=${encodeURIComponent(endpointMethod)}`
421
- );
422
- await loadChangeHistory({
423
- path: endpointPath,
424
- method: endpointMethod,
425
- });
426
- renderEndpointDetail(detail);
427
- }
428
-
429
- async function captureSnapshot() {
430
- const capturedDiff = await requestJson(`${API_BASE}/capture`, { method: 'POST' });
431
- state.diffResult = capturedDiff;
432
- await fetchSnapshots();
433
- await loadChangeHistory({
434
- path: state.historyFilter.path ?? undefined,
435
- method: state.historyFilter.method ?? undefined,
436
- });
437
- if (capturedDiff.summary?.baselineSnapshotId) {
438
- elements.baselineSelect.value = capturedDiff.summary.baselineSnapshotId;
439
- }
440
- if (capturedDiff.summary?.targetSnapshotId) {
441
- elements.targetSelect.value = capturedDiff.summary.targetSnapshotId;
442
- }
443
- renderDiffSummary();
444
- }
445
-
446
- function setLoading(isLoading) {
447
- elements.refreshButton.disabled = isLoading;
448
- elements.captureButton.disabled = isLoading;
449
- }
450
-
451
- async function loadInitialData() {
452
- setLoading(true);
453
- try {
454
- await fetchSnapshots();
455
- await loadChangeHistory();
456
- if (state.snapshots.length >= 2) {
457
- await compareSnapshots();
458
- } else {
459
- renderDiffSummary();
460
- }
461
- setAlert('Dashboard loaded.');
462
- } catch (error) {
463
- const message = error instanceof Error ? error.message : String(error);
464
- setAlert(message, 'error');
465
- renderDiffSummary();
466
- } finally {
467
- setLoading(false);
468
- }
469
- }
470
-
471
- elements.refreshButton.addEventListener('click', () => {
472
- void loadInitialData();
473
- });
474
-
475
- elements.captureButton.addEventListener('click', () => {
476
- setLoading(true);
477
- void captureSnapshot()
478
- .then(() => {
479
- setAlert('Snapshot captured successfully.');
480
- })
481
- .catch(error => {
482
- const message = error instanceof Error ? error.message : String(error);
483
- setAlert(message, 'error');
484
- })
485
- .finally(() => {
486
- setLoading(false);
487
- });
488
- });
489
-
490
- elements.compareForm.addEventListener('submit', event => {
491
- event.preventDefault();
492
- setLoading(true);
493
- void compareSnapshots()
494
- .then(() => {
495
- setAlert('Diff updated.');
496
- resetDetail();
497
- })
498
- .catch(error => {
499
- const message = error instanceof Error ? error.message : String(error);
500
- setAlert(message, 'error');
501
- })
502
- .finally(() => {
503
- setLoading(false);
504
- });
505
- });
506
-
507
- elements.historyFilterForm.addEventListener('submit', event => {
508
- event.preventDefault();
509
- setLoading(true);
510
- void loadChangeHistory(readHistoryFilterInputs())
511
- .then(() => {
512
- setAlert('Change history updated.');
513
- })
514
- .catch(error => {
515
- const message = error instanceof Error ? error.message : String(error);
516
- setAlert(message, 'error');
517
- })
518
- .finally(() => {
519
- setLoading(false);
520
- });
521
- });
522
-
523
- elements.historyClearButton.addEventListener('click', () => {
524
- elements.historyPathInput.value = '';
525
- elements.historyMethodSelect.value = '';
526
- setLoading(true);
527
- void loadChangeHistory()
528
- .then(() => {
529
- setAlert('Change history filter cleared.');
530
- })
531
- .catch(error => {
532
- const message = error instanceof Error ? error.message : String(error);
533
- setAlert(message, 'error');
534
- })
535
- .finally(() => {
536
- setLoading(false);
537
- });
538
- });
539
-
540
- void loadInitialData();