@glasshome/widget-sdk 1.10.3 → 1.11.1
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/README.md +6 -0
- package/dist/framework/theming/tone.d.ts +3 -3
- package/dist/index.js +3 -2
- package/dist/introspect-core-DYv9Uufi.js +72 -0
- package/dist/vite/fixtures/orphan-parent.d.ts +1 -0
- package/dist/vite/index.d.ts +8 -1
- package/dist/vite/index.js +323 -172
- package/dist/vite/introspect-core.d.ts +33 -0
- package/dist/vite/introspect-session.d.ts +30 -0
- package/dist/vite/introspect-worker.d.ts +10 -0
- package/dist/vite/introspect-worker.js +76 -0
- package/dist/vite/introspect.d.ts +6 -15
- package/dist/vite/introspect.js +5 -55
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -86,6 +86,12 @@ The `area` widget renders quick-glance metric pills (CO2 traffic-light, humidity
|
|
|
86
86
|
|
|
87
87
|
If your widget needs full visual control (a chart, a video stream, a custom animated surface), follow these patterns: keep the `<Widget>` shell neutral (no `tone`/`color`), place your custom DOM inside `<Widget.Content>`, and document the choice in the widget's README.
|
|
88
88
|
|
|
89
|
+
## Troubleshooting
|
|
90
|
+
|
|
91
|
+
| Variable | Effect |
|
|
92
|
+
| ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
|
|
93
|
+
| `GLASSHOME_WIDGET_NO_WORKER` | Set to `1` to make `bun widget connect` check each rebuild in a fresh process again, instead of reusing one worker for the session. |
|
|
94
|
+
|
|
89
95
|
## Peer Dependencies
|
|
90
96
|
|
|
91
97
|
| Package | Required | Notes |
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
export declare const ToneSchema: z.ZodEnum<{
|
|
3
|
-
|
|
4
|
-
warning: "warning";
|
|
3
|
+
accent: "accent";
|
|
5
4
|
danger: "danger";
|
|
6
5
|
info: "info";
|
|
7
6
|
neutral: "neutral";
|
|
8
|
-
|
|
7
|
+
success: "success";
|
|
8
|
+
warning: "warning";
|
|
9
9
|
}>;
|
|
10
10
|
export type Tone = z.infer<typeof ToneSchema>;
|
package/dist/index.js
CHANGED
|
@@ -1451,7 +1451,7 @@ function Un() {
|
|
|
1451
1451
|
//#region src/version.ts
|
|
1452
1452
|
var Wn = {
|
|
1453
1453
|
name: "@glasshome/widget-sdk",
|
|
1454
|
-
version: "1.
|
|
1454
|
+
version: "1.11.1",
|
|
1455
1455
|
description: "SDK for building GlassHome dashboard widgets with SolidJS",
|
|
1456
1456
|
keywords: [
|
|
1457
1457
|
"glasshome",
|
|
@@ -1520,6 +1520,7 @@ var Wn = {
|
|
|
1520
1520
|
"@tailwindcss/vite": "^4.1.13",
|
|
1521
1521
|
clsx: "^2.1.1",
|
|
1522
1522
|
"happy-dom": "^20.0.0",
|
|
1523
|
+
lightningcss: "^1.33.0",
|
|
1523
1524
|
"tailwind-merge": "^3.5.0",
|
|
1524
1525
|
tailwindcss: "^4.1.13",
|
|
1525
1526
|
zod: "^4.1.13"
|
|
@@ -1529,7 +1530,7 @@ var Wn = {
|
|
|
1529
1530
|
"@solidjs/testing-library": "^0.8.10",
|
|
1530
1531
|
"@types/bun": "latest",
|
|
1531
1532
|
"@types/node": "^25.2.2",
|
|
1532
|
-
typescript: "^
|
|
1533
|
+
typescript: "^7.0.2",
|
|
1533
1534
|
vite: "^8.1.0",
|
|
1534
1535
|
"vite-plugin-solid": "^2.11.12",
|
|
1535
1536
|
vitest: "^4.1.10"
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { copyFileSync as e, rmSync as t } from "node:fs";
|
|
2
|
+
import { pathToFileURL as n } from "node:url";
|
|
3
|
+
//#region src/vite/introspect-core.ts
|
|
4
|
+
function r(e) {
|
|
5
|
+
return typeof e?.safeParse == "function";
|
|
6
|
+
}
|
|
7
|
+
function i(e, t) {
|
|
8
|
+
if (!r(e) || !Array.isArray(t)) return [];
|
|
9
|
+
let n = [];
|
|
10
|
+
for (let [r, i] of t.entries()) {
|
|
11
|
+
let t = i?.config;
|
|
12
|
+
if (typeof t != "object" || !t) continue;
|
|
13
|
+
let a = e.safeParse(t);
|
|
14
|
+
a.success || n.push({
|
|
15
|
+
index: r,
|
|
16
|
+
label: i.label,
|
|
17
|
+
problems: (a.error?.issues ?? []).map((e) => {
|
|
18
|
+
let t = e.path.map(String).join(".");
|
|
19
|
+
return t ? `${t}: ${e.message}` : e.message;
|
|
20
|
+
})
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
return n;
|
|
24
|
+
}
|
|
25
|
+
var a = [
|
|
26
|
+
"window",
|
|
27
|
+
"document",
|
|
28
|
+
"navigator",
|
|
29
|
+
"location",
|
|
30
|
+
"customElements",
|
|
31
|
+
"HTMLElement",
|
|
32
|
+
"Element",
|
|
33
|
+
"Node",
|
|
34
|
+
"CSS",
|
|
35
|
+
"getComputedStyle",
|
|
36
|
+
"requestAnimationFrame",
|
|
37
|
+
"cancelAnimationFrame",
|
|
38
|
+
"matchMedia"
|
|
39
|
+
];
|
|
40
|
+
async function o() {
|
|
41
|
+
let { Window: e } = await import("happy-dom"), t = new e({ url: "http://localhost" });
|
|
42
|
+
for (let e of a) try {
|
|
43
|
+
Object.defineProperty(globalThis, e, {
|
|
44
|
+
value: t[e],
|
|
45
|
+
configurable: !0,
|
|
46
|
+
writable: !0
|
|
47
|
+
});
|
|
48
|
+
} catch {}
|
|
49
|
+
}
|
|
50
|
+
async function s(r, i) {
|
|
51
|
+
let a = `${r}.introspect-${i}.mjs`;
|
|
52
|
+
e(r, a);
|
|
53
|
+
try {
|
|
54
|
+
return await import(n(a).href);
|
|
55
|
+
} finally {
|
|
56
|
+
t(a, { force: !0 });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async function c(e, t) {
|
|
60
|
+
let r = (t ? await s(e, t) : await import(n(e).href)).default, a = null;
|
|
61
|
+
if (r?.configSchema) {
|
|
62
|
+
let { z: e } = await import("zod");
|
|
63
|
+
a = e.toJSONSchema(r.configSchema, { unrepresentable: "any" });
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
manifest: r?.manifest ?? null,
|
|
67
|
+
jsonSchema: a,
|
|
68
|
+
exampleConfigIssues: i(r?.configSchema, r?.manifest?.examples)
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
//#endregion
|
|
72
|
+
export { c as n, o as t };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/vite/index.d.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import type { InlineConfig, Plugin } from "vite";
|
|
2
|
+
import type { ExampleConfigIssue, WidgetIntrospection } from "./introspect-core";
|
|
3
|
+
import { createIntrospectSession, type IntrospectSession, type IntrospectSessionOptions, introspectOnce } from "./introspect-session";
|
|
4
|
+
export { createIntrospectSession, introspectOnce };
|
|
5
|
+
export type { ExampleConfigIssue, IntrospectSession, IntrospectSessionOptions, WidgetIntrospection, };
|
|
2
6
|
export interface GlasshomeWidgetOptions {
|
|
3
7
|
/** Entry file for the widget (default: "src/index.tsx") */
|
|
4
8
|
entry?: string;
|
|
@@ -16,6 +20,8 @@ export declare function runSchemaGuard(args: {
|
|
|
16
20
|
hashFile: string;
|
|
17
21
|
widgetName: string;
|
|
18
22
|
manifestPath?: string;
|
|
23
|
+
/** Persistent worker to reuse. Only `bun widget connect`'s watch loop passes one. */
|
|
24
|
+
session?: IntrospectSession;
|
|
19
25
|
}): Promise<void>;
|
|
20
26
|
/**
|
|
21
27
|
* Packages provided by the host import map — widgets must not bundle these.
|
|
@@ -74,6 +80,8 @@ export interface BuildWidgetsOptions {
|
|
|
74
80
|
viteConfig?: InlineConfig;
|
|
75
81
|
/** Build only these widget names (subdirectory names). Skips full clean. */
|
|
76
82
|
only?: string[];
|
|
83
|
+
/** Persistent introspection worker to reuse across rebuilds. */
|
|
84
|
+
session?: IntrospectSession;
|
|
77
85
|
}
|
|
78
86
|
/**
|
|
79
87
|
* Build each widget as a separate Vite invocation so shared code is inlined
|
|
@@ -103,4 +111,3 @@ export interface GlasshomeWidgetsOptions {
|
|
|
103
111
|
* default build, and `closeBundle()` runs the actual per-widget builds.
|
|
104
112
|
*/
|
|
105
113
|
export declare function glasshomeWidgets(options?: GlasshomeWidgetsOptions): Plugin[];
|
|
106
|
-
export {};
|
package/dist/vite/index.js
CHANGED
|
@@ -1,19 +1,193 @@
|
|
|
1
1
|
import { deprecations as e, formatDeprecation as t } from "../deprecations.js";
|
|
2
2
|
import { formatSchemaError as n, isHostProvidedModule as r, widgetManifestSchema as i } from "@glasshome/widget-contract";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import
|
|
9
|
-
import
|
|
3
|
+
import { createHash as a } from "node:crypto";
|
|
4
|
+
import { existsSync as o, mkdirSync as s, readFileSync as c, readdirSync as l, rmSync as u, statSync as d, writeFileSync as f } from "node:fs";
|
|
5
|
+
import { createRequire as p } from "node:module";
|
|
6
|
+
import { dirname as m, join as h, relative as g, resolve as _, sep as v } from "node:path";
|
|
7
|
+
import { fileURLToPath as y } from "node:url";
|
|
8
|
+
import ee from "@tailwindcss/vite";
|
|
9
|
+
import { spawn as te, spawnSync as b } from "node:child_process";
|
|
10
|
+
//#region src/vite/introspect-session.ts
|
|
11
|
+
var ne = 262144e3, x = 3e4, S = 3e3;
|
|
12
|
+
function C(e) {
|
|
13
|
+
let t = m(y(import.meta.url));
|
|
14
|
+
return [h(t, `${e}.js`), h(t, `${e}.ts`)].find((e) => o(e)) ?? null;
|
|
15
|
+
}
|
|
16
|
+
function w(e) {
|
|
17
|
+
let t = C("introspect");
|
|
18
|
+
if (!t) return {
|
|
19
|
+
ok: !1,
|
|
20
|
+
reason: "introspect not found next to the sdk"
|
|
21
|
+
};
|
|
22
|
+
let n = b(process.execPath, [
|
|
23
|
+
"--conditions",
|
|
24
|
+
"browser",
|
|
25
|
+
t,
|
|
26
|
+
e
|
|
27
|
+
], {
|
|
28
|
+
encoding: "utf-8",
|
|
29
|
+
timeout: x
|
|
30
|
+
});
|
|
31
|
+
if (n.error) return {
|
|
32
|
+
ok: !1,
|
|
33
|
+
reason: n.error.message
|
|
34
|
+
};
|
|
35
|
+
if (n.status !== 0) return {
|
|
36
|
+
ok: !1,
|
|
37
|
+
reason: (n.stderr || "").trim().split("\n")[0] || `exit ${n.status}`
|
|
38
|
+
};
|
|
39
|
+
try {
|
|
40
|
+
return {
|
|
41
|
+
ok: !0,
|
|
42
|
+
value: JSON.parse(n.stdout)
|
|
43
|
+
};
|
|
44
|
+
} catch {
|
|
45
|
+
return {
|
|
46
|
+
ok: !1,
|
|
47
|
+
reason: "introspect returned unparseable output"
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function re() {
|
|
52
|
+
return {
|
|
53
|
+
introspect: async (e) => w(e),
|
|
54
|
+
dispose: async () => {},
|
|
55
|
+
childPid: null
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function ie(e) {
|
|
59
|
+
if (process.env.GLASSHOME_WIDGET_NO_WORKER === "1") return re();
|
|
60
|
+
let t = e?.rssLimitBytes ?? ne, n = e?.timeoutMs ?? x, r = e?.slowMs ?? S, i = null, a = null, o = !1, s = !1, c = 0, l = Promise.resolve(), u = /* @__PURE__ */ new Map();
|
|
61
|
+
function d(e) {
|
|
62
|
+
for (let t of u.values()) t({
|
|
63
|
+
ok: !1,
|
|
64
|
+
reason: e
|
|
65
|
+
});
|
|
66
|
+
u.clear();
|
|
67
|
+
}
|
|
68
|
+
function f(e) {
|
|
69
|
+
let t = i;
|
|
70
|
+
i = null, a = null, o = !1, d(e), t?.kill();
|
|
71
|
+
}
|
|
72
|
+
function p(e) {
|
|
73
|
+
let n;
|
|
74
|
+
try {
|
|
75
|
+
n = JSON.parse(e);
|
|
76
|
+
} catch {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (typeof n.id != "number") return;
|
|
80
|
+
let i = u.get(n.id);
|
|
81
|
+
i && (u.delete(n.id), (n.rss ?? 0) > t && (o = !0), (n.importMs ?? 0) > r && console.warn(`[widget-sdk] introspection took ${n.importMs}ms`), i(n.ok ? {
|
|
82
|
+
ok: !0,
|
|
83
|
+
value: n.value
|
|
84
|
+
} : {
|
|
85
|
+
ok: !1,
|
|
86
|
+
reason: n.reason ?? "introspection failed"
|
|
87
|
+
}));
|
|
88
|
+
}
|
|
89
|
+
function m() {
|
|
90
|
+
if (i && a && !o) return a;
|
|
91
|
+
(o || i) && f("worker was replaced");
|
|
92
|
+
let e = C("introspect-worker");
|
|
93
|
+
if (!e) return Promise.reject(/* @__PURE__ */ Error("introspect-worker not found next to the sdk"));
|
|
94
|
+
let t = Date.now(), n = te(process.execPath, [
|
|
95
|
+
"--conditions",
|
|
96
|
+
"browser",
|
|
97
|
+
e
|
|
98
|
+
], { stdio: [
|
|
99
|
+
"pipe",
|
|
100
|
+
"pipe",
|
|
101
|
+
"inherit"
|
|
102
|
+
] });
|
|
103
|
+
i = n;
|
|
104
|
+
let s = new Promise((e, o) => {
|
|
105
|
+
let s = "", c = !1;
|
|
106
|
+
n.stdout?.setEncoding("utf-8"), n.stdout?.on("data", (n) => {
|
|
107
|
+
s += n;
|
|
108
|
+
let i = s.indexOf("\n");
|
|
109
|
+
for (; i >= 0;) {
|
|
110
|
+
let n = s.slice(0, i).trim();
|
|
111
|
+
if (s = s.slice(i + 1), n) {
|
|
112
|
+
if (!c && n.includes("\"ready\"")) {
|
|
113
|
+
c = !0;
|
|
114
|
+
let n = Date.now() - t;
|
|
115
|
+
n > r && console.warn(`[widget-sdk] introspect worker took ${n}ms to boot`), e();
|
|
116
|
+
} else p(n);
|
|
117
|
+
}
|
|
118
|
+
i = s.indexOf("\n");
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
let l = (e) => {
|
|
122
|
+
i === n && (i = null, a = null), d(e), c || o(Error(e));
|
|
123
|
+
};
|
|
124
|
+
n.on("error", (e) => l(e.message)), n.on("exit", (e, t) => l(`introspect worker exited (${t ?? `code ${e}`})`));
|
|
125
|
+
});
|
|
126
|
+
return a = s, s;
|
|
127
|
+
}
|
|
128
|
+
async function h(e) {
|
|
129
|
+
if (s) return {
|
|
130
|
+
ok: !1,
|
|
131
|
+
reason: "introspect session disposed"
|
|
132
|
+
};
|
|
133
|
+
try {
|
|
134
|
+
await m();
|
|
135
|
+
} catch (e) {
|
|
136
|
+
return {
|
|
137
|
+
ok: !1,
|
|
138
|
+
reason: e instanceof Error ? e.message : String(e)
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
let t = i;
|
|
142
|
+
if (!t?.stdin?.writable) return {
|
|
143
|
+
ok: !1,
|
|
144
|
+
reason: "introspect worker is not writable"
|
|
145
|
+
};
|
|
146
|
+
let r = ++c;
|
|
147
|
+
return new Promise((i) => {
|
|
148
|
+
let a = setTimeout(() => {
|
|
149
|
+
u.delete(r), f(`introspection timed out after ${n}ms`), i({
|
|
150
|
+
ok: !1,
|
|
151
|
+
reason: `introspection timed out after ${n}ms`
|
|
152
|
+
});
|
|
153
|
+
}, n);
|
|
154
|
+
u.set(r, (e) => {
|
|
155
|
+
clearTimeout(a), i(e);
|
|
156
|
+
});
|
|
157
|
+
try {
|
|
158
|
+
t.stdin?.write(`${JSON.stringify({
|
|
159
|
+
id: r,
|
|
160
|
+
outFile: e
|
|
161
|
+
})}\n`);
|
|
162
|
+
} catch (e) {
|
|
163
|
+
clearTimeout(a), u.delete(r), o = !0, i({
|
|
164
|
+
ok: !1,
|
|
165
|
+
reason: e instanceof Error ? e.message : String(e)
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
introspect(e) {
|
|
172
|
+
let t = l.then(() => h(e));
|
|
173
|
+
return l = t.catch(() => void 0), t;
|
|
174
|
+
},
|
|
175
|
+
async dispose() {
|
|
176
|
+
s = !0, f("introspect session disposed");
|
|
177
|
+
},
|
|
178
|
+
get childPid() {
|
|
179
|
+
return i?.pid ?? null;
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
//#endregion
|
|
10
184
|
//#region src/vite/index.ts
|
|
11
|
-
function
|
|
185
|
+
function T(e) {
|
|
12
186
|
let t = JSON.stringify(e, Object.keys(e).sort());
|
|
13
|
-
return
|
|
187
|
+
return a("sha256").update(t).digest("hex").slice(0, 16);
|
|
14
188
|
}
|
|
15
|
-
var
|
|
16
|
-
async function
|
|
189
|
+
var E = 20;
|
|
190
|
+
async function D(e, t) {
|
|
17
191
|
if (e === void 0) return;
|
|
18
192
|
let n;
|
|
19
193
|
try {
|
|
@@ -33,7 +207,7 @@ async function w(e, t) {
|
|
|
33
207
|
let a = i.error.issues.map((e) => ` examples${e.path.length ? `[${e.path.join("][")}]` : ""}: ${e.message}`).join("\n");
|
|
34
208
|
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.`);
|
|
35
209
|
}
|
|
36
|
-
function
|
|
210
|
+
function O(e, t) {
|
|
37
211
|
if (!e?.length) return;
|
|
38
212
|
let n = e.map((e) => {
|
|
39
213
|
let t = e.label ? `"${e.label}"` : `#${e.index}`;
|
|
@@ -41,42 +215,7 @@ function T(e, t) {
|
|
|
41
215
|
}).join("\n");
|
|
42
216
|
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.`);
|
|
43
217
|
}
|
|
44
|
-
|
|
45
|
-
let t = h(b(import.meta.url)), n = [g(t, "introspect.js"), g(t, "introspect.ts")].find((e) => s(e));
|
|
46
|
-
if (!n) return {
|
|
47
|
-
ok: !1,
|
|
48
|
-
reason: `introspect not found next to ${t}`
|
|
49
|
-
};
|
|
50
|
-
let r = a(process.execPath, [
|
|
51
|
-
"--conditions",
|
|
52
|
-
"browser",
|
|
53
|
-
n,
|
|
54
|
-
e
|
|
55
|
-
], {
|
|
56
|
-
encoding: "utf-8",
|
|
57
|
-
timeout: 3e4
|
|
58
|
-
});
|
|
59
|
-
if (r.error) return {
|
|
60
|
-
ok: !1,
|
|
61
|
-
reason: r.error.message
|
|
62
|
-
};
|
|
63
|
-
if (r.status !== 0) return {
|
|
64
|
-
ok: !1,
|
|
65
|
-
reason: (r.stderr || "").trim().split("\n")[0] || `exit ${r.status}`
|
|
66
|
-
};
|
|
67
|
-
try {
|
|
68
|
-
return {
|
|
69
|
-
ok: !0,
|
|
70
|
-
value: JSON.parse(r.stdout)
|
|
71
|
-
};
|
|
72
|
-
} catch {
|
|
73
|
-
return {
|
|
74
|
-
ok: !1,
|
|
75
|
-
reason: "introspect returned unparseable output"
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
var D = [
|
|
218
|
+
var k = [
|
|
80
219
|
"schema",
|
|
81
220
|
"defaultConfig",
|
|
82
221
|
"examples",
|
|
@@ -89,80 +228,80 @@ var D = [
|
|
|
89
228
|
"minSize",
|
|
90
229
|
"maxSize"
|
|
91
230
|
];
|
|
92
|
-
function
|
|
93
|
-
let r =
|
|
231
|
+
function A(e, t, n) {
|
|
232
|
+
let r = c(e, "utf-8"), i = JSON.parse(r), a = {
|
|
94
233
|
...t.manifest ?? {},
|
|
95
234
|
schema: n
|
|
96
235
|
};
|
|
97
|
-
for (let e of
|
|
236
|
+
for (let e of k) a[e] !== void 0 && (i[e] = a[e]);
|
|
98
237
|
let o = `${JSON.stringify(i, null, 2)}\n`;
|
|
99
|
-
o !== r &&
|
|
238
|
+
o !== r && f(e, o);
|
|
100
239
|
}
|
|
101
|
-
async function
|
|
102
|
-
if (!
|
|
103
|
-
let t =
|
|
240
|
+
async function j(e) {
|
|
241
|
+
if (!o(e.outFile)) return;
|
|
242
|
+
let t = e.session ? await e.session.introspect(e.outFile) : w(e.outFile);
|
|
104
243
|
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.
|
|
105
244
|
This usually means the bundle throws while being imported.`);
|
|
106
245
|
let n = t.value, r = n.manifest?.name ?? e.widgetName;
|
|
107
|
-
await
|
|
246
|
+
await D(n.manifest?.examples, r), O(n.exampleConfigIssues, r);
|
|
108
247
|
let i = n.jsonSchema;
|
|
109
248
|
if (!i) return;
|
|
110
|
-
e.manifestPath &&
|
|
111
|
-
let a =
|
|
112
|
-
if (
|
|
113
|
-
let t =
|
|
249
|
+
e.manifestPath && o(e.manifestPath) && A(e.manifestPath, n, i);
|
|
250
|
+
let a = T(i), s = n.manifest?.configVersion ?? null, l = n.manifest?.name ?? e.widgetName;
|
|
251
|
+
if (o(e.hashFile)) {
|
|
252
|
+
let t = c(e.hashFile, "utf-8").trim(), n = null;
|
|
114
253
|
if (t.startsWith("{")) try {
|
|
115
254
|
n = JSON.parse(t);
|
|
116
255
|
} catch {
|
|
117
256
|
n = null;
|
|
118
257
|
}
|
|
119
258
|
if (n) {
|
|
120
|
-
if (n.hash !== a && n.configVersion ===
|
|
121
|
-
} else t !== a && console.warn(`[widget-sdk] Schema shape changed for "${
|
|
259
|
+
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}`);
|
|
260
|
+
} else t !== a && console.warn(`[widget-sdk] Schema shape changed for "${l}" — verify configVersion was bumped`);
|
|
122
261
|
}
|
|
123
|
-
|
|
262
|
+
f(e.hashFile, `${JSON.stringify({
|
|
124
263
|
hash: a,
|
|
125
|
-
configVersion:
|
|
264
|
+
configVersion: s
|
|
126
265
|
})}\n`);
|
|
127
266
|
}
|
|
128
|
-
var
|
|
129
|
-
function
|
|
130
|
-
return
|
|
267
|
+
var ae = "virtual:glasshome-widget", M = "\0virtual:glasshome-widget", N = "/@glasshome/preview";
|
|
268
|
+
function P(e) {
|
|
269
|
+
return v === "\\" ? e.split(v).join("/") : e;
|
|
131
270
|
}
|
|
132
|
-
function
|
|
133
|
-
let e =
|
|
134
|
-
return
|
|
271
|
+
function F() {
|
|
272
|
+
let e = y(import.meta.url);
|
|
273
|
+
return _(m(e), "../../preview");
|
|
135
274
|
}
|
|
136
|
-
function
|
|
275
|
+
function I(e) {
|
|
137
276
|
return r(e);
|
|
138
277
|
}
|
|
139
|
-
var
|
|
140
|
-
function
|
|
141
|
-
let n =
|
|
142
|
-
return n.includes("/node_modules/") || !
|
|
278
|
+
var L = /\.(ts|tsx|js|jsx)$/, R = /from\s+["']@glasshome\/sync-layer(\/[^"']*)?["']/;
|
|
279
|
+
function z(e, t) {
|
|
280
|
+
let n = P(e).split("?")[0] ?? "";
|
|
281
|
+
return n.includes("/node_modules/") || !L.test(n) ? !1 : R.test(t);
|
|
143
282
|
}
|
|
144
|
-
function
|
|
283
|
+
function B() {
|
|
145
284
|
return {
|
|
146
285
|
name: "glasshome-widget:sync-layer-guard",
|
|
147
286
|
apply: "build",
|
|
148
287
|
enforce: "pre",
|
|
149
288
|
transform(e, t) {
|
|
150
|
-
if (
|
|
289
|
+
if (z(t, e)) throw Error(`Widgets must not import "@glasshome/sync-layer" directly (imported by ${P(g(process.cwd(), t.split("?")[0] ?? t))}). Import the equivalent hook from "@glasshome/widget-sdk" instead (e.g. useEntity, useEntities, useService).`);
|
|
151
290
|
return null;
|
|
152
291
|
}
|
|
153
292
|
};
|
|
154
293
|
}
|
|
155
|
-
function
|
|
156
|
-
let r =
|
|
157
|
-
if (r.includes("/node_modules/") || !
|
|
294
|
+
function V(t, n) {
|
|
295
|
+
let r = P(t).split("?")[0] ?? "";
|
|
296
|
+
if (r.includes("/node_modules/") || !L.test(r)) return !1;
|
|
158
297
|
let i = e.find((e) => e.id === "direct-ui-import");
|
|
159
298
|
return i?.sourcePattern ? new RegExp(i.sourcePattern).test(n) : !1;
|
|
160
299
|
}
|
|
161
|
-
function
|
|
300
|
+
function H(e = /* @__PURE__ */ new Set()) {
|
|
162
301
|
let t = /* @__PURE__ */ new Set();
|
|
163
302
|
return {
|
|
164
303
|
scan(e, n) {
|
|
165
|
-
|
|
304
|
+
V(e, n) && t.add(P(g(process.cwd(), e.split("?")[0] ?? e)));
|
|
166
305
|
},
|
|
167
306
|
take() {
|
|
168
307
|
let n = [...t].filter((t) => !e.has(t));
|
|
@@ -171,8 +310,8 @@ function V(e = /* @__PURE__ */ new Set()) {
|
|
|
171
310
|
}
|
|
172
311
|
};
|
|
173
312
|
}
|
|
174
|
-
function
|
|
175
|
-
let r =
|
|
313
|
+
function U(n) {
|
|
314
|
+
let r = H(n);
|
|
176
315
|
return {
|
|
177
316
|
name: "glasshome-widget:ui-import-guard",
|
|
178
317
|
apply: "build",
|
|
@@ -188,15 +327,15 @@ function H(n) {
|
|
|
188
327
|
}
|
|
189
328
|
};
|
|
190
329
|
}
|
|
191
|
-
var
|
|
192
|
-
function
|
|
330
|
+
var W = "node_modules/.cache/glasshome-widgets";
|
|
331
|
+
function G(e) {
|
|
193
332
|
try {
|
|
194
|
-
return
|
|
333
|
+
return p(h(process.cwd(), "package.json")).resolve(e);
|
|
195
334
|
} catch {
|
|
196
335
|
return null;
|
|
197
336
|
}
|
|
198
337
|
}
|
|
199
|
-
var
|
|
338
|
+
var K = [
|
|
200
339
|
"--radius",
|
|
201
340
|
"--background",
|
|
202
341
|
"--foreground",
|
|
@@ -206,74 +345,78 @@ var G = [
|
|
|
206
345
|
"--muted",
|
|
207
346
|
"--accent"
|
|
208
347
|
];
|
|
209
|
-
function
|
|
210
|
-
let n =
|
|
211
|
-
if (!
|
|
212
|
-
let r =
|
|
348
|
+
function q(e, t) {
|
|
349
|
+
let n = h(e, `${t}.css`);
|
|
350
|
+
if (!o(n)) return;
|
|
351
|
+
let r = c(n, "utf-8");
|
|
213
352
|
for (let e of r.matchAll(/:host[^{]*\{([^}]*)\}/g)) {
|
|
214
|
-
let n = e[1] ?? "", r =
|
|
353
|
+
let n = e[1] ?? "", r = K.find((e) => n.includes(`${e}:`));
|
|
215
354
|
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.`);
|
|
216
355
|
}
|
|
217
356
|
}
|
|
218
|
-
function
|
|
219
|
-
let r =
|
|
220
|
-
|
|
221
|
-
let i =
|
|
357
|
+
function J(e, t, n) {
|
|
358
|
+
let r = _(e, W);
|
|
359
|
+
s(r, { recursive: !0 });
|
|
360
|
+
let i = G("@glasshome/ui/styles");
|
|
222
361
|
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.");
|
|
223
|
-
let a =
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
...l ? [
|
|
227
|
-
|
|
228
|
-
].filter((e) =>
|
|
229
|
-
|
|
362
|
+
let a = _(m(i), "../.."), c = G("@glasshome/widget-sdk/tailwind-sources"), l = c ? m(c) : null, u = [
|
|
363
|
+
h(a, "src"),
|
|
364
|
+
h(a, "dist"),
|
|
365
|
+
...l ? [h(l, "src"), h(l, "dist")] : [],
|
|
366
|
+
m(n)
|
|
367
|
+
].filter((e) => o(e)), d = _(r, `${t}.tailwind.css`);
|
|
368
|
+
f(d, [
|
|
230
369
|
"@import \"tailwindcss\" source(none);",
|
|
231
370
|
"@import \"tw-animate-css\";",
|
|
232
371
|
"@import \"@glasshome/ui/styles/theme\";",
|
|
233
|
-
...u.map((e) => `@source "${
|
|
372
|
+
...u.map((e) => `@source "${P(e)}";`),
|
|
234
373
|
"@custom-variant dark {",
|
|
235
374
|
" &:is(.dark *) { @slot }",
|
|
236
375
|
" :host(.dark) & { @slot }",
|
|
237
376
|
"}",
|
|
238
377
|
""
|
|
239
378
|
].join("\n"));
|
|
240
|
-
let
|
|
241
|
-
return p
|
|
242
|
-
`import "${
|
|
243
|
-
`export { default } from "${
|
|
379
|
+
let p = _(r, `${t}.entry.ts`);
|
|
380
|
+
return f(p, [
|
|
381
|
+
`import "${P(d)}";`,
|
|
382
|
+
`export { default } from "${P(n)}";`,
|
|
244
383
|
""
|
|
245
|
-
].join("\n")),
|
|
384
|
+
].join("\n")), p;
|
|
385
|
+
}
|
|
386
|
+
function Y() {
|
|
387
|
+
let { Features: e } = p(import.meta.url)("lightningcss");
|
|
388
|
+
return { lightningcss: { include: e.HexAlphaColors } };
|
|
246
389
|
}
|
|
247
|
-
function
|
|
248
|
-
let e =
|
|
390
|
+
function X() {
|
|
391
|
+
let e = ee();
|
|
249
392
|
for (let t of e) t.apply ??= "build";
|
|
250
393
|
return e;
|
|
251
394
|
}
|
|
252
|
-
function
|
|
395
|
+
function Z(e) {
|
|
253
396
|
let t = [];
|
|
254
|
-
if (!
|
|
255
|
-
for (let n of
|
|
256
|
-
let r =
|
|
257
|
-
if (!
|
|
258
|
-
let i =
|
|
259
|
-
|
|
397
|
+
if (!o(e)) return t;
|
|
398
|
+
for (let n of l(e)) {
|
|
399
|
+
let r = _(e, n);
|
|
400
|
+
if (!d(r).isDirectory()) continue;
|
|
401
|
+
let i = _(r, "index.tsx"), a = _(r, "manifest.json");
|
|
402
|
+
o(i) && o(a) && t.push({
|
|
260
403
|
name: n,
|
|
261
404
|
entry: i
|
|
262
405
|
});
|
|
263
406
|
}
|
|
264
407
|
return t;
|
|
265
408
|
}
|
|
266
|
-
function
|
|
409
|
+
function Q(e, t) {
|
|
267
410
|
let r = [];
|
|
268
|
-
if (
|
|
269
|
-
if (!
|
|
270
|
-
let
|
|
271
|
-
if (!s
|
|
272
|
-
let
|
|
273
|
-
if (!
|
|
274
|
-
let u =
|
|
411
|
+
if (o(e)) for (let a of l(e)) {
|
|
412
|
+
if (!d(h(e, a)).isDirectory()) continue;
|
|
413
|
+
let s = h(e, a, "manifest.json");
|
|
414
|
+
if (!o(s)) continue;
|
|
415
|
+
let l = i.safeParse(JSON.parse(c(s, "utf-8")));
|
|
416
|
+
if (!l.success) throw Error(`[registry] Invalid manifest for "${a}": ${n(l.error)}`);
|
|
417
|
+
let u = o(h(t, `${a}.css`));
|
|
275
418
|
r.push({
|
|
276
|
-
...
|
|
419
|
+
...l.data,
|
|
277
420
|
bundleUrl: `./${a}.js`,
|
|
278
421
|
...u ? { cssUrl: `./${a}.css` } : {}
|
|
279
422
|
});
|
|
@@ -284,10 +427,10 @@ function X(e, t) {
|
|
|
284
427
|
baseUrl: "./",
|
|
285
428
|
widgets: r
|
|
286
429
|
};
|
|
287
|
-
|
|
430
|
+
o(t) || s(t, { recursive: !0 }), f(h(t, "registry.json"), JSON.stringify(a, null, 2)), console.log(`[registry] Generated registry.json with ${r.length} widget(s)`);
|
|
288
431
|
}
|
|
289
|
-
async function
|
|
290
|
-
let { build: t } = await import("vite"), n = process.cwd(), r =
|
|
432
|
+
async function $(e) {
|
|
433
|
+
let { build: t } = await import("vite"), n = process.cwd(), r = _(n, e?.srcDir ?? "src"), i = _(n, e?.outDir ?? "dist"), a = Z(r);
|
|
291
434
|
if (a.length === 0) {
|
|
292
435
|
console.warn("[glasshome-widgets] No widgets found in", r);
|
|
293
436
|
return;
|
|
@@ -296,73 +439,81 @@ async function Z(e) {
|
|
|
296
439
|
let t = new Set(e.only);
|
|
297
440
|
a = a.filter((e) => t.has(e.name));
|
|
298
441
|
}
|
|
299
|
-
e?.only ||
|
|
300
|
-
let
|
|
442
|
+
e?.only || o(i) && u(i, { recursive: !0 }), o(i) || s(i, { recursive: !0 });
|
|
443
|
+
let c = /* @__PURE__ */ new Set();
|
|
301
444
|
for (let r of a) await t({
|
|
302
445
|
configFile: !1,
|
|
303
446
|
root: n,
|
|
304
447
|
plugins: [
|
|
305
|
-
...
|
|
448
|
+
...X(),
|
|
306
449
|
...e?.plugins ?? [],
|
|
307
|
-
|
|
308
|
-
|
|
450
|
+
B(),
|
|
451
|
+
U(c)
|
|
309
452
|
],
|
|
310
453
|
...e?.viteConfig,
|
|
454
|
+
css: {
|
|
455
|
+
...e?.viteConfig?.css,
|
|
456
|
+
...Y()
|
|
457
|
+
},
|
|
311
458
|
build: {
|
|
312
459
|
lib: {
|
|
313
|
-
entry:
|
|
460
|
+
entry: J(n, r.name, r.entry),
|
|
314
461
|
formats: ["es"],
|
|
315
462
|
fileName: r.name,
|
|
316
463
|
cssFileName: r.name
|
|
317
464
|
},
|
|
318
|
-
rollupOptions: { external:
|
|
465
|
+
rollupOptions: { external: I },
|
|
319
466
|
outDir: i,
|
|
320
467
|
emptyOutDir: !1,
|
|
321
468
|
copyPublicDir: !1,
|
|
322
469
|
...e?.viteConfig?.build
|
|
323
470
|
},
|
|
324
471
|
logLevel: "warn"
|
|
325
|
-
}),
|
|
326
|
-
outFile:
|
|
327
|
-
hashFile:
|
|
472
|
+
}), q(i, r.name), await j({
|
|
473
|
+
outFile: h(i, `${r.name}.js`),
|
|
474
|
+
hashFile: h(m(r.entry), ".schema-hash"),
|
|
328
475
|
widgetName: r.name,
|
|
329
|
-
manifestPath:
|
|
476
|
+
manifestPath: h(m(r.entry), "manifest.json"),
|
|
477
|
+
session: e?.session
|
|
330
478
|
});
|
|
331
|
-
|
|
479
|
+
Q(r, i);
|
|
332
480
|
}
|
|
333
|
-
function
|
|
481
|
+
function oe(e) {
|
|
334
482
|
let t = e?.entry ?? "src/index.tsx", n = {
|
|
335
483
|
name: "glasshome-widget:build",
|
|
336
484
|
apply: "build",
|
|
337
485
|
config() {
|
|
338
486
|
let e = process.cwd();
|
|
339
|
-
return {
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
487
|
+
return {
|
|
488
|
+
css: Y(),
|
|
489
|
+
build: {
|
|
490
|
+
lib: {
|
|
491
|
+
entry: J(e, "index", _(e, t)),
|
|
492
|
+
formats: ["es"],
|
|
493
|
+
fileName: "index",
|
|
494
|
+
cssFileName: "index"
|
|
495
|
+
},
|
|
496
|
+
rollupOptions: { external: I }
|
|
497
|
+
}
|
|
498
|
+
};
|
|
348
499
|
}
|
|
349
500
|
}, r = {
|
|
350
501
|
name: "glasshome-widget:dev",
|
|
351
502
|
apply: "serve",
|
|
352
503
|
enforce: "pre",
|
|
353
504
|
config() {
|
|
354
|
-
let e =
|
|
505
|
+
let e = F(), t = process.cwd();
|
|
355
506
|
return { server: { fs: { allow: [
|
|
356
507
|
e,
|
|
357
508
|
t,
|
|
358
|
-
|
|
509
|
+
_(t, "..", "..")
|
|
359
510
|
] } } };
|
|
360
511
|
},
|
|
361
512
|
configureServer(e) {
|
|
362
|
-
let t =
|
|
513
|
+
let t = F(), n = h(t, "preview.html");
|
|
363
514
|
e.middlewares.use(async (t, r, i) => {
|
|
364
515
|
if (t.url === "/" || t.url === "/index.html") try {
|
|
365
|
-
let i =
|
|
516
|
+
let i = c(n, "utf-8");
|
|
366
517
|
i = await e.transformIndexHtml(t.url, i), r.setHeader("Content-Type", "text/html"), r.statusCode = 200, r.end(i);
|
|
367
518
|
} catch (e) {
|
|
368
519
|
i(e);
|
|
@@ -371,37 +522,37 @@ function Q(e) {
|
|
|
371
522
|
});
|
|
372
523
|
},
|
|
373
524
|
resolveId(e) {
|
|
374
|
-
if (e ===
|
|
375
|
-
let e =
|
|
376
|
-
return
|
|
525
|
+
if (e === N) {
|
|
526
|
+
let e = F();
|
|
527
|
+
return P(h(e, "host.tsx"));
|
|
377
528
|
}
|
|
378
|
-
if (e ===
|
|
529
|
+
if (e === ae) return M;
|
|
379
530
|
},
|
|
380
531
|
load(e) {
|
|
381
|
-
if (e ===
|
|
532
|
+
if (e === M) return `export { default } from "${P(_(process.cwd(), t))}";`;
|
|
382
533
|
}
|
|
383
534
|
}, i = {
|
|
384
535
|
name: "glasshome-widget:schema",
|
|
385
536
|
apply: "build",
|
|
386
537
|
async closeBundle() {
|
|
387
|
-
|
|
388
|
-
outFile:
|
|
389
|
-
hashFile:
|
|
538
|
+
q(_(process.cwd(), "dist"), "index"), await j({
|
|
539
|
+
outFile: _(process.cwd(), "dist", "index.js"),
|
|
540
|
+
hashFile: _(process.cwd(), ".schema-hash"),
|
|
390
541
|
widgetName: "index",
|
|
391
|
-
manifestPath:
|
|
542
|
+
manifestPath: _(process.cwd(), "manifest.json")
|
|
392
543
|
});
|
|
393
544
|
}
|
|
394
545
|
};
|
|
395
546
|
return [
|
|
396
|
-
...
|
|
547
|
+
...X(),
|
|
397
548
|
n,
|
|
398
|
-
|
|
399
|
-
|
|
549
|
+
B(),
|
|
550
|
+
U(),
|
|
400
551
|
i,
|
|
401
552
|
r
|
|
402
553
|
];
|
|
403
554
|
}
|
|
404
|
-
function
|
|
555
|
+
function se(e) {
|
|
405
556
|
let t = e?.srcDir ?? "src", n = e?.outDir ?? "dist", r = [];
|
|
406
557
|
return [{
|
|
407
558
|
name: "glasshome-widgets:build",
|
|
@@ -420,7 +571,7 @@ function $(e) {
|
|
|
420
571
|
if (e === "\0glasshome-noop") return "export {}";
|
|
421
572
|
},
|
|
422
573
|
async closeBundle() {
|
|
423
|
-
await
|
|
574
|
+
await $({
|
|
424
575
|
srcDir: t,
|
|
425
576
|
outDir: n,
|
|
426
577
|
plugins: r
|
|
@@ -429,4 +580,4 @@ function $(e) {
|
|
|
429
580
|
}];
|
|
430
581
|
}
|
|
431
582
|
//#endregion
|
|
432
|
-
export {
|
|
583
|
+
export { E as MAX_EXAMPLES, $ as buildWidgets, ie as createIntrospectSession, H as createUiImportTracker, Z as discoverWidgets, Q as generateRegistry, oe as glasshomeWidget, se as glasshomeWidgets, w as introspectOnce, z as isDirectSyncLayerImportSource, V as isDirectUiImportSource, I as isWidgetExternal, j as runSchemaGuard };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reads a built widget bundle's definition under a fake DOM.
|
|
3
|
+
*
|
|
4
|
+
* A widget bundle cannot be imported by the build process: `solid-js/web`
|
|
5
|
+
* resolves to its server build under Node's conditions and throws the moment
|
|
6
|
+
* the module initialises, and browser conditions then need a DOM for the
|
|
7
|
+
* templates Solid creates at import time. Both callers (the one-shot
|
|
8
|
+
* `introspect.ts` CLI and the persistent `introspect-worker.ts`) run under
|
|
9
|
+
* `--conditions browser` and install a DOM before importing.
|
|
10
|
+
*/
|
|
11
|
+
export interface ExampleConfigIssue {
|
|
12
|
+
index: number;
|
|
13
|
+
label?: string;
|
|
14
|
+
problems: string[];
|
|
15
|
+
}
|
|
16
|
+
export interface WidgetIntrospection {
|
|
17
|
+
manifest: ({
|
|
18
|
+
name?: string;
|
|
19
|
+
configVersion?: number;
|
|
20
|
+
examples?: unknown;
|
|
21
|
+
} & Record<string, unknown>) | null;
|
|
22
|
+
/** JSON Schema derived from the widget's configSchema, when it has one. */
|
|
23
|
+
jsonSchema: object | null;
|
|
24
|
+
/** Examples whose config the widget's own configSchema rejects. */
|
|
25
|
+
exampleConfigIssues: ExampleConfigIssue[];
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Install a fresh DOM. Unconditional: the persistent worker calls this per
|
|
29
|
+
* request, and skipping already-set keys would leak the previous widget's
|
|
30
|
+
* mutated window into the next one.
|
|
31
|
+
*/
|
|
32
|
+
export declare function installDom(): Promise<void>;
|
|
33
|
+
export declare function introspectBundle(bundlePath: string, cacheBust?: string): Promise<WidgetIntrospection>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { WidgetIntrospection } from "./introspect-core";
|
|
2
|
+
export type IntrospectResult = {
|
|
3
|
+
ok: true;
|
|
4
|
+
value: WidgetIntrospection;
|
|
5
|
+
} | {
|
|
6
|
+
ok: false;
|
|
7
|
+
reason: string;
|
|
8
|
+
};
|
|
9
|
+
export interface IntrospectSessionOptions {
|
|
10
|
+
/** Respawn once the worker's RSS crosses this, between requests only. */
|
|
11
|
+
rssLimitBytes?: number;
|
|
12
|
+
/** Per-request ceiling; a module-eval hang can only be cured by a kill. */
|
|
13
|
+
timeoutMs?: number;
|
|
14
|
+
/** Log spawn and import durations above this. */
|
|
15
|
+
slowMs?: number;
|
|
16
|
+
}
|
|
17
|
+
export interface IntrospectSession {
|
|
18
|
+
introspect(outFile: string): Promise<IntrospectResult>;
|
|
19
|
+
dispose(): Promise<void>;
|
|
20
|
+
readonly childPid: number | null;
|
|
21
|
+
}
|
|
22
|
+
/** One process per check. The path `build`, `capture` and the fallback take. */
|
|
23
|
+
export declare function introspectOnce(outFile: string): IntrospectResult;
|
|
24
|
+
/**
|
|
25
|
+
* A worker reused across rebuilds, for callers that own a session.
|
|
26
|
+
*
|
|
27
|
+
* Only `bun widget connect`'s watch loop does: it is the one call site that
|
|
28
|
+
* re-pays the per-check process boot tax on every save.
|
|
29
|
+
*/
|
|
30
|
+
export declare function createIntrospectSession(options?: IntrospectSessionOptions): IntrospectSession;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Long-lived introspection worker for `bun widget connect`.
|
|
3
|
+
*
|
|
4
|
+
* Newline-delimited JSON over stdio. Every request carries an id: a widget
|
|
5
|
+
* bundle can print at module-eval time, and without id pairing one stray line
|
|
6
|
+
* offsets every later response.
|
|
7
|
+
*
|
|
8
|
+
* Usage: bun --conditions browser introspect-worker.js
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { n as e, t } from "../introspect-core-DYv9Uufi.js";
|
|
2
|
+
//#region src/vite/introspect-worker.ts
|
|
3
|
+
var n = process.stdout.write.bind(process.stdout);
|
|
4
|
+
process.stdout.write = ((e, ...t) => process.stderr.write(e, ...t));
|
|
5
|
+
function r(e) {
|
|
6
|
+
n(`${JSON.stringify(e)}\n`);
|
|
7
|
+
}
|
|
8
|
+
function i(e) {
|
|
9
|
+
return e instanceof Error ? e.message : String(e);
|
|
10
|
+
}
|
|
11
|
+
var a = null, o = 0, s = Promise.resolve();
|
|
12
|
+
async function c(n, s) {
|
|
13
|
+
a = n;
|
|
14
|
+
let c = performance.now();
|
|
15
|
+
try {
|
|
16
|
+
await t(), r({
|
|
17
|
+
id: n,
|
|
18
|
+
ok: !0,
|
|
19
|
+
value: await e(s, String(++o)),
|
|
20
|
+
rss: process.memoryUsage().rss,
|
|
21
|
+
importMs: Math.round(performance.now() - c)
|
|
22
|
+
});
|
|
23
|
+
} catch (e) {
|
|
24
|
+
r({
|
|
25
|
+
id: n,
|
|
26
|
+
ok: !1,
|
|
27
|
+
reason: i(e),
|
|
28
|
+
rss: process.memoryUsage().rss,
|
|
29
|
+
importMs: Math.round(performance.now() - c)
|
|
30
|
+
});
|
|
31
|
+
} finally {
|
|
32
|
+
a = null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function l(e) {
|
|
36
|
+
let t;
|
|
37
|
+
try {
|
|
38
|
+
t = JSON.parse(e);
|
|
39
|
+
} catch {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (typeof t.id != "number" || typeof t.outFile != "string") return;
|
|
43
|
+
let { id: n, outFile: r } = t;
|
|
44
|
+
s = s.then(() => c(n, r));
|
|
45
|
+
}
|
|
46
|
+
function u(e) {
|
|
47
|
+
a !== null && r({
|
|
48
|
+
id: a,
|
|
49
|
+
ok: !1,
|
|
50
|
+
reason: i(e),
|
|
51
|
+
rss: process.memoryUsage().rss,
|
|
52
|
+
importMs: 0
|
|
53
|
+
}), process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
process.on("uncaughtException", u), process.on("unhandledRejection", u);
|
|
56
|
+
var d = "";
|
|
57
|
+
process.stdin.setEncoding("utf-8"), process.stdin.on("data", (e) => {
|
|
58
|
+
d += e;
|
|
59
|
+
let t = d.indexOf("\n");
|
|
60
|
+
for (; t >= 0;) {
|
|
61
|
+
let e = d.slice(0, t).trim();
|
|
62
|
+
d = d.slice(t + 1), e && l(e), t = d.indexOf("\n");
|
|
63
|
+
}
|
|
64
|
+
}), process.stdin.on("end", () => process.exit(0)), process.stdin.on("close", () => process.exit(0)), process.stdin.resume();
|
|
65
|
+
var f = process.ppid;
|
|
66
|
+
setInterval(() => {
|
|
67
|
+
try {
|
|
68
|
+
process.kill(f, 0);
|
|
69
|
+
} catch {
|
|
70
|
+
process.exit(0);
|
|
71
|
+
}
|
|
72
|
+
}, 5e3), r({
|
|
73
|
+
id: 0,
|
|
74
|
+
ready: !0
|
|
75
|
+
});
|
|
76
|
+
//#endregion
|
|
@@ -1,21 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Reads a built widget bundle's definition and prints it as JSON.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* initialises, and forcing browser conditions then needs a DOM for the
|
|
8
|
-
* templates Solid creates at import time. The build spawns this with
|
|
9
|
-
* `--conditions browser` and a DOM in place, so the import succeeds.
|
|
10
|
-
*
|
|
11
|
-
* Until this existed the guard swallowed that import failure and silently did
|
|
12
|
-
* nothing — for every widget, on every build.
|
|
4
|
+
* One-shot counterpart to introspect-worker.ts: a fresh process per check, used
|
|
5
|
+
* by `bun widget build`, the render worker's capture, and as the
|
|
6
|
+
* GLASSHOME_WIDGET_NO_WORKER fallback.
|
|
13
7
|
*
|
|
14
8
|
* Usage: bun --conditions browser introspect.js <bundle.js>
|
|
15
|
-
* Prints: { manifest, jsonSchema } on stdout, or exits
|
|
9
|
+
* Prints: { manifest, jsonSchema, exampleConfigIssues } on stdout, or exits
|
|
10
|
+
* non-zero with a reason.
|
|
16
11
|
*/
|
|
17
|
-
export
|
|
18
|
-
index: number;
|
|
19
|
-
label?: string;
|
|
20
|
-
problems: string[];
|
|
21
|
-
}
|
|
12
|
+
export {};
|
package/dist/vite/introspect.js
CHANGED
|
@@ -1,60 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as e, t } from "../introspect-core-DYv9Uufi.js";
|
|
2
2
|
//#region src/vite/introspect.ts
|
|
3
|
-
function
|
|
4
|
-
|
|
3
|
+
async function n() {
|
|
4
|
+
let n = process.argv[2];
|
|
5
|
+
n || (process.stderr.write("introspect: no bundle path given\n"), process.exit(2)), await t(), process.stdout.write(JSON.stringify(await e(n)));
|
|
5
6
|
}
|
|
6
|
-
|
|
7
|
-
if (!t(e) || !Array.isArray(n)) return [];
|
|
8
|
-
let r = [];
|
|
9
|
-
for (let [t, i] of n.entries()) {
|
|
10
|
-
let n = i?.config;
|
|
11
|
-
if (typeof n != "object" || !n) continue;
|
|
12
|
-
let a = e.safeParse(n);
|
|
13
|
-
a.success || r.push({
|
|
14
|
-
index: t,
|
|
15
|
-
label: i.label,
|
|
16
|
-
problems: (a.error?.issues ?? []).map((e) => {
|
|
17
|
-
let t = e.path.map(String).join(".");
|
|
18
|
-
return t ? `${t}: ${e.message}` : e.message;
|
|
19
|
-
})
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
return r;
|
|
23
|
-
}
|
|
24
|
-
async function r() {
|
|
25
|
-
let { Window: e } = await import("happy-dom"), t = new e({ url: "http://localhost" });
|
|
26
|
-
for (let e of [
|
|
27
|
-
"window",
|
|
28
|
-
"document",
|
|
29
|
-
"navigator",
|
|
30
|
-
"location",
|
|
31
|
-
"customElements",
|
|
32
|
-
"HTMLElement",
|
|
33
|
-
"Element",
|
|
34
|
-
"Node",
|
|
35
|
-
"CSS",
|
|
36
|
-
"getComputedStyle",
|
|
37
|
-
"requestAnimationFrame",
|
|
38
|
-
"cancelAnimationFrame",
|
|
39
|
-
"matchMedia"
|
|
40
|
-
]) globalThis[e] === void 0 && (globalThis[e] = t[e]);
|
|
41
|
-
}
|
|
42
|
-
async function i() {
|
|
43
|
-
let t = process.argv[2];
|
|
44
|
-
t || (process.stderr.write("introspect: no bundle path given\n"), process.exit(2)), await r();
|
|
45
|
-
let i = (await import(e(t).href)).default, a = null;
|
|
46
|
-
if (i?.configSchema) {
|
|
47
|
-
let { z: e } = await import("zod");
|
|
48
|
-
a = e.toJSONSchema(i.configSchema, { unrepresentable: "any" });
|
|
49
|
-
}
|
|
50
|
-
let o = {
|
|
51
|
-
manifest: i?.manifest ?? null,
|
|
52
|
-
jsonSchema: a,
|
|
53
|
-
exampleConfigIssues: n(i?.configSchema, i?.manifest?.examples)
|
|
54
|
-
};
|
|
55
|
-
process.stdout.write(JSON.stringify(o));
|
|
56
|
-
}
|
|
57
|
-
i().catch((e) => {
|
|
7
|
+
n().catch((e) => {
|
|
58
8
|
process.stderr.write(`${e instanceof Error ? e.message : String(e)}\n`), process.exit(1);
|
|
59
9
|
});
|
|
60
10
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@glasshome/widget-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.1",
|
|
4
4
|
"description": "SDK for building GlassHome dashboard widgets with SolidJS",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"glasshome",
|
|
@@ -69,6 +69,7 @@
|
|
|
69
69
|
"@tailwindcss/vite": "^4.1.13",
|
|
70
70
|
"clsx": "^2.1.1",
|
|
71
71
|
"happy-dom": "^20.0.0",
|
|
72
|
+
"lightningcss": "^1.33.0",
|
|
72
73
|
"tailwind-merge": "^3.5.0",
|
|
73
74
|
"tailwindcss": "^4.1.13",
|
|
74
75
|
"zod": "^4.1.13"
|
|
@@ -78,7 +79,7 @@
|
|
|
78
79
|
"@solidjs/testing-library": "^0.8.10",
|
|
79
80
|
"@types/bun": "latest",
|
|
80
81
|
"@types/node": "^25.2.2",
|
|
81
|
-
"typescript": "^
|
|
82
|
+
"typescript": "^7.0.2",
|
|
82
83
|
"vite": "^8.1.0",
|
|
83
84
|
"vite-plugin-solid": "^2.11.12",
|
|
84
85
|
"vitest": "^4.1.10"
|