@leftium/gg 0.0.50 → 0.0.51

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/gg.js CHANGED
@@ -16,13 +16,13 @@ function createGgDebugger(namespace) {
16
16
  if (originalFormatArgs) {
17
17
  originalFormatArgs.call(dbg, args);
18
18
  }
19
- // Extract the callpoint from namespace (strip 'gg:' prefix and any URL suffix)
20
- const nsMatch = dbg.namespace.match(/^gg:([^h]+?)(?:http|$)/);
21
- const callpoint = nsMatch ? nsMatch[1] : dbg.namespace.replace(/^gg:/, '');
19
+ // Pad the namespace for aligned console output (strip URL suffix if present)
20
+ const nsMatch = dbg.namespace.match(/^([^h]+?)(?:http|$)/);
21
+ const callpoint = nsMatch ? nsMatch[1] : dbg.namespace;
22
22
  const paddedCallpoint = callpoint.padEnd(maxCallpointLength, ' ');
23
23
  // Replace the namespace in the formatted string with padded version
24
24
  if (typeof args[0] === 'string') {
25
- args[0] = args[0].replace(dbg.namespace, `gg:${paddedCallpoint}`);
25
+ args[0] = args[0].replace(dbg.namespace, paddedCallpoint);
26
26
  }
27
27
  };
28
28
  return dbg;
