@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/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
- * or a stitch-bearing element kept real so its inner stitches can mount
39
- * (`{ c: className, k: chunk[] }`). The framework-neutral render contract: an
40
- * adapter injects the strings and mounts a child plate at each stitch (ADR 0008).
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 view-merge + classify but before chunk serialization: the
50
- * build-time RenderNode tree plus its scoped CSS. Produced by `mergeViews` and
51
- * `capture`, then serialized into the shipped `Plate` (chunks) at load
52
- * (`serializePlate`). Never shipped to the client.
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. What the dev client ships to the
104
- * plugin, and what the plate file accumulates (ADR 0004).
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
- /** The committed `plates/<name>.json`: per-view captures, merged at load. */
115
- interface PlateFile {
116
- v: number;
117
- name: string;
118
- /** Width thresholds (view boundaries) derived so far. */
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) => void;
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/merge.d.ts
363
+ //#region src/chunk.d.ts
197
364
  /**
198
- * Turn the per-view captures in a plate file into the one plate a Skeleton
199
- * renders.
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
- * Each view is kept whole — its own subtree, its own ids, its own rules
202
- * and shown only across the viewport range it owns, via `@media display:none`
203
- * gates. We do NOT merge the views' trees into one. An earlier design (ADR
204
- * 0004) did, sharing common structure and forking the rest, but aligning two
205
- * genuinely-different DOM trees (mobile stacked vs desktop two-column) without
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
- * Pure data-in/data-out — runs in node (plugin load) and in tests.
386
+ * @module
213
387
  */
214
- declare function mergeViews(file: PlateFile): MergedPlate;
215
- /** Distinct plate names referenced by the tree's stitches, in first-seen order. */
216
- declare function collectRefs(tree: RenderNode | null): string[];
217
- /** Fold a fresh capture into the plate file: union breakpoints, one capture per view. */
218
- declare function addCapture(file: PlateFile | null, name: string, capture: ViewCapture, breakpoints: readonly number[]): PlateFile;
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 + captured spans, read from the committed plate files.
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
- * Fold a posted capture into its plate file and hot-swap the plate in place.
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 handleCapturePost(req: CaptureRequest, res: CaptureResponse, server: CaptureServer, platePath: (name: string) => string): void;
525
+ declare function readFixtures(dir: string): Record<string, string>;
298
526
  /**
299
- * Names come from import specifiers and POST bodies — keep them inside the plates dir.
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 isValidPlateName(name: string): boolean;
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 PlateFile, type PlateNode, type PlateRule, type ViewCapture, XrayOptions, addCapture, collectRefs, handleCapturePost, isValidPlateName, mergeViews, readCoverage, renderPlateModule, xrayVitePlugin };
566
+ export { type LeafKind, type Plate, type PlateNode, type PlateRule, type StoredPlate, type ViewCapture, XrayOptions, collectRefs, handleCapturePost, handleFixturePost, isValidPlateName, readCoverage, readFixtures, renderPlateModule, xrayVitePlugin };