@glasshome/widget-sdk 1.22.1 → 1.23.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/schemas.js CHANGED
@@ -1,2 +1 @@
1
- import { GridSizeSchema as e, PublishBodySchema as t, PublishConfirmSchema as n, PublishRequestSchema as r, capabilitiesSchema as i, capabilityGrantSchema as a, formatSchemaError as o, parseGridSize as s, publishManifestSchema as c, serializeGridSize as l, widgetManifestSchema as u, widgetManifestSchema as d } from "@glasshome/widget-contract";
2
- export { e as GridSizeSchema, t as PublishBodySchema, n as PublishConfirmSchema, r as PublishRequestSchema, d as WidgetManifestSchema, i as capabilitiesSchema, a as capabilityGrantSchema, o as formatSchemaError, s as parseGridSize, c as publishManifestSchema, l as serializeGridSize, u as widgetManifestSchema };
1
+ export { GridSizeSchema, PublishBodySchema, PublishConfirmSchema, PublishRequestSchema, widgetManifestSchema as WidgetManifestSchema, capabilitiesSchema, capabilityGrantSchema, formatSchemaError, parseGridSize, publishManifestSchema, serializeGridSize, widgetManifestSchema } from "@glasshome/widget-contract";
@@ -0,0 +1,2 @@
1
+ import type { Introspector } from "./index";
2
+ export declare const inProcessIntrospector: Introspector;
@@ -1,7 +1,7 @@
1
1
  import { type CapabilityGrant } from "@glasshome/widget-contract";
2
2
  import type { InlineConfig, Plugin } from "vite";
3
3
  import type { ExampleConfigIssue, WidgetIntrospection } from "./introspect-core";
4
- import { createIntrospectSession, type IntrospectSession, type IntrospectSessionOptions, introspectOnce } from "./introspect-session";
4
+ import { createIntrospectSession, type IntrospectResult, type IntrospectSession, type IntrospectSessionOptions, introspectOnce } from "./introspect-session";
5
5
  export { createIntrospectSession, introspectOnce };
6
6
  export type { ExampleConfigIssue, IntrospectSession, IntrospectSessionOptions, WidgetIntrospection, };
