@hueest/xray 0.1.0 → 0.2.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/{serialize-Bq6yJnNV.js → breakpoints-CMoFUUOv.js} +426 -104
- package/dist/client.d.ts +38 -3
- package/dist/client.js +716 -27
- package/dist/core.d.ts +8 -3
- package/dist/core.js +12 -6
- package/dist/css-escape-N7bOusGW.js +52 -0
- package/dist/hud.js +46 -0
- package/dist/index.d.ts +231 -5
- package/dist/index.js +753 -71
- package/dist/{plate-DboxMKg-.d.ts → plate-Bv6W-GkA.d.ts} +142 -0
- package/dist/plate-DoE1HEXp.js +152 -0
- package/dist/react.core-BzMG_cDy.d.ts +79 -0
- package/dist/react.core-IyFy2b_8.js +267 -0
- package/dist/react.d.ts +2 -8
- package/dist/react.dev.d.ts +2 -9
- package/dist/react.dev.js +153 -23
- package/dist/react.js +24 -12
- package/dist/serialize-B--oY2bV.d.ts +143 -0
- package/package.json +7 -7
- package/virtual.d.ts +6 -0
- package/dist/breakpoints-CBNlh7lQ.js +0 -47
- package/dist/plate-BuzRMPx4.js +0 -94
- package/dist/react.core-CqnDjfAJ.js +0 -59
- package/dist/react.core-fgWG_svU.d.ts +0 -21
- package/dist/serialize.d.ts +0 -88
- package/dist/serialize.js +0 -2
package/dist/client.js
CHANGED
|
@@ -1,5 +1,531 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { a as captureRegime, c as formatDiagnostic, l as makeDiagnostic, n as regimeFor, r as CaptureTooLargeError, s as diagnosticsHeader, t as deriveBreakpoints } from "./breakpoints-CMoFUUOv.js";
|
|
2
|
+
//#region ../../node_modules/.pnpm/devalue@5.8.1/node_modules/devalue/src/base64.js
|
|
3
|
+
/** @type {(array_buffer: ArrayBuffer) => string} */
|
|
4
|
+
function encode_native(array_buffer) {
|
|
5
|
+
return new Uint8Array(array_buffer).toBase64();
|
|
6
|
+
}
|
|
7
|
+
/** @type {(base64: string) => ArrayBuffer} */
|
|
8
|
+
function decode_native(base64) {
|
|
9
|
+
return Uint8Array.fromBase64(base64).buffer;
|
|
10
|
+
}
|
|
11
|
+
/** @type {(array_buffer: ArrayBuffer) => string} */
|
|
12
|
+
function encode_buffer(array_buffer) {
|
|
13
|
+
return Buffer.from(array_buffer).toString("base64");
|
|
14
|
+
}
|
|
15
|
+
/** @type {(base64: string) => ArrayBuffer} */
|
|
16
|
+
function decode_buffer(base64) {
|
|
17
|
+
return Uint8Array.from(Buffer.from(base64, "base64")).buffer;
|
|
18
|
+
}
|
|
19
|
+
/** @type {(array_buffer: ArrayBuffer) => string} */
|
|
20
|
+
function encode_legacy(array_buffer) {
|
|
21
|
+
const array = new Uint8Array(array_buffer);
|
|
22
|
+
let binary = "";
|
|
23
|
+
const chunk_size = 32768;
|
|
24
|
+
for (let i = 0; i < array.length; i += chunk_size) {
|
|
25
|
+
const chunk = array.subarray(i, i + chunk_size);
|
|
26
|
+
binary += String.fromCharCode.apply(null, chunk);
|
|
27
|
+
}
|
|
28
|
+
return btoa(binary);
|
|
29
|
+
}
|
|
30
|
+
/** @type {(base64: string) => ArrayBuffer} */
|
|
31
|
+
function decode_legacy(base64) {
|
|
32
|
+
const binary_string = atob(base64);
|
|
33
|
+
const len = binary_string.length;
|
|
34
|
+
const array = new Uint8Array(len);
|
|
35
|
+
for (let i = 0; i < len; i++) array[i] = binary_string.charCodeAt(i);
|
|
36
|
+
return array.buffer;
|
|
37
|
+
}
|
|
38
|
+
const native = typeof Uint8Array.fromBase64 === "function";
|
|
39
|
+
const buffer = typeof process === "object" && process.versions?.node !== void 0;
|
|
40
|
+
const encode64 = native ? encode_native : buffer ? encode_buffer : encode_legacy;
|
|
41
|
+
const decode64 = native ? decode_native : buffer ? decode_buffer : decode_legacy;
|
|
42
|
+
//#endregion
|
|
43
|
+
//#region ../../node_modules/.pnpm/devalue@5.8.1/node_modules/devalue/src/constants.js
|
|
44
|
+
const MAX_ARRAY_LEN = 2 ** 32 - 1;
|
|
45
|
+
const MAX_ARRAY_INDEX = MAX_ARRAY_LEN - 1;
|
|
46
|
+
//#endregion
|
|
47
|
+
//#region ../../node_modules/.pnpm/devalue@5.8.1/node_modules/devalue/src/utils.js
|
|
48
|
+
var DevalueError = class extends Error {
|
|
49
|
+
/**
|
|
50
|
+
* @param {string} message
|
|
51
|
+
* @param {string[]} keys
|
|
52
|
+
* @param {any} [value] - The value that failed to be serialized
|
|
53
|
+
* @param {any} [root] - The root value being serialized
|
|
54
|
+
*/
|
|
55
|
+
constructor(message, keys, value, root) {
|
|
56
|
+
super(message);
|
|
57
|
+
this.name = "DevalueError";
|
|
58
|
+
this.path = keys.join("");
|
|
59
|
+
this.value = value;
|
|
60
|
+
this.root = root;
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
/** @param {any} thing */
|
|
64
|
+
function is_primitive(thing) {
|
|
65
|
+
return thing === null || typeof thing !== "object" && typeof thing !== "function";
|
|
66
|
+
}
|
|
67
|
+
const object_proto_names = /* @__PURE__ */ Object.getOwnPropertyNames(Object.prototype).sort().join("\0");
|
|
68
|
+
/** @param {any} thing */
|
|
69
|
+
function is_plain_object(thing) {
|
|
70
|
+
const proto = Object.getPrototypeOf(thing);
|
|
71
|
+
return proto === Object.prototype || proto === null || Object.getPrototypeOf(proto) === null || Object.getOwnPropertyNames(proto).sort().join("\0") === object_proto_names;
|
|
72
|
+
}
|
|
73
|
+
/** @param {any} thing */
|
|
74
|
+
function get_type(thing) {
|
|
75
|
+
return Object.prototype.toString.call(thing).slice(8, -1);
|
|
76
|
+
}
|
|
77
|
+
/** @param {string} char */
|
|
78
|
+
function get_escaped_char(char) {
|
|
79
|
+
switch (char) {
|
|
80
|
+
case "\"": return "\\\"";
|
|
81
|
+
case "<": return "\\u003C";
|
|
82
|
+
case "\\": return "\\\\";
|
|
83
|
+
case "\n": return "\\n";
|
|
84
|
+
case "\r": return "\\r";
|
|
85
|
+
case " ": return "\\t";
|
|
86
|
+
case "\b": return "\\b";
|
|
87
|
+
case "\f": return "\\f";
|
|
88
|
+
case "\u2028": return "\\u2028";
|
|
89
|
+
case "\u2029": return "\\u2029";
|
|
90
|
+
default: return char < " " ? `\\u${char.charCodeAt(0).toString(16).padStart(4, "0")}` : "";
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/** @param {string} str */
|
|
94
|
+
function stringify_string(str) {
|
|
95
|
+
let result = "";
|
|
96
|
+
let last_pos = 0;
|
|
97
|
+
const len = str.length;
|
|
98
|
+
for (let i = 0; i < len; i += 1) {
|
|
99
|
+
const char = str[i];
|
|
100
|
+
const replacement = get_escaped_char(char);
|
|
101
|
+
if (replacement) {
|
|
102
|
+
result += str.slice(last_pos, i) + replacement;
|
|
103
|
+
last_pos = i + 1;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return `"${last_pos === 0 ? str : result + str.slice(last_pos)}"`;
|
|
107
|
+
}
|
|
108
|
+
/** @param {Record<string | symbol, any>} object */
|
|
109
|
+
function enumerable_symbols(object) {
|
|
110
|
+
return Object.getOwnPropertySymbols(object).filter((symbol) => Object.getOwnPropertyDescriptor(object, symbol).enumerable);
|
|
111
|
+
}
|
|
112
|
+
const is_identifier = /^[a-zA-Z_$][a-zA-Z_$0-9]*$/;
|
|
113
|
+
/** @param {string} key */
|
|
114
|
+
function stringify_key(key) {
|
|
115
|
+
return is_identifier.test(key) ? "." + key : "[" + JSON.stringify(key) + "]";
|
|
116
|
+
}
|
|
117
|
+
/** @param {number} n */
|
|
118
|
+
function is_valid_array_index(n) {
|
|
119
|
+
if (!Number.isInteger(n)) return false;
|
|
120
|
+
if (n < 0) return false;
|
|
121
|
+
if (n > MAX_ARRAY_INDEX) return false;
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
/** @param {number} n */
|
|
125
|
+
function is_valid_array_len(n) {
|
|
126
|
+
if (!Number.isInteger(n)) return false;
|
|
127
|
+
if (n < 0) return false;
|
|
128
|
+
if (n > MAX_ARRAY_LEN) return false;
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
/** @param {string} s */
|
|
132
|
+
function is_valid_array_index_string(s) {
|
|
133
|
+
if (s.length === 0) return false;
|
|
134
|
+
if (s.length > 1 && s.charCodeAt(0) === 48) return false;
|
|
135
|
+
for (let i = 0; i < s.length; i++) {
|
|
136
|
+
const c = s.charCodeAt(i);
|
|
137
|
+
if (c < 48 || c > 57) return false;
|
|
138
|
+
}
|
|
139
|
+
return is_valid_array_index(+s);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Finds the populated indices of an array.
|
|
143
|
+
* @param {unknown[]} array
|
|
144
|
+
*/
|
|
145
|
+
function valid_array_indices(array) {
|
|
146
|
+
const keys = Object.keys(array);
|
|
147
|
+
for (var i = keys.length - 1; i >= 0; i--) if (is_valid_array_index_string(keys[i])) break;
|
|
148
|
+
keys.length = i + 1;
|
|
149
|
+
return keys;
|
|
150
|
+
}
|
|
151
|
+
//#endregion
|
|
152
|
+
//#region ../../node_modules/.pnpm/devalue@5.8.1/node_modules/devalue/src/parse.js
|
|
153
|
+
/**
|
|
154
|
+
* Revive a value serialized with `devalue.stringify`
|
|
155
|
+
* @param {string} serialized
|
|
156
|
+
* @param {Record<string, (value: any) => any>} [revivers]
|
|
157
|
+
*/
|
|
158
|
+
function parse(serialized, revivers) {
|
|
159
|
+
return unflatten(JSON.parse(serialized), revivers);
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Revive a value flattened with `devalue.stringify`
|
|
163
|
+
* @param {number | any[]} parsed
|
|
164
|
+
* @param {Record<string, (value: any) => any>} [revivers]
|
|
165
|
+
*/
|
|
166
|
+
function unflatten(parsed, revivers) {
|
|
167
|
+
if (typeof parsed === "number") return hydrate(parsed, true);
|
|
168
|
+
if (!Array.isArray(parsed) || parsed.length === 0) throw new Error("Invalid input");
|
|
169
|
+
const values = parsed;
|
|
170
|
+
const hydrated = Array(values.length);
|
|
171
|
+
/**
|
|
172
|
+
* A set of values currently being hydrated with custom revivers,
|
|
173
|
+
* used to detect invalid cyclical dependencies
|
|
174
|
+
* @type {Set<number> | null}
|
|
175
|
+
*/
|
|
176
|
+
let hydrating = null;
|
|
177
|
+
/**
|
|
178
|
+
* @param {number} index
|
|
179
|
+
* @returns {any}
|
|
180
|
+
*/
|
|
181
|
+
function hydrate(index, standalone = false) {
|
|
182
|
+
if (index === -1) return void 0;
|
|
183
|
+
if (index === -3) return NaN;
|
|
184
|
+
if (index === -4) return Infinity;
|
|
185
|
+
if (index === -5) return -Infinity;
|
|
186
|
+
if (index === -6) return -0;
|
|
187
|
+
if (standalone || typeof index !== "number") throw new Error(`Invalid input`);
|
|
188
|
+
if (index in hydrated) return hydrated[index];
|
|
189
|
+
const value = values[index];
|
|
190
|
+
if (!value || typeof value !== "object") hydrated[index] = value;
|
|
191
|
+
else if (Array.isArray(value)) if (typeof value[0] === "string") {
|
|
192
|
+
const type = value[0];
|
|
193
|
+
const reviver = revivers && Object.hasOwn(revivers, type) ? revivers[type] : void 0;
|
|
194
|
+
if (reviver) {
|
|
195
|
+
let i = value[1];
|
|
196
|
+
if (typeof i !== "number") i = values.push(value[1]) - 1;
|
|
197
|
+
hydrating ??= /* @__PURE__ */ new Set();
|
|
198
|
+
if (hydrating.has(i)) throw new Error("Invalid circular reference");
|
|
199
|
+
hydrating.add(i);
|
|
200
|
+
hydrated[index] = reviver(hydrate(i));
|
|
201
|
+
hydrating.delete(i);
|
|
202
|
+
return hydrated[index];
|
|
203
|
+
}
|
|
204
|
+
switch (type) {
|
|
205
|
+
case "Date":
|
|
206
|
+
hydrated[index] = new Date(value[1]);
|
|
207
|
+
break;
|
|
208
|
+
case "Set":
|
|
209
|
+
const set = /* @__PURE__ */ new Set();
|
|
210
|
+
hydrated[index] = set;
|
|
211
|
+
for (let i = 1; i < value.length; i += 1) set.add(hydrate(value[i]));
|
|
212
|
+
break;
|
|
213
|
+
case "Map":
|
|
214
|
+
const map = /* @__PURE__ */ new Map();
|
|
215
|
+
hydrated[index] = map;
|
|
216
|
+
for (let i = 1; i < value.length; i += 2) map.set(hydrate(value[i]), hydrate(value[i + 1]));
|
|
217
|
+
break;
|
|
218
|
+
case "RegExp":
|
|
219
|
+
hydrated[index] = new RegExp(value[1], value[2]);
|
|
220
|
+
break;
|
|
221
|
+
case "Object": {
|
|
222
|
+
const wrapped_index = value[1];
|
|
223
|
+
if (typeof values[wrapped_index] === "object" && values[wrapped_index][0] !== "BigInt") throw new Error("Invalid input");
|
|
224
|
+
hydrated[index] = Object(hydrate(wrapped_index));
|
|
225
|
+
break;
|
|
226
|
+
}
|
|
227
|
+
case "BigInt":
|
|
228
|
+
hydrated[index] = BigInt(value[1]);
|
|
229
|
+
break;
|
|
230
|
+
case "null":
|
|
231
|
+
const obj = Object.create(null);
|
|
232
|
+
hydrated[index] = obj;
|
|
233
|
+
for (let i = 1; i < value.length; i += 2) {
|
|
234
|
+
if (value[i] === "__proto__") throw new Error("Cannot parse an object with a `__proto__` property");
|
|
235
|
+
obj[value[i]] = hydrate(value[i + 1]);
|
|
236
|
+
}
|
|
237
|
+
break;
|
|
238
|
+
case "Int8Array":
|
|
239
|
+
case "Uint8Array":
|
|
240
|
+
case "Uint8ClampedArray":
|
|
241
|
+
case "Int16Array":
|
|
242
|
+
case "Uint16Array":
|
|
243
|
+
case "Float16Array":
|
|
244
|
+
case "Int32Array":
|
|
245
|
+
case "Uint32Array":
|
|
246
|
+
case "Float32Array":
|
|
247
|
+
case "Float64Array":
|
|
248
|
+
case "BigInt64Array":
|
|
249
|
+
case "BigUint64Array":
|
|
250
|
+
case "DataView": {
|
|
251
|
+
if (values[value[1]][0] !== "ArrayBuffer") throw new Error("Invalid data");
|
|
252
|
+
const TypedArrayConstructor = globalThis[type];
|
|
253
|
+
const buffer = hydrate(value[1]);
|
|
254
|
+
hydrated[index] = value[2] !== void 0 ? new TypedArrayConstructor(buffer, value[2], value[3]) : new TypedArrayConstructor(buffer);
|
|
255
|
+
break;
|
|
256
|
+
}
|
|
257
|
+
case "ArrayBuffer": {
|
|
258
|
+
const base64 = value[1];
|
|
259
|
+
if (typeof base64 !== "string") throw new Error("Invalid ArrayBuffer encoding");
|
|
260
|
+
hydrated[index] = decode64(base64);
|
|
261
|
+
break;
|
|
262
|
+
}
|
|
263
|
+
case "Temporal.Duration":
|
|
264
|
+
case "Temporal.Instant":
|
|
265
|
+
case "Temporal.PlainDate":
|
|
266
|
+
case "Temporal.PlainTime":
|
|
267
|
+
case "Temporal.PlainDateTime":
|
|
268
|
+
case "Temporal.PlainMonthDay":
|
|
269
|
+
case "Temporal.PlainYearMonth":
|
|
270
|
+
case "Temporal.ZonedDateTime": {
|
|
271
|
+
const temporalName = type.slice(9);
|
|
272
|
+
hydrated[index] = Temporal[temporalName].from(value[1]);
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
case "URL":
|
|
276
|
+
hydrated[index] = new URL(value[1]);
|
|
277
|
+
break;
|
|
278
|
+
case "URLSearchParams":
|
|
279
|
+
hydrated[index] = new URLSearchParams(value[1]);
|
|
280
|
+
break;
|
|
281
|
+
default: throw new Error(`Unknown type ${type}`);
|
|
282
|
+
}
|
|
283
|
+
} else if (value[0] === -7) {
|
|
284
|
+
const len = value[1];
|
|
285
|
+
if (!is_valid_array_len(len)) throw new Error("Invalid input");
|
|
286
|
+
/** @type {any[]} */
|
|
287
|
+
const array = [];
|
|
288
|
+
hydrated[index] = array;
|
|
289
|
+
array[MAX_ARRAY_INDEX] = void 0;
|
|
290
|
+
delete array[MAX_ARRAY_INDEX];
|
|
291
|
+
for (let i = 2; i < value.length; i += 2) {
|
|
292
|
+
const idx = value[i];
|
|
293
|
+
if (!is_valid_array_index(idx) || idx >= len) throw new Error("Invalid input");
|
|
294
|
+
array[idx] = hydrate(value[i + 1]);
|
|
295
|
+
}
|
|
296
|
+
array.length = len;
|
|
297
|
+
} else {
|
|
298
|
+
const array = new Array(value.length);
|
|
299
|
+
hydrated[index] = array;
|
|
300
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
301
|
+
const n = value[i];
|
|
302
|
+
if (n === -2) continue;
|
|
303
|
+
array[i] = hydrate(n);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
/** @type {Record<string, any>} */
|
|
308
|
+
const object = {};
|
|
309
|
+
hydrated[index] = object;
|
|
310
|
+
for (const key of Object.keys(value)) {
|
|
311
|
+
if (key === "__proto__") throw new Error("Cannot parse an object with a `__proto__` property");
|
|
312
|
+
const n = value[key];
|
|
313
|
+
object[key] = hydrate(n);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return hydrated[index];
|
|
317
|
+
}
|
|
318
|
+
return hydrate(0);
|
|
319
|
+
}
|
|
320
|
+
//#endregion
|
|
321
|
+
//#region ../../node_modules/.pnpm/devalue@5.8.1/node_modules/devalue/src/stringify.js
|
|
322
|
+
/**
|
|
323
|
+
* Turn a value into a JSON string that can be parsed with `devalue.parse`
|
|
324
|
+
* @param {any} value
|
|
325
|
+
* @param {Record<string, (value: any) => any>} [reducers]
|
|
326
|
+
*/
|
|
327
|
+
function stringify(value, reducers) {
|
|
328
|
+
const stringified = run(false, value, reducers);
|
|
329
|
+
return typeof stringified === "string" ? stringified : `[${stringified.join(",")}]`;
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* @param {boolean} async
|
|
333
|
+
* @param {any} value
|
|
334
|
+
* @param {Record<string, (value: any) => any>} [reducers]
|
|
335
|
+
*/
|
|
336
|
+
function run(async, value, reducers) {
|
|
337
|
+
/** @type {any[]} */
|
|
338
|
+
const stringified = [];
|
|
339
|
+
/** @type {Map<any, number>} */
|
|
340
|
+
const indexes = /* @__PURE__ */ new Map();
|
|
341
|
+
/** @type {Array<{ key: string, fn: (value: any) => any }>} */
|
|
342
|
+
const custom = [];
|
|
343
|
+
if (reducers) for (const key of Object.getOwnPropertyNames(reducers)) custom.push({
|
|
344
|
+
key,
|
|
345
|
+
fn: reducers[key]
|
|
346
|
+
});
|
|
347
|
+
/** @type {string[]} */
|
|
348
|
+
const keys = [];
|
|
349
|
+
let p = 0;
|
|
350
|
+
/**
|
|
351
|
+
* @param {any} thing
|
|
352
|
+
* @param {number} [index]
|
|
353
|
+
*/
|
|
354
|
+
function flatten(thing, index) {
|
|
355
|
+
if (thing === void 0) return -1;
|
|
356
|
+
if (Number.isNaN(thing)) return -3;
|
|
357
|
+
if (thing === Infinity) return -4;
|
|
358
|
+
if (thing === -Infinity) return -5;
|
|
359
|
+
if (thing === 0 && 1 / thing < 0) return -6;
|
|
360
|
+
if (indexes.has(thing)) return indexes.get(thing);
|
|
361
|
+
index ??= p++;
|
|
362
|
+
indexes.set(thing, index);
|
|
363
|
+
for (const { key, fn } of custom) {
|
|
364
|
+
const value = fn(thing);
|
|
365
|
+
if (value) {
|
|
366
|
+
stringified[index] = `["${key}",${flatten(value)}]`;
|
|
367
|
+
return index;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
if (typeof thing === "function") throw new DevalueError(`Cannot stringify a function`, keys, thing, value);
|
|
371
|
+
else if (typeof thing === "symbol") throw new DevalueError(`Cannot stringify a Symbol primitive`, keys, thing, value);
|
|
372
|
+
/** @type {string | Promise<any>} */
|
|
373
|
+
let str = "";
|
|
374
|
+
if (is_primitive(thing)) str = stringify_primitive(thing);
|
|
375
|
+
else if (typeof thing.then === "function") {
|
|
376
|
+
if (!async) throw new DevalueError(`Cannot stringify a Promise or thenable — use stringifyAsync instead`, keys, thing, value);
|
|
377
|
+
str = Promise.resolve(thing).then((value) => {
|
|
378
|
+
const i = flatten(value, index);
|
|
379
|
+
if (i < 0) stringified[index] = i;
|
|
380
|
+
});
|
|
381
|
+
} else {
|
|
382
|
+
const type = get_type(thing);
|
|
383
|
+
switch (type) {
|
|
384
|
+
case "Number":
|
|
385
|
+
case "String":
|
|
386
|
+
case "Boolean":
|
|
387
|
+
case "BigInt":
|
|
388
|
+
str = `["Object",${flatten(thing.valueOf())}]`;
|
|
389
|
+
break;
|
|
390
|
+
case "Date":
|
|
391
|
+
str = `["Date","${!isNaN(thing.getDate()) ? thing.toISOString() : ""}"]`;
|
|
392
|
+
break;
|
|
393
|
+
case "URL":
|
|
394
|
+
str = `["URL",${stringify_string(thing.toString())}]`;
|
|
395
|
+
break;
|
|
396
|
+
case "URLSearchParams":
|
|
397
|
+
str = `["URLSearchParams",${stringify_string(thing.toString())}]`;
|
|
398
|
+
break;
|
|
399
|
+
case "RegExp":
|
|
400
|
+
const { source, flags } = thing;
|
|
401
|
+
str = flags ? `["RegExp",${stringify_string(source)},"${flags}"]` : `["RegExp",${stringify_string(source)}]`;
|
|
402
|
+
break;
|
|
403
|
+
case "Array": {
|
|
404
|
+
let mostly_dense = false;
|
|
405
|
+
str = "[";
|
|
406
|
+
for (let i = 0; i < thing.length; i += 1) {
|
|
407
|
+
if (i > 0) str += ",";
|
|
408
|
+
if (Object.hasOwn(thing, i)) {
|
|
409
|
+
keys.push(`[${i}]`);
|
|
410
|
+
str += flatten(thing[i]);
|
|
411
|
+
keys.pop();
|
|
412
|
+
} else if (mostly_dense) str += -2;
|
|
413
|
+
else {
|
|
414
|
+
const populated_keys = valid_array_indices(thing);
|
|
415
|
+
const population = populated_keys.length;
|
|
416
|
+
const d = String(thing.length).length;
|
|
417
|
+
if ((thing.length - population) * 3 > 4 + d + population * (d + 1)) {
|
|
418
|
+
str = "[-7," + thing.length;
|
|
419
|
+
for (let j = 0; j < populated_keys.length; j++) {
|
|
420
|
+
const key = populated_keys[j];
|
|
421
|
+
keys.push(`[${key}]`);
|
|
422
|
+
str += "," + key + "," + flatten(thing[key]);
|
|
423
|
+
keys.pop();
|
|
424
|
+
}
|
|
425
|
+
break;
|
|
426
|
+
} else {
|
|
427
|
+
mostly_dense = true;
|
|
428
|
+
str += -2;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
str += "]";
|
|
433
|
+
break;
|
|
434
|
+
}
|
|
435
|
+
case "Set":
|
|
436
|
+
str = "[\"Set\"";
|
|
437
|
+
for (const value of thing) str += `,${flatten(value)}`;
|
|
438
|
+
str += "]";
|
|
439
|
+
break;
|
|
440
|
+
case "Map":
|
|
441
|
+
str = "[\"Map\"";
|
|
442
|
+
for (const [key, value] of thing) {
|
|
443
|
+
keys.push(`.get(${is_primitive(key) ? stringify_primitive(key) : "..."})`);
|
|
444
|
+
str += `,${flatten(key)},${flatten(value)}`;
|
|
445
|
+
keys.pop();
|
|
446
|
+
}
|
|
447
|
+
str += "]";
|
|
448
|
+
break;
|
|
449
|
+
case "Int8Array":
|
|
450
|
+
case "Uint8Array":
|
|
451
|
+
case "Uint8ClampedArray":
|
|
452
|
+
case "Int16Array":
|
|
453
|
+
case "Uint16Array":
|
|
454
|
+
case "Float16Array":
|
|
455
|
+
case "Int32Array":
|
|
456
|
+
case "Uint32Array":
|
|
457
|
+
case "Float32Array":
|
|
458
|
+
case "Float64Array":
|
|
459
|
+
case "BigInt64Array":
|
|
460
|
+
case "BigUint64Array":
|
|
461
|
+
case "DataView": {
|
|
462
|
+
/** @type {import("./types.js").TypedArray} */
|
|
463
|
+
const typedArray = thing;
|
|
464
|
+
str = "[\"" + type + "\"," + flatten(typedArray.buffer);
|
|
465
|
+
if (typedArray.byteLength !== typedArray.buffer.byteLength) str += `,${typedArray.byteOffset},${typedArray.length}`;
|
|
466
|
+
str += "]";
|
|
467
|
+
break;
|
|
468
|
+
}
|
|
469
|
+
case "ArrayBuffer":
|
|
470
|
+
str = `["ArrayBuffer","${encode64(thing)}"]`;
|
|
471
|
+
break;
|
|
472
|
+
case "Temporal.Duration":
|
|
473
|
+
case "Temporal.Instant":
|
|
474
|
+
case "Temporal.PlainDate":
|
|
475
|
+
case "Temporal.PlainTime":
|
|
476
|
+
case "Temporal.PlainDateTime":
|
|
477
|
+
case "Temporal.PlainMonthDay":
|
|
478
|
+
case "Temporal.PlainYearMonth":
|
|
479
|
+
case "Temporal.ZonedDateTime":
|
|
480
|
+
str = `["${type}",${stringify_string(thing.toString())}]`;
|
|
481
|
+
break;
|
|
482
|
+
default:
|
|
483
|
+
if (!is_plain_object(thing)) throw new DevalueError(`Cannot stringify arbitrary non-POJOs`, keys, thing, value);
|
|
484
|
+
if (enumerable_symbols(thing).length > 0) throw new DevalueError(`Cannot stringify POJOs with symbolic keys`, keys, thing, value);
|
|
485
|
+
if (Object.getPrototypeOf(thing) === null) {
|
|
486
|
+
str = "[\"null\"";
|
|
487
|
+
for (const key of Object.keys(thing)) {
|
|
488
|
+
if (key === "__proto__") throw new DevalueError(`Cannot stringify objects with __proto__ keys`, keys, thing, value);
|
|
489
|
+
keys.push(stringify_key(key));
|
|
490
|
+
str += `,${stringify_string(key)},${flatten(thing[key])}`;
|
|
491
|
+
keys.pop();
|
|
492
|
+
}
|
|
493
|
+
str += "]";
|
|
494
|
+
} else {
|
|
495
|
+
str = "{";
|
|
496
|
+
let started = false;
|
|
497
|
+
for (const key of Object.keys(thing)) {
|
|
498
|
+
if (key === "__proto__") throw new DevalueError(`Cannot stringify objects with __proto__ keys`, keys, thing, value);
|
|
499
|
+
if (started) str += ",";
|
|
500
|
+
started = true;
|
|
501
|
+
keys.push(stringify_key(key));
|
|
502
|
+
str += `${stringify_string(key)}:${flatten(thing[key])}`;
|
|
503
|
+
keys.pop();
|
|
504
|
+
}
|
|
505
|
+
str += "}";
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
stringified[index] = str;
|
|
510
|
+
return index;
|
|
511
|
+
}
|
|
512
|
+
const index = flatten(value);
|
|
513
|
+
if (index < 0) return `${index}`;
|
|
514
|
+
return stringified;
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* @param {any} thing
|
|
518
|
+
* @returns {string}
|
|
519
|
+
*/
|
|
520
|
+
function stringify_primitive(thing) {
|
|
521
|
+
const type = typeof thing;
|
|
522
|
+
if (type === "string") return stringify_string(thing);
|
|
523
|
+
if (thing === void 0) return (-1).toString();
|
|
524
|
+
if (thing === 0 && 1 / thing < 0) return (-6).toString();
|
|
525
|
+
if (type === "bigint") return `["BigInt","${thing}"]`;
|
|
526
|
+
return String(thing);
|
|
527
|
+
}
|
|
528
|
+
//#endregion
|
|
3
529
|
//#region src/flash.ts
|
|
4
530
|
/**
|
|
5
531
|
* Dev-only radiographic capture effect — pure delight. When a capture lands we
|
|
@@ -88,6 +614,23 @@ function isExtensionAck(value) {
|
|
|
88
614
|
const DEFAULT_SETTLE_CAP = 5e3;
|
|
89
615
|
const DEFAULT_MAX_NODES = 4e3;
|
|
90
616
|
const RESIZE_DEBOUNCE = 300;
|
|
617
|
+
/**
|
|
618
|
+
* The capture roots for a site, the ONE place the auto/manual root models
|
|
619
|
+
* diverge (ADR 0014, finding #6).
|
|
620
|
+
*
|
|
621
|
+
* - AUTO: `site.el` is the `display:contents` wrapper, a layout-neutral box, so
|
|
622
|
+
* the content is its CHILDREN — the synthetic root (id 0) stands in for the
|
|
623
|
+
* wrapper and the children become the plate's top level (today's behavior).
|
|
624
|
+
* `<STYLE>` children are dropped (a stitched-in scoped sheet, never content).
|
|
625
|
+
* - MANUAL: `site.el` is the consumer's OWN styled element, so the element
|
|
626
|
+
* ITSELF is the root. Capturing its children would drop its box, classes,
|
|
627
|
+
* padding, border, background, and radius — the exact bug this fixes — so the
|
|
628
|
+
* element is the single root and its own box is serialized.
|
|
629
|
+
*/
|
|
630
|
+
function rootsFor(site) {
|
|
631
|
+
if (site.mode === "manual") return [site.el];
|
|
632
|
+
return Array.from(site.el.children).filter((child) => child.tagName !== "STYLE");
|
|
633
|
+
}
|
|
91
634
|
function installXrayClient(options) {
|
|
92
635
|
const win = window;
|
|
93
636
|
const defaultDelay = options.delay ?? 200;
|
|
@@ -95,6 +638,7 @@ function installXrayClient(options) {
|
|
|
95
638
|
const maxNodes = options.maxNodes ?? DEFAULT_MAX_NODES;
|
|
96
639
|
const sites = /* @__PURE__ */ new Set();
|
|
97
640
|
const matchMediaQueries = /* @__PURE__ */ new Set();
|
|
641
|
+
let captureGeneration = 0;
|
|
98
642
|
const livePlates = /* @__PURE__ */ new Map();
|
|
99
643
|
const plateSerialized = /* @__PURE__ */ new Map();
|
|
100
644
|
const plateListeners = /* @__PURE__ */ new Map();
|
|
@@ -129,6 +673,9 @@ function installXrayClient(options) {
|
|
|
129
673
|
};
|
|
130
674
|
const lightbox = createToggleStore(win, LIGHTBOX_KEY, LIGHTBOX_PARAM, false);
|
|
131
675
|
const captureEnabled = createToggleStore(win, CAPTURE_KEY, CAPTURE_PARAM, options.capture ?? true);
|
|
676
|
+
const replayEnabled = createToggleStore(win, REPLAY_KEY, REPLAY_PARAM, options.replay === "prefer" || options.replay === "missing-only");
|
|
677
|
+
const replayMode = options.replay === "missing-only" ? "missing-only" : "prefer";
|
|
678
|
+
const recordMode = options.record ?? "manual";
|
|
132
679
|
const isPrimaryForName = (site) => {
|
|
133
680
|
for (const other of sites) {
|
|
134
681
|
if (other === site || other.disposed || other.name !== site.name || !other.el.isConnected) continue;
|
|
@@ -136,17 +683,25 @@ function installXrayClient(options) {
|
|
|
136
683
|
}
|
|
137
684
|
return true;
|
|
138
685
|
};
|
|
139
|
-
const
|
|
686
|
+
const runCapture = async (site, force, awaitSink) => {
|
|
140
687
|
if (!force && !captureEnabled.get() || site.tooLarge || !isPrimaryForName(site)) return;
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
688
|
+
const generation = captureGeneration;
|
|
689
|
+
await settle(site.el, site.delay, settleCap, site.mode);
|
|
690
|
+
if (generation !== captureGeneration || site.disposed || site.tooLarge || !site.el.isConnected || !isPrimaryForName(site)) return;
|
|
691
|
+
const roots = rootsFor(site);
|
|
144
692
|
if (roots.length === 0) return;
|
|
693
|
+
let payload;
|
|
694
|
+
let key;
|
|
145
695
|
try {
|
|
146
|
-
const capture = captureRegime(roots, {
|
|
696
|
+
const capture = captureRegime(roots, {
|
|
697
|
+
maxNodes,
|
|
698
|
+
walker: site.walker,
|
|
699
|
+
captureRootIsBoundary: site.mode === "manual"
|
|
700
|
+
});
|
|
701
|
+
reportDiagnostics(site.name, capture.diagnostics);
|
|
147
702
|
const breakpoints = deriveBreakpoints(capture.rules, [...matchMediaQueries]);
|
|
148
703
|
const span = regimeFor(capture.width, breakpoints);
|
|
149
|
-
|
|
704
|
+
payload = {
|
|
150
705
|
name: site.name,
|
|
151
706
|
capture: {
|
|
152
707
|
...capture,
|
|
@@ -154,31 +709,69 @@ function installXrayClient(options) {
|
|
|
154
709
|
},
|
|
155
710
|
breakpoints
|
|
156
711
|
};
|
|
157
|
-
|
|
712
|
+
key = JSON.stringify(span);
|
|
158
713
|
const body = JSON.stringify(payload);
|
|
159
714
|
if (site.sent.get(key) === body) return;
|
|
160
715
|
site.sent.set(key, body);
|
|
161
|
-
options.sink(payload);
|
|
162
|
-
flashCapture(site.el);
|
|
163
716
|
} catch (error) {
|
|
164
717
|
if (error instanceof CaptureTooLargeError) {
|
|
165
718
|
site.tooLarge = true;
|
|
166
|
-
|
|
719
|
+
reportDiagnostics(site.name, [makeDiagnostic(error.code, { detail: `${error.nodeCount} nodes (max ${maxNodes})` })]);
|
|
167
720
|
return;
|
|
168
721
|
}
|
|
169
722
|
console.warn("[xray] capture failed for", site.name, error);
|
|
723
|
+
return;
|
|
724
|
+
}
|
|
725
|
+
if (generation !== captureGeneration || site.disposed) {
|
|
726
|
+
site.sent.delete(key);
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
await sendCapture(site, payload, key, awaitSink);
|
|
730
|
+
};
|
|
731
|
+
const sendCapture = async (site, payload, key, awaitSink) => {
|
|
732
|
+
try {
|
|
733
|
+
await options.sink(payload);
|
|
734
|
+
} catch (error) {
|
|
735
|
+
site.sent.delete(key);
|
|
736
|
+
if (awaitSink) throw error;
|
|
737
|
+
console.warn("[xray] capture send failed for", site.name, error);
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
740
|
+
flashCapture(site.el);
|
|
741
|
+
};
|
|
742
|
+
const scheduleCapture = (site, force = false, awaitSink = false) => {
|
|
743
|
+
if (site.inflight) {
|
|
744
|
+
site.dirty = true;
|
|
745
|
+
site.dirtyForce = site.dirtyForce || force;
|
|
746
|
+
site.dirtyAwaitSink = site.dirtyAwaitSink || awaitSink;
|
|
747
|
+
return site.inflight;
|
|
170
748
|
}
|
|
749
|
+
const loop = async (f, a) => {
|
|
750
|
+
await runCapture(site, f, a);
|
|
751
|
+
if (!site.dirty) return;
|
|
752
|
+
site.dirty = false;
|
|
753
|
+
const nextForce = site.dirtyForce;
|
|
754
|
+
const nextAwaitSink = site.dirtyAwaitSink;
|
|
755
|
+
site.dirtyForce = false;
|
|
756
|
+
site.dirtyAwaitSink = false;
|
|
757
|
+
await loop(nextForce, nextAwaitSink);
|
|
758
|
+
};
|
|
759
|
+
const promise = loop(force, awaitSink).finally(() => {
|
|
760
|
+
site.inflight = void 0;
|
|
761
|
+
});
|
|
762
|
+
site.inflight = promise;
|
|
763
|
+
return promise;
|
|
171
764
|
};
|
|
172
765
|
let resizeTimer;
|
|
173
766
|
const onResize = () => {
|
|
174
767
|
clearTimeout(resizeTimer);
|
|
175
768
|
resizeTimer = setTimeout(() => {
|
|
176
|
-
for (const site of sites)
|
|
769
|
+
for (const site of sites) scheduleCapture(site);
|
|
177
770
|
}, RESIZE_DEBOUNCE);
|
|
178
771
|
};
|
|
179
772
|
win.addEventListener("resize", onResize);
|
|
180
773
|
const unsubscribeCapture = captureEnabled.subscribe(() => {
|
|
181
|
-
if (captureEnabled.get()) for (const site of sites)
|
|
774
|
+
if (captureEnabled.get()) for (const site of sites) scheduleCapture(site);
|
|
182
775
|
});
|
|
183
776
|
const extAcks = /* @__PURE__ */ new Map();
|
|
184
777
|
let nextExtReqId = 1;
|
|
@@ -222,6 +815,58 @@ function installXrayClient(options) {
|
|
|
222
815
|
return () => coverageListeners.delete(cb);
|
|
223
816
|
}
|
|
224
817
|
};
|
|
818
|
+
const liveData = /* @__PURE__ */ new Map();
|
|
819
|
+
const recorded = /* @__PURE__ */ new Map();
|
|
820
|
+
const fixtureListeners = /* @__PURE__ */ new Set();
|
|
821
|
+
const notifyFixtures = () => {
|
|
822
|
+
for (const cb of fixtureListeners) cb();
|
|
823
|
+
};
|
|
824
|
+
const fixtures = {
|
|
825
|
+
register: (name, data) => {
|
|
826
|
+
const entry = { data };
|
|
827
|
+
const stack = liveData.get(name);
|
|
828
|
+
if (stack) stack.push(entry);
|
|
829
|
+
else liveData.set(name, [entry]);
|
|
830
|
+
return () => {
|
|
831
|
+
const current = liveData.get(name);
|
|
832
|
+
if (!current) return;
|
|
833
|
+
const i = current.indexOf(entry);
|
|
834
|
+
if (i !== -1) current.splice(i, 1);
|
|
835
|
+
if (current.length === 0) liveData.delete(name);
|
|
836
|
+
};
|
|
837
|
+
},
|
|
838
|
+
has: (name) => recorded.has(name),
|
|
839
|
+
read: (name) => recorded.get(name),
|
|
840
|
+
record: async (name) => {
|
|
841
|
+
const live = liveData.get(name)?.at(-1);
|
|
842
|
+
if (!live) throw new Error(`[xray] cannot record fixture for "${name}": no render-prop \`data\` is in scope. Only render-prop <Skeleton data={…}> sites participate in fixtures.`);
|
|
843
|
+
const encoded = stringify(live.data);
|
|
844
|
+
await options.recordFixtureSink?.({
|
|
845
|
+
name,
|
|
846
|
+
data: encoded
|
|
847
|
+
});
|
|
848
|
+
recorded.set(name, { data: parse(encoded) });
|
|
849
|
+
notifyFixtures();
|
|
850
|
+
},
|
|
851
|
+
recordAll: async () => {
|
|
852
|
+
await Promise.all([...liveData.keys()].map((name) => fixtures.record(name)));
|
|
853
|
+
},
|
|
854
|
+
seed: (encoded) => {
|
|
855
|
+
let changed = false;
|
|
856
|
+
for (const [name, str] of Object.entries(encoded)) {
|
|
857
|
+
if (recorded.has(name)) continue;
|
|
858
|
+
try {
|
|
859
|
+
recorded.set(name, { data: parse(str) });
|
|
860
|
+
changed = true;
|
|
861
|
+
} catch {}
|
|
862
|
+
}
|
|
863
|
+
if (changed) notifyFixtures();
|
|
864
|
+
},
|
|
865
|
+
subscribe: (cb) => {
|
|
866
|
+
fixtureListeners.add(cb);
|
|
867
|
+
return () => fixtureListeners.delete(cb);
|
|
868
|
+
}
|
|
869
|
+
};
|
|
225
870
|
const sweepWidths = () => {
|
|
226
871
|
const set = new Set(deriveBreakpoints([], [...matchMediaQueries]));
|
|
227
872
|
for (const site of sites) for (const bp of coverageMap.get(site.name)?.breakpoints ?? []) set.add(bp);
|
|
@@ -230,16 +875,25 @@ function installXrayClient(options) {
|
|
|
230
875
|
const first = min === void 0 ? 390 : Math.max(1, Math.min(390, min - 1));
|
|
231
876
|
return [...new Set([first, ...breakpoints])].toSorted((a, b) => a - b);
|
|
232
877
|
};
|
|
878
|
+
const MAX_SWEEP_PASSES = 8;
|
|
233
879
|
const captureAllViews = async () => {
|
|
234
|
-
const
|
|
880
|
+
const visited = /* @__PURE__ */ new Set();
|
|
235
881
|
try {
|
|
236
|
-
for (
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
882
|
+
for (let pass = 0;; pass++) {
|
|
883
|
+
const pending = sweepWidths().filter((width) => !visited.has(width));
|
|
884
|
+
if (pending.length === 0) break;
|
|
885
|
+
if (pass >= MAX_SWEEP_PASSES) throw new Error(`xray capture-all did not converge after ${MAX_SWEEP_PASSES} passes (still discovering widths: ${pending.join(", ")})`);
|
|
886
|
+
for (const width of pending) {
|
|
887
|
+
visited.add(width);
|
|
888
|
+
captureGeneration++;
|
|
889
|
+
await extCommand("emulate", {
|
|
890
|
+
width,
|
|
891
|
+
height: 2400,
|
|
892
|
+
mobile: width < 600
|
|
893
|
+
});
|
|
894
|
+
await Promise.all([...sites].map((site) => scheduleCapture(site, true, true)));
|
|
895
|
+
}
|
|
896
|
+
await options.refreshCoverage?.();
|
|
243
897
|
}
|
|
244
898
|
} finally {
|
|
245
899
|
await extCommand("reset").catch(() => void 0);
|
|
@@ -252,21 +906,31 @@ function installXrayClient(options) {
|
|
|
252
906
|
updatePlate,
|
|
253
907
|
captureAllViews,
|
|
254
908
|
coverage,
|
|
909
|
+
replay: replayEnabled,
|
|
910
|
+
replayMode,
|
|
911
|
+
recordMode,
|
|
912
|
+
fixtures,
|
|
255
913
|
captured: (name, el, opts) => {
|
|
256
914
|
const site = {
|
|
257
915
|
name,
|
|
258
916
|
el,
|
|
259
917
|
delay: opts?.delay ?? defaultDelay,
|
|
918
|
+
mode: opts?.mode ?? "auto",
|
|
919
|
+
walker: opts?.walker,
|
|
260
920
|
sent: /* @__PURE__ */ new Map(),
|
|
261
921
|
disposed: false,
|
|
262
|
-
tooLarge: false
|
|
922
|
+
tooLarge: false,
|
|
923
|
+
dirty: false,
|
|
924
|
+
dirtyForce: false,
|
|
925
|
+
dirtyAwaitSink: false
|
|
263
926
|
};
|
|
264
927
|
sites.add(site);
|
|
265
|
-
|
|
928
|
+
scheduleCapture(site);
|
|
266
929
|
return () => {
|
|
267
930
|
site.disposed = true;
|
|
268
931
|
sites.delete(site);
|
|
269
|
-
|
|
932
|
+
captureGeneration++;
|
|
933
|
+
for (const other of sites) if (other.name === site.name) scheduleCapture(other);
|
|
270
934
|
};
|
|
271
935
|
}
|
|
272
936
|
};
|
|
@@ -277,13 +941,37 @@ function installXrayClient(options) {
|
|
|
277
941
|
clearTimeout(resizeTimer);
|
|
278
942
|
unsubscribeCapture();
|
|
279
943
|
globalThis.__XRAY__ = void 0;
|
|
944
|
+
captureGeneration++;
|
|
945
|
+
for (const site of sites) site.disposed = true;
|
|
280
946
|
sites.clear();
|
|
281
947
|
};
|
|
282
948
|
}
|
|
949
|
+
/**
|
|
950
|
+
* Surface a capture's dev-only diagnostics as ONE grouped `console.warn` keyed
|
|
951
|
+
* by plate name: a header line, then one formatted line per diagnostic via the
|
|
952
|
+
* shared formatter (diagnostics.ts) — the same text the Vite server prints, so
|
|
953
|
+
* the two never drift. `console.group` collapses the lines under the header;
|
|
954
|
+
* we fall back to a single multi-line warn where it's unavailable. No-op when
|
|
955
|
+
* there are no diagnostics, so a clean capture is silent.
|
|
956
|
+
*/
|
|
957
|
+
function reportDiagnostics(name, diagnostics) {
|
|
958
|
+
if (!diagnostics || diagnostics.length === 0) return;
|
|
959
|
+
const header = diagnosticsHeader(name);
|
|
960
|
+
const lines = diagnostics.map((d) => formatDiagnostic(d));
|
|
961
|
+
if (typeof console.group === "function" && typeof console.groupEnd === "function") {
|
|
962
|
+
console.group(header);
|
|
963
|
+
for (const line of lines) console.warn(line);
|
|
964
|
+
console.groupEnd();
|
|
965
|
+
return;
|
|
966
|
+
}
|
|
967
|
+
console.warn([header, ...lines].join("\n"));
|
|
968
|
+
}
|
|
283
969
|
const LIGHTBOX_KEY = "xray:lightbox";
|
|
284
970
|
const LIGHTBOX_PARAM = "xray-lightbox";
|
|
285
971
|
const CAPTURE_KEY = "xray:capture";
|
|
286
972
|
const CAPTURE_PARAM = "xray-capture";
|
|
973
|
+
const REPLAY_KEY = "xray:replay";
|
|
974
|
+
const REPLAY_PARAM = "xray-replay";
|
|
287
975
|
/**
|
|
288
976
|
* A sticky boolean toggle: per-tab in sessionStorage, seedable to `true` via
|
|
289
977
|
* `?<param>` in the URL, flipped from the HUD or the console
|
|
@@ -322,7 +1010,7 @@ function createToggleStore(win, key, param, initial) {
|
|
|
322
1010
|
* made single-shot capture non-deterministic in the M1 harness; this is the
|
|
323
1011
|
* cure. Hard-capped so a perpetually-animating subtree still captures.
|
|
324
1012
|
*/
|
|
325
|
-
function settle(el, delay, cap) {
|
|
1013
|
+
function settle(el, delay, cap, mode = "auto") {
|
|
326
1014
|
const doc = el.ownerDocument;
|
|
327
1015
|
return (doc.fonts ? doc.fonts.ready : Promise.resolve()).then(() => new Promise((resolve) => {
|
|
328
1016
|
let timer;
|
|
@@ -345,7 +1033,8 @@ function settle(el, delay, cap) {
|
|
|
345
1033
|
childList: true,
|
|
346
1034
|
characterData: true
|
|
347
1035
|
});
|
|
348
|
-
if (resizeObserver)
|
|
1036
|
+
if (resizeObserver) if (mode === "manual") resizeObserver.observe(el);
|
|
1037
|
+
else for (const child of Array.from(el.children)) resizeObserver.observe(child);
|
|
349
1038
|
timer = setTimeout(done, delay);
|
|
350
1039
|
capTimer = setTimeout(done, cap);
|
|
351
1040
|
}));
|