@@ -110,9 +110,11 @@ function isGgEnabled() {
110
110
  // GG_ENABLED=false: completely removes gg (even in DEV)
111
111
  // GG_ENABLED=true: force-enables gg (even in PROD, e.g. Vercel deployments)
112
112
  if (BROWSER) {
113
- if (typeof import.meta.env?.VITE_GG_ENABLED === 'string' &&
114
- import.meta.env.VITE_GG_ENABLED === 'false') {
115
- return false;
113
+ if (typeof import.meta.env?.VITE_GG_ENABLED === 'string') {
114
+ if (import.meta.env.VITE_GG_ENABLED === 'false')
115
+ return false;
116
+ if (import.meta.env.VITE_GG_ENABLED === 'true')
117
+ return true;
116
118
  }
117
119
  }
118
120
  else {
@@ -235,7 +237,7 @@ gg.here = function () {
235
237
  return { fileName: '', functionName: '', url: '' };
236
238
  }
237
239
  const callpoint = resolveCallpoint(3);
238
- const namespace = `gg:${callpoint}`;
240
+ const namespace = callpoint;
239
241
  // Log the call-site info
240
242
  const ggLogFunction = namespaceToLogFunction.get(namespace) ||
241
243
  namespaceToLogFunction.set(namespace, createGgDebugger(namespace)).get(namespace);
@@ -303,12 +305,10 @@ export class GgChain {
303
305
  #args;
304
306
  #options;
305
307
  #flushed = false;
306
- #disabled;
307
308
  constructor(value, args, options, disabled = false) {
308
309
  this.#value = value;
309
310
  this.#args = args;
310
311
  this.#options = options;
311
- this.#disabled = disabled;
312
312
  if (!disabled) {
313
313
  // Auto-flush on microtask if not flushed synchronously by .v or another trigger
314
314
  queueMicrotask(() => this.#flush());
@@ -380,14 +380,67 @@ export class GgChain {
380
380
  *
381
381
  * Handles namespace resolution, debug output, capture hook, and return value.
382
382
  */
383
+ /**
384
+ * Format a single value for the flat `msg` string written to the file sink
385
+ * (and stored on CapturedEntry.message). Produces readable output for the
386
+ * common types that String() handles poorly:
387
+ *
388
+ * - plain objects / arrays → compact JSON (depth-limited, no circular crash)
389
+ * - DOM nodes → "tag#id.firstClass" summary
390
+ * - everything else → String() as before
391
+ *
392
+ * This mirrors what `debug`'s %o/%O formatters do and what Node's util.inspect
393
+ * does by default — display formatting, not round-trip serialization.
394
+ */
395
+ function formatValue(v) {
396
+ if (v === null)
397
+ return 'null';
398
+ if (v === undefined)
399
+ return 'undefined';
400
+ if (typeof v === 'string')
401
+ return v;
402
+ if (typeof v !== 'object' && typeof v !== 'function')
403
+ return String(v);
404
+ // DOM nodes: "div#id.firstClass" summary
405
+ if (typeof Element !== 'undefined' && v instanceof Element) {
406
+ let s = v.tagName.toLowerCase();
407
+ if (v.id)
408
+ s += `#${v.id}`;
409
+ if (v.classList.length)
410
+ s += `.${v.classList[0]}`;
411
+ return s;
412
+ }
413
+ if (typeof Node !== 'undefined' && v instanceof Node) {
414
+ return `[${v.constructor?.name ?? 'Node'}]`;
415
+ }
416
+ // Arrays and plain objects: compact JSON, depth-limited
417
+ try {
418
+ return JSON.stringify(v, jsonReplacer, 0) ?? String(v);
419
+ }
420
+ catch {
421
+ return String(v);
422
+ }
423
+ }
424
+ /** JSON replacer that limits nesting depth to avoid huge output. */
425
+ function jsonReplacer(key, value) {
426
+ // 'this' is the parent object; key === '' at the root level
427
+ if (key !== '' && typeof value === 'object' && value !== null && !Array.isArray(value)) {
428
+ // Count depth by checking how many ancestors are objects/arrays
429
+ // Simple approximation: truncate any nested object at depth > 2
430
+ const str = JSON.stringify(value);
431
+ if (str && str.length > 120)
432
+ return '{…}';
433
+ }
434
+ return value;
435
+ }
383
436
  function ggLog(options, ...args) {
384
437
  const { ns: nsLabel, file, line, col, src, level, stack, tableData } = options;
385
438
  if (!ggConfig.enabled) {
386
439
  return args.length ? args[0] : { fileName: '', functionName: '', url: '' };
387
440
  }
388
- const namespace = `gg:${nsLabel}`;
389
- if (nsLabel.length < 80 && nsLabel.length > maxCallpointLength) {
390
- maxCallpointLength = nsLabel.length;
441
+ const namespace = nsLabel.startsWith('gg:') ? nsLabel : `gg:${nsLabel}`;
442
+ if (namespace.length < 80 && namespace.length > maxCallpointLength) {
443
+ maxCallpointLength = namespace.length;
391
444
  }
392
445
  const ggLogFunction = namespaceToLogFunction.get(namespace) ||
393
446
  namespaceToLogFunction.set(namespace, createGgDebugger(namespace)).get(namespace);
@@ -424,7 +477,7 @@ function ggLog(options, ...args) {
424
477
  namespace,
425
478
  color: ggLogFunction.color,
426
479
  diff,
427
- message: logArgs.length === 1 ? String(logArgs[0]) : logArgs.map(String).join(' '),
480
+ message: logArgs.length === 1 ? formatValue(logArgs[0]) : logArgs.map(formatValue).join(' '),
428
481
  args: logArgs,
429
482
  timestamp: Date.now(),
430
483
  file,
@@ -435,11 +488,14 @@ function ggLog(options, ...args) {
435
488
  stack,
436
489
  tableData
437
490
  };
438
- if (_onLogCallback) {
439
- _onLogCallback(entry);
440
- }
441
- else {
442
- earlyLogBuffer.push(entry);
491
+ // Always buffer — earlyLogBuffer is a persistent replay log so late-registering
492
+ // listeners (e.g. Eruda mounting after the file-sink listener) still receive entries
493
+ // that fired before they registered. Dispatched to all current listeners too.
494
+ earlyLogBuffer.push(entry);
495
+ if (earlyLogBuffer.length > EARLY_BUFFER_MAX)
496
+ earlyLogBuffer.shift();
497
+ if (_logListeners.size > 0) {
498
+ _dispatchToListeners(entry);
443
499
  }
444
500
  }
445
501
  /**
@@ -466,10 +522,10 @@ gg._here = function (options) {
466
522
  return { fileName: '', functionName: '', url: '' };
467
523
  }
468
524
  const { ns: nsLabel, file, line, col } = options;
469
- const namespace = `gg:${nsLabel}`;
525
+ const namespace = nsLabel.startsWith('gg:') ? nsLabel : `gg:${nsLabel}`;
470
526
  const ggLogFunction = namespaceToLogFunction.get(namespace) ||
471
527
  namespaceToLogFunction.set(namespace, createGgDebugger(namespace)).get(namespace);
472
- ggLogFunction(` 📝 ${nsLabel}`);
528
+ ggLogFunction(` 📝 ${namespace}`);
473
529
  const fileName = file ? file.replace(srcRootRegex, '') : nsLabel;
474
530
  const functionName = nsLabel.includes('@') ? nsLabel.split('@').pop() || '' : '';
475
531
  const url = file ? openInEditorUrl(file, line, col) : '';
@@ -918,23 +974,65 @@ export function dim() {
918
974
  return createColorFunction('', '', STYLE_CODES.dim);
919
975
  }
920
976
  /**
921
- * Hook for capturing gg() output (used by Eruda plugin)
922
- * Set this to a callback function to receive log entries
977
+ * Multi-listener hook for capturing gg() output.
978
+ *
979
+ * Listeners can be added/removed via gg.addLogListener / gg.removeLogListener.
980
+ * The legacy gg._onLog setter is preserved as a backward-compatible alias
981
+ * (it replaces the single "legacy" slot without affecting other listeners).
923
982
  */
924
- // Buffer for capturing early logs before Eruda initializes
983
+ // Persistent replay buffer every new listener gets a full replay of recent entries,
984
+ // so late-registering listeners (e.g. Eruda mounting after the file-sink listener) still
985
+ // receive entries that fired before they registered. In practice this only holds a handful
986
+ // of page-load entries. Capped at 2000 to bound memory in pathological cases.
987
+ // Note: this does NOT cap the JSONL file — the file grows unbounded and agents clear it
988
+ // explicitly via DELETE /__gg/logs.
989
+ const EARLY_BUFFER_MAX = 2000;
925
990
  const earlyLogBuffer = [];
926
- let _onLogCallback = null;
927
- // Proxy property that replays buffered logs when hook is registered
991
+ const _logListeners = new Set();
992
+ // Legacy single-slot: tracks the callback assigned via `gg._onLog = fn`
993
+ let _legacyOnLogCallback = null;
994
+ function _dispatchToListeners(entry) {
995
+ _logListeners.forEach((fn) => fn(entry));
996
+ }
997
+ // gg.addLogListener / gg.removeLogListener — primary multi-listener API
998
+ Object.defineProperty(gg, 'addLogListener', {
999
+ value(callback) {
1000
+ _logListeners.add(callback);
1001
+ // Replay early buffer to every new listener — buffer accumulates entries that
1002
+ // fired before any listener was registered. Each listener gets the full replay;
1003
+ // the buffer is never cleared so late-registering listeners (e.g. Eruda mounting
1004
+ // after the file-sink listener) still receive the early entries.
1005
+ if (earlyLogBuffer.length > 0) {
1006
+ earlyLogBuffer.forEach((entry) => callback(entry));
1007
+ }
1008
+ },
1009
+ writable: false,
1010
+ configurable: true
1011
+ });
1012
+ Object.defineProperty(gg, 'removeLogListener', {
1013
+ value(callback) {
1014
+ _logListeners.delete(callback);
1015
+ },
1016
+ writable: false,
1017
+ configurable: true
1018
+ });
1019
+ // Legacy gg._onLog — backward-compatible single-slot alias
928
1020
  Object.defineProperty(gg, '_onLog', {
929
1021
  get() {
930
- return _onLogCallback;
1022
+ return _legacyOnLogCallback;
931
1023
  },
932
1024
  set(callback) {
933
- _onLogCallback = callback;
934
- // Replay buffered logs when callback is first registered
935
- if (callback && earlyLogBuffer.length > 0) {
936
- earlyLogBuffer.forEach((entry) => callback(entry));
937
- earlyLogBuffer.length = 0; // Clear buffer after replay
1025
+ // Remove previous legacy callback if any
1026
+ if (_legacyOnLogCallback) {
1027
+ _logListeners.delete(_legacyOnLogCallback);
1028
+ }
1029
+ _legacyOnLogCallback = callback;
1030
+ if (callback) {
1031
+ _logListeners.add(callback);
1032
+ // Replay early buffer — same policy as addLogListener
1033
+ if (earlyLogBuffer.length > 0) {
1034
+ earlyLogBuffer.forEach((entry) => callback(entry));
1035
+ }
938
1036
  }
939
1037
  }
940
1038
  });
@@ -956,7 +1054,7 @@ export async function runGgDiagnostics() {
956
1054
  await serverModulesReady;
957
1055
  await debugReady;
958
1056
  // Create test debugger for server-side enabled check
959
- const ggLogTest = debugFactory('gg:TEST');
1057
+ const ggLogTest = debugFactory('TEST');
960
1058
  let ggMessage = '\n';
961
1059
  const message = (s) => (ggMessage += `${s}\n`);
962
1060
  const checkbox = (test) => (test ? '✅' : '❌');
@@ -983,15 +1081,19 @@ export async function runGgDiagnostics() {
983
1081
  }
984
1082
  message(`${checkbox(ggConfig.enabled)} gg enabled: ${ggConfig.enabled}${enableHint}`);
985
1083
  if (!BROWSER) {
986
- // Server-side: check DEBUG env var (the only output path on the server)
987
- const hint = makeHint(!ggLogTest.enabled, ' (Try `DEBUG=gg:* npm run dev` or use --env-file=.env)');
988
- message(`${checkbox(ggLogTest.enabled)} DEBUG env variable: ${process?.env?.DEBUG}${hint}`);
1084
+ // Server-side: GG_KEEP controls which namespaces are output to the server console.
1085
+ // Falls back to '*' (all) if not set, so gg works zero-config.
1086
+ const hint = makeHint(!ggLogTest.enabled, ' (Try setting GG_KEEP=* in your .env file or shell)');
1087
+ message(`${checkbox(ggLogTest.enabled)} GG_KEEP env variable: ${process?.env?.GG_KEEP ?? '* (default)'}${hint}`);
989
1088
  }
990
- // Optional plugin diagnostics
991
- message(makeHint(_ggCallSitesPlugin, `✅ gg-call-sites vite plugin detected! Call-site namespaces and open-in-editor links baked in at build time.`, `⚠️ gg-call-sites vite plugin not detected. Add ggCallSitesPlugin() to vite.config.ts for file:line call-site namespaces and open-in-editor links. Without plugin, using word-tuple names (e.g. calm-fox) as call-site identifiers.`));
992
1089
  if (BROWSER && DEV) {
1090
+ // Optional plugin diagnostics — only meaningful in browser where Vite's define
1091
+ // replacements apply. Server-side Node.js load never sees __GG_TAG_PLUGIN__ = true.
1092
+ message(makeHint(_ggCallSitesPlugin, `✅ gg-call-sites vite plugin detected! Call-site namespaces and open-in-editor links baked in at build time.`, `⚠️ gg-call-sites vite plugin not detected. Add ggCallSitesPlugin() to vite.config.ts for file:line call-site namespaces and open-in-editor links. Without plugin, using word-tuple names (e.g. calm-fox) as call-site identifiers.`));
993
1093
  const { status } = await fetch('/__open-in-editor?file=+');
994
1094
  message(makeHint(status === 222, `✅ (optional) open-in-editor vite plugin detected! (status code: ${status}) Clickable links open source files in editor.`, `⚠️ (optional) open-in-editor vite plugin not detected. (status code: ${status}) Add openInEditorPlugin() to vite.config.ts for clickable links that open source files in editor`));
1095
+ const fileSinkStatus = await fetch('/__gg/logs', { method: 'HEAD' }).then((r) => r.status, () => 0);
1096
+ message(makeHint(fileSinkStatus === 200, `✅ gg-file-sink vite plugin detected! Logs written to .gg/logs-{port}.jsonl. Agent API at /__gg/logs.`, `ℹ️ (optional) gg-file-sink vite plugin not detected. Add fileSink: true to ggPlugins() options to write logs to .gg/logs-{port}.jsonl for coding agent access.`));
995
1097
  }
996
1098
  console.log(ggMessage);
997
1099
  resetNamespaceWidth();
@@ -36,7 +36,7 @@ export default function openInEditorPlugin(specifiedEditor, srcRoot, onErrorCall
36
36
  }
37
37
  });
38
38
  // Expose project root for client-side $ROOT variable (forward slashes for URI compat)
39
- server.middlewares.use('/__gg-project-root', (_req, res) => {
39
+ server.middlewares.use('/__gg/project-root', (_req, res) => {
40
40
  res.setHeader('Content-Type', 'text/plain');
41
41
  res.end(srcRoot.replace(/\\/g, '/'));
42
42
  });
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Shared namespace pattern matching used by both the Eruda plugin and the
3
+ * gg-file-sink plugin.
4
+ *
5
+ * Patterns are comma-separated globs. A `-` prefix marks an exclusion:
6
+ * "gg:*" — all gg namespaces
7
+ * "gg:api:*,-gg:api:verbose:*" — gg:api:* except verbose
8
+ */
9
+ /**
10
+ * Test whether `str` matches a single glob `pattern`.
11
+ * Supports `*` as a wildcard. Both sides are trimmed before comparison
12
+ * (namespaces may have trailing spaces from padEnd in the Eruda display).
13
+ */
14
+ export declare function matchesGlob(str: string, pattern: string): boolean;
15
+ /**
16
+ * Test whether `ns` matches a comma-separated pattern string.
17
+ *
18
+ * - Empty pattern, `*`, or `gg:*` → always true (fast path).
19
+ * - Inclusions (no `-` prefix) are OR-ed; at least one must match.
20
+ * - Exclusions (`-` prefix) take priority: any match → false.
21
+ * - If the pattern contains only exclusions, `ns` is included by default.
22
+ */
23
+ export declare function matchesPattern(ns: string, pattern: string): boolean;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Shared namespace pattern matching used by both the Eruda plugin and the
3
+ * gg-file-sink plugin.
4
+ *
5
+ * Patterns are comma-separated globs. A `-` prefix marks an exclusion:
6
+ * "gg:*" — all gg namespaces
7
+ * "gg:api:*,-gg:api:verbose:*" — gg:api:* except verbose
8
+ */
9
+ /**
10
+ * Test whether `str` matches a single glob `pattern`.
11
+ * Supports `*` as a wildcard. Both sides are trimmed before comparison
12
+ * (namespaces may have trailing spaces from padEnd in the Eruda display).
13
+ */
14
+ export function matchesGlob(str, pattern) {
15
+ const s = str.trim();
16
+ const p = pattern.trim();
17
+ const regexPattern = p.replace(/[.+^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*');
18
+ const regex = new RegExp(`^${regexPattern}$`);
19
+ return regex.test(s);
20
+ }
21
+ /**
22
+ * Test whether `ns` matches a comma-separated pattern string.
23
+ *
24
+ * - Empty pattern, `*`, or `gg:*` → always true (fast path).
25
+ * - Inclusions (no `-` prefix) are OR-ed; at least one must match.
26
+ * - Exclusions (`-` prefix) take priority: any match → false.
27
+ * - If the pattern contains only exclusions, `ns` is included by default.
28
+ */
29
+ export function matchesPattern(ns, pattern) {
30
+ if (!pattern || pattern === '*' || pattern === 'gg:*')
31
+ return true;
32
+ const parts = pattern
33
+ .split(',')
34
+ .map((p) => p.trim())
35
+ .filter(Boolean);
36
+ const inclusions = parts.filter((p) => !p.startsWith('-'));
37
+ const exclusions = parts.filter((p) => p.startsWith('-')).map((p) => p.slice(1));
38
+ const included = inclusions.length === 0 || inclusions.some((p) => matchesGlob(ns, p));
39
+ const excluded = exclusions.some((p) => matchesGlob(ns, p));
40
+ return included && !excluded;
41
+ }
package/dist/vite.d.ts CHANGED
@@ -2,6 +2,8 @@ import type { Plugin } from 'vite';
2
2
  import ggCallSitesPlugin from './gg-call-sites-plugin.js';
3
3
  import type { GgCallSitesPluginOptions } from './gg-call-sites-plugin.js';
4
4
  import openInEditorPlugin from './open-in-editor.js';
5
+ import ggFileSinkPlugin from './gg-file-sink-plugin.js';
6
+ import type { GgFileSinkOptions } from './gg-file-sink-plugin.js';
5
7
  export interface GgPluginsOptions {
6
8
  /**
7
9
  * Options for the call-sites Vite plugin (source metadata rewriting).
@@ -14,6 +16,13 @@ export interface GgPluginsOptions {
14
16
  * @default true
15
17
  */
16
18
  openInEditor?: boolean;
19
+ /**
20
+ * Enable the file sink plugin — writes all gg() entries to `.gg/logs-{port}.jsonl`
21
+ * for coding agent access. Exposes `GET`/`DELETE /__gg/logs` for agent workflows.
22
+ * Set to `false` to disable.
23
+ * @default true
24
+ */
25
+ fileSink?: boolean | GgFileSinkOptions;
17
26
  }
18
27
  /**
19
28
  * All gg Vite plugins bundled together.
@@ -21,6 +30,7 @@ export interface GgPluginsOptions {
21
30
  * Includes:
22
31
  * - `ggCallSitesPlugin` — rewrites `gg()` calls with source file/line/col metadata
23
32
  * - `openInEditorPlugin` — adds `/__open-in-editor` dev server middleware
33
+ * - `ggFileSinkPlugin` — writes gg() entries to `.gg/logs-{port}.jsonl` for agent access
24
34
  * @example
25
35
  * ```ts
26
36
  * import ggPlugins from '@leftium/gg/vite';
@@ -31,5 +41,5 @@ export interface GgPluginsOptions {
31
41
  * ```
32
42
  */
33
43
  export default function ggPlugins(options?: GgPluginsOptions): Plugin[];
34
- export { ggCallSitesPlugin, openInEditorPlugin };
35
- export type { GgCallSitesPluginOptions };
44
+ export { ggCallSitesPlugin, openInEditorPlugin, ggFileSinkPlugin };
45
+ export type { GgCallSitesPluginOptions, GgFileSinkOptions };
package/dist/vite.js CHANGED
@@ -1,11 +1,13 @@
1
1
  import ggCallSitesPlugin from './gg-call-sites-plugin.js';
2
2
  import openInEditorPlugin from './open-in-editor.js';
3
+ import ggFileSinkPlugin from './gg-file-sink-plugin.js';
3
4
  /**
4
5
  * All gg Vite plugins bundled together.
5
6
  *
6
7
  * Includes:
7
8
  * - `ggCallSitesPlugin` — rewrites `gg()` calls with source file/line/col metadata
8
9
  * - `openInEditorPlugin` — adds `/__open-in-editor` dev server middleware
10
+ * - `ggFileSinkPlugin` — writes gg() entries to `.gg/logs-{port}.jsonl` for agent access
9
11
  * @example
10
12
  * ```ts
11
13
  * import ggPlugins from '@leftium/gg/vite';
@@ -21,7 +23,11 @@ export default function ggPlugins(options = {}) {
21
23
  if (options.openInEditor !== false) {
22
24
  plugins.push(openInEditorPlugin());
23
25
  }
26
+ if (options.fileSink !== false) {
27
+ const fileSinkOptions = typeof options.fileSink === 'object' ? options.fileSink : {};
28
+ plugins.push(ggFileSinkPlugin(fileSinkOptions));
29
+ }
24
30
  return plugins;
25
31
  }
26
32
  // Allow granular imports for advanced users
27
- export { ggCallSitesPlugin, openInEditorPlugin };
33
+ export { ggCallSitesPlugin, openInEditorPlugin, ggFileSinkPlugin };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leftium/gg",
3
- "version": "0.0.50",
3
+ "version": "0.0.51",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/Leftium/gg.git"
@@ -40,27 +40,28 @@
40
40
  }
41
41
  },
42
42
  "devDependencies": {
43
- "@eslint/compat": "^2.0.2",
43
+ "@eslint/compat": "^2.0.3",
44
44
  "@eslint/js": "^10.0.1",
45
45
  "@picocss/pico": "^2.1.1",
46
- "@sveltejs/adapter-vercel": "^6.3.1",
47
- "@sveltejs/kit": "^2.50.2",
46
+ "@sveltejs/adapter-vercel": "^6.3.3",
47
+ "@sveltejs/kit": "^2.53.4",
48
48
  "@sveltejs/package": "^2.5.7",
49
49
  "@sveltejs/vite-plugin-svelte": "^6.2.4",
50
- "@types/node": "^25.2.2",
50
+ "@types/estree": "^1.0.8",
51
+ "@types/node": "^25.3.5",
51
52
  "add": "^2.0.6",
52
- "eslint": "^10.0.0",
53
+ "eslint": "^10.0.3",
53
54
  "eslint-config-prettier": "^10.1.8",
54
- "eslint-plugin-svelte": "^3.14.0",
55
- "globals": "^17.3.0",
55
+ "eslint-plugin-svelte": "^3.15.0",
56
+ "globals": "^17.4.0",
56
57
  "prettier": "^3.8.1",
57
- "prettier-plugin-svelte": "^3.4.1",
58
- "publint": "^0.3.17",
58
+ "prettier-plugin-svelte": "^3.5.1",
59
+ "publint": "^0.3.18",
59
60
  "supports-color": "^10.2.2",
60
- "svelte": "^5.50.1",
61
- "svelte-check": "^4.3.6",
61
+ "svelte": "^5.53.7",
62
+ "svelte-check": "^4.4.4",
62
63
  "typescript": "^5.9.3",
63
- "typescript-eslint": "^8.55.0",
64
+ "typescript-eslint": "^8.56.1",
64
65
  "vite": "^7.3.1",
65
66
  "vite-plugin-devtools-json": "^1.0.0",
66
67
  "vitest": "^4.0.18"
@@ -70,12 +71,11 @@
70
71
  ],
71
72
  "dependencies": {
72
73
  "@sveltejs/acorn-typescript": "^1.0.9",
73
- "@tanstack/virtual-core": "^3.13.18",
74
- "@types/estree": "^1.0.8",
75
- "acorn": "^8.15.0",
74
+ "@tanstack/virtual-core": "^3.13.21",
75
+ "acorn": "^8.16.0",
76
76
  "eruda": "^3.4.3",
77
77
  "esm-env": "^1.2.2",
78
- "launch-editor": "^2.12.0"
78
+ "launch-editor": "^2.13.1"
79
79
  },
80
80
  "scripts": {
81
81
  "dev": "vite dev",