7
7
  export interface GlasshomeWidgetOptions {
@@ -21,6 +21,7 @@ export declare function sdkImportsOf(code: string): Set<string>;
21
21
  /** SDK hooks that read a fixed entity on the widget's behalf, so the widget must declare that read. */
22
22
  export declare const HOOK_READS: Record<string, string>;
23
23
  export declare function assertHookReadsDeclared(code: string, capabilities: readonly CapabilityGrant[], widgetName: string): void;
24
+ export type Introspector = (outFile: string) => IntrospectResult | Promise<IntrospectResult>;
24
25
  export declare function runSchemaGuard(args: {
25
26
  outFile: string;
26
27
  hashFile: string;
@@ -28,6 +29,8 @@ export declare function runSchemaGuard(args: {
28
29
  manifestPath?: string;
29
30
  /** Persistent worker to reuse. Only `bun widget connect`'s watch loop passes one. */
30
31
  session?: IntrospectSession;
32
+ /** Reads the bundle when no session is given. Defaults to one subprocess per call. */
33
+ introspect?: Introspector;
31
34
  }): Promise<void>;
32
35
  /**
33
36
  * Packages provided by the host import map — widgets must not bundle these.
@@ -6,40 +6,46 @@ import { createRequire as m } from "node:module";
6
6
  import { dirname as h, join as g, relative as _, resolve as v, sep as y } from "node:path";
7
7
  import { fileURLToPath as b } from "node:url";
8
8
  import ee from "@tailwindcss/vite";
9
- import { spawn as te, spawnSync as ne } from "node:child_process";
10
- //#region src/vite/introspect-session.ts
11
- var x = 262144e3, S = 3e4, re = 3e3;
12
- function C(e) {
9
+ import { spawn as te, spawnSync as x } from "node:child_process";
10
+ //#region node_modules/@glasshome/widget-contract/dist/clock.mjs
11
+ var S = (e) => e, C = (e) => e, ne = {
12
+ now: () => Date.now(),
13
+ setTimeout: (e, t) => S(globalThis.setTimeout(e, t)),
14
+ clearTimeout: (e) => globalThis.clearTimeout(C(e)),
15
+ setInterval: (e, t) => S(globalThis.setInterval(e, t)),
16
+ clearInterval: (e) => globalThis.clearInterval(C(e))
17
+ }, re = 262144e3, w = 3e4, ie = 3e3;
18
+ function T(e) {
13
19
  let t = h(b(import.meta.url));
14
20
  return [g(t, `${e}.js`), g(t, `${e}.ts`)].find((e) => s(e)) ?? null;
15
21
  }
16
- function w(e) {
17
- let t = C("introspect");
18
- if (!t) return {
22
+ function E(e, t) {
23
+ let n = T("introspect");
24
+ if (!n) return {
19
25
  ok: !1,
20
26
  reason: "introspect not found next to the sdk"
21
27
  };
22
- let n = ne(process.execPath, [
28
+ let r = x(process.execPath, [
23
29
  "--conditions",
24
30
  "browser",
25
- t,
31
+ n,
26
32
  e
27
33
  ], {
28
34
  encoding: "utf-8",
29
- timeout: S
35
+ timeout: t?.timeoutMs ?? w
30
36
  });
31
- if (n.error) return {
37
+ if (r.error) return {
32
38
  ok: !1,
33
- reason: n.error.message
39
+ reason: r.error.message
34
40
  };
35
- if (n.status !== 0) return {
41
+ if (r.status !== 0) return {
36
42
  ok: !1,
37
- reason: (n.stderr || "").trim().split("\n")[0] || `exit ${n.status}`
43
+ reason: (r.stderr || "").trim().split("\n")[0] || `exit ${r.status}`
38
44
  };
39
45
  try {
40
46
  return {
41
47
  ok: !0,
42
- value: JSON.parse(n.stdout)
48
+ value: JSON.parse(r.stdout)
43
49
  };
44
50
  } catch {
45
51
  return {
@@ -48,27 +54,63 @@ function w(e) {
48
54
  };
49
55
  }
50
56
  }
51
- function ie() {
57
+ function ae(e) {
58
+ let t = te(process.execPath, [
59
+ "--conditions",
60
+ "browser",
61
+ e
62
+ ], { stdio: [
63
+ "pipe",
64
+ "pipe",
65
+ "inherit"
66
+ ] });
67
+ return {
68
+ write(e) {
69
+ return t.stdin?.writable ? (t.stdin.write(e), !0) : !1;
70
+ },
71
+ onLine(e) {
72
+ let n = "";
73
+ t.stdout?.setEncoding("utf-8"), t.stdout?.on("data", (t) => {
74
+ n += t;
75
+ let r = n.indexOf("\n");
76
+ for (; r >= 0;) {
77
+ let t = n.slice(0, r).trim();
78
+ n = n.slice(r + 1), t && e(t), r = n.indexOf("\n");
79
+ }
80
+ });
81
+ },
82
+ onExit(e) {
83
+ t.on("error", (t) => e(t.message)), t.on("exit", (t, n) => e(`introspect worker exited (${n ?? `code ${t}`})`));
84
+ },
85
+ kill() {
86
+ t.kill();
87
+ },
88
+ get pid() {
89
+ return t.pid ?? null;
90
+ }
91
+ };
92
+ }
93
+ function oe(e) {
52
94
  return {
53
- introspect: async (e) => w(e),
95
+ introspect: async (t) => E(t, { timeoutMs: e }),
54
96
  dispose: async () => {},
55
97
  childPid: null
56
98
  };
57
99
  }
58
- function ae(e) {
59
- if (process.env.GLASSHOME_WIDGET_NO_WORKER === "1") return ie();
60
- let t = e?.rssLimitBytes ?? x, n = e?.timeoutMs ?? S, r = e?.slowMs ?? re, i = null, a = !1, o = 0, s = Promise.resolve();
61
- function c(e, t) {
100
+ function se(e) {
101
+ if (process.env.GLASSHOME_WIDGET_NO_WORKER === "1") return oe(e?.timeoutMs);
102
+ let t = e?.rssLimitBytes ?? re, n = e?.timeoutMs ?? w, r = e?.slowMs ?? ie, i = e?.clock ?? ne, a = e?.spawnWorker ?? ae, o = null, s = !1, c = 0, l = Promise.resolve();
103
+ function u(e, t) {
62
104
  for (let n of e.pending.values()) n({
63
105
  ok: !1,
64
106
  reason: t
65
107
  });
66
108
  e.pending.clear();
67
109
  }
68
- function l(e, t) {
69
- i === e && (i = null), c(e, t), e.proc.kill();
110
+ function d(e, t) {
111
+ o === e && (o = null), u(e, t), e.proc.kill();
70
112
  }
71
- function u(e, n) {
113
+ function f(e, n) {
72
114
  let i;
73
115
  try {
74
116
  i = JSON.parse(n);
@@ -85,56 +127,38 @@ function ae(e) {
85
127
  reason: i.reason ?? "introspection failed"
86
128
  }));
87
129
  }
88
- function d(e) {
89
- let t = Date.now(), n = te(process.execPath, [
90
- "--conditions",
91
- "browser",
92
- e
93
- ], { stdio: [
94
- "pipe",
95
- "pipe",
96
- "inherit"
97
- ] }), a = {
98
- proc: n,
130
+ function p(e) {
131
+ let t = i.now(), n = {
132
+ proc: a(e),
99
133
  ready: Promise.resolve(),
100
134
  pending: /* @__PURE__ */ new Map(),
101
135
  retireAfterRequest: !1
102
136
  };
103
- return a.ready = new Promise((e, o) => {
104
- let s = "", l = !1;
105
- n.stdout?.setEncoding("utf-8"), n.stdout?.on("data", (n) => {
106
- s += n;
107
- let i = s.indexOf("\n");
108
- for (; i >= 0;) {
109
- let n = s.slice(0, i).trim();
110
- if (s = s.slice(i + 1), n) {
111
- if (!l && n.includes("\"ready\"")) {
112
- l = !0;
113
- let n = Date.now() - t;
114
- n > r && console.warn(`[widget-sdk] introspect worker took ${n}ms to boot`), e();
115
- } else u(a, n);
116
- }
117
- i = s.indexOf("\n");
118
- }
137
+ return n.ready = new Promise((e, a) => {
138
+ let s = !1;
139
+ n.proc.onLine((a) => {
140
+ if (!s && a.includes("\"ready\"")) {
141
+ s = !0;
142
+ let n = i.now() - t;
143
+ n > r && console.warn(`[widget-sdk] introspect worker took ${n}ms to boot`), e();
144
+ } else f(n, a);
145
+ }), n.proc.onExit((e) => {
146
+ s || a(Error(e)), o === n && (o = null), u(n, e);
119
147
  });
120
- let d = (e) => {
121
- l || o(Error(e)), i === a && (i = null), c(a, e);
122
- };
123
- n.on("error", (e) => d(e.message)), n.on("exit", (e, t) => d(`introspect worker exited (${t ?? `code ${e}`})`));
124
- }), a;
148
+ }), n;
125
149
  }
126
- async function f(e) {
127
- if (a) return {
150
+ async function m(e) {
151
+ if (s) return {
128
152
  ok: !1,
129
153
  reason: "introspect session disposed"
130
154
  };
131
- let t = C("introspect-worker");
155
+ let t = T("introspect-worker");
132
156
  if (!t) return {
133
157
  ok: !1,
134
158
  reason: "introspect-worker not found next to the sdk"
135
159
  };
136
- i?.retireAfterRequest && l(i, "worker was replaced");
137
- let r = i ??= d(t);
160
+ o?.retireAfterRequest && d(o, "worker was replaced");
161
+ let r = o ??= p(t);
138
162
  try {
139
163
  await r.ready;
140
164
  } catch (e) {
@@ -143,28 +167,24 @@ function ae(e) {
143
167
  reason: e instanceof Error ? e.message : String(e)
144
168
  };
145
169
  }
146
- if (!r.proc.stdin?.writable) return {
147
- ok: !1,
148
- reason: "introspect worker is not writable"
149
- };
150
- let s = ++o;
170
+ let a = ++c;
151
171
  return new Promise((t) => {
152
- let i = setTimeout(() => {
153
- r.pending.delete(s), l(r, `introspection timed out after ${n}ms`), t({
172
+ let o = i.setTimeout(() => {
173
+ r.pending.delete(a), d(r, `introspection timed out after ${n}ms`), t({
154
174
  ok: !1,
155
175
  reason: `introspection timed out after ${n}ms`
156
176
  });
157
177
  }, n);
158
- r.pending.set(s, (e) => {
159
- clearTimeout(i), t(e);
178
+ r.pending.set(a, (e) => {
179
+ i.clearTimeout(o), t(e);
160
180
  });
161
181
  try {
162
- r.proc.stdin?.write(`${JSON.stringify({
163
- id: s,
182
+ if (!r.proc.write(`${JSON.stringify({
183
+ id: a,
164
184
  outFile: e
165
- })}\n`);
185
+ })}\n`)) throw Error("introspect worker is not writable");
166
186
  } catch (e) {
167
- clearTimeout(i), r.pending.delete(s), r.retireAfterRequest = !0, t({
187
+ i.clearTimeout(o), r.pending.delete(a), r.retireAfterRequest = !0, t({
168
188
  ok: !1,
169
189
  reason: e instanceof Error ? e.message : String(e)
170
190
  });
@@ -173,33 +193,33 @@ function ae(e) {
173
193
  }
174
194
  return {
175
195
  introspect(e) {
176
- let t = s.then(() => f(e));
177
- return s = t.catch(() => void 0), t;
196
+ let t = l.then(() => m(e));
197
+ return l = t.catch(() => void 0), t;
178
198
  },
179
199
  async dispose() {
180
- a = !0, i && l(i, "introspect session disposed");
200
+ s = !0, o && d(o, "introspect session disposed");
181
201
  },
182
202
  get childPid() {
183
- return i?.proc.pid ?? null;
203
+ return o?.proc.pid ?? null;
184
204
  }
185
205
  };
186
206
  }
187
207
  //#endregion
188
208
  //#region src/vite/index.ts
189
- var T = 2;
190
- function E(e) {
191
- if (Array.isArray(e)) return `[${e.map(E).join(",")}]`;
209
+ var D = 2;
210
+ function O(e) {
211
+ if (Array.isArray(e)) return `[${e.map(O).join(",")}]`;
192
212
  if (typeof e == "object" && e) {
193
213
  let t = e;
194
- return `{${Object.keys(t).sort().filter((e) => t[e] !== void 0).map((e) => `${JSON.stringify(e)}:${E(t[e])}`).join(",")}}`;
214
+ return `{${Object.keys(t).sort().filter((e) => t[e] !== void 0).map((e) => `${JSON.stringify(e)}:${O(t[e])}`).join(",")}}`;
195
215
  }
196
216
  return JSON.stringify(e) ?? "null";
197
217
  }
198
- function oe(e) {
199
- return o("sha256").update(E(e)).digest("hex").slice(0, 16);
218
+ function ce(e) {
219
+ return o("sha256").update(O(e)).digest("hex").slice(0, 16);
200
220
  }
201
- var se = 20;
202
- async function D(e, t) {
221
+ var le = 20;
222
+ async function ue(e, t) {
203
223
  if (e === void 0) return;
204
224
  let n;
205
225
  try {
@@ -219,7 +239,7 @@ async function D(e, t) {
219
239
  let a = i.error.issues.map((e) => ` examples${e.path.length ? `[${e.path.join("][")}]` : ""}: ${e.message}`).join("\n");
220
240
  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.`);
221
241
  }
222
- function O(e, t) {
242
+ function de(e, t) {
223
243
  if (!e?.length) return;
224
244
  let n = e.map((e) => {
225
245
  let t = e.label ? `"${e.label}"` : `#${e.index}`;
@@ -227,7 +247,7 @@ function O(e, t) {
227
247
  }).join("\n");
228
248
  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.`);
229
249
  }
230
- var ce = [
250
+ var fe = [
231
251
  "schema",
232
252
  "defaultConfig",
233
253
  "examples",
@@ -240,28 +260,23 @@ var ce = [
240
260
  "minSize",
241
261
  "maxSize"
242
262
  ];
243
- function le(e, t, n) {
263
+ function pe(e, t, n) {
244
264
  let r = l(e, "utf-8"), i = JSON.parse(r), a = {
245
265
  ...t.manifest ?? {},
246
266
  schema: n
247
267
  };
248
- for (let e of ce) a[e] !== void 0 && (i[e] = a[e]);
268
+ for (let e of fe) a[e] !== void 0 && (i[e] = a[e]);
249
269
  let o = `${JSON.stringify(i, null, 2)}\n`;
250
270
  o !== r && p(e, o);
251
271
  }
252
- var ue = /import\s*(\{[^}]*\}|\*\s*as\s+[\w$]+)\s*from\s*["']@glasshome\/widget-sdk["']/g, de = /import\(\s*["']@glasshome\/widget-sdk["']\s*\)/;
272
+ var me = /import\s*(\{[^}]*\}|\*\s*as\s+[\w$]+)\s*from\s*["']@glasshome\/widget-sdk["']/g, he = /import\(\s*["']@glasshome\/widget-sdk["']\s*\)/;
253
273
  function k(e) {
254
274
  let t = /* @__PURE__ */ new Set();
255
- de.test(e) && t.add("*");
256
- for (let [, n = ""] of e.matchAll(ue)) {
257
- if (!n.startsWith("{")) {
258
- t.add("*");
259
- continue;
260
- }
261
- for (let e of n.slice(1, -1).split(",")) {
262
- let n = e.trim().split(/\s+as\s+/)[0];
263
- n && t.add(n);
264
- }
275
+ he.test(e) && t.add("*");
276
+ for (let [, n = ""] of e.matchAll(me)) if (!n.startsWith("{")) t.add("*");
277
+ else for (let e of n.slice(1, -1).split(",")) {
278
+ let n = e.trim().split(/\s+as\s+/)[0];
279
+ n && t.add(n);
265
280
  }
266
281
  return t;
267
282
  }
@@ -276,16 +291,16 @@ function j(e, t, n) {
276
291
  }
277
292
  async function M(e) {
278
293
  if (!s(e.outFile)) return;
279
- let t = e.session ? await e.session.introspect(e.outFile) : w(e.outFile);
294
+ let t = e.session ? await e.session.introspect(e.outFile) : await (e.introspect ?? E)(e.outFile);
280
295
  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.
281
296
  This usually means the bundle throws while being imported.`);
282
297
  let n = t.value, r = n.manifest?.name ?? e.widgetName;
283
- await D(n.manifest?.examples, r), O(n.exampleConfigIssues, r);
298
+ await ue(n.manifest?.examples, r), de(n.exampleConfigIssues, r);
284
299
  let i = n.jsonSchema, a = e.manifestPath && s(e.manifestPath) ? e.manifestPath : null;
285
- i && a && le(a, n, i);
300
+ i && a && pe(a, n, i);
286
301
  let o = a ? JSON.parse(l(a, "utf-8")).capabilities : n.manifest?.capabilities;
287
302
  if (j(l(e.outFile, "utf-8"), o ?? [], r), !i) return;
288
- let c = oe(i), u = n.manifest?.configVersion ?? null, d = n.manifest?.name ?? e.widgetName, f = s(e.hashFile) ? l(e.hashFile, "utf-8") : null;
303
+ let c = ce(i), u = n.manifest?.configVersion ?? null, d = n.manifest?.name ?? e.widgetName, f = s(e.hashFile) ? l(e.hashFile, "utf-8") : null;
289
304
  if (f !== null) {
290
305
  let t = f.trim(), n = null;
291
306
  if (t.startsWith("{")) try {
@@ -293,32 +308,32 @@ async function M(e) {
293
308
  } catch {
294
309
  n = null;
295
310
  }
296
- if (n && n.hashVersion === T) {
311
+ if (n && n.hashVersion === D) {
297
312
  if (n.hash !== c && n.configVersion === u) throw Error(`[widget-sdk] Config schema shape changed for "${d}" without a configVersion bump. Bump configVersion in defineWidget (currently ${u ?? "unset"}) or revert the schema change, then rebuild. Recorded in ${e.hashFile}`);
298
- } else console.warn(`[widget-sdk] Schema shape record for "${d}" was written by an older SDK, so it cannot be compared. Re-recording it under hash format ${T}. This build is not checked for a missing configVersion bump; the next one is. (${e.hashFile})`);
313
+ } else console.warn(`[widget-sdk] Schema shape record for "${d}" was written by an older SDK, so it cannot be compared. Re-recording it under hash format ${D}. This build is not checked for a missing configVersion bump; the next one is. (${e.hashFile})`);
299
314
  }
300
315
  let m = `${JSON.stringify({
301
316
  hash: c,
302
317
  configVersion: u,
303
- hashVersion: T
318
+ hashVersion: D
304
319
  })}\n`;
305
320
  m !== f && p(e.hashFile, m);
306
321
  }
307
- var fe = "virtual:glasshome-widget", N = "\0virtual:glasshome-widget", pe = "/@glasshome/preview";
308
- function P(e) {
322
+ var ge = "virtual:glasshome-widget", N = "\0virtual:glasshome-widget", P = "/@glasshome/preview";
323
+ function F(e) {
309
324
  return y === "\\" ? e.split(y).join("/") : e;
310
325
  }
311
- function F() {
326
+ function I() {
312
327
  let e = b(import.meta.url);
313
328
  return v(h(e), "../../preview");
314
329
  }
315
- function I(e) {
330
+ function L(e) {
316
331
  return r(e);
317
332
  }
318
- var L = /\.(ts|tsx|js|jsx)$/, R = /from\s+["']@glasshome\/sync-layer(\/[^"']*)?["']/;
333
+ var R = /\.(ts|tsx|js|jsx)$/, _e = /from\s+["']@glasshome\/sync-layer(\/[^"']*)?["']/;
319
334
  function z(e, t) {
320
- let n = P(e).split("?")[0] ?? "";
321
- return n.includes("/node_modules/") || !L.test(n) ? !1 : R.test(t);
335
+ let n = F(e).split("?")[0] ?? "";
336
+ return n.includes("/node_modules/") || !R.test(n) ? !1 : _e.test(t);
322
337
  }
323
338
  function B() {
324
339
  return {
@@ -326,14 +341,14 @@ function B() {
326
341
  apply: "build",
327
342
  enforce: "pre",
328
343
  transform(e, t) {
329
- if (z(t, e)) throw Error(`Widgets must not import "@glasshome/sync-layer" directly (imported by ${P(_(process.cwd(), t.split("?")[0] ?? t))}). Import the equivalent hook from "@glasshome/widget-sdk" instead (e.g. useEntity, useEntities, useService).`);
344
+ if (z(t, e)) throw Error(`Widgets must not import "@glasshome/sync-layer" directly (imported by ${F(_(process.cwd(), t.split("?")[0] ?? t))}). Import the equivalent hook from "@glasshome/widget-sdk" instead (e.g. useEntity, useEntities, useService).`);
330
345
  return null;
331
346
  }
332
347
  };
333
348
  }
334
349
  function V(t, n) {
335
- let r = P(t).split("?")[0] ?? "";
336
- if (r.includes("/node_modules/") || !L.test(r)) return !1;
350
+ let r = F(t).split("?")[0] ?? "";
351
+ if (r.includes("/node_modules/") || !R.test(r)) return !1;
337
352
  let i = e.find((e) => e.id === "direct-ui-import");
338
353
  return i?.sourcePattern ? new RegExp(i.sourcePattern).test(n) : !1;
339
354
  }
@@ -341,7 +356,7 @@ function H(e = /* @__PURE__ */ new Set()) {
341
356
  let t = /* @__PURE__ */ new Set();
342
357
  return {
343
358
  scan(e, n) {
344
- V(e, n) && t.add(P(_(process.cwd(), e.split("?")[0] ?? e)));
359
+ V(e, n) && t.add(F(_(process.cwd(), e.split("?")[0] ?? e)));
345
360
  },
346
361
  take() {
347
362
  let n = [...t].filter((t) => !e.has(t));
@@ -367,7 +382,7 @@ function U(n) {
367
382
  }
368
383
  };
369
384
  }
370
- var me = "node_modules/.cache/glasshome-widgets";
385
+ var ve = "node_modules/.cache/glasshome-widgets";
371
386
  function W(e) {
372
387
  try {
373
388
  return m(g(process.cwd(), "package.json")).resolve(e);
@@ -375,7 +390,7 @@ function W(e) {
375
390
  return null;
376
391
  }
377
392
  }
378
- var he = [
393
+ var ye = [
379
394
  "--radius",
380
395
  "--background",
381
396
  "--foreground",
@@ -394,7 +409,7 @@ function K(e, t) {
394
409
  let r = l(n, "utf-8"), i = G(r);
395
410
  i !== r && p(n, i);
396
411
  for (let e of i.matchAll(/:host[^{]*\{([^}]*)\}/g)) {
397
- let n = e[1] ?? "", r = he.find((e) => n.includes(`${e}:`));
412
+ let n = e[1] ?? "", r = ye.find((e) => n.includes(`${e}:`));
398
413
  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.`);
399
414
  }
400
415
  }
@@ -403,7 +418,7 @@ function q(e, t) {
403
418
  return [r, ...u(n, { withFileTypes: !0 }).filter((e) => e.isDirectory()).map((e) => g(n, e.name)).filter((e) => !i.has(e))];
404
419
  }
405
420
  function J(e, t, n, r) {
406
- let i = v(e, me);
421
+ let i = v(e, ve);
407
422
  c(i, { recursive: !0 });
408
423
  let a = W("@glasshome/ui/styles");
409
424
  if (!a) 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.");
@@ -417,7 +432,7 @@ function J(e, t, n, r) {
417
432
  "@import \"tailwindcss\" source(none);",
418
433
  "@import \"tw-animate-css\";",
419
434
  "@import \"@glasshome/ui/styles/theme\";",
420
- ...d.map((e) => `@source "${P(e)}";`),
435
+ ...d.map((e) => `@source "${F(e)}";`),
421
436
  "@custom-variant dark {",
422
437
  " &:is(.dark *) { @slot }",
423
438
  " :host(.dark) & { @slot }",
@@ -426,8 +441,8 @@ function J(e, t, n, r) {
426
441
  ].join("\n"));
427
442
  let m = v(i, `${t}.entry.ts`);
428
443
  return p(m, [
429
- `import "${P(f)}";`,
430
- `export { default } from "${P(n)}";`,
444
+ `import "${F(f)}";`,
445
+ `export { default } from "${F(n)}";`,
431
446
  ""
432
447
  ].join("\n")), m;
433
448
  }
@@ -510,7 +525,7 @@ async function $(e) {
510
525
  fileName: s.name,
511
526
  cssFileName: s.name
512
527
  },
513
- rollupOptions: { external: I },
528
+ rollupOptions: { external: L },
514
529
  outDir: i,
515
530
  emptyOutDir: !1,
516
531
  copyPublicDir: !1,
@@ -526,7 +541,7 @@ async function $(e) {
526
541
  });
527
542
  Q(r, i);
528
543
  }
529
- function ge(e) {
544
+ function be(e) {
530
545
  let t = e?.entry ?? "src/index.tsx", n = {
531
546
  name: "glasshome-widget:build",
532
547
  apply: "build",
@@ -541,7 +556,7 @@ function ge(e) {
541
556
  fileName: "index",
542
557
  cssFileName: "index"
543
558
  },
544
- rollupOptions: { external: I }
559
+ rollupOptions: { external: L }
545
560
  }
546
561
  };
547
562
  }
@@ -550,7 +565,7 @@ function ge(e) {
550
565
  apply: "serve",
551
566
  enforce: "pre",
552
567
  config() {
553
- let e = F(), t = process.cwd();
568
+ let e = I(), t = process.cwd();
554
569
  return { server: { fs: { allow: [
555
570
  e,
556
571
  t,
@@ -558,7 +573,7 @@ function ge(e) {
558
573
  ] } } };
559
574
  },
560
575
  configureServer(e) {
561
- let t = F(), n = g(t, "preview.html");
576
+ let t = I(), n = g(t, "preview.html");
562
577
  e.middlewares.use(async (t, r, i) => {
563
578
  if (t.url === "/" || t.url === "/index.html") try {
564
579
  let i = l(n, "utf-8");
@@ -570,14 +585,14 @@ function ge(e) {
570
585
  });
571
586
  },
572
587
  resolveId(e) {
573
- if (e === pe) {
574
- let e = F();
575
- return P(g(e, "host.tsx"));
588
+ if (e === P) {
589
+ let e = I();
590
+ return F(g(e, "host.tsx"));
576
591
  }
577
- if (e === fe) return N;
592
+ if (e === ge) return N;
578
593
  },
579
594
  load(e) {
580
- if (e === N) return `export { default } from "${P(v(process.cwd(), t))}";`;
595
+ if (e === N) return `export { default } from "${F(v(process.cwd(), t))}";`;
581
596
  }
582
597
  }, i = {
583
598
  name: "glasshome-widget:schema",
@@ -600,7 +615,7 @@ function ge(e) {
600
615
  r
601
616
  ];
602
617
  }
603
- function _e(e) {
618
+ function xe(e) {
604
619
  let t = e?.srcDir ?? "src", n = e?.outDir ?? "dist", r = [];
605
620
  return [{
606
621
  name: "glasshome-widgets:build",
@@ -628,4 +643,4 @@ function _e(e) {
628
643
  }];
629
644
  }
630
645
  //#endregion
631
- export { A as HOOK_READS, se as MAX_EXAMPLES, j as assertHookReadsDeclared, $ as buildWidgets, ae as createIntrospectSession, H as createUiImportTracker, Z as discoverWidgets, G as dropSelfReferencingVars, Q as generateRegistry, ge as glasshomeWidget, _e as glasshomeWidgets, w as introspectOnce, z as isDirectSyncLayerImportSource, V as isDirectUiImportSource, I as isWidgetExternal, M as runSchemaGuard, k as sdkImportsOf, q as widgetProjectScanDirs };
646
+ export { A as HOOK_READS, le as MAX_EXAMPLES, j as assertHookReadsDeclared, $ as buildWidgets, se as createIntrospectSession, H as createUiImportTracker, Z as discoverWidgets, G as dropSelfReferencingVars, Q as generateRegistry, be as glasshomeWidget, xe as glasshomeWidgets, E as introspectOnce, z as isDirectSyncLayerImportSource, V as isDirectUiImportSource, L as isWidgetExternal, M as runSchemaGuard, k as sdkImportsOf, q as widgetProjectScanDirs };
@@ -1,3 +1,4 @@
1
+ import { type Clock } from "@glasshome/widget-contract/clock";
1
2
  import type { WidgetIntrospection } from "./introspect-core";
2
3
  export type IntrospectResult = {
3
4
  ok: true;
@@ -13,6 +14,15 @@ export interface IntrospectSessionOptions {
13
14
  timeoutMs?: number;
14
15
  /** Log spawn and import durations above this. */
15
16
  slowMs?: number;
17
+ clock?: Clock;
18
+ spawnWorker?: (probe: string) => WorkerTransport;
19
+ }
20
+ export interface WorkerTransport {
21
+ write(line: string): boolean;
22
+ onLine(cb: (line: string) => void): void;
23
+ onExit(cb: (reason: string) => void): void;
24
+ kill(): void;
25
+ readonly pid: number | null;
16
26
  }
17
27
  export interface IntrospectSession {
18
28
  introspect(outFile: string): Promise<IntrospectResult>;
@@ -20,7 +30,9 @@ export interface IntrospectSession {
20
30
  readonly childPid: number | null;
21
31
  }
22
32
  /** One process per check. The path `build`, `capture` and the fallback take. */
23
- export declare function introspectOnce(outFile: string): IntrospectResult;
33
+ export declare function introspectOnce(outFile: string, options?: {
34
+ timeoutMs?: number;
35
+ }): IntrospectResult;
24
36
  /**
25
37
  * A worker reused across rebuilds, for callers that own a session.
26
38
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glasshome/widget-sdk",
3
- "version": "1.22.1",
3
+ "version": "1.23.0",
4
4
  "description": "SDK for building GlassHome dashboard widgets with SolidJS",
5
5
  "keywords": [
6
6
  "dashboard",
@@ -75,7 +75,7 @@
75
75
  "lint": "oxlint --type-aware . && oxfmt --check ."
76
76
  },
77
77
  "dependencies": {
78
- "@glasshome/widget-contract": "0.3.0",
78
+ "@glasshome/widget-contract": "0.4.0",
79
79
  "@tailwindcss/vite": "^4.1.13",
80
80
  "clsx": "^2.1.1",
81
81
  "happy-dom": "^20.0.0",
@@ -85,7 +85,7 @@
85
85
  "zod": "^4.1.13"
86
86
  },
87
87
  "devDependencies": {
88
- "@glasshome/ui": "1.28.0",
88
+ "@glasshome/ui": "1.33.0",
89
89
  "@solidjs/testing-library": "^0.8.10",
90
90
  "@types/bun": "^1.4.0",
91
91
  "@types/node": "^25.2.2",
@@ -100,7 +100,7 @@
100
100
  },
101
101
  "peerDependencies": {
102
102
  "@glasshome/sync-layer": "0.9.1",
103
- "@glasshome/ui": "1.28.0",
103
+ "@glasshome/ui": "1.30.0",
104
104
  "solid-js": "^1.9.11"
105
105
  },
106
106
  "engines": {