@glasshome/widget-sdk 1.15.0 → 1.16.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/deprecations.js +16 -0
- package/dist/framework/backgrounds/WidgetSliderFill.d.ts +1 -1
- package/dist/framework/core/Widget.d.ts +1 -1
- package/dist/framework/dialogs/debug-view.d.ts +27 -0
- package/dist/framework/dialogs/index.d.ts +1 -0
- package/dist/framework/index.d.ts +2 -2
- package/dist/framework/types.d.ts +1 -1
- package/dist/framework/utils/clipboard.d.ts +9 -0
- package/dist/framework/utils/index.d.ts +1 -0
- package/dist/framework/variants/variant-utils.d.ts +5 -5
- package/dist/index.js +756 -342
- package/dist/vite/index.js +83 -74
- package/package.json +3 -3
package/dist/vite/index.js
CHANGED
|
@@ -6,27 +6,27 @@ import { createRequire as p } from "node:module";
|
|
|
6
6
|
import { dirname as m, join as h, relative as g, resolve as _, sep as v } from "node:path";
|
|
7
7
|
import { fileURLToPath as y } from "node:url";
|
|
8
8
|
import ee from "@tailwindcss/vite";
|
|
9
|
-
import { spawn as te, spawnSync as
|
|
9
|
+
import { spawn as te, spawnSync as ne } from "node:child_process";
|
|
10
10
|
//#region src/vite/introspect-session.ts
|
|
11
|
-
var
|
|
12
|
-
function
|
|
11
|
+
var re = 262144e3, b = 3e4, ie = 3e3;
|
|
12
|
+
function x(e) {
|
|
13
13
|
let t = m(y(import.meta.url));
|
|
14
14
|
return [h(t, `${e}.js`), h(t, `${e}.ts`)].find((e) => o(e)) ?? null;
|
|
15
15
|
}
|
|
16
|
-
function
|
|
17
|
-
let t =
|
|
16
|
+
function S(e) {
|
|
17
|
+
let t = x("introspect");
|
|
18
18
|
if (!t) return {
|
|
19
19
|
ok: !1,
|
|
20
20
|
reason: "introspect not found next to the sdk"
|
|
21
21
|
};
|
|
22
|
-
let n =
|
|
22
|
+
let n = ne(process.execPath, [
|
|
23
23
|
"--conditions",
|
|
24
24
|
"browser",
|
|
25
25
|
t,
|
|
26
26
|
e
|
|
27
27
|
], {
|
|
28
28
|
encoding: "utf-8",
|
|
29
|
-
timeout:
|
|
29
|
+
timeout: b
|
|
30
30
|
});
|
|
31
31
|
if (n.error) return {
|
|
32
32
|
ok: !1,
|
|
@@ -48,16 +48,16 @@ function w(e) {
|
|
|
48
48
|
};
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
|
-
function
|
|
51
|
+
function ae() {
|
|
52
52
|
return {
|
|
53
|
-
introspect: async (e) =>
|
|
53
|
+
introspect: async (e) => S(e),
|
|
54
54
|
dispose: async () => {},
|
|
55
55
|
childPid: null
|
|
56
56
|
};
|
|
57
57
|
}
|
|
58
|
-
function
|
|
59
|
-
if (process.env.GLASSHOME_WIDGET_NO_WORKER === "1") return
|
|
60
|
-
let t = e?.rssLimitBytes ??
|
|
58
|
+
function C(e) {
|
|
59
|
+
if (process.env.GLASSHOME_WIDGET_NO_WORKER === "1") return ae();
|
|
60
|
+
let t = e?.rssLimitBytes ?? re, n = e?.timeoutMs ?? b, r = e?.slowMs ?? ie, i = null, a = !1, o = 0, s = Promise.resolve();
|
|
61
61
|
function c(e, t) {
|
|
62
62
|
for (let n of e.pending.values()) n({
|
|
63
63
|
ok: !1,
|
|
@@ -128,7 +128,7 @@ function ie(e) {
|
|
|
128
128
|
ok: !1,
|
|
129
129
|
reason: "introspect session disposed"
|
|
130
130
|
};
|
|
131
|
-
let t =
|
|
131
|
+
let t = x("introspect-worker");
|
|
132
132
|
if (!t) return {
|
|
133
133
|
ok: !1,
|
|
134
134
|
reason: "introspect-worker not found next to the sdk"
|
|
@@ -186,12 +186,20 @@ function ie(e) {
|
|
|
186
186
|
}
|
|
187
187
|
//#endregion
|
|
188
188
|
//#region src/vite/index.ts
|
|
189
|
+
var w = 2;
|
|
189
190
|
function T(e) {
|
|
190
|
-
|
|
191
|
-
|
|
191
|
+
if (Array.isArray(e)) return `[${e.map(T).join(",")}]`;
|
|
192
|
+
if (typeof e == "object" && e) {
|
|
193
|
+
let t = e;
|
|
194
|
+
return `{${Object.keys(t).sort().filter((e) => t[e] !== void 0).map((e) => `${JSON.stringify(e)}:${T(t[e])}`).join(",")}}`;
|
|
195
|
+
}
|
|
196
|
+
return JSON.stringify(e) ?? "null";
|
|
197
|
+
}
|
|
198
|
+
function E(e) {
|
|
199
|
+
return a("sha256").update(T(e)).digest("hex").slice(0, 16);
|
|
192
200
|
}
|
|
193
|
-
var
|
|
194
|
-
async function
|
|
201
|
+
var D = 20;
|
|
202
|
+
async function O(e, t) {
|
|
195
203
|
if (e === void 0) return;
|
|
196
204
|
let n;
|
|
197
205
|
try {
|
|
@@ -211,7 +219,7 @@ async function D(e, t) {
|
|
|
211
219
|
let a = i.error.issues.map((e) => ` examples${e.path.length ? `[${e.path.join("][")}]` : ""}: ${e.message}`).join("\n");
|
|
212
220
|
throw Error(`[widget-sdk] Invalid \`examples\` for "${t}":\n${a}\nEach example needs a \`config\` object and a \`size\` of whole positive tiles, and at most 20 are rendered.`);
|
|
213
221
|
}
|
|
214
|
-
function
|
|
222
|
+
function k(e, t) {
|
|
215
223
|
if (!e?.length) return;
|
|
216
224
|
let n = e.map((e) => {
|
|
217
225
|
let t = e.label ? `"${e.label}"` : `#${e.index}`;
|
|
@@ -219,7 +227,7 @@ function O(e, t) {
|
|
|
219
227
|
}).join("\n");
|
|
220
228
|
throw Error(`[widget-sdk] Example config rejected by "${t}"'s own configSchema:\n${n}\nEach example's \`config\` must be a config the widget would actually accept, or its preview renders empty.`);
|
|
221
229
|
}
|
|
222
|
-
var
|
|
230
|
+
var A = [
|
|
223
231
|
"schema",
|
|
224
232
|
"defaultConfig",
|
|
225
233
|
"examples",
|
|
@@ -232,26 +240,26 @@ var k = [
|
|
|
232
240
|
"minSize",
|
|
233
241
|
"maxSize"
|
|
234
242
|
];
|
|
235
|
-
function
|
|
243
|
+
function j(e, t, n) {
|
|
236
244
|
let r = c(e, "utf-8"), i = JSON.parse(r), a = {
|
|
237
245
|
...t.manifest ?? {},
|
|
238
246
|
schema: n
|
|
239
247
|
};
|
|
240
|
-
for (let e of
|
|
248
|
+
for (let e of A) a[e] !== void 0 && (i[e] = a[e]);
|
|
241
249
|
let o = `${JSON.stringify(i, null, 2)}\n`;
|
|
242
250
|
o !== r && f(e, o);
|
|
243
251
|
}
|
|
244
|
-
async function
|
|
252
|
+
async function M(e) {
|
|
245
253
|
if (!o(e.outFile)) return;
|
|
246
|
-
let t = e.session ? await e.session.introspect(e.outFile) :
|
|
254
|
+
let t = e.session ? await e.session.introspect(e.outFile) : S(e.outFile);
|
|
247
255
|
if (!t.ok) throw Error(`[widget-sdk] Could not read "${e.widgetName}" to check it: ${t.reason}\n Its examples and config schema could not be validated, so the build cannot continue.
|
|
248
256
|
This usually means the bundle throws while being imported.`);
|
|
249
257
|
let n = t.value, r = n.manifest?.name ?? e.widgetName;
|
|
250
|
-
await
|
|
258
|
+
await O(n.manifest?.examples, r), k(n.exampleConfigIssues, r);
|
|
251
259
|
let i = n.jsonSchema;
|
|
252
260
|
if (!i) return;
|
|
253
|
-
e.manifestPath && o(e.manifestPath) &&
|
|
254
|
-
let a =
|
|
261
|
+
e.manifestPath && o(e.manifestPath) && j(e.manifestPath, n, i);
|
|
262
|
+
let a = E(i), s = n.manifest?.configVersion ?? null, l = n.manifest?.name ?? e.widgetName, u = o(e.hashFile) ? c(e.hashFile, "utf-8") : null;
|
|
255
263
|
if (u !== null) {
|
|
256
264
|
let t = u.trim(), n = null;
|
|
257
265
|
if (t.startsWith("{")) try {
|
|
@@ -259,54 +267,55 @@ async function j(e) {
|
|
|
259
267
|
} catch {
|
|
260
268
|
n = null;
|
|
261
269
|
}
|
|
262
|
-
if (n) {
|
|
270
|
+
if (n && n.hashVersion === w) {
|
|
263
271
|
if (n.hash !== a && n.configVersion === s) throw Error(`[widget-sdk] Config schema shape changed for "${l}" without a configVersion bump. Bump configVersion in defineWidget (currently ${s ?? "unset"}) or revert the schema change, then rebuild. Recorded in ${e.hashFile}`);
|
|
264
|
-
} else
|
|
272
|
+
} else console.warn(`[widget-sdk] Schema shape record for "${l}" was written by an older SDK, so it cannot be compared. Re-recording it under hash format ${w}. This build is not checked for a missing configVersion bump; the next one is. (${e.hashFile})`);
|
|
265
273
|
}
|
|
266
274
|
let d = `${JSON.stringify({
|
|
267
275
|
hash: a,
|
|
268
|
-
configVersion: s
|
|
276
|
+
configVersion: s,
|
|
277
|
+
hashVersion: w
|
|
269
278
|
})}\n`;
|
|
270
279
|
d !== u && f(e.hashFile, d);
|
|
271
280
|
}
|
|
272
|
-
var
|
|
273
|
-
function
|
|
281
|
+
var oe = "virtual:glasshome-widget", N = "\0virtual:glasshome-widget", P = "/@glasshome/preview";
|
|
282
|
+
function F(e) {
|
|
274
283
|
return v === "\\" ? e.split(v).join("/") : e;
|
|
275
284
|
}
|
|
276
|
-
function
|
|
285
|
+
function I() {
|
|
277
286
|
let e = y(import.meta.url);
|
|
278
287
|
return _(m(e), "../../preview");
|
|
279
288
|
}
|
|
280
|
-
function
|
|
289
|
+
function L(e) {
|
|
281
290
|
return r(e);
|
|
282
291
|
}
|
|
283
|
-
var
|
|
284
|
-
function
|
|
285
|
-
let n =
|
|
286
|
-
return n.includes("/node_modules/") || !
|
|
292
|
+
var R = /\.(ts|tsx|js|jsx)$/, z = /from\s+["']@glasshome\/sync-layer(\/[^"']*)?["']/;
|
|
293
|
+
function B(e, t) {
|
|
294
|
+
let n = F(e).split("?")[0] ?? "";
|
|
295
|
+
return n.includes("/node_modules/") || !R.test(n) ? !1 : z.test(t);
|
|
287
296
|
}
|
|
288
|
-
function
|
|
297
|
+
function V() {
|
|
289
298
|
return {
|
|
290
299
|
name: "glasshome-widget:sync-layer-guard",
|
|
291
300
|
apply: "build",
|
|
292
301
|
enforce: "pre",
|
|
293
302
|
transform(e, t) {
|
|
294
|
-
if (
|
|
303
|
+
if (B(t, e)) throw Error(`Widgets must not import "@glasshome/sync-layer" directly (imported by ${F(g(process.cwd(), t.split("?")[0] ?? t))}). Import the equivalent hook from "@glasshome/widget-sdk" instead (e.g. useEntity, useEntities, useService).`);
|
|
295
304
|
return null;
|
|
296
305
|
}
|
|
297
306
|
};
|
|
298
307
|
}
|
|
299
|
-
function
|
|
300
|
-
let r =
|
|
301
|
-
if (r.includes("/node_modules/") || !
|
|
308
|
+
function H(t, n) {
|
|
309
|
+
let r = F(t).split("?")[0] ?? "";
|
|
310
|
+
if (r.includes("/node_modules/") || !R.test(r)) return !1;
|
|
302
311
|
let i = e.find((e) => e.id === "direct-ui-import");
|
|
303
312
|
return i?.sourcePattern ? new RegExp(i.sourcePattern).test(n) : !1;
|
|
304
313
|
}
|
|
305
|
-
function
|
|
314
|
+
function U(e = /* @__PURE__ */ new Set()) {
|
|
306
315
|
let t = /* @__PURE__ */ new Set();
|
|
307
316
|
return {
|
|
308
317
|
scan(e, n) {
|
|
309
|
-
|
|
318
|
+
H(e, n) && t.add(F(g(process.cwd(), e.split("?")[0] ?? e)));
|
|
310
319
|
},
|
|
311
320
|
take() {
|
|
312
321
|
let n = [...t].filter((t) => !e.has(t));
|
|
@@ -315,8 +324,8 @@ function H(e = /* @__PURE__ */ new Set()) {
|
|
|
315
324
|
}
|
|
316
325
|
};
|
|
317
326
|
}
|
|
318
|
-
function
|
|
319
|
-
let r =
|
|
327
|
+
function W(n) {
|
|
328
|
+
let r = U(n);
|
|
320
329
|
return {
|
|
321
330
|
name: "glasshome-widget:ui-import-guard",
|
|
322
331
|
apply: "build",
|
|
@@ -332,15 +341,15 @@ function U(n) {
|
|
|
332
341
|
}
|
|
333
342
|
};
|
|
334
343
|
}
|
|
335
|
-
var
|
|
336
|
-
function
|
|
344
|
+
var G = "node_modules/.cache/glasshome-widgets";
|
|
345
|
+
function K(e) {
|
|
337
346
|
try {
|
|
338
347
|
return p(h(process.cwd(), "package.json")).resolve(e);
|
|
339
348
|
} catch {
|
|
340
349
|
return null;
|
|
341
350
|
}
|
|
342
351
|
}
|
|
343
|
-
var
|
|
352
|
+
var se = [
|
|
344
353
|
"--radius",
|
|
345
354
|
"--background",
|
|
346
355
|
"--foreground",
|
|
@@ -355,16 +364,16 @@ function q(e, t) {
|
|
|
355
364
|
if (!o(n)) return;
|
|
356
365
|
let r = c(n, "utf-8");
|
|
357
366
|
for (let e of r.matchAll(/:host[^{]*\{([^}]*)\}/g)) {
|
|
358
|
-
let n = e[1] ?? "", r =
|
|
367
|
+
let n = e[1] ?? "", r = se.find((e) => n.includes(`${e}:`));
|
|
359
368
|
if (r) throw Error(`[glasshome-widget] "${t}.css" defines theme variable "${r}" on :host. Theme variables must inherit from the document so widgets follow the household theme — never re-scope them into the widget's shadow root.`);
|
|
360
369
|
}
|
|
361
370
|
}
|
|
362
371
|
function J(e, t, n) {
|
|
363
|
-
let r = _(e,
|
|
372
|
+
let r = _(e, G);
|
|
364
373
|
s(r, { recursive: !0 });
|
|
365
|
-
let i =
|
|
374
|
+
let i = K("@glasshome/ui/styles");
|
|
366
375
|
if (!i) throw Error("[widget-sdk] @glasshome/ui is required to build widgets (it provides the theme every GlassHome widget ships with). Add it to your widget's dependencies.");
|
|
367
|
-
let a = _(m(i), "../.."), c =
|
|
376
|
+
let a = _(m(i), "../.."), c = K("@glasshome/widget-sdk/tailwind-sources"), l = c ? m(c) : null, u = [
|
|
368
377
|
h(a, "src"),
|
|
369
378
|
h(a, "dist"),
|
|
370
379
|
...l ? [h(l, "src"), h(l, "dist")] : [],
|
|
@@ -374,7 +383,7 @@ function J(e, t, n) {
|
|
|
374
383
|
"@import \"tailwindcss\" source(none);",
|
|
375
384
|
"@import \"tw-animate-css\";",
|
|
376
385
|
"@import \"@glasshome/ui/styles/theme\";",
|
|
377
|
-
...u.map((e) => `@source "${
|
|
386
|
+
...u.map((e) => `@source "${F(e)}";`),
|
|
378
387
|
"@custom-variant dark {",
|
|
379
388
|
" &:is(.dark *) { @slot }",
|
|
380
389
|
" :host(.dark) & { @slot }",
|
|
@@ -383,8 +392,8 @@ function J(e, t, n) {
|
|
|
383
392
|
].join("\n"));
|
|
384
393
|
let p = _(r, `${t}.entry.ts`);
|
|
385
394
|
return f(p, [
|
|
386
|
-
`import "${
|
|
387
|
-
`export { default } from "${
|
|
395
|
+
`import "${F(d)}";`,
|
|
396
|
+
`export { default } from "${F(n)}";`,
|
|
388
397
|
""
|
|
389
398
|
].join("\n")), p;
|
|
390
399
|
}
|
|
@@ -452,8 +461,8 @@ async function $(e) {
|
|
|
452
461
|
plugins: [
|
|
453
462
|
...X(),
|
|
454
463
|
...e?.plugins ?? [],
|
|
455
|
-
|
|
456
|
-
|
|
464
|
+
V(),
|
|
465
|
+
W(c)
|
|
457
466
|
],
|
|
458
467
|
...e?.viteConfig,
|
|
459
468
|
css: {
|
|
@@ -467,14 +476,14 @@ async function $(e) {
|
|
|
467
476
|
fileName: r.name,
|
|
468
477
|
cssFileName: r.name
|
|
469
478
|
},
|
|
470
|
-
rollupOptions: { external:
|
|
479
|
+
rollupOptions: { external: L },
|
|
471
480
|
outDir: i,
|
|
472
481
|
emptyOutDir: !1,
|
|
473
482
|
copyPublicDir: !1,
|
|
474
483
|
...e?.viteConfig?.build
|
|
475
484
|
},
|
|
476
485
|
logLevel: "warn"
|
|
477
|
-
}), q(i, r.name), await
|
|
486
|
+
}), q(i, r.name), await M({
|
|
478
487
|
outFile: h(i, `${r.name}.js`),
|
|
479
488
|
hashFile: h(m(r.entry), ".schema-hash"),
|
|
480
489
|
widgetName: r.name,
|
|
@@ -483,7 +492,7 @@ async function $(e) {
|
|
|
483
492
|
});
|
|
484
493
|
Q(r, i);
|
|
485
494
|
}
|
|
486
|
-
function
|
|
495
|
+
function ce(e) {
|
|
487
496
|
let t = e?.entry ?? "src/index.tsx", n = {
|
|
488
497
|
name: "glasshome-widget:build",
|
|
489
498
|
apply: "build",
|
|
@@ -498,7 +507,7 @@ function oe(e) {
|
|
|
498
507
|
fileName: "index",
|
|
499
508
|
cssFileName: "index"
|
|
500
509
|
},
|
|
501
|
-
rollupOptions: { external:
|
|
510
|
+
rollupOptions: { external: L }
|
|
502
511
|
}
|
|
503
512
|
};
|
|
504
513
|
}
|
|
@@ -507,7 +516,7 @@ function oe(e) {
|
|
|
507
516
|
apply: "serve",
|
|
508
517
|
enforce: "pre",
|
|
509
518
|
config() {
|
|
510
|
-
let e =
|
|
519
|
+
let e = I(), t = process.cwd();
|
|
511
520
|
return { server: { fs: { allow: [
|
|
512
521
|
e,
|
|
513
522
|
t,
|
|
@@ -515,7 +524,7 @@ function oe(e) {
|
|
|
515
524
|
] } } };
|
|
516
525
|
},
|
|
517
526
|
configureServer(e) {
|
|
518
|
-
let t =
|
|
527
|
+
let t = I(), n = h(t, "preview.html");
|
|
519
528
|
e.middlewares.use(async (t, r, i) => {
|
|
520
529
|
if (t.url === "/" || t.url === "/index.html") try {
|
|
521
530
|
let i = c(n, "utf-8");
|
|
@@ -527,20 +536,20 @@ function oe(e) {
|
|
|
527
536
|
});
|
|
528
537
|
},
|
|
529
538
|
resolveId(e) {
|
|
530
|
-
if (e ===
|
|
531
|
-
let e =
|
|
532
|
-
return
|
|
539
|
+
if (e === P) {
|
|
540
|
+
let e = I();
|
|
541
|
+
return F(h(e, "host.tsx"));
|
|
533
542
|
}
|
|
534
|
-
if (e ===
|
|
543
|
+
if (e === oe) return N;
|
|
535
544
|
},
|
|
536
545
|
load(e) {
|
|
537
|
-
if (e ===
|
|
546
|
+
if (e === N) return `export { default } from "${F(_(process.cwd(), t))}";`;
|
|
538
547
|
}
|
|
539
548
|
}, i = {
|
|
540
549
|
name: "glasshome-widget:schema",
|
|
541
550
|
apply: "build",
|
|
542
551
|
async closeBundle() {
|
|
543
|
-
q(_(process.cwd(), "dist"), "index"), await
|
|
552
|
+
q(_(process.cwd(), "dist"), "index"), await M({
|
|
544
553
|
outFile: _(process.cwd(), "dist", "index.js"),
|
|
545
554
|
hashFile: _(process.cwd(), ".schema-hash"),
|
|
546
555
|
widgetName: "index",
|
|
@@ -551,13 +560,13 @@ function oe(e) {
|
|
|
551
560
|
return [
|
|
552
561
|
...X(),
|
|
553
562
|
n,
|
|
554
|
-
|
|
555
|
-
|
|
563
|
+
V(),
|
|
564
|
+
W(),
|
|
556
565
|
i,
|
|
557
566
|
r
|
|
558
567
|
];
|
|
559
568
|
}
|
|
560
|
-
function
|
|
569
|
+
function le(e) {
|
|
561
570
|
let t = e?.srcDir ?? "src", n = e?.outDir ?? "dist", r = [];
|
|
562
571
|
return [{
|
|
563
572
|
name: "glasshome-widgets:build",
|
|
@@ -585,4 +594,4 @@ function se(e) {
|
|
|
585
594
|
}];
|
|
586
595
|
}
|
|
587
596
|
//#endregion
|
|
588
|
-
export {
|
|
597
|
+
export { D as MAX_EXAMPLES, $ as buildWidgets, C as createIntrospectSession, U as createUiImportTracker, Z as discoverWidgets, Q as generateRegistry, ce as glasshomeWidget, le as glasshomeWidgets, S as introspectOnce, B as isDirectSyncLayerImportSource, H as isDirectUiImportSource, L as isWidgetExternal, M as runSchemaGuard };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@glasshome/widget-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.16.0",
|
|
4
4
|
"description": "SDK for building GlassHome dashboard widgets with SolidJS",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"glasshome",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
},
|
|
61
61
|
"peerDependencies": {
|
|
62
62
|
"@glasshome/sync-layer": "0.8.1",
|
|
63
|
-
"@glasshome/ui": "1.
|
|
63
|
+
"@glasshome/ui": "1.18.0",
|
|
64
64
|
"solid-js": "^1.9.11"
|
|
65
65
|
},
|
|
66
66
|
"dependencies": {
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"zod": "^4.1.13"
|
|
76
76
|
},
|
|
77
77
|
"devDependencies": {
|
|
78
|
-
"@glasshome/ui": "1.
|
|
78
|
+
"@glasshome/ui": "1.18.0",
|
|
79
79
|
"@solidjs/testing-library": "^0.8.10",
|
|
80
80
|
"@types/bun": "^1.4.0",
|
|
81
81
|
"@types/node": "^25.2.2",
|