@hueest/xray 0.1.0 → 0.3.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/client.d.ts +64 -16
- package/dist/client.js +903 -72
- package/dist/core.d.ts +21 -27
- package/dist/core.js +39 -210
- package/dist/css-escape-N7bOusGW.js +52 -0
- package/dist/hud.js +46 -0
- package/dist/index.d.ts +297 -48
- package/dist/index.js +674 -353
- package/dist/plate-BIr62u7l.d.ts +363 -0
- package/dist/plate-BRR6d8Se.js +156 -0
- package/dist/project-DORoPAo7.js +10224 -0
- package/dist/react.core-BUj8ziwh.d.ts +79 -0
- package/dist/react.core-DMIhXHZF.js +272 -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-Cs7hUxmK.d.ts +107 -0
- package/package.json +8 -7
- package/virtual.d.ts +6 -0
- package/dist/breakpoints-CBNlh7lQ.js +0 -47
- package/dist/plate-BuzRMPx4.js +0 -94
- package/dist/plate-DboxMKg-.d.ts +0 -196
- package/dist/react.core-CqnDjfAJ.js +0 -59
- package/dist/react.core-fgWG_svU.d.ts +0 -21
- package/dist/serialize-Bq6yJnNV.js +0 -1071
- package/dist/serialize.d.ts +0 -88
- package/dist/serialize.js +0 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,55 @@
|
|
|
1
1
|
import { Plugin } from "vite";
|
|
2
2
|
|
|
3
|
+
//#region src/diagnostics.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Dev-only capture diagnostics: a small, serializable record of why a Plate may
|
|
6
|
+
* be incomplete or lower-fidelity, plus the one shared formatter both the
|
|
7
|
+
* browser console (`client.ts`) and the Vite server output (`index.ts`) print.
|
|
8
|
+
*
|
|
9
|
+
* Motivation: the serializer silently swallows a handful of capture omissions
|
|
10
|
+
* (an unreadable cross-origin stylesheet, skipped pseudo-elements, off-screen
|
|
11
|
+
* bones dropped at the fold, an over-the-limit subtree). Early adopters asking "why does this
|
|
12
|
+
* skeleton look wrong?" had to read the serializer source to find out. These
|
|
13
|
+
* diagnostics surface those omissions as data the capture process returns, so a
|
|
14
|
+
* caller can log them — without the core performing any console side effects.
|
|
15
|
+
*
|
|
16
|
+
* Strictly DEV-ONLY (see the plan's decisions and ADR 0008): this type is NOT a
|
|
17
|
+
* public package export and MUST NEVER reach the persisted Plate JSON, the
|
|
18
|
+
* `MergedPlate`, or the production adapter. It rides on the TRANSIENT
|
|
19
|
+
* `ViewIR.diagnostics` field only, which `captureView` populates and the
|
|
20
|
+
* dev client strips before posting (so committed `plates/<name>.json` stays
|
|
21
|
+
* byte-identical to a capture that produced no diagnostics).
|
|
22
|
+
*
|
|
23
|
+
* Centralized on purpose: a follow-up plan (capture-input-validation) reuses
|
|
24
|
+
* this same messaging for invalid posted/committed plates, so the code table
|
|
25
|
+
* and formatter are kept reusable. Browser and server share `formatDiagnostic`
|
|
26
|
+
* so their warning text cannot drift.
|
|
27
|
+
*
|
|
28
|
+
* @module
|
|
29
|
+
*/
|
|
30
|
+
/**
|
|
31
|
+
* One aggregated capture omission. Serializable on purpose: NO DOM nodes, NO
|
|
32
|
+
* CSS bodies, NO captured text — only a stable `code`, a human message, an
|
|
33
|
+
* optional aggregate `count`, and an optional short `detail` (e.g. a plate name
|
|
34
|
+
* or a node count, never user content). `count` aggregates occurrences: there
|
|
35
|
+
* is at most one record per code per capture, never one per skipped selector or
|
|
36
|
+
* node.
|
|
37
|
+
*/
|
|
38
|
+
interface CaptureDiagnostic {
|
|
39
|
+
code: DiagnosticCode;
|
|
40
|
+
message: string;
|
|
41
|
+
count?: number;
|
|
42
|
+
detail?: string;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The closed set of capture-omission codes. Each maps to one silent site in the
|
|
46
|
+
* serializer (or an existing oversized-capture warning folded into the same
|
|
47
|
+
* vocabulary). Kept as a const union so the message table is exhaustive.
|
|
48
|
+
*/
|
|
49
|
+
type DiagnosticCode = 'unreadable-stylesheet' | 'unreadable-import' | 'skipped-dynamic-pseudo' | 'skipped-pseudo-element' | 'unsupported-rule' | 'dropped-tag' | 'dropped-subset' | 'dropped-cropped' | 'too-large' | 'invalid-plate-file';
|
|
50
|
+
//#endregion
|
|
3
51
|
//#region src/plate.d.ts
|
|
4
|
-
type LeafKind = 'text' | 'media' | 'box';
|
|
52
|
+
type LeafKind = 'text' | 'text-block' | 'media' | 'box';
|
|
5
53
|
/**
|
|
6
54
|
* The shipped structural capture of a component's rendered DOM — the data a
|
|
7
55
|
* Skeleton renders from (see CONTEXT.md). Self-contained: pre-serialized chunks
|
|
@@ -35,21 +83,27 @@ interface Plate {
|
|
|
35
83
|
/**
|
|
36
84
|
* One serialized child of a plate root (or of a stitch-bearing node): a
|
|
37
85
|
* stitch-free HTML run (`string`), a stitch to a child plate (`{ r: name }`),
|
|
38
|
-
*
|
|
39
|
-
* (`{ c: className, k: chunk[] }`)
|
|
40
|
-
*
|
|
86
|
+
* a stitch-bearing element kept real so its inner stitches can mount
|
|
87
|
+
* (`{ c: className, k: chunk[] }`), or a templated uniform run (`{ t: html, n }`):
|
|
88
|
+
* the cell's HTML once plus a repeat count the adapter expands to N copies (Phase
|
|
89
|
+
* 2). The framework-neutral render contract: an adapter injects the strings,
|
|
90
|
+
* mounts a child plate at each stitch, and repeats each template (ADR 0008).
|
|
41
91
|
*/
|
|
42
92
|
type Chunk = string | {
|
|
43
93
|
r: string;
|
|
44
94
|
} | {
|
|
45
95
|
c: string;
|
|
46
96
|
k: Chunk[];
|
|
97
|
+
} | {
|
|
98
|
+
t: string;
|
|
99
|
+
n: number;
|
|
47
100
|
};
|
|
48
101
|
/**
|
|
49
|
-
* A plate after
|
|
50
|
-
* build-time RenderNode tree plus its scoped
|
|
51
|
-
*
|
|
52
|
-
*
|
|
102
|
+
* A plate after the in-browser Serialize (emit + merge/gate + classify) but
|
|
103
|
+
* before chunk serialization: the build-time RenderNode tree plus its scoped
|
|
104
|
+
* CSS. Produced by `serializePlateIR` (the structural part of `StoredPlate`) and
|
|
105
|
+
* the single-View `capture`, then serialized into the shipped `Plate` (chunks) at
|
|
106
|
+
* load (`serializePlate`). Never shipped to the client.
|
|
53
107
|
*/
|
|
54
108
|
interface MergedPlate {
|
|
55
109
|
v: number;
|
|
@@ -68,6 +122,12 @@ interface PlateNode {
|
|
|
68
122
|
* against `Plate.refs` (prod) or the live store (dev).
|
|
69
123
|
*/
|
|
70
124
|
ref?: string;
|
|
125
|
+
/**
|
|
126
|
+
* A templated uniform run (Phase 2): render this node's subtree `count` times.
|
|
127
|
+
* The serializer emits the subtree's HTML once plus the count; the adapter
|
|
128
|
+
* expands it to N copies at render. Absent on ordinary nodes.
|
|
129
|
+
*/
|
|
130
|
+
count?: number;
|
|
71
131
|
kids?: PlateNode[];
|
|
72
132
|
}
|
|
73
133
|
/**
|
|
@@ -80,6 +140,8 @@ interface RenderNode {
|
|
|
80
140
|
leaf?: LeafKind;
|
|
81
141
|
ref?: string;
|
|
82
142
|
cls?: string;
|
|
143
|
+
/** A templated uniform run (Phase 2): serialize this subtree once + a count the adapter repeats. */
|
|
144
|
+
count?: number;
|
|
83
145
|
kids?: RenderNode[];
|
|
84
146
|
}
|
|
85
147
|
/**
|
|
@@ -100,8 +162,11 @@ interface PlateRule {
|
|
|
100
162
|
}
|
|
101
163
|
/**
|
|
102
164
|
* One capture of a component within one viewport view — `[min, max)` in px,
|
|
103
|
-
* either side open-ended when undefined.
|
|
104
|
-
*
|
|
165
|
+
* either side open-ended when undefined. The id-form `{ tree, rules }` shape
|
|
166
|
+
* `emit` projects a measured View into (ADR 0004). NO LONGER the disk/wire shape
|
|
167
|
+
* after the ADR 0022 cutover — `StoredPlate` (below) is. Kept as the
|
|
168
|
+
* framework-neutral per-View capture shape, exported for consumers reading the
|
|
169
|
+
* id-form projection directly.
|
|
105
170
|
*/
|
|
106
171
|
interface ViewCapture {
|
|
107
172
|
/** Viewport width at capture time. */
|
|
@@ -110,14 +175,33 @@ interface ViewCapture {
|
|
|
110
175
|
max?: number;
|
|
111
176
|
tree: PlateNode;
|
|
112
177
|
rules: PlateRule[];
|
|
178
|
+
/**
|
|
179
|
+
* TRANSIENT, dev-only capture diagnostics (why this view may be lower
|
|
180
|
+
* fidelity). Captured in the browser (mirrored onto `ViewIR.diagnostics`); the
|
|
181
|
+
* dev client logs it and posts it alongside the `StoredPlate` (so the Vite
|
|
182
|
+
* server can print the same text). It MUST NEVER be persisted: it is not part
|
|
183
|
+
* of the committed
|
|
184
|
+
* `StoredPlate` disk shape, the validator does not carry it, and the POST
|
|
185
|
+
* envelope keeps it in a SIBLING field that never reaches the stored artifact
|
|
186
|
+
* (ADR 0008). Absent in production (the prod adapter ships no capture). See
|
|
187
|
+
* diagnostics.ts and ADR 0008.
|
|
188
|
+
*/
|
|
189
|
+
diagnostics?: CaptureDiagnostic[];
|
|
113
190
|
}
|
|
114
|
-
/**
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
191
|
+
/**
|
|
192
|
+
* The committed `plates/<name>.json` shape AFTER the ADR 0022 cutover: the whole
|
|
193
|
+
* Plate already projected in the browser (the in-browser Serialize folds emit +
|
|
194
|
+
* the multi-View merge/gate + classify in one session), so disk holds ONE gated,
|
|
195
|
+
* content-addressed tree + css instead of per-View captures merged at load. It is
|
|
196
|
+
* a `MergedPlate` (the structural `{tree, css}` `serializePlate` consumes) PLUS the
|
|
197
|
+
* `breakpoints` the HUD coverage reads — the analysis-only geometry never crosses
|
|
198
|
+
* the wire (ADR 0021). The gen-side runs `serializePlate(stored) → Plate` (chunks)
|
|
199
|
+
* at module-load, leaving the Tailwind/Bundle passthrough a clean gen-side seam.
|
|
200
|
+
* Replaces `PlateFile` (+ `ViewCapture`) on disk and on the `/__xray` wire.
|
|
201
|
+
*/
|
|
202
|
+
interface StoredPlate extends MergedPlate {
|
|
203
|
+
/** Width thresholds (view boundaries); the HUD coverage derives its bands from these. */
|
|
119
204
|
breakpoints: number[];
|
|
120
|
-
views: ViewCapture[];
|
|
121
205
|
}
|
|
122
206
|
/**
|
|
123
207
|
* A dev-only sticky boolean toggle (get/set/subscribe), persisted per tab.
|
|
@@ -151,6 +235,68 @@ interface Span {
|
|
|
151
235
|
min?: number;
|
|
152
236
|
max?: number;
|
|
153
237
|
}
|
|
238
|
+
/**
|
|
239
|
+
* Dev-only Fixture store (ADR 0015): stored render-INPUT `data` per Plate,
|
|
240
|
+
* recorded to and replayed from a `plates/<name>.fixture.json` sidecar. Entirely
|
|
241
|
+
* dev-only — devalue and this store never reach the production bundle.
|
|
242
|
+
*
|
|
243
|
+
* Two cooperating halves, both flowing through the render-prop `data` seam (ADR
|
|
244
|
+
* 0014):
|
|
245
|
+
* - The dev adapter (`react.dev.tsx`) calls `register(name, data)` at
|
|
246
|
+
* `loading=false` with the live `data` it is about to render, so the latest
|
|
247
|
+
* recordable input for each mounted Plate is always in scope for Record.
|
|
248
|
+
* - `record(name)` serializes that registered live `data` with devalue and POSTs
|
|
249
|
+
* it to the sidecar (the HUD button and the `first-ready`/`always` modes call
|
|
250
|
+
* it). `has(name)` reports whether a Fixture exists (HUD coverage marker), and
|
|
251
|
+
* `read(name)` returns the parsed Fixture `data` for Replay to substitute in
|
|
252
|
+
* front of the render prop.
|
|
253
|
+
*
|
|
254
|
+
* The store holds DECODED values (the dev client owns the devalue parse, since it
|
|
255
|
+
* has the live runtime); only the on-disk sidecar and the wire carry the opaque
|
|
256
|
+
* devalue string.
|
|
257
|
+
*/
|
|
258
|
+
interface FixtureStore {
|
|
259
|
+
/**
|
|
260
|
+
* Record the latest live `data` the dev adapter is about to render for `name`,
|
|
261
|
+
* so Record always has the current recordable input in scope. A `register` with
|
|
262
|
+
* the same value is cheap; the adapter calls it on every ready render. Returns a
|
|
263
|
+
* disposer the adapter runs on unmount so an unmounted Skeleton's `data` never
|
|
264
|
+
* lingers in the store: an unmounted instance must never be recorded or shadow a
|
|
265
|
+
* still-mounted one (ADR 0015). With several Skeletons of the same plate name
|
|
266
|
+
* mounted, the live value is the most-recently-registered still-mounted instance.
|
|
267
|
+
*/
|
|
268
|
+
register: (name: string, data: unknown) => () => void;
|
|
269
|
+
/** True once a recorded Fixture exists for `name` (drives the HUD coverage marker). */
|
|
270
|
+
has: (name: string) => boolean;
|
|
271
|
+
/** The parsed Fixture `data` for `name`, or `undefined` when none is recorded. */
|
|
272
|
+
read: (name: string) => {
|
|
273
|
+
data: unknown;
|
|
274
|
+
} | undefined;
|
|
275
|
+
/**
|
|
276
|
+
* Serialize the registered live `data` for `name` with devalue and POST it to
|
|
277
|
+
* the sidecar. Returns a promise that resolves once the write is
|
|
278
|
+
* server-confirmed (so the HUD can surface a failed Record). Throws loudly when
|
|
279
|
+
* the live `data` is unrecordable (devalue's contract) or no live `data` was
|
|
280
|
+
* registered (a plain-children `<Skeleton>` has none).
|
|
281
|
+
*/
|
|
282
|
+
record: (name: string) => Promise<void>;
|
|
283
|
+
/**
|
|
284
|
+
* Record every Skeleton that currently has live `data` registered (the HUD's
|
|
285
|
+
* single "Record fixture" button — one click captures the inputs of all mounted
|
|
286
|
+
* render-prop Skeletons). Resolves once all writes are server-confirmed; a
|
|
287
|
+
* rejected write surfaces to the HUD.
|
|
288
|
+
*/
|
|
289
|
+
recordAll: () => Promise<void>;
|
|
290
|
+
/**
|
|
291
|
+
* Seed the store from the on-disk sidecars on boot: a `{ <name>: devalueString }`
|
|
292
|
+
* map (the opaque strings the server serves). The client decodes each with
|
|
293
|
+
* devalue. Existing entries are not clobbered, so a Fixture recorded this
|
|
294
|
+
* session survives a re-seed.
|
|
295
|
+
*/
|
|
296
|
+
seed: (encoded: Record<string, string>) => void;
|
|
297
|
+
/** React subscription so the HUD re-renders when a Fixture is recorded or seeded. */
|
|
298
|
+
subscribe: (onChange: () => void) => () => void;
|
|
299
|
+
}
|
|
154
300
|
/** A plate's detected width thresholds and the spans actually captured, from disk. */
|
|
155
301
|
interface PlateCoverage {
|
|
156
302
|
breakpoints: number[];
|
|
@@ -175,6 +321,8 @@ interface CoverageStore {
|
|
|
175
321
|
interface CaptureHook {
|
|
176
322
|
captured: (name: string, el: Element, opts?: {
|
|
177
323
|
delay?: number;
|
|
324
|
+
walker?: unknown;
|
|
325
|
+
mode?: 'auto' | 'manual';
|
|
178
326
|
}) => (() => void) | undefined;
|
|
179
327
|
/** Light Box toggle: force every `<Skeleton>` to its skeleton, read by `<Skeleton>`. */
|
|
180
328
|
lightbox?: ToggleStore;
|
|
@@ -182,40 +330,76 @@ interface CaptureHook {
|
|
|
182
330
|
capture?: ToggleStore;
|
|
183
331
|
/** Read side for `<Skeleton>`. */
|
|
184
332
|
plates?: PlateStore;
|
|
185
|
-
/** Write side for the dev client's HMR-event handler. */
|
|
186
|
-
updatePlate?: (name: string, plate: Plate) =>
|
|
333
|
+
/** Write side for the dev client's HMR-event handler. Returns whether the plate changed. */
|
|
334
|
+
updatePlate?: (name: string, plate: Plate) => boolean;
|
|
187
335
|
/** Dev-only: run the HUD-triggered capture-all-views sweep via the extension (ADR 0010). */
|
|
188
336
|
captureAllViews?: () => Promise<void>;
|
|
189
337
|
/** Dev-only per-plate breakpoints + captured spans, from disk; fed by the bootstrap. */
|
|
190
338
|
coverage?: CoverageStore;
|
|
339
|
+
/**
|
|
340
|
+
* Replay toggle (ADR 0015): when active, the dev adapter substitutes a recorded
|
|
341
|
+
* Fixture's `data` for the live `data` at `loading=false`. The boolean is the
|
|
342
|
+
* coarse on/off the HUD owns per tab; the per-mode policy (`prefer` vs
|
|
343
|
+
* `missing-only`) is carried alongside in `replayMode`.
|
|
344
|
+
*/
|
|
345
|
+
replay?: ToggleStore;
|
|
346
|
+
/** Dev-only Replay policy: how a Fixture substitutes for live `data` (ADR 0015). */
|
|
347
|
+
replayMode?: 'prefer' | 'missing-only';
|
|
348
|
+
/**
|
|
349
|
+
* Dev-only Record policy (ADR 0015): when the dev adapter records the live
|
|
350
|
+
* `data` it is about to render. `'manual'` (the default) records nothing
|
|
351
|
+
* automatically — only the HUD button does; `'first-ready'` records once when a
|
|
352
|
+
* Skeleton goes ready and has no Fixture yet; `'always'` records on every ready
|
|
353
|
+
* render.
|
|
354
|
+
*/
|
|
355
|
+
recordMode?: 'manual' | 'first-ready' | 'always';
|
|
356
|
+
/** Dev-only Fixture record/replay store (ADR 0015); never present in production. */
|
|
357
|
+
fixtures?: FixtureStore;
|
|
191
358
|
}
|
|
192
359
|
declare global {
|
|
193
360
|
var __XRAY__: CaptureHook | undefined;
|
|
194
361
|
}
|
|
195
362
|
//#endregion
|
|
196
|
-
//#region src/
|
|
363
|
+
//#region src/chunk.d.ts
|
|
197
364
|
/**
|
|
198
|
-
*
|
|
199
|
-
*
|
|
365
|
+
* Distinct plate names referenced by the tree's stitches, in first-seen order.
|
|
366
|
+
* Walks a `RenderNode` tree (the stored, post-classify shape), so it drives the
|
|
367
|
+
* gen-side stitch import graph straight off a `StoredPlate.tree`
|
|
368
|
+
* (`renderPlateModule`, index.ts). Relocated here from the now-deleted `merge.ts`
|
|
369
|
+
* in the ADR 0022 cutover — it is the one node-side primitive that survived the
|
|
370
|
+
* `mergeViews`/`addCapture` removal (the multi-View merge/gate now lives in the
|
|
371
|
+
* in-browser `serializePlateIR`, project.ts).
|
|
372
|
+
*/
|
|
373
|
+
declare function collectRefs(tree: RenderNode | null): string[];
|
|
374
|
+
//#endregion
|
|
375
|
+
//#region src/name.d.ts
|
|
376
|
+
/**
|
|
377
|
+
* Plate-name validation, shared by the Vite plugin (`index.ts`) and the
|
|
378
|
+
* Node-only POST validator (`validate.ts`).
|
|
200
379
|
*
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
*
|
|
206
|
-
* stable keys proved unreliable: every heuristic mis-paired some nodes and
|
|
207
|
-
* remapped one view's rules onto another's elements, corrupting both. A lone
|
|
208
|
-
* capture is pixel-faithful; keeping each view lone preserves that. The cost
|
|
209
|
-
* is a larger plate (roughly the sum of the views) — bounded by the plate
|
|
210
|
-
* size cap — in exchange for correctness.
|
|
380
|
+
* Lives in its own dependency-free module so `validate.ts` can reuse it without
|
|
381
|
+
* importing the plugin entry (which would be a cycle), and so the regex stays
|
|
382
|
+
* the single source of truth for what a plate name may be — the same rule the
|
|
383
|
+
* loader, the file watcher, the coverage reader, the POST endpoint, and the
|
|
384
|
+
* ref-graph emitter all gate on.
|
|
211
385
|
*
|
|
212
|
-
*
|
|
386
|
+
* @module
|
|
213
387
|
*/
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
388
|
+
/**
|
|
389
|
+
* Names come from import specifiers and POST bodies — keep them inside the plates dir.
|
|
390
|
+
*
|
|
391
|
+
* Each path segment is `\w+(-\w+)*` — word chars with single interior hyphens,
|
|
392
|
+
* never doubled and never at a segment edge — and segments are joined by `/`.
|
|
393
|
+
* The class-prefix encoder (`classify.ts`) renders `/` as `--` to keep nested
|
|
394
|
+
* names readable, and that prefix is the isolation boundary across stitched
|
|
395
|
+
* plates (ADR 0007), so the encoding must be injective. Banning a literal `--`
|
|
396
|
+
* and any edge hyphen guarantees no `-` ever abuts a `/`, so every `--` in the
|
|
397
|
+
* prefix came from exactly one `/` — making distinct names always map to
|
|
398
|
+
* distinct prefixes (else e.g. `a/-b` and `a-/b` would both encode to `a---b`).
|
|
399
|
+
*
|
|
400
|
+
* @internal
|
|
401
|
+
*/
|
|
402
|
+
declare function isValidPlateName(name: string): boolean;
|
|
219
403
|
//#endregion
|
|
220
404
|
//#region src/index.d.ts
|
|
221
405
|
/**
|
|
@@ -243,6 +427,29 @@ interface XrayOptions {
|
|
|
243
427
|
settleCap?: number;
|
|
244
428
|
/** Refuse captures larger than this many nodes (Skeleton too high in the tree). Default: `4000`. */
|
|
245
429
|
maxNodes?: number;
|
|
430
|
+
/**
|
|
431
|
+
* Dev-only Fixtures (ADR 0015): record a `<Skeleton>`'s render-input `data` to a
|
|
432
|
+
* `plates/<name>.fixture.json` sidecar and replay it in place of live data so
|
|
433
|
+
* capture is deterministic. Boot state only — the HUD is per-tab authority, just
|
|
434
|
+
* like `capture`. Entirely dev-only; nothing here ships in production.
|
|
435
|
+
*/
|
|
436
|
+
fixtures?: {
|
|
437
|
+
/**
|
|
438
|
+
* When to record the live `data`: `'manual'` (HUD button only — the default,
|
|
439
|
+
* so live app data is never silently committed), `'first-ready'` (record once
|
|
440
|
+
* the first time a Skeleton goes ready with no Fixture yet), or `'always'`
|
|
441
|
+
* (record on every ready render). Default: `'manual'`.
|
|
442
|
+
*/
|
|
443
|
+
record?: 'manual' | 'first-ready' | 'always';
|
|
444
|
+
/**
|
|
445
|
+
* Whether to replay a recorded Fixture in place of live `data`: `false` (off,
|
|
446
|
+
* the default), `'prefer'` (always use the Fixture when one exists), or
|
|
447
|
+
* `'missing-only'` (use the Fixture only when live `data` is nullish —
|
|
448
|
+
* `missing ≡ nullish`, so an empty array/object is present and stays
|
|
449
|
+
* capturable). Default: `false`.
|
|
450
|
+
*/
|
|
451
|
+
replay?: false | 'prefer' | 'missing-only';
|
|
452
|
+
};
|
|
246
453
|
}
|
|
247
454
|
/**
|
|
248
455
|
* Create the Vite plugins that resolve committed Plates and, during dev,
|
|
@@ -265,7 +472,10 @@ interface XrayOptions {
|
|
|
265
472
|
declare function xrayVitePlugin(options?: XrayOptions): Plugin[];
|
|
266
473
|
interface CaptureRequest {
|
|
267
474
|
method?: string;
|
|
475
|
+
headers?: Record<string, string | string[] | undefined>;
|
|
268
476
|
on(event: 'data' | 'end', listener: (chunk?: Buffer | string) => void): unknown;
|
|
477
|
+
/** Stop buffering the request body once the byte cap is exceeded. */
|
|
478
|
+
destroy?: () => void;
|
|
269
479
|
}
|
|
270
480
|
interface CaptureResponse {
|
|
271
481
|
statusCode: number;
|
|
@@ -274,33 +484,67 @@ interface CaptureResponse {
|
|
|
274
484
|
interface PlateBroadcaster {
|
|
275
485
|
send(payload: unknown): void;
|
|
276
486
|
}
|
|
487
|
+
/** The subset of Vite's logger we use; it preserves the formatted string verbatim. */
|
|
488
|
+
interface ServerLogger {
|
|
489
|
+
warn(message: string): void;
|
|
490
|
+
}
|
|
277
491
|
interface CaptureServer {
|
|
278
492
|
ws: PlateBroadcaster;
|
|
493
|
+
/**
|
|
494
|
+
* Vite's logger (`server.config.logger`). When present its `warn` prints the
|
|
495
|
+
* diagnostic text to the server output; otherwise we fall back to
|
|
496
|
+
* `console.warn`. Either way the SAME shared formatter (diagnostics.ts) is
|
|
497
|
+
* used, so server output matches the browser console exactly.
|
|
498
|
+
*/
|
|
499
|
+
config?: {
|
|
500
|
+
logger?: ServerLogger;
|
|
501
|
+
};
|
|
279
502
|
}
|
|
280
503
|
/**
|
|
281
|
-
* Per-plate breakpoints +
|
|
504
|
+
* Per-plate breakpoints + derived coverage bands, read from the committed
|
|
505
|
+
* StoredPlate files. The `spans` derive from each plate's `breakpoints` (ADR 0022
|
|
506
|
+
* cutover — per-View spans no longer exist on disk; a StoredPlate sweeps all
|
|
507
|
+
* widths in one session, so its coverage is complete).
|
|
282
508
|
*
|
|
283
509
|
* @internal
|
|
284
510
|
*/
|
|
285
511
|
declare function readCoverage(dir: string): Record<string, {
|
|
286
512
|
breakpoints: number[];
|
|
287
|
-
spans:
|
|
288
|
-
min?: number;
|
|
289
|
-
max?: number;
|
|
290
|
-
}[];
|
|
513
|
+
spans: Span[];
|
|
291
514
|
}>;
|
|
292
515
|
/**
|
|
293
|
-
*
|
|
516
|
+
* All recorded Fixtures as `{ <name>: <devalueString> }`, read from the
|
|
517
|
+
* `<name>.fixture.json` sidecars (ADR 0015). The map values are the OPAQUE
|
|
518
|
+
* devalue strings exactly as written — the node side never parses them; the
|
|
519
|
+
* browser dev client owns the devalue decode. The `.fixture.json` suffix keeps
|
|
520
|
+
* these distinct from Plate files (`isValidPlateName` rejects the dotted name, so
|
|
521
|
+
* `readCoverage` never picks them up — Fixtures never leak into Plate coverage).
|
|
294
522
|
*
|
|
295
523
|
* @internal
|
|
296
524
|
*/
|
|
297
|
-
declare function
|
|
525
|
+
declare function readFixtures(dir: string): Record<string, string>;
|
|
298
526
|
/**
|
|
299
|
-
*
|
|
527
|
+
* Write a recorded Fixture sidecar (ADR 0015). Reuses the capture endpoint's
|
|
528
|
+
* front-door checks (method, content type, declared/streamed size cap) and the
|
|
529
|
+
* `isValidPlateName` gate, but treats the devalue `data` payload as OPAQUE TEXT:
|
|
530
|
+
* it is validated only as a string and written verbatim, never eval'd or parsed
|
|
531
|
+
* server-side. The sidecar lives NEXT TO the Plate but is never merged into it,
|
|
532
|
+
* so the persisted Plate stays purely structural.
|
|
300
533
|
*
|
|
301
534
|
* @internal
|
|
302
535
|
*/
|
|
303
|
-
declare function
|
|
536
|
+
declare function handleFixturePost(req: CaptureRequest, res: CaptureResponse, fixturePath: (name: string) => string): void;
|
|
537
|
+
/**
|
|
538
|
+
* Validate a posted `StoredPlate` envelope, write it AS-IS, and hot-swap the
|
|
539
|
+
* plate in place. After the ADR 0022 cutover the dev client posts a finished
|
|
540
|
+
* StoredPlate (the whole Plate already projected in the browser) inside a
|
|
541
|
+
* `{ plate, diagnostics? }` envelope — there is NO server-side accumulation/merge
|
|
542
|
+
* anymore. `diagnostics` is a SIBLING field the server logs and then drops; it
|
|
543
|
+
* never reaches the stored artifact (ADR 0008).
|
|
544
|
+
*
|
|
545
|
+
* @internal
|
|
546
|
+
*/
|
|
547
|
+
declare function handleCapturePost(req: CaptureRequest, res: CaptureResponse, server: CaptureServer, platePath: (name: string) => string): void;
|
|
304
548
|
/**
|
|
305
549
|
* Render a plate as the virtual module's source. Each stitch (`ref` node) becomes
|
|
306
550
|
* a real import of that child's virtual module, wired into a `refs` map on the
|
|
@@ -310,8 +554,13 @@ declare function isValidPlateName(name: string): boolean;
|
|
|
310
554
|
* import itself); an unknown child resolves to its own empty plate, never a
|
|
311
555
|
* build break.
|
|
312
556
|
*
|
|
557
|
+
* `name` defaults to the plate's own `name`, but the loader passes the
|
|
558
|
+
* PATH-derived name so a hand-edited StoredPlate whose `name` field drifted from
|
|
559
|
+
* its filename still serializes and self-references under the canonical name
|
|
560
|
+
* (`collectRefs` reads `stored.tree`, a RenderNode, unchanged by the cutover).
|
|
561
|
+
*
|
|
313
562
|
* @internal
|
|
314
563
|
*/
|
|
315
|
-
declare function renderPlateModule(merged: MergedPlate): string;
|
|
564
|
+
declare function renderPlateModule(merged: MergedPlate, name?: string): string;
|
|
316
565
|
//#endregion
|
|
317
|
-
export { type LeafKind, type Plate, type
|
|
566
|
+
export { type LeafKind, type Plate, type PlateNode, type PlateRule, type StoredPlate, type ViewCapture, XrayOptions, collectRefs, handleCapturePost, handleFixturePost, isValidPlateName, readCoverage, readFixtures, renderPlateModule, xrayVitePlugin };
|