@vertz/ui-server 0.2.47 → 0.2.52
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/CHANGELOG.md +715 -0
- package/dist/bun-dev-server.d.ts +35 -0
- package/dist/bun-dev-server.js +104 -2
- package/dist/bun-plugin/state-inspector.d.ts +59 -0
- package/dist/bun-plugin/state-inspector.js +270 -0
- package/dist/dom-shim/index.d.ts +7 -0
- package/dist/dom-shim/index.js +1 -1
- package/dist/index.d.ts +24 -0
- package/dist/index.js +24 -8
- package/dist/node-handler.d.ts +22 -0
- package/dist/node-handler.js +3 -3
- package/dist/shared/{chunk-szvdd1qq.js → chunk-bp1ez7v4.js} +46 -2
- package/dist/shared/{chunk-tm5aeq94.js → chunk-c22cgv96.js} +39 -5
- package/dist/shared/{chunk-ck3n699k.js → chunk-nstzxw4e.js} +34 -7
- package/dist/shared/{chunk-kzycr5v0.js → chunk-x8r5mh0q.js} +31 -10
- package/dist/ssr/index.d.ts +22 -0
- package/dist/ssr/index.js +3 -3
- package/package.json +9 -5
package/dist/bun-dev-server.d.ts
CHANGED
|
@@ -1,3 +1,32 @@
|
|
|
1
|
+
interface QuerySnapshot {
|
|
2
|
+
data: SerializedValue;
|
|
3
|
+
loading: boolean;
|
|
4
|
+
revalidating: boolean;
|
|
5
|
+
error: SerializedValue;
|
|
6
|
+
idle: boolean;
|
|
7
|
+
key?: string;
|
|
8
|
+
}
|
|
9
|
+
interface InstanceSnapshot {
|
|
10
|
+
index: number;
|
|
11
|
+
key?: string;
|
|
12
|
+
signals: Record<string, SerializedValue>;
|
|
13
|
+
queries: Record<string, QuerySnapshot>;
|
|
14
|
+
}
|
|
15
|
+
interface ComponentSnapshot {
|
|
16
|
+
name: string;
|
|
17
|
+
moduleId: string;
|
|
18
|
+
instanceCount: number;
|
|
19
|
+
instances: InstanceSnapshot[];
|
|
20
|
+
}
|
|
21
|
+
interface StateSnapshot {
|
|
22
|
+
components: ComponentSnapshot[];
|
|
23
|
+
totalInstances: number;
|
|
24
|
+
connectedClients: number;
|
|
25
|
+
timestamp: string;
|
|
26
|
+
message?: string;
|
|
27
|
+
truncated?: boolean;
|
|
28
|
+
}
|
|
29
|
+
type SerializedValue = string | number | boolean | null | object;
|
|
1
30
|
import { AccessSet } from "@vertz/ui/auth";
|
|
2
31
|
interface SessionData {
|
|
3
32
|
user: {
|
|
@@ -198,6 +227,12 @@ interface BunDevServer {
|
|
|
198
227
|
clearErrorForFileChange(): void;
|
|
199
228
|
/** Set the last changed file path (for testing). */
|
|
200
229
|
setLastChangedFile(file: string): void;
|
|
230
|
+
/**
|
|
231
|
+
* Request a state inspection from connected browser clients.
|
|
232
|
+
* Broadcasts an `inspect-state` command and waits for the first
|
|
233
|
+
* `state-snapshot` response. Returns the snapshot JSON or an error.
|
|
234
|
+
*/
|
|
235
|
+
inspectState(filter?: string): Promise<StateSnapshot>;
|
|
201
236
|
}
|
|
202
237
|
interface HMRAssets {
|
|
203
238
|
/** Discovered `/_bun/client/<hash>.js` URL, or null if not found */
|
package/dist/bun-dev-server.js
CHANGED
|
@@ -2060,11 +2060,50 @@ class SSRElement extends SSRNode {
|
|
|
2060
2060
|
set name(value) {
|
|
2061
2061
|
this.attrs.name = value;
|
|
2062
2062
|
}
|
|
2063
|
+
_selectValue;
|
|
2063
2064
|
get value() {
|
|
2065
|
+
if (this.tag === "select") {
|
|
2066
|
+
return this._selectValue ?? "";
|
|
2067
|
+
}
|
|
2064
2068
|
return this.attrs.value ?? "";
|
|
2065
2069
|
}
|
|
2066
2070
|
set value(value) {
|
|
2067
|
-
this.
|
|
2071
|
+
if (this.tag === "select") {
|
|
2072
|
+
this._selectValue = value;
|
|
2073
|
+
this._applySelectValue();
|
|
2074
|
+
} else {
|
|
2075
|
+
this.attrs.value = value;
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
2078
|
+
_applySelectValue() {
|
|
2079
|
+
if (this._selectValue === undefined)
|
|
2080
|
+
return;
|
|
2081
|
+
this._applySelectValueToChildren(this.children);
|
|
2082
|
+
}
|
|
2083
|
+
_applySelectValueToChildren(children) {
|
|
2084
|
+
for (const child of children) {
|
|
2085
|
+
if (child instanceof SSRElement) {
|
|
2086
|
+
if (child.tag === "option") {
|
|
2087
|
+
const optValue = child.attrs.value ?? this._getChildTextContent(child);
|
|
2088
|
+
if (optValue === this._selectValue) {
|
|
2089
|
+
child.attrs.selected = "";
|
|
2090
|
+
} else {
|
|
2091
|
+
delete child.attrs.selected;
|
|
2092
|
+
}
|
|
2093
|
+
} else if (child.tag === "optgroup") {
|
|
2094
|
+
this._applySelectValueToChildren(child.children);
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
}
|
|
2098
|
+
}
|
|
2099
|
+
_getChildTextContent(el) {
|
|
2100
|
+
return el.children.map((c) => {
|
|
2101
|
+
if (typeof c === "string")
|
|
2102
|
+
return c;
|
|
2103
|
+
if (c instanceof SSRElement)
|
|
2104
|
+
return this._getChildTextContent(c);
|
|
2105
|
+
return "";
|
|
2106
|
+
}).join("");
|
|
2068
2107
|
}
|
|
2069
2108
|
get src() {
|
|
2070
2109
|
return this.attrs.src ?? "";
|
|
@@ -2135,9 +2174,14 @@ class SSRElement extends SSRNode {
|
|
|
2135
2174
|
addEventListener(_event, _handler) {}
|
|
2136
2175
|
removeEventListener(_event, _handler) {}
|
|
2137
2176
|
toVNode() {
|
|
2177
|
+
let attrs = { ...this.attrs };
|
|
2178
|
+
if (this.tag === "select") {
|
|
2179
|
+
this._applySelectValue();
|
|
2180
|
+
delete attrs.value;
|
|
2181
|
+
}
|
|
2138
2182
|
return {
|
|
2139
2183
|
tag: this.tag,
|
|
2140
|
-
attrs
|
|
2184
|
+
attrs,
|
|
2141
2185
|
children: this.children.map((child) => {
|
|
2142
2186
|
if (typeof child === "string") {
|
|
2143
2187
|
return this._innerHTML != null ? rawHtml(child) : child;
|
|
@@ -3632,6 +3676,7 @@ function createBunDevServer(options) {
|
|
|
3632
3676
|
let ssrFallback = false;
|
|
3633
3677
|
const wsClients = new Set;
|
|
3634
3678
|
let currentError = null;
|
|
3679
|
+
const pendingInspections = new Map;
|
|
3635
3680
|
const sourceMapResolver = createSourceMapResolver(projectRoot);
|
|
3636
3681
|
let clearGraceUntil = 0;
|
|
3637
3682
|
let runtimeDebounceTimer = null;
|
|
@@ -4098,6 +4143,10 @@ function createBunDevServer(options) {
|
|
|
4098
4143
|
const frInitPath = resolve(devDir, "fast-refresh-init.ts");
|
|
4099
4144
|
writeFileSync3(frInitPath, `import '@vertz/ui-server/fast-refresh-runtime';
|
|
4100
4145
|
if (import.meta.hot) import.meta.hot.accept();
|
|
4146
|
+
`);
|
|
4147
|
+
const siInitPath = resolve(devDir, "state-inspector-init.ts");
|
|
4148
|
+
writeFileSync3(siInitPath, `import '@vertz/ui-server/state-inspector';
|
|
4149
|
+
if (import.meta.hot) import.meta.hot.accept();
|
|
4101
4150
|
`);
|
|
4102
4151
|
const hmrShellHtml = `<!doctype html>
|
|
4103
4152
|
<html lang="en"><head>
|
|
@@ -4105,6 +4154,7 @@ if (import.meta.hot) import.meta.hot.accept();
|
|
|
4105
4154
|
<title>HMR Shell</title>
|
|
4106
4155
|
</head><body>
|
|
4107
4156
|
<script type="module" src="./fast-refresh-init.ts"></script>
|
|
4157
|
+
<script type="module" src="./state-inspector-init.ts"></script>
|
|
4108
4158
|
<script type="module" src="${clientSrc}"></script>
|
|
4109
4159
|
</body></html>`;
|
|
4110
4160
|
const hmrShellPath = resolve(devDir, "hmr-shell.html");
|
|
@@ -4431,6 +4481,13 @@ data: {}
|
|
|
4431
4481
|
devServer.restart();
|
|
4432
4482
|
} else if (data.type === "ping") {
|
|
4433
4483
|
ws.sendText(JSON.stringify({ type: "pong" }));
|
|
4484
|
+
} else if (data.type === "state-snapshot" && data.requestId) {
|
|
4485
|
+
const pending = pendingInspections.get(data.requestId);
|
|
4486
|
+
if (pending) {
|
|
4487
|
+
clearTimeout(pending.timer);
|
|
4488
|
+
pendingInspections.delete(data.requestId);
|
|
4489
|
+
pending.resolve(data.snapshot);
|
|
4490
|
+
}
|
|
4434
4491
|
} else if (data.type === "resolve-stack" && data.stack) {
|
|
4435
4492
|
const selfFetch = async (url) => {
|
|
4436
4493
|
const absUrl = url.startsWith("http") ? url : `http://${host}:${server?.port}${url}`;
|
|
@@ -4803,8 +4860,53 @@ data: {}
|
|
|
4803
4860
|
setLastChangedFile(file) {
|
|
4804
4861
|
lastChangedFile = file;
|
|
4805
4862
|
},
|
|
4863
|
+
async inspectState(filter) {
|
|
4864
|
+
if (wsClients.size === 0) {
|
|
4865
|
+
return {
|
|
4866
|
+
components: [],
|
|
4867
|
+
totalInstances: 0,
|
|
4868
|
+
connectedClients: 0,
|
|
4869
|
+
timestamp: new Date().toISOString(),
|
|
4870
|
+
message: "No browser clients connected. Open the app in a browser first."
|
|
4871
|
+
};
|
|
4872
|
+
}
|
|
4873
|
+
const requestId = crypto.randomUUID();
|
|
4874
|
+
const TIMEOUT_MS = 5000;
|
|
4875
|
+
return new Promise((resolve2) => {
|
|
4876
|
+
const timer = setTimeout(() => {
|
|
4877
|
+
pendingInspections.delete(requestId);
|
|
4878
|
+
resolve2({
|
|
4879
|
+
components: [],
|
|
4880
|
+
totalInstances: 0,
|
|
4881
|
+
connectedClients: wsClients.size,
|
|
4882
|
+
timestamp: new Date().toISOString(),
|
|
4883
|
+
message: "State inspection timed out after 5 seconds."
|
|
4884
|
+
});
|
|
4885
|
+
}, TIMEOUT_MS);
|
|
4886
|
+
pendingInspections.set(requestId, { resolve: resolve2, timer });
|
|
4887
|
+
const cmd = JSON.stringify({
|
|
4888
|
+
type: "inspect-state",
|
|
4889
|
+
requestId,
|
|
4890
|
+
...filter ? { filter } : {}
|
|
4891
|
+
});
|
|
4892
|
+
for (const client of wsClients) {
|
|
4893
|
+
client.sendText(cmd);
|
|
4894
|
+
}
|
|
4895
|
+
});
|
|
4896
|
+
},
|
|
4806
4897
|
async stop() {
|
|
4807
4898
|
stopped = true;
|
|
4899
|
+
for (const [, { resolve: res, timer }] of pendingInspections) {
|
|
4900
|
+
clearTimeout(timer);
|
|
4901
|
+
res({
|
|
4902
|
+
components: [],
|
|
4903
|
+
totalInstances: 0,
|
|
4904
|
+
connectedClients: 0,
|
|
4905
|
+
timestamp: new Date().toISOString(),
|
|
4906
|
+
message: "Server stopped."
|
|
4907
|
+
});
|
|
4908
|
+
}
|
|
4909
|
+
pendingInspections.clear();
|
|
4808
4910
|
if (refreshTimeout) {
|
|
4809
4911
|
clearTimeout(refreshTimeout);
|
|
4810
4912
|
refreshTimeout = null;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
interface QuerySnapshot {
|
|
2
|
+
data: SerializedValue;
|
|
3
|
+
loading: boolean;
|
|
4
|
+
revalidating: boolean;
|
|
5
|
+
error: SerializedValue;
|
|
6
|
+
idle: boolean;
|
|
7
|
+
key?: string;
|
|
8
|
+
}
|
|
9
|
+
interface InstanceSnapshot {
|
|
10
|
+
index: number;
|
|
11
|
+
key?: string;
|
|
12
|
+
signals: Record<string, SerializedValue>;
|
|
13
|
+
queries: Record<string, QuerySnapshot>;
|
|
14
|
+
}
|
|
15
|
+
interface ComponentSnapshot {
|
|
16
|
+
name: string;
|
|
17
|
+
moduleId: string;
|
|
18
|
+
instanceCount: number;
|
|
19
|
+
instances: InstanceSnapshot[];
|
|
20
|
+
}
|
|
21
|
+
interface StateSnapshot {
|
|
22
|
+
components: ComponentSnapshot[];
|
|
23
|
+
totalInstances: number;
|
|
24
|
+
connectedClients: number;
|
|
25
|
+
timestamp: string;
|
|
26
|
+
message?: string;
|
|
27
|
+
truncated?: boolean;
|
|
28
|
+
}
|
|
29
|
+
type SerializedValue = string | number | boolean | null | object;
|
|
30
|
+
/**
|
|
31
|
+
* Serialize any JavaScript value to a JSON-safe representation.
|
|
32
|
+
* Handles functions, DOM nodes, circular references, Date, Map, Set,
|
|
33
|
+
* Error, Promise, Symbol, WeakRef, ArrayBuffer, and depth limiting.
|
|
34
|
+
*/
|
|
35
|
+
declare function safeSerialize(value: unknown, maxDepth?: number, seen?: WeakSet<object>): SerializedValue;
|
|
36
|
+
/**
|
|
37
|
+
* Walk the Fast Refresh registry and collect a state snapshot.
|
|
38
|
+
* Optionally filter by component function name (case-sensitive).
|
|
39
|
+
*/
|
|
40
|
+
declare function collectStateSnapshot(filter?: string): StateSnapshot;
|
|
41
|
+
/**
|
|
42
|
+
* Handle incoming `inspect-state` WebSocket messages.
|
|
43
|
+
* Collects a state snapshot and sends it back with the matching requestId.
|
|
44
|
+
*/
|
|
45
|
+
declare function handleInspectMessage(event: MessageEvent, ws: WebSocket): void;
|
|
46
|
+
/**
|
|
47
|
+
* Set up the state inspector's WebSocket listener.
|
|
48
|
+
*
|
|
49
|
+
* Uses `addEventListener` instead of replacing `onmessage` to coexist with
|
|
50
|
+
* the error overlay handler. Polls for `__vertz_overlay._ws` reference changes
|
|
51
|
+
* so that reconnections (which create a new WebSocket instance) are re-hooked.
|
|
52
|
+
*
|
|
53
|
+
* NOTE: When multiple browser tabs are connected, the server broadcasts
|
|
54
|
+
* inspect-state to all. The first response wins — other tabs' responses are
|
|
55
|
+
* dropped. This is acceptable for v0.1.x; a future improvement could merge
|
|
56
|
+
* responses from multiple tabs.
|
|
57
|
+
*/
|
|
58
|
+
declare function setupStateInspector(): void;
|
|
59
|
+
export { setupStateInspector, safeSerialize, handleInspectMessage, collectStateSnapshot, StateSnapshot, QuerySnapshot, InstanceSnapshot, ComponentSnapshot };
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import"../shared/chunk-pshsm7ck.js";
|
|
3
|
+
|
|
4
|
+
// src/bun-plugin/state-inspector.ts
|
|
5
|
+
var REGISTRY_KEY = Symbol.for("vertz:fast-refresh:registry");
|
|
6
|
+
var MAX_RESPONSE_SIZE = 2 * 1024 * 1024;
|
|
7
|
+
var DEFAULT_MAX_DEPTH = 4;
|
|
8
|
+
var QUERY_SIGNAL_NAMES = ["data", "loading", "revalidating", "error", "idle"];
|
|
9
|
+
function safeSerialize(value, maxDepth = DEFAULT_MAX_DEPTH, seen = new WeakSet) {
|
|
10
|
+
if (value === null)
|
|
11
|
+
return null;
|
|
12
|
+
if (value === undefined)
|
|
13
|
+
return null;
|
|
14
|
+
if (typeof value === "boolean")
|
|
15
|
+
return value;
|
|
16
|
+
if (typeof value === "number") {
|
|
17
|
+
if (Number.isNaN(value))
|
|
18
|
+
return "[NaN]";
|
|
19
|
+
if (!Number.isFinite(value))
|
|
20
|
+
return value > 0 ? "[Infinity]" : "[-Infinity]";
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
if (typeof value === "string")
|
|
24
|
+
return value;
|
|
25
|
+
if (typeof value === "bigint")
|
|
26
|
+
return value.toString();
|
|
27
|
+
if (typeof value === "symbol") {
|
|
28
|
+
const desc = value.description;
|
|
29
|
+
return desc ? `[Symbol: ${desc}]` : "[Symbol]";
|
|
30
|
+
}
|
|
31
|
+
if (typeof value === "function") {
|
|
32
|
+
const name = value.name;
|
|
33
|
+
return name && name !== "anonymous" ? `[Function: ${name}]` : "[Function]";
|
|
34
|
+
}
|
|
35
|
+
const obj = value;
|
|
36
|
+
if (seen.has(obj))
|
|
37
|
+
return "[Circular]";
|
|
38
|
+
if (obj instanceof Date)
|
|
39
|
+
return obj.toISOString();
|
|
40
|
+
if (obj instanceof Error) {
|
|
41
|
+
return { name: obj.name, message: obj.message };
|
|
42
|
+
}
|
|
43
|
+
if (obj instanceof Promise)
|
|
44
|
+
return "[Promise]";
|
|
45
|
+
if (obj instanceof Map)
|
|
46
|
+
return `[Map: ${obj.size} entries]`;
|
|
47
|
+
if (obj instanceof Set)
|
|
48
|
+
return `[Set: ${obj.size} items]`;
|
|
49
|
+
if (obj instanceof WeakMap)
|
|
50
|
+
return "[WeakMap]";
|
|
51
|
+
if (obj instanceof WeakSet)
|
|
52
|
+
return "[WeakSet]";
|
|
53
|
+
if (typeof WeakRef !== "undefined" && obj instanceof WeakRef)
|
|
54
|
+
return "[WeakRef]";
|
|
55
|
+
if (obj instanceof ArrayBuffer)
|
|
56
|
+
return `[ArrayBuffer: ${obj.byteLength} bytes]`;
|
|
57
|
+
if (ArrayBuffer.isView(obj) && "byteLength" in obj) {
|
|
58
|
+
return `[ArrayBuffer: ${obj.byteLength} bytes]`;
|
|
59
|
+
}
|
|
60
|
+
if (typeof HTMLElement !== "undefined" && obj instanceof HTMLElement) {
|
|
61
|
+
return `[HTMLElement: ${obj.tagName}]`;
|
|
62
|
+
}
|
|
63
|
+
if (typeof Node !== "undefined" && obj instanceof Node) {
|
|
64
|
+
return `[Node: ${obj.nodeName}]`;
|
|
65
|
+
}
|
|
66
|
+
if (maxDepth <= 0) {
|
|
67
|
+
if (Array.isArray(obj))
|
|
68
|
+
return `[Array: ${obj.length} items]`;
|
|
69
|
+
return `[Object: ${Object.keys(obj).length} keys]`;
|
|
70
|
+
}
|
|
71
|
+
seen.add(obj);
|
|
72
|
+
if (Array.isArray(obj)) {
|
|
73
|
+
const result2 = obj.map((item) => safeSerialize(item, maxDepth - 1, seen));
|
|
74
|
+
seen.delete(obj);
|
|
75
|
+
return result2;
|
|
76
|
+
}
|
|
77
|
+
const result = {};
|
|
78
|
+
for (const key of Object.keys(obj)) {
|
|
79
|
+
result[key] = safeSerialize(obj[key], maxDepth - 1, seen);
|
|
80
|
+
}
|
|
81
|
+
seen.delete(obj);
|
|
82
|
+
return result;
|
|
83
|
+
}
|
|
84
|
+
function peekSafe(sig) {
|
|
85
|
+
try {
|
|
86
|
+
return sig.peek();
|
|
87
|
+
} catch (e) {
|
|
88
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
89
|
+
return `[Error: ${msg}]`;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function collectStateSnapshot(filter) {
|
|
93
|
+
const registry = globalThis[REGISTRY_KEY];
|
|
94
|
+
if (!registry || registry.size === 0) {
|
|
95
|
+
return emptySnapshot(filter ? `${filter} is not in the component registry. Check the name spelling or ensure the file has been loaded.` : undefined);
|
|
96
|
+
}
|
|
97
|
+
const components = [];
|
|
98
|
+
let totalInstances = 0;
|
|
99
|
+
let foundInRegistry = false;
|
|
100
|
+
for (const [moduleId, moduleMap] of registry) {
|
|
101
|
+
for (const [name, record] of moduleMap) {
|
|
102
|
+
if (filter && name !== filter)
|
|
103
|
+
continue;
|
|
104
|
+
if (filter)
|
|
105
|
+
foundInRegistry = true;
|
|
106
|
+
const instances = [];
|
|
107
|
+
for (let i = 0;i < record.instances.length; i++) {
|
|
108
|
+
const inst = record.instances[i];
|
|
109
|
+
if (!inst.element?.isConnected)
|
|
110
|
+
continue;
|
|
111
|
+
const signals = {};
|
|
112
|
+
const queries = {};
|
|
113
|
+
const queryGroups = new Map;
|
|
114
|
+
const standaloneSignals = [];
|
|
115
|
+
for (const sig of inst.signals) {
|
|
116
|
+
const group = sig._queryGroup;
|
|
117
|
+
if (group) {
|
|
118
|
+
if (!queryGroups.has(group))
|
|
119
|
+
queryGroups.set(group, []);
|
|
120
|
+
queryGroups.get(group).push(sig);
|
|
121
|
+
} else {
|
|
122
|
+
standaloneSignals.push(sig);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
let positionalIdx = 0;
|
|
126
|
+
for (const sig of standaloneSignals) {
|
|
127
|
+
const key = sig._hmrKey ?? `signal_${positionalIdx++}`;
|
|
128
|
+
signals[key] = safeSerialize(peekSafe(sig));
|
|
129
|
+
}
|
|
130
|
+
for (const [groupKey, groupSignals] of queryGroups) {
|
|
131
|
+
queries[groupKey] = buildQuerySnapshot(groupSignals, groupKey);
|
|
132
|
+
}
|
|
133
|
+
instances.push({ index: i, signals, queries });
|
|
134
|
+
totalInstances++;
|
|
135
|
+
}
|
|
136
|
+
if (instances.length > 0) {
|
|
137
|
+
components.push({ name, moduleId, instanceCount: instances.length, instances });
|
|
138
|
+
} else if (filter && foundInRegistry) {}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
let message;
|
|
142
|
+
if (filter && components.length === 0) {
|
|
143
|
+
if (foundInRegistry) {
|
|
144
|
+
let moduleId = "";
|
|
145
|
+
for (const [mid, moduleMap] of registry) {
|
|
146
|
+
if (moduleMap.has(filter)) {
|
|
147
|
+
moduleId = mid;
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
message = `${filter} is registered (in ${moduleId}) but has 0 mounted instances on the current page. Navigate to a page that renders it.`;
|
|
152
|
+
} else {
|
|
153
|
+
message = `${filter} is not in the component registry. Check the name spelling or ensure the file has been loaded.`;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
const snapshot = {
|
|
157
|
+
components,
|
|
158
|
+
totalInstances,
|
|
159
|
+
connectedClients: 0,
|
|
160
|
+
timestamp: new Date().toISOString(),
|
|
161
|
+
...message ? { message } : {}
|
|
162
|
+
};
|
|
163
|
+
const jsonStr = JSON.stringify(snapshot);
|
|
164
|
+
if (jsonStr.length > MAX_RESPONSE_SIZE) {
|
|
165
|
+
return truncateSnapshot(snapshot);
|
|
166
|
+
}
|
|
167
|
+
return snapshot;
|
|
168
|
+
}
|
|
169
|
+
function emptySnapshot(message) {
|
|
170
|
+
return {
|
|
171
|
+
components: [],
|
|
172
|
+
totalInstances: 0,
|
|
173
|
+
connectedClients: 0,
|
|
174
|
+
timestamp: new Date().toISOString(),
|
|
175
|
+
...message ? { message } : {}
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
function buildQuerySnapshot(signals, groupKey) {
|
|
179
|
+
const named = new Map;
|
|
180
|
+
const unnamed = [];
|
|
181
|
+
for (const sig of signals) {
|
|
182
|
+
const val = peekSafe(sig);
|
|
183
|
+
if (sig._hmrKey && QUERY_SIGNAL_NAMES.includes(sig._hmrKey)) {
|
|
184
|
+
named.set(sig._hmrKey, val);
|
|
185
|
+
} else {
|
|
186
|
+
unnamed.push(val);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
data: safeSerialize(named.get("data") ?? unnamed[0] ?? null),
|
|
191
|
+
loading: Boolean(named.get("loading") ?? unnamed[1] ?? false),
|
|
192
|
+
revalidating: Boolean(named.get("revalidating") ?? unnamed[2] ?? false),
|
|
193
|
+
error: safeSerialize(named.get("error") ?? unnamed[3] ?? null),
|
|
194
|
+
idle: Boolean(named.get("idle") ?? unnamed[4] ?? false),
|
|
195
|
+
key: groupKey
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
function handleInspectMessage(event, ws) {
|
|
199
|
+
if (typeof event.data !== "string")
|
|
200
|
+
return;
|
|
201
|
+
try {
|
|
202
|
+
const msg = JSON.parse(event.data);
|
|
203
|
+
if (msg.type === "inspect-state") {
|
|
204
|
+
const snapshot = collectStateSnapshot(msg.filter ?? undefined);
|
|
205
|
+
ws.send(JSON.stringify({
|
|
206
|
+
type: "state-snapshot",
|
|
207
|
+
requestId: msg.requestId,
|
|
208
|
+
snapshot
|
|
209
|
+
}));
|
|
210
|
+
}
|
|
211
|
+
} catch {}
|
|
212
|
+
}
|
|
213
|
+
function setupStateInspector() {
|
|
214
|
+
if (typeof window === "undefined")
|
|
215
|
+
return;
|
|
216
|
+
let currentWs = null;
|
|
217
|
+
const MAX_INIT_RETRIES = 10;
|
|
218
|
+
let initRetries = 0;
|
|
219
|
+
function hookWs(ws) {
|
|
220
|
+
if (ws === currentWs)
|
|
221
|
+
return;
|
|
222
|
+
currentWs = ws;
|
|
223
|
+
ws.addEventListener("message", (event) => {
|
|
224
|
+
handleInspectMessage(event, ws);
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
function poll() {
|
|
228
|
+
const overlay = window.__vertz_overlay;
|
|
229
|
+
if (!overlay) {
|
|
230
|
+
if (initRetries++ < MAX_INIT_RETRIES) {
|
|
231
|
+
setTimeout(poll, 500);
|
|
232
|
+
}
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
const checkWs = () => {
|
|
236
|
+
if (overlay._ws && overlay._ws !== currentWs) {
|
|
237
|
+
hookWs(overlay._ws);
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
checkWs();
|
|
241
|
+
setInterval(checkWs, 2000);
|
|
242
|
+
}
|
|
243
|
+
poll();
|
|
244
|
+
}
|
|
245
|
+
if (typeof document !== "undefined") {
|
|
246
|
+
if (document.readyState === "loading") {
|
|
247
|
+
document.addEventListener("DOMContentLoaded", setupStateInspector);
|
|
248
|
+
} else {
|
|
249
|
+
setupStateInspector();
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
function truncateSnapshot(snapshot) {
|
|
253
|
+
const truncated = {
|
|
254
|
+
...snapshot,
|
|
255
|
+
truncated: true,
|
|
256
|
+
components: snapshot.components.map((comp) => ({
|
|
257
|
+
...comp,
|
|
258
|
+
instances: comp.instances.slice(0, 3),
|
|
259
|
+
instanceCount: comp.instanceCount
|
|
260
|
+
}))
|
|
261
|
+
};
|
|
262
|
+
truncated.totalInstances = truncated.components.reduce((sum, c) => sum + c.instances.length, 0);
|
|
263
|
+
return truncated;
|
|
264
|
+
}
|
|
265
|
+
export {
|
|
266
|
+
setupStateInspector,
|
|
267
|
+
safeSerialize,
|
|
268
|
+
handleInspectMessage,
|
|
269
|
+
collectStateSnapshot
|
|
270
|
+
};
|
package/dist/dom-shim/index.d.ts
CHANGED
|
@@ -113,8 +113,15 @@ declare class SSRElement extends SSRNode {
|
|
|
113
113
|
set type(value: string);
|
|
114
114
|
get name(): string;
|
|
115
115
|
set name(value: string);
|
|
116
|
+
/** Internal storage for select.value (not serialized as an attribute). */
|
|
117
|
+
private _selectValue;
|
|
116
118
|
get value(): string;
|
|
117
119
|
set value(value: string);
|
|
120
|
+
/** Walk <option> children and set/remove "selected" to match _selectValue. */
|
|
121
|
+
private _applySelectValue;
|
|
122
|
+
private _applySelectValueToChildren;
|
|
123
|
+
/** Get concatenated text content of an element's children. */
|
|
124
|
+
private _getChildTextContent;
|
|
118
125
|
get src(): string;
|
|
119
126
|
set src(value: string);
|
|
120
127
|
get alt(): string;
|
package/dist/dom-shim/index.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -536,6 +536,28 @@ interface SSRHandlerOptions {
|
|
|
536
536
|
* (JSON files, third-party APIs, custom DB clients).
|
|
537
537
|
*/
|
|
538
538
|
aotDataResolver?: AotDataResolver;
|
|
539
|
+
/**
|
|
540
|
+
* Derive attributes to set on the `<html>` tag from the incoming request.
|
|
541
|
+
*
|
|
542
|
+
* Useful for setting `data-theme`, `dir`, `lang`, or other attributes that
|
|
543
|
+
* must be on `<html>` to avoid FOUC. The callback runs before SSR rendering
|
|
544
|
+
* so the attributes are available in the first byte of the response.
|
|
545
|
+
*
|
|
546
|
+
* If the template already has an attribute with the same name, the callback's
|
|
547
|
+
* value overrides it. Values are HTML-escaped automatically. Keys must be
|
|
548
|
+
* valid HTML attribute names (`/^[a-zA-Z][a-zA-Z0-9-]*$/`).
|
|
549
|
+
*
|
|
550
|
+
* Return `undefined`, `null`, or `{}` to skip injection.
|
|
551
|
+
*
|
|
552
|
+
* @example
|
|
553
|
+
* ```ts
|
|
554
|
+
* htmlAttributes: (request) => ({
|
|
555
|
+
* 'data-theme': getThemeCookie(request) ?? 'dark',
|
|
556
|
+
* dir: getDirection(request),
|
|
557
|
+
* })
|
|
558
|
+
* ```
|
|
559
|
+
*/
|
|
560
|
+
htmlAttributes?: (request: Request) => Record<string, string> | null | undefined;
|
|
539
561
|
}
|
|
540
562
|
declare function createSSRHandler(options: SSRHandlerOptions): (request: Request) => Promise<Response>;
|
|
541
563
|
type NodeHandlerOptions = SSRHandlerOptions;
|
|
@@ -741,6 +763,8 @@ interface AotBarrelResult {
|
|
|
741
763
|
* Write each entry as `<tempDir>/<filename>.ts` alongside the barrel.
|
|
742
764
|
*/
|
|
743
765
|
files: Record<string, string>;
|
|
766
|
+
/** Function names skipped due to residual JSX in compiled output. */
|
|
767
|
+
skippedFns: string[];
|
|
744
768
|
}
|
|
745
769
|
/**
|
|
746
770
|
* Generate a barrel module that re-exports __ssr_* functions from compiled files.
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createSSRHandler,
|
|
3
3
|
loadAotManifest
|
|
4
|
-
} from "./shared/chunk-
|
|
4
|
+
} from "./shared/chunk-nstzxw4e.js";
|
|
5
5
|
import {
|
|
6
6
|
createNodeHandler
|
|
7
|
-
} from "./shared/chunk-
|
|
7
|
+
} from "./shared/chunk-x8r5mh0q.js";
|
|
8
8
|
import {
|
|
9
9
|
clearRouteCssCache,
|
|
10
10
|
collectStreamChunks,
|
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
ssrStreamNavQueries,
|
|
35
35
|
streamToString,
|
|
36
36
|
toPrefetchSession
|
|
37
|
-
} from "./shared/chunk-
|
|
37
|
+
} from "./shared/chunk-c22cgv96.js";
|
|
38
38
|
import {
|
|
39
39
|
clearGlobalSSRTimeout,
|
|
40
40
|
createSSRAdapter,
|
|
@@ -46,7 +46,7 @@ import {
|
|
|
46
46
|
registerSSRQuery,
|
|
47
47
|
setGlobalSSRTimeout,
|
|
48
48
|
ssrStorage
|
|
49
|
-
} from "./shared/chunk-
|
|
49
|
+
} from "./shared/chunk-bp1ez7v4.js";
|
|
50
50
|
import {
|
|
51
51
|
__commonJS,
|
|
52
52
|
__require,
|
|
@@ -171578,26 +171578,39 @@ function generateAotBarrel(compiledFiles, routeMap, appEntry) {
|
|
|
171578
171578
|
"import { __esc, __esc_attr, __ssr_spread, __ssr_style_object } from '@vertz/ui-server';"
|
|
171579
171579
|
];
|
|
171580
171580
|
const files = {};
|
|
171581
|
+
const skippedFns = [];
|
|
171581
171582
|
let fileIndex = 0;
|
|
171582
171583
|
for (const [filePath, fns] of fileToFns) {
|
|
171583
171584
|
const moduleName = basename(filePath, ".tsx").replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
171584
171585
|
const tempFileName = `__aot_${fileIndex}_${moduleName}`;
|
|
171585
171586
|
const moduleRef = `./${tempFileName}.ts`;
|
|
171586
|
-
lines.push(`export { ${fns.sort().join(", ")} } from '${moduleRef}';`);
|
|
171587
171587
|
const compiled = compiledFiles[filePath];
|
|
171588
171588
|
if (compiled) {
|
|
171589
171589
|
const helperImport = `import { __esc, __esc_attr, __ssr_spread, __ssr_style_object } from '@vertz/ui-server';
|
|
171590
171590
|
` + `import type { SSRAotContext } from '@vertz/ui-server';
|
|
171591
171591
|
`;
|
|
171592
|
-
const
|
|
171593
|
-
|
|
171592
|
+
const cleanFns = [];
|
|
171593
|
+
for (const fn of fns) {
|
|
171594
|
+
const fnCode = extractSsrFunctions(compiled.code, [fn]);
|
|
171595
|
+
if (hasResidualJsx(fnCode)) {
|
|
171596
|
+
skippedFns.push(fn);
|
|
171597
|
+
} else {
|
|
171598
|
+
cleanFns.push(fn);
|
|
171599
|
+
}
|
|
171600
|
+
}
|
|
171601
|
+
if (cleanFns.length > 0) {
|
|
171602
|
+
const extracted = extractSsrFunctions(compiled.code, cleanFns);
|
|
171603
|
+
lines.push(`export { ${cleanFns.sort().join(", ")} } from '${moduleRef}';`);
|
|
171604
|
+
files[`${tempFileName}.ts`] = helperImport + extracted;
|
|
171605
|
+
}
|
|
171594
171606
|
}
|
|
171595
171607
|
fileIndex++;
|
|
171596
171608
|
}
|
|
171597
171609
|
return {
|
|
171598
171610
|
barrelSource: lines.join(`
|
|
171599
171611
|
`),
|
|
171600
|
-
files
|
|
171612
|
+
files,
|
|
171613
|
+
skippedFns
|
|
171601
171614
|
};
|
|
171602
171615
|
}
|
|
171603
171616
|
function extractSsrFunctions(code, fnNames) {
|
|
@@ -171624,6 +171637,9 @@ function extractSsrFunctions(code, fnNames) {
|
|
|
171624
171637
|
return extracted.join(`
|
|
171625
171638
|
`);
|
|
171626
171639
|
}
|
|
171640
|
+
function hasResidualJsx(code) {
|
|
171641
|
+
return /<[A-Za-z]\w*\s+[\w-]+=\{/.test(code);
|
|
171642
|
+
}
|
|
171627
171643
|
function findAppComponent(components) {
|
|
171628
171644
|
for (const [name, comp] of Object.entries(components)) {
|
|
171629
171645
|
if (comp.tier === "runtime-fallback")
|