@svelte-vitals/vite 0.12.1 → 0.13.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.
- package/dist/index.js +582 -24
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -175,30 +175,551 @@ Scanned ${components.length} component(s) under src/ for Correctness/Security/Ar
|
|
|
175
175
|
};
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
-
// src/ui/
|
|
179
|
-
import {
|
|
180
|
-
|
|
178
|
+
// src/ui/snapshot.ts
|
|
179
|
+
import { buildJsonReport, safeHref } from "@svelte-vitals/core";
|
|
180
|
+
function sanitizeDocsUrl(issue) {
|
|
181
|
+
if (issue.docsUrl === void 0) return issue;
|
|
182
|
+
if (safeHref(issue.docsUrl) !== null) return issue;
|
|
183
|
+
return { ...issue, docsUrl: void 0 };
|
|
184
|
+
}
|
|
185
|
+
function sanitizeReport(report) {
|
|
186
|
+
return {
|
|
187
|
+
...report,
|
|
188
|
+
routes: report.routes.map((route) => ({ ...route, issues: route.issues.map(sanitizeDocsUrl) })),
|
|
189
|
+
siteIssues: report.siteIssues.map(sanitizeDocsUrl)
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
function buildSnapshot(store, config, meta) {
|
|
193
|
+
return {
|
|
194
|
+
report: sanitizeReport(buildJsonReport(store.snapshot(), config, meta)),
|
|
195
|
+
badges: store.badges(),
|
|
196
|
+
analyzing: store.isAnalyzing(),
|
|
197
|
+
sequence: store.sequence(),
|
|
198
|
+
meta
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// src/ui/dashboard-style.ts
|
|
203
|
+
var DASHBOARD_STYLE = `
|
|
204
|
+
:root{--ground:#f6f7f9;--panel:#fff;--ink:#0c1322;--muted:#5a6472;--faint:#8c95a3;--line:#e4e7ec;--line-strong:#d3d8e0;--accent:#ff3e00;--good:#2fa968;--warn:#e8a317;--poor:#e5484d;--code-bg:#0e1525;--code-ink:#e7ecf4;--active-bg:#0c1322;--active-ink:#fff;--mono:ui-monospace,"SF Mono","JetBrains Mono",Menlo,Consolas,monospace;--sans:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif}
|
|
205
|
+
:root[data-theme="dark"]{--ground:#0b0e14;--panel:#12161f;--ink:#e7ecf4;--muted:#9aa4b2;--faint:#6b7484;--line:#232838;--line-strong:#2d3345;--code-bg:#05070c;--code-ink:#e7ecf4;--active-bg:#e7ecf4;--active-ink:#0b0e14}
|
|
206
|
+
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]){--ground:#0b0e14;--panel:#12161f;--ink:#e7ecf4;--muted:#9aa4b2;--faint:#6b7484;--line:#232838;--line-strong:#2d3345;--code-bg:#05070c;--code-ink:#e7ecf4;--active-bg:#e7ecf4;--active-ink:#0b0e14}}
|
|
207
|
+
*{box-sizing:border-box}
|
|
208
|
+
html,body{margin:0;height:100%}
|
|
209
|
+
body{background:var(--ground);color:var(--ink);font-family:var(--sans);line-height:1.5;-webkit-font-smoothing:antialiased}
|
|
210
|
+
.dv-app{display:grid;grid-template-rows:auto 1fr;grid-template-columns:280px 1fr;grid-template-areas:"top top" "side main";height:100vh}
|
|
211
|
+
.dv-topbar{grid-area:top;border-bottom:1px solid var(--line);background:var(--panel)}
|
|
212
|
+
.dv-topbar-inner{display:flex;align-items:center;justify-content:space-between;gap:16px;flex-wrap:wrap;padding:12px 20px}
|
|
213
|
+
.dv-brand{display:flex;align-items:baseline;gap:8px;font-weight:700;font-size:16px}
|
|
214
|
+
.dv-brand .bolt{color:var(--accent)}
|
|
215
|
+
.dv-meta{font-family:var(--mono);font-size:12px;color:var(--muted);display:flex;gap:12px;flex-wrap:wrap}
|
|
216
|
+
.dv-status{display:flex;align-items:center;gap:10px}
|
|
217
|
+
.dv-analyzing{font-size:12px;color:var(--accent);font-weight:600}
|
|
218
|
+
.dv-conn{width:8px;height:8px;border-radius:50%;background:var(--faint);display:inline-block}
|
|
219
|
+
.dv-conn-connected{background:var(--good)}
|
|
220
|
+
.dv-conn-reconnecting{background:var(--warn)}
|
|
221
|
+
.dv-menu-toggle{display:none;border:1px solid var(--line-strong);background:var(--panel);color:var(--ink);border-radius:8px;width:28px;height:28px;cursor:pointer}
|
|
222
|
+
.dv-theme-toggle{border:1px solid var(--line-strong);background:var(--panel);color:var(--ink);border-radius:999px;width:28px;height:28px;cursor:pointer}
|
|
223
|
+
.dv-theme-toggle:focus-visible,.dv-menu-toggle:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
|
|
224
|
+
.dv-sidebar{grid-area:side;border-right:1px solid var(--line);background:var(--panel);overflow-y:auto}
|
|
225
|
+
.dv-sidebar-inner{display:flex;flex-direction:column;gap:10px;padding:14px}
|
|
226
|
+
.dv-search{font:inherit;font-size:13px;padding:7px 10px;border:1px solid var(--line-strong);border-radius:8px;background:var(--ground);color:var(--ink)}
|
|
227
|
+
.dv-sort{font:inherit;font-size:12.5px;padding:6px 8px;border:1px solid var(--line-strong);border-radius:8px;background:var(--ground);color:var(--ink)}
|
|
228
|
+
.dv-nav{display:flex;flex-direction:column;gap:2px}
|
|
229
|
+
.dv-nav-item{display:flex;flex-direction:column;gap:4px;padding:8px 10px;border-radius:8px;cursor:pointer}
|
|
230
|
+
.dv-nav-item:hover{background:var(--ground)}
|
|
231
|
+
.dv-nav-item.active{background:var(--active-bg);color:var(--active-ink)}
|
|
232
|
+
.dv-nav-item:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
|
|
233
|
+
.dv-nav-label{font-family:var(--mono);font-size:13px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
234
|
+
.dv-nav-meta{display:flex;align-items:center;gap:8px;font-size:11.5px;color:var(--muted)}
|
|
235
|
+
.dv-nav-item.active .dv-nav-meta{color:inherit}
|
|
236
|
+
.dv-nav-score{font-family:var(--mono);font-weight:700}
|
|
237
|
+
.dv-badge{font-size:9.5px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;padding:1px 6px;border-radius:999px}
|
|
238
|
+
.dv-badge-measured{background:rgba(47,169,104,.16);color:var(--good)}
|
|
239
|
+
.dv-badge-static{background:rgba(140,149,163,.2);color:var(--muted)}
|
|
240
|
+
.dv-detail{grid-area:main;overflow-y:auto;padding:24px 28px 80px}
|
|
241
|
+
.dv-gauge{position:relative;width:132px;height:132px;margin-bottom:20px}
|
|
242
|
+
.dv-gauge svg{position:absolute;inset:0;transform:rotate(-90deg)}
|
|
243
|
+
.dv-gauge-track{stroke:var(--line)}
|
|
244
|
+
.dv-gauge-num{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center}
|
|
245
|
+
.dv-gauge-num strong{font-family:var(--mono);font-size:36px;font-weight:600}
|
|
246
|
+
.dv-gauge-num span{font-size:10px;text-transform:uppercase;letter-spacing:.14em;color:var(--muted)}
|
|
247
|
+
.dv-cats{display:flex;gap:22px;flex-wrap:wrap;margin-bottom:20px}
|
|
248
|
+
.dv-cat{min-width:180px;flex:1}
|
|
249
|
+
.dv-cat-top{display:flex;justify-content:space-between;font-size:13px;margin-bottom:6px}
|
|
250
|
+
.dv-bar{height:7px;border-radius:999px;background:var(--line);overflow:hidden}
|
|
251
|
+
.dv-bar>i{display:block;height:100%;border-radius:999px}
|
|
252
|
+
.dv-filters{display:flex;gap:8px;flex-wrap:wrap;margin:16px 0}
|
|
253
|
+
.dv-chip{font:inherit;font-size:12.5px;font-weight:600;cursor:pointer;background:var(--panel);border:1px solid var(--line-strong);color:var(--muted);padding:5px 12px;border-radius:999px}
|
|
254
|
+
.dv-chip[aria-pressed="true"]{background:var(--active-bg);border-color:var(--active-bg);color:var(--active-ink)}
|
|
255
|
+
.dv-chip:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
|
|
256
|
+
.dv-section h2{font-size:12px;text-transform:uppercase;letter-spacing:.12em;color:var(--muted);margin:24px 0 12px}
|
|
257
|
+
.dv-detail-header{display:flex;align-items:center;gap:12px;margin-bottom:14px;flex-wrap:wrap}
|
|
258
|
+
.dv-route-path{font-family:var(--mono);font-size:16px;font-weight:600}
|
|
259
|
+
.dv-score-chip{font-family:var(--mono);font-weight:700}
|
|
260
|
+
.dv-finding{background:var(--panel);border:1px solid var(--line);border-left-width:3px;border-radius:10px;padding:16px 18px;margin:0 0 12px}
|
|
261
|
+
.dv-finding-critical{border-left-color:var(--poor)}
|
|
262
|
+
.dv-finding-warning{border-left-color:var(--warn)}
|
|
263
|
+
.dv-finding-info{border-left-color:var(--faint)}
|
|
264
|
+
.dv-f-head{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
|
|
265
|
+
.dv-ruleid{font-family:var(--mono);font-size:12px;font-weight:600;background:var(--ground);padding:2px 8px;border-radius:6px}
|
|
266
|
+
.dv-f-title{font-weight:650;font-size:15px}
|
|
267
|
+
.dv-sev-tag{margin-left:auto;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.08em}
|
|
268
|
+
.dv-sev-critical{color:var(--poor)}
|
|
269
|
+
.dv-sev-warning{color:var(--warn)}
|
|
270
|
+
.dv-sev-info{color:var(--faint)}
|
|
271
|
+
.dv-f-loc{font-family:var(--mono);font-size:12.5px;color:var(--muted);margin:8px 0 0}
|
|
272
|
+
.dv-f-rec{font-size:14px;margin:10px 0 0}
|
|
273
|
+
.dv-fix{margin:12px 0 0;background:var(--code-bg);border-radius:8px;overflow:hidden}
|
|
274
|
+
.dv-fix-label{font-family:var(--mono);font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:#8da0bd;padding:8px 14px 0}
|
|
275
|
+
.dv-fix pre{margin:0;padding:8px 14px 14px;overflow-x:auto}
|
|
276
|
+
.dv-fix code{font-family:var(--mono);font-size:12.5px;color:var(--code-ink);line-height:1.65;white-space:pre}
|
|
277
|
+
.tok-kw{color:#ff7ab8}
|
|
278
|
+
.tok-str{color:#9ece6a}
|
|
279
|
+
.tok-num{color:#ff9e64}
|
|
280
|
+
.tok-cm{color:#6b7280;font-style:italic}
|
|
281
|
+
.tok-id{color:var(--code-ink)}
|
|
282
|
+
.tok-pn{color:#8da0bd}
|
|
283
|
+
.dv-f-link{display:inline-block;margin-top:12px;font-size:13px;font-weight:600;color:var(--accent);text-decoration:none}
|
|
284
|
+
.dv-f-link:hover{text-decoration:underline}
|
|
285
|
+
.dv-empty{color:var(--muted);font-size:13px}
|
|
286
|
+
@media (max-width:640px){.dv-app{grid-template-columns:1fr;grid-template-areas:"top" "main"}.dv-menu-toggle{display:inline-flex}.dv-sidebar{position:fixed;inset:0 20% 0 0;transform:translateX(-100%);transition:transform .2s ease;z-index:10}.dv-sidebar.open{transform:translateX(0)}}
|
|
287
|
+
@media (prefers-reduced-motion:reduce){*{transition:none!important}}
|
|
288
|
+
`;
|
|
289
|
+
|
|
290
|
+
// src/ui/dashboard-script.ts
|
|
291
|
+
var DASHBOARD_SCRIPT = `
|
|
181
292
|
(function(){
|
|
182
|
-
var
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
293
|
+
var BAND_COLOR = { good: '#2fa968', warn: '#e8a317', poor: '#e5484d' };
|
|
294
|
+
function scoreBand(score) { return score >= 90 ? 'good' : score >= 50 ? 'warn' : 'poor'; }
|
|
295
|
+
|
|
296
|
+
function h(tag, attrs, kids) {
|
|
297
|
+
var n = document.createElement(tag);
|
|
298
|
+
if (attrs) {
|
|
299
|
+
for (var k in attrs) {
|
|
300
|
+
if (!Object.prototype.hasOwnProperty.call(attrs, k)) continue;
|
|
301
|
+
var v = attrs[k];
|
|
302
|
+
if (v === undefined || v === null || v === false) continue;
|
|
303
|
+
if (k === 'class') n.className = v;
|
|
304
|
+
else if (k === 'text') n.textContent = v;
|
|
305
|
+
else if (k.indexOf('on') === 0 && typeof v === 'function') n.addEventListener(k.slice(2), v);
|
|
306
|
+
else n.setAttribute(k, v === true ? '' : String(v));
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
(kids || []).forEach(function (c) {
|
|
310
|
+
if (c === undefined || c === null || c === false) return;
|
|
311
|
+
n.appendChild(typeof c === 'string' ? document.createTextNode(c) : c);
|
|
312
|
+
});
|
|
313
|
+
return n;
|
|
314
|
+
}
|
|
315
|
+
function clear(n) { while (n.firstChild) n.removeChild(n.firstChild); }
|
|
316
|
+
function mount(id, node) { var el = document.getElementById(id); clear(el); el.appendChild(node); }
|
|
317
|
+
|
|
318
|
+
var HL_KEYWORDS = ['import','export','from','const','let','var','function','return','if','else','for','while','class','new','await','async','default','type','interface','extends','implements','this','typeof','instanceof','of','in','true','false','null','undefined'];
|
|
319
|
+
var HL_LANGS = { js: 1, javascript: 1, ts: 1, typescript: 1, svelte: 1, html: 1, css: 1 };
|
|
320
|
+
|
|
321
|
+
function highlightTokens(code) {
|
|
322
|
+
var tokens = [];
|
|
323
|
+
var i = 0;
|
|
324
|
+
var n = code.length;
|
|
325
|
+
var reIdent = /[A-Za-z_$][A-Za-z0-9_$]*/y;
|
|
326
|
+
var reNum = /\\d+(\\.\\d+)?/y;
|
|
327
|
+
while (i < n) {
|
|
328
|
+
var ch = code[i];
|
|
329
|
+
if (ch === '/' && code[i + 1] === '/') {
|
|
330
|
+
var end = code.indexOf('\\n', i);
|
|
331
|
+
if (end === -1) end = n;
|
|
332
|
+
tokens.push({ text: code.slice(i, end), cls: 'cm' });
|
|
333
|
+
i = end;
|
|
334
|
+
continue;
|
|
335
|
+
}
|
|
336
|
+
if (ch === '/' && code[i + 1] === '*') {
|
|
337
|
+
var end2 = code.indexOf('*/', i + 2);
|
|
338
|
+
end2 = end2 === -1 ? n : end2 + 2;
|
|
339
|
+
tokens.push({ text: code.slice(i, end2), cls: 'cm' });
|
|
340
|
+
i = end2;
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
if (ch === '"' || ch === "'" || ch === '\`') {
|
|
344
|
+
var quote = ch;
|
|
345
|
+
var j = i + 1;
|
|
346
|
+
while (j < n && code[j] !== quote) {
|
|
347
|
+
if (code[j] === '\\\\') j++;
|
|
348
|
+
j++;
|
|
349
|
+
}
|
|
350
|
+
j = Math.min(j + 1, n);
|
|
351
|
+
tokens.push({ text: code.slice(i, j), cls: 'str' });
|
|
352
|
+
i = j;
|
|
353
|
+
continue;
|
|
354
|
+
}
|
|
355
|
+
reIdent.lastIndex = i;
|
|
356
|
+
var mIdent = reIdent.exec(code);
|
|
357
|
+
if (mIdent && mIdent.index === i) {
|
|
358
|
+
var word = mIdent[0];
|
|
359
|
+
tokens.push({ text: word, cls: HL_KEYWORDS.indexOf(word) !== -1 ? 'kw' : 'id' });
|
|
360
|
+
i += word.length;
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
reNum.lastIndex = i;
|
|
364
|
+
var mNum = reNum.exec(code);
|
|
365
|
+
if (mNum && mNum.index === i) {
|
|
366
|
+
tokens.push({ text: mNum[0], cls: 'num' });
|
|
367
|
+
i += mNum[0].length;
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
tokens.push({ text: ch, cls: 'pn' });
|
|
371
|
+
i += 1;
|
|
372
|
+
}
|
|
373
|
+
return tokens;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function renderFixSnippet(fix) {
|
|
377
|
+
var pre = h('pre', null, []);
|
|
378
|
+
var code = h('code', null, []);
|
|
379
|
+
var lang = (fix.lang || 'svelte').toLowerCase();
|
|
380
|
+
if (HL_LANGS[lang]) {
|
|
381
|
+
highlightTokens(fix.snippet).forEach(function (t) {
|
|
382
|
+
code.appendChild(h('span', { class: 'tok-' + t.cls, text: t.text }, []));
|
|
383
|
+
});
|
|
384
|
+
} else {
|
|
385
|
+
code.textContent = fix.snippet;
|
|
386
|
+
}
|
|
387
|
+
pre.appendChild(code);
|
|
388
|
+
return pre;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
var state = {
|
|
392
|
+
snapshot: null,
|
|
393
|
+
selected: 'overview',
|
|
394
|
+
search: '',
|
|
395
|
+
sort: 'score-asc',
|
|
396
|
+
filter: 'all',
|
|
397
|
+
theme: initialTheme(),
|
|
398
|
+
connection: 'connecting',
|
|
399
|
+
routeBySlug: {}
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
function initialTheme() {
|
|
403
|
+
try {
|
|
404
|
+
var stored = localStorage.getItem('svelte-vitals-theme');
|
|
405
|
+
if (stored === 'dark' || stored === 'light') return stored;
|
|
406
|
+
} catch (e) {}
|
|
407
|
+
return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
408
|
+
}
|
|
409
|
+
function applyTheme() { document.documentElement.setAttribute('data-theme', state.theme); }
|
|
410
|
+
function toggleTheme() {
|
|
411
|
+
state.theme = state.theme === 'dark' ? 'light' : 'dark';
|
|
412
|
+
try { localStorage.setItem('svelte-vitals-theme', state.theme); } catch (e) {}
|
|
413
|
+
applyTheme();
|
|
414
|
+
renderTopbar();
|
|
415
|
+
}
|
|
416
|
+
function toggleSidebar() {
|
|
417
|
+
var sb = document.getElementById('dv-sidebar');
|
|
418
|
+
if (sb) sb.classList.toggle('open');
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
function renderTopbar() {
|
|
422
|
+
var s = state.snapshot;
|
|
423
|
+
var findings = s.report.routes.reduce(function (n, r) { return n + r.issues.length; }, 0) + s.report.siteIssues.length;
|
|
424
|
+
var kids = [
|
|
425
|
+
h('button', { type: 'button', class: 'dv-menu-toggle', 'aria-label': 'Toggle route list', onclick: toggleSidebar, text: '\u2261' }, []),
|
|
426
|
+
h('div', { class: 'dv-brand' }, [h('span', { class: 'bolt', text: '\u21AF' }, []), document.createTextNode('svelte-vitals')]),
|
|
427
|
+
h('div', { class: 'dv-meta' }, [
|
|
428
|
+
h('span', { text: 'v' + s.meta.version }, []),
|
|
429
|
+
s.meta.coreVersion ? h('span', { title: '@svelte-vitals/core version', text: 'core v' + s.meta.coreVersion }, []) : null,
|
|
430
|
+
h('span', { text: s.report.routes.length + ' routes' }, []),
|
|
431
|
+
h('span', { text: findings + ' findings' }, [])
|
|
432
|
+
].filter(Boolean)),
|
|
433
|
+
h('div', { class: 'dv-status' }, [
|
|
434
|
+
s.analyzing ? h('span', { class: 'dv-analyzing', text: 'Analyzing\u2026' }, []) : null,
|
|
435
|
+
h('span', { class: 'dv-conn dv-conn-' + state.connection, title: state.connection }, []),
|
|
436
|
+
h('button', { type: 'button', class: 'dv-theme-toggle', 'aria-label': 'Toggle dark mode', onclick: toggleTheme, text: state.theme === 'dark' ? '\u2600' : '\u263E' }, [])
|
|
437
|
+
].filter(Boolean))
|
|
438
|
+
];
|
|
439
|
+
mount('dv-topbar', h('div', { class: 'dv-topbar-inner' }, kids));
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
function slugify(route) {
|
|
443
|
+
return 'route-' + route.replace(/[^a-zA-Z0-9]+/g, '-').replace(/^-|-$/g, '').toLowerCase();
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
function matchesSearch(route, q) {
|
|
447
|
+
if (!q) return true;
|
|
448
|
+
q = q.toLowerCase();
|
|
449
|
+
if (route.route.toLowerCase().indexOf(q) !== -1) return true;
|
|
450
|
+
return route.issues.some(function (iss) {
|
|
451
|
+
return (iss.id + ' ' + iss.title + ' ' + (iss.location || '')).toLowerCase().indexOf(q) !== -1;
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
function sortedRoutes() {
|
|
456
|
+
var s = state.snapshot;
|
|
457
|
+
var q = state.search.trim();
|
|
458
|
+
var list = s.report.routes.filter(function (r) { return matchesSearch(r, q); }).slice();
|
|
459
|
+
var sort = state.sort;
|
|
460
|
+
if (sort === 'score-asc') list.sort(function (a, b) { return a.score - b.score; });
|
|
461
|
+
else if (sort === 'score-desc') list.sort(function (a, b) { return b.score - a.score; });
|
|
462
|
+
else if (sort === 'alpha') list.sort(function (a, b) { return a.route.localeCompare(b.route); });
|
|
463
|
+
else if (sort === 'most-findings') list.sort(function (a, b) { return b.issues.length - a.issues.length; });
|
|
464
|
+
return list;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
function renderNavItem(label, key, route, active) {
|
|
468
|
+
var kids = [h('span', { class: 'dv-nav-label', text: label }, [])];
|
|
469
|
+
if (route) {
|
|
470
|
+
var band = scoreBand(route.score);
|
|
471
|
+
var crit = route.issues.filter(function (i) { return i.severity === 'critical'; }).length;
|
|
472
|
+
var warn = route.issues.filter(function (i) { return i.severity === 'warning'; }).length;
|
|
473
|
+
var info = route.issues.filter(function (i) { return i.severity === 'info'; }).length;
|
|
474
|
+
var summary = [];
|
|
475
|
+
if (crit) summary.push(crit + ' critical');
|
|
476
|
+
if (warn) summary.push(warn + ' warning' + (warn > 1 ? 's' : ''));
|
|
477
|
+
if (info) summary.push(info + ' info');
|
|
478
|
+
var badge = state.snapshot.badges[route.route];
|
|
479
|
+
kids.push(h('span', { class: 'dv-nav-meta' }, [
|
|
480
|
+
badge ? h('span', { class: 'dv-badge dv-badge-' + badge, text: badge }, []) : null,
|
|
481
|
+
h('span', { class: 'dv-nav-score', style: 'color:' + BAND_COLOR[band], text: String(route.score) }, []),
|
|
482
|
+
h('span', { class: 'dv-nav-sum', text: summary.length ? summary.join(' \xB7 ') : 'no issues' }, [])
|
|
483
|
+
].filter(Boolean)));
|
|
484
|
+
}
|
|
485
|
+
return h('div', {
|
|
486
|
+
class: 'dv-nav-item' + (active ? ' active' : ''),
|
|
487
|
+
role: 'option',
|
|
488
|
+
'aria-selected': active ? 'true' : 'false',
|
|
489
|
+
tabindex: '0',
|
|
490
|
+
onclick: function () { selectItem(key); },
|
|
491
|
+
onkeydown: function (e) { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); selectItem(key); } }
|
|
492
|
+
}, kids);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
function selectItem(key) {
|
|
496
|
+
state.selected = key;
|
|
497
|
+
location.hash = key === 'overview' ? 'overview' : 'route/' + slugify(key);
|
|
498
|
+
var sb = document.getElementById('dv-sidebar');
|
|
499
|
+
if (sb) sb.classList.remove('open');
|
|
500
|
+
renderSidebar();
|
|
501
|
+
renderDetail();
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
function renderSidebar() {
|
|
505
|
+
var s = state.snapshot;
|
|
506
|
+
state.routeBySlug = {};
|
|
507
|
+
|
|
508
|
+
// mount() clears and rebuilds the whole sidebar, including the <input> itself, so
|
|
509
|
+
// a naive re-render on every keystroke drops focus and cursor position \u2014 capture
|
|
510
|
+
// them before rebuilding and restore them on the freshly-created input afterward.
|
|
511
|
+
var prevSearch = document.querySelector('.dv-search');
|
|
512
|
+
var hadFocus = !!prevSearch && document.activeElement === prevSearch;
|
|
513
|
+
var selStart = hadFocus ? prevSearch.selectionStart : null;
|
|
514
|
+
var selEnd = hadFocus ? prevSearch.selectionEnd : null;
|
|
515
|
+
|
|
516
|
+
var searchInput = h('input', {
|
|
517
|
+
type: 'search',
|
|
518
|
+
class: 'dv-search',
|
|
519
|
+
placeholder: 'Search routes or rules\u2026',
|
|
520
|
+
value: state.search,
|
|
521
|
+
oninput: function (e) { state.search = e.target.value; renderSidebar(); }
|
|
522
|
+
}, []);
|
|
523
|
+
|
|
524
|
+
var sortSelect = h('select', { class: 'dv-sort', 'aria-label': 'Sort routes', onchange: function (e) { state.sort = e.target.value; renderSidebar(); } }, [
|
|
525
|
+
h('option', { value: 'score-asc', selected: state.sort === 'score-asc' || undefined, text: 'Score (worst first)' }, []),
|
|
526
|
+
h('option', { value: 'score-desc', selected: state.sort === 'score-desc' || undefined, text: 'Score (best first)' }, []),
|
|
527
|
+
h('option', { value: 'alpha', selected: state.sort === 'alpha' || undefined, text: 'Alphabetical' }, []),
|
|
528
|
+
h('option', { value: 'most-findings', selected: state.sort === 'most-findings' || undefined, text: 'Most findings' }, [])
|
|
529
|
+
]);
|
|
530
|
+
|
|
531
|
+
var items = [renderNavItem('Overview', 'overview', null, state.selected === 'overview')];
|
|
532
|
+
sortedRoutes().forEach(function (r) {
|
|
533
|
+
var slug = slugify(r.route);
|
|
534
|
+
state.routeBySlug[slug] = r.route;
|
|
535
|
+
items.push(renderNavItem(r.route, r.route, r, state.selected === r.route));
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
var nav = h('div', { class: 'dv-nav', role: 'listbox', 'aria-label': 'Routes' }, items);
|
|
539
|
+
mount('dv-sidebar', h('div', { class: 'dv-sidebar-inner' }, [searchInput, sortSelect, nav]));
|
|
540
|
+
|
|
541
|
+
if (hadFocus) {
|
|
542
|
+
searchInput.focus();
|
|
543
|
+
if (selStart !== null && searchInput.setSelectionRange) {
|
|
544
|
+
searchInput.setSelectionRange(selStart, selEnd);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
function renderFilterChips(categories) {
|
|
550
|
+
var chip = function (filter, label) {
|
|
551
|
+
return h('button', {
|
|
552
|
+
type: 'button', class: 'dv-chip', 'aria-pressed': state.filter === filter ? 'true' : 'false',
|
|
553
|
+
onclick: function () { state.filter = filter; renderDetail(); },
|
|
554
|
+
text: label
|
|
555
|
+
}, []);
|
|
556
|
+
};
|
|
557
|
+
var catChips = Object.keys(categories).map(function (cat) {
|
|
558
|
+
var name = cat === 'seo' ? 'SEO' : cat.charAt(0).toUpperCase() + cat.slice(1);
|
|
559
|
+
return chip(cat, name);
|
|
560
|
+
});
|
|
561
|
+
return h('div', { class: 'dv-filters', role: 'group', 'aria-label': 'Filter findings' },
|
|
562
|
+
[chip('all', 'All'), chip('critical', 'Critical'), chip('warning', 'Warning'), chip('info', 'Info')].concat(catChips));
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
function passesFilter(issue) {
|
|
566
|
+
var f = state.filter;
|
|
567
|
+
return f === 'all' || issue.severity === f || issue.category === f;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
function renderFinding(issue) {
|
|
571
|
+
var kids = [
|
|
572
|
+
h('div', { class: 'dv-f-head' }, [
|
|
573
|
+
h('span', { class: 'dv-ruleid', text: issue.id }, []),
|
|
574
|
+
h('span', { class: 'dv-f-title', text: issue.title }, []),
|
|
575
|
+
h('span', { class: 'dv-sev-tag dv-sev-' + issue.severity, text: issue.severity }, [])
|
|
576
|
+
])
|
|
577
|
+
];
|
|
578
|
+
if (issue.location) {
|
|
579
|
+
kids.push(h('p', { class: 'dv-f-loc', text: issue.location + (issue.line !== undefined ? ':' + issue.line : '') }, []));
|
|
580
|
+
}
|
|
581
|
+
if (issue.recommendation) {
|
|
582
|
+
kids.push(h('p', { class: 'dv-f-rec', text: issue.recommendation }, []));
|
|
583
|
+
}
|
|
584
|
+
if (issue.fix && issue.fix.snippet) {
|
|
585
|
+
kids.push(h('div', { class: 'dv-fix' }, [h('div', { class: 'dv-fix-label', text: 'fix' }, []), renderFixSnippet(issue.fix)]));
|
|
586
|
+
}
|
|
587
|
+
if (issue.docsUrl) {
|
|
588
|
+
kids.push(h('a', { class: 'dv-f-link', href: issue.docsUrl, text: 'Learn more' }, []));
|
|
589
|
+
}
|
|
590
|
+
return h('article', { class: 'dv-finding dv-finding-' + issue.severity }, kids);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
function renderGauge(score) {
|
|
594
|
+
var band = scoreBand(score);
|
|
595
|
+
var svgNs = 'http://www.w3.org/2000/svg';
|
|
596
|
+
var C = 2 * Math.PI * 58;
|
|
597
|
+
var offset = (C * (1 - score / 100)).toFixed(1);
|
|
598
|
+
var svg = document.createElementNS(svgNs, 'svg');
|
|
599
|
+
svg.setAttribute('width', '132');
|
|
600
|
+
svg.setAttribute('height', '132');
|
|
601
|
+
svg.setAttribute('viewBox', '0 0 132 132');
|
|
602
|
+
var bg = document.createElementNS(svgNs, 'circle');
|
|
603
|
+
bg.setAttribute('cx', '66'); bg.setAttribute('cy', '66'); bg.setAttribute('r', '58');
|
|
604
|
+
bg.setAttribute('fill', 'none'); bg.setAttribute('class', 'dv-gauge-track'); bg.setAttribute('stroke-width', '11');
|
|
605
|
+
var arc = document.createElementNS(svgNs, 'circle');
|
|
606
|
+
arc.setAttribute('cx', '66'); arc.setAttribute('cy', '66'); arc.setAttribute('r', '58');
|
|
607
|
+
arc.setAttribute('fill', 'none'); arc.setAttribute('stroke', BAND_COLOR[band]); arc.setAttribute('stroke-width', '11');
|
|
608
|
+
arc.setAttribute('stroke-linecap', 'round');
|
|
609
|
+
arc.setAttribute('stroke-dasharray', C.toFixed(1));
|
|
610
|
+
arc.setAttribute('stroke-dashoffset', offset);
|
|
611
|
+
svg.appendChild(bg);
|
|
612
|
+
svg.appendChild(arc);
|
|
613
|
+
var wrap = h('div', { class: 'dv-gauge' }, [h('div', { class: 'dv-gauge-num' }, [h('strong', { text: String(score) }, []), h('span', { text: 'Health' }, [])])]);
|
|
614
|
+
wrap.insertBefore(svg, wrap.firstChild);
|
|
615
|
+
return wrap;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
function renderOverview(s) {
|
|
619
|
+
var gauge = renderGauge(s.report.score);
|
|
620
|
+
var cats = Object.keys(s.report.categories).map(function (cat) {
|
|
621
|
+
var c = s.report.categories[cat];
|
|
622
|
+
var band = scoreBand(c.score);
|
|
623
|
+
var weight = s.report.weights[cat];
|
|
624
|
+
var name = cat === 'seo' ? 'SEO' : cat.charAt(0).toUpperCase() + cat.slice(1);
|
|
625
|
+
return h('div', { class: 'dv-cat' }, [
|
|
626
|
+
h('div', { class: 'dv-cat-top' }, [
|
|
627
|
+
h('span', { text: name + (weight !== undefined ? ' (weight ' + weight + ')' : '') }, []),
|
|
628
|
+
h('span', { style: 'color:' + BAND_COLOR[band], text: String(c.score) }, [])
|
|
629
|
+
]),
|
|
630
|
+
h('div', { class: 'dv-bar' }, [h('i', { style: 'width:' + c.score + '%;background:' + BAND_COLOR[band] }, [])])
|
|
631
|
+
]);
|
|
632
|
+
});
|
|
633
|
+
var chips = renderFilterChips(s.report.categories);
|
|
634
|
+
var siteFindings = s.report.siteIssues.filter(passesFilter);
|
|
635
|
+
var siteChecks = s.report.siteIssues.length
|
|
636
|
+
? h('section', { class: 'dv-section' }, [h('h2', { text: 'Site checks' }, [])].concat(siteFindings.map(renderFinding)))
|
|
637
|
+
: null;
|
|
638
|
+
return h('div', { class: 'dv-overview' }, [gauge, h('div', { class: 'dv-cats' }, cats), chips, siteChecks].filter(Boolean));
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
function renderRouteDetail(route) {
|
|
642
|
+
var badge = state.snapshot.badges[route.route];
|
|
643
|
+
var band = scoreBand(route.score);
|
|
644
|
+
var header = h('div', { class: 'dv-detail-header' }, [
|
|
645
|
+
h('span', { class: 'dv-route-path', text: route.route }, []),
|
|
646
|
+
badge ? h('span', { class: 'dv-badge dv-badge-' + badge, text: badge }, []) : null,
|
|
647
|
+
h('span', { class: 'dv-score-chip', style: 'color:' + BAND_COLOR[band], text: String(route.score) }, [])
|
|
648
|
+
].filter(Boolean));
|
|
649
|
+
var chips = renderFilterChips(state.snapshot.report.categories);
|
|
650
|
+
var findings = route.issues.filter(passesFilter);
|
|
651
|
+
var body = findings.length ? findings.map(renderFinding) : [h('p', { class: 'dv-empty', text: 'No issues match the current filter.' }, [])];
|
|
652
|
+
return h('div', { class: 'dv-route-detail' }, [header, chips].concat(body));
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
function renderDetail() {
|
|
656
|
+
var s = state.snapshot;
|
|
657
|
+
if (state.selected === 'overview') {
|
|
658
|
+
mount('dv-detail', renderOverview(s));
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
var route = s.report.routes.filter(function (r) { return r.route === state.selected; })[0];
|
|
662
|
+
if (!route) {
|
|
663
|
+
state.selected = 'overview';
|
|
664
|
+
mount('dv-detail', renderOverview(s));
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
mount('dv-detail', renderRouteDetail(route));
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
function renderAll() {
|
|
671
|
+
renderTopbar();
|
|
672
|
+
renderSidebar();
|
|
673
|
+
renderDetail();
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
function restoreSelectionFromHash() {
|
|
677
|
+
var raw = location.hash.replace(/^#/, '');
|
|
678
|
+
if (!raw || raw === 'overview') { state.selected = 'overview'; return; }
|
|
679
|
+
var m = /^route\\/(.+)$/.exec(raw);
|
|
680
|
+
if (m && state.routeBySlug[m[1]]) state.selected = state.routeBySlug[m[1]];
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
function fetchSnapshot() {
|
|
684
|
+
fetch('/__svelte-vitals/data.json').then(function (r) { return r.json(); }).then(function (data) {
|
|
685
|
+
if (state.snapshot && data.sequence <= state.snapshot.sequence) return;
|
|
686
|
+
state.snapshot = data;
|
|
687
|
+
renderAll();
|
|
688
|
+
}).catch(function () {});
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
function boot() {
|
|
692
|
+
var raw = document.getElementById('svelte-vitals-data');
|
|
693
|
+
state.snapshot = JSON.parse(raw.textContent);
|
|
694
|
+
applyTheme();
|
|
695
|
+
renderSidebar(); // populates routeBySlug before the hash can be trusted
|
|
696
|
+
restoreSelectionFromHash();
|
|
697
|
+
renderAll();
|
|
698
|
+
|
|
699
|
+
window.addEventListener('hashchange', function () {
|
|
700
|
+
restoreSelectionFromHash();
|
|
701
|
+
renderSidebar();
|
|
702
|
+
renderDetail();
|
|
703
|
+
});
|
|
704
|
+
|
|
705
|
+
if (typeof EventSource !== 'undefined') {
|
|
706
|
+
var es = new EventSource('/__svelte-vitals/events');
|
|
707
|
+
es.addEventListener('open', function () { state.connection = 'connected'; renderTopbar(); fetchSnapshot(); });
|
|
708
|
+
es.addEventListener('update', fetchSnapshot);
|
|
709
|
+
es.addEventListener('error', function () { state.connection = 'reconnecting'; renderTopbar(); });
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
boot();
|
|
193
714
|
})();
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
return html
|
|
715
|
+
`;
|
|
716
|
+
|
|
717
|
+
// src/ui/dashboard.ts
|
|
718
|
+
function embedJson(value) {
|
|
719
|
+
return JSON.stringify(value).replace(/</g, "\\u003c").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
720
|
+
}
|
|
721
|
+
function renderDashboardShell(snapshot) {
|
|
722
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>svelte-vitals dashboard</title><style>${DASHBOARD_STYLE}</style></head><body><div class="dv-app" id="dv-app"><header class="dv-topbar" id="dv-topbar"></header><nav class="dv-sidebar" id="dv-sidebar"></nav><main class="dv-detail" id="dv-detail"></main></div><script type="application/json" id="svelte-vitals-data">${embedJson(snapshot)}</script><script>${DASHBOARD_SCRIPT}</script></body></html>`;
|
|
202
723
|
}
|
|
203
724
|
|
|
204
725
|
// src/ui/middleware.ts
|
|
@@ -266,8 +787,19 @@ function installUiMiddleware(server, config, version, store, coreVersion) {
|
|
|
266
787
|
req.on("close", () => clients.delete(res));
|
|
267
788
|
return;
|
|
268
789
|
}
|
|
790
|
+
if (url.startsWith("/data.json")) {
|
|
791
|
+
try {
|
|
792
|
+
const snapshot = buildSnapshot(store, config, { version, coreVersion });
|
|
793
|
+
res.setHeader("Content-Type", "application/json");
|
|
794
|
+
res.end(JSON.stringify(snapshot));
|
|
795
|
+
} catch {
|
|
796
|
+
res.statusCode = 500;
|
|
797
|
+
res.end("{}");
|
|
798
|
+
}
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
269
801
|
try {
|
|
270
|
-
const html =
|
|
802
|
+
const html = renderDashboardShell(buildSnapshot(store, config, { version, coreVersion }));
|
|
271
803
|
res.setHeader("Content-Type", "text/html");
|
|
272
804
|
res.end(html);
|
|
273
805
|
} catch {
|
|
@@ -312,7 +844,10 @@ function createStore() {
|
|
|
312
844
|
let staticResults = [];
|
|
313
845
|
const liveByRoute = /* @__PURE__ */ new Map();
|
|
314
846
|
const subs = /* @__PURE__ */ new Set();
|
|
847
|
+
let analyzing = false;
|
|
848
|
+
let seq = 0;
|
|
315
849
|
function notify() {
|
|
850
|
+
seq += 1;
|
|
316
851
|
for (const fn of subs) fn();
|
|
317
852
|
}
|
|
318
853
|
return {
|
|
@@ -327,12 +862,22 @@ function createStore() {
|
|
|
327
862
|
staticResults = results;
|
|
328
863
|
notify();
|
|
329
864
|
},
|
|
865
|
+
setAnalyzing(next) {
|
|
866
|
+
analyzing = next;
|
|
867
|
+
notify();
|
|
868
|
+
},
|
|
869
|
+
isAnalyzing() {
|
|
870
|
+
return analyzing;
|
|
871
|
+
},
|
|
330
872
|
snapshot() {
|
|
331
873
|
return composeSnapshot(staticResults, liveByRoute);
|
|
332
874
|
},
|
|
333
875
|
badges() {
|
|
334
876
|
return composeBadges(staticResults, liveByRoute);
|
|
335
877
|
},
|
|
878
|
+
sequence() {
|
|
879
|
+
return seq;
|
|
880
|
+
},
|
|
336
881
|
subscribe(fn) {
|
|
337
882
|
subs.add(fn);
|
|
338
883
|
return () => subs.delete(fn);
|
|
@@ -358,6 +903,7 @@ function createAnalysisRunner(opts) {
|
|
|
358
903
|
async function runOnce() {
|
|
359
904
|
if (stopped) return;
|
|
360
905
|
running = true;
|
|
906
|
+
opts.onStatusChange?.(true);
|
|
361
907
|
try {
|
|
362
908
|
const analyze2 = await getAnalyze();
|
|
363
909
|
const { results } = await analyze2({
|
|
@@ -372,6 +918,7 @@ function createAnalysisRunner(opts) {
|
|
|
372
918
|
if (!stopped) opts.onError(err);
|
|
373
919
|
} finally {
|
|
374
920
|
running = false;
|
|
921
|
+
opts.onStatusChange?.(false);
|
|
375
922
|
if (!stopped && pending) {
|
|
376
923
|
pending = false;
|
|
377
924
|
void runOnce();
|
|
@@ -478,7 +1025,8 @@ function svelteVitals(options = {}) {
|
|
|
478
1025
|
rules: options.rules,
|
|
479
1026
|
failOn: options.failOn,
|
|
480
1027
|
onResults: (results) => store.setStatic(results),
|
|
481
|
-
onError: (err) => console.warn("[svelte-vitals] dev analysis failed:", err)
|
|
1028
|
+
onError: (err) => console.warn("[svelte-vitals] dev analysis failed:", err),
|
|
1029
|
+
onStatusChange: (analyzing) => store.setAnalyzing(analyzing)
|
|
482
1030
|
});
|
|
483
1031
|
runner.start();
|
|
484
1032
|
server.watcher?.on("all", (_event, file) => {
|
|
@@ -489,6 +1037,16 @@ function svelteVitals(options = {}) {
|
|
|
489
1037
|
runner.stop();
|
|
490
1038
|
});
|
|
491
1039
|
installUiMiddleware(server, config, readPackageVersion(), store, readCoreVersion());
|
|
1040
|
+
if (typeof server.printUrls === "function") {
|
|
1041
|
+
const printUrls = server.printUrls.bind(server);
|
|
1042
|
+
server.printUrls = () => {
|
|
1043
|
+
printUrls();
|
|
1044
|
+
const printed = server.resolvedUrls?.local?.[0] ?? server.resolvedUrls?.network?.[0];
|
|
1045
|
+
if (printed) {
|
|
1046
|
+
console.log(` \u279C svelte-vitals: ${new URL(printed).origin}/__svelte-vitals/`);
|
|
1047
|
+
}
|
|
1048
|
+
};
|
|
1049
|
+
}
|
|
492
1050
|
}
|
|
493
1051
|
};
|
|
494
1052
|
return [buildPlugin, uiPlugin];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@svelte-vitals/vite",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Vite/SvelteKit plugin for svelte-vitals — analyzes prerendered HTML during vite build.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -42,8 +42,8 @@
|
|
|
42
42
|
"esm-env": "^1.2.2",
|
|
43
43
|
"node-html-parser": "^9.0.0",
|
|
44
44
|
"tinyglobby": "^0.2.17",
|
|
45
|
-
"@svelte-vitals/core": "0.22.
|
|
46
|
-
"svelte-vitals": "0.22.
|
|
45
|
+
"@svelte-vitals/core": "0.22.1",
|
|
46
|
+
"svelte-vitals": "0.22.1"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
49
|
"@sveltejs/kit": "^2.69.1",
|