@hueest/xray 0.1.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/breakpoints-CBNlh7lQ.js +47 -0
- package/dist/client.d.ts +42 -0
- package/dist/client.js +354 -0
- package/dist/core.d.ts +55 -0
- package/dist/core.js +230 -0
- package/dist/hud.d.ts +18 -0
- package/dist/hud.js +165 -0
- package/dist/index.d.ts +317 -0
- package/dist/index.js +726 -0
- package/dist/plate-BuzRMPx4.js +94 -0
- package/dist/plate-DboxMKg-.d.ts +196 -0
- package/dist/react.core-CqnDjfAJ.js +59 -0
- package/dist/react.core-fgWG_svU.d.ts +21 -0
- package/dist/react.d.ts +29 -0
- package/dist/react.dev.d.ts +19 -0
- package/dist/react.dev.js +101 -0
- package/dist/react.js +58 -0
- package/dist/serialize-Bq6yJnNV.js +1071 -0
- package/dist/serialize.d.ts +88 -0
- package/dist/serialize.js +2 -0
- package/package.json +95 -0
- package/virtual.d.ts +53 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import { Plugin } from "vite";
|
|
2
|
+
|
|
3
|
+
//#region src/plate.d.ts
|
|
4
|
+
type LeafKind = 'text' | 'media' | 'box';
|
|
5
|
+
/**
|
|
6
|
+
* The shipped structural capture of a component's rendered DOM — the data a
|
|
7
|
+
* Skeleton renders from (see CONTEXT.md). Self-contained: pre-serialized chunks
|
|
8
|
+
* plus one scoped CSS string; depends on nothing in the consumer's build (ADR
|
|
9
|
+
* 0003). Built from the RenderNode tree at plugin load (see chunk.ts), so the
|
|
10
|
+
* adapter injects strings instead of walking a tree on the latency-critical
|
|
11
|
+
* first paint — and no per-framework serializer ships to the client (ADR 0008).
|
|
12
|
+
*/
|
|
13
|
+
interface Plate {
|
|
14
|
+
v: number;
|
|
15
|
+
/** Capture name; comes from the virtual-module specifier, never typed at a call site. */
|
|
16
|
+
name: string;
|
|
17
|
+
/**
|
|
18
|
+
* The root's children, pre-serialized (ADR 0008): a stitch-free run is one
|
|
19
|
+
* HTML `string`; a stitch is `{ r: name }`; a stitch-bearing element is
|
|
20
|
+
* `{ c: className, k: chunk[] }`. Null until first capture.
|
|
21
|
+
*/
|
|
22
|
+
chunks: Chunk[] | null;
|
|
23
|
+
/** The root node's own content classes (its inherited font context), if any. */
|
|
24
|
+
rootCls?: string;
|
|
25
|
+
/** Layout-relevant CSS (incl. `@media`) scoped under the plate root, paint stripped. */
|
|
26
|
+
css: string;
|
|
27
|
+
/**
|
|
28
|
+
* Child plates named by the chunks' `{ r }` stitches, resolved through the
|
|
29
|
+
* import graph (ADR 0006). Populated by the loader's generated imports, never
|
|
30
|
+
* serialized into the plates JSON; absent in production when there are no
|
|
31
|
+
* stitches. In dev the live store takes precedence, so this need not be present.
|
|
32
|
+
*/
|
|
33
|
+
refs?: Record<string, Plate>;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* One serialized child of a plate root (or of a stitch-bearing node): a
|
|
37
|
+
* 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).
|
|
41
|
+
*/
|
|
42
|
+
type Chunk = string | {
|
|
43
|
+
r: string;
|
|
44
|
+
} | {
|
|
45
|
+
c: string;
|
|
46
|
+
k: Chunk[];
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
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.
|
|
53
|
+
*/
|
|
54
|
+
interface MergedPlate {
|
|
55
|
+
v: number;
|
|
56
|
+
name: string;
|
|
57
|
+
tree: RenderNode | null;
|
|
58
|
+
css: string;
|
|
59
|
+
}
|
|
60
|
+
interface PlateNode {
|
|
61
|
+
/** Plate-local id; rules reference it pre-classify. Stripped from the shipped tree. */
|
|
62
|
+
id: number;
|
|
63
|
+
/** Present on paint leaves; absent on layout containers. */
|
|
64
|
+
leaf?: LeafKind;
|
|
65
|
+
/**
|
|
66
|
+
* A stitch: render the named child plate's skeleton here instead of own
|
|
67
|
+
* structure (ADR 0006). Mutually exclusive with `leaf`/`kids`. Resolved
|
|
68
|
+
* against `Plate.refs` (prod) or the live store (dev).
|
|
69
|
+
*/
|
|
70
|
+
ref?: string;
|
|
71
|
+
kids?: PlateNode[];
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* A node in the shipped tree, after classify (ADR 0007). Carries the
|
|
75
|
+
* content-addressed style classes it needs instead of an id — the CSS targets
|
|
76
|
+
* those classes, not a per-node `data-xr` value, so node scoping (the old
|
|
77
|
+
* `nodeKey`) is gone. `cls` is a space-joined class-token list.
|
|
78
|
+
*/
|
|
79
|
+
interface RenderNode {
|
|
80
|
+
leaf?: LeafKind;
|
|
81
|
+
ref?: string;
|
|
82
|
+
cls?: string;
|
|
83
|
+
kids?: RenderNode[];
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* A captured style rule in id form — selectors are generated at render time,
|
|
87
|
+
* so view merging can remap node ids without string surgery. `ids: []`
|
|
88
|
+
* targets the plate root.
|
|
89
|
+
*/
|
|
90
|
+
interface PlateRule {
|
|
91
|
+
ids: number[];
|
|
92
|
+
decls: string[];
|
|
93
|
+
/** Media-condition stack, outermost first; conditions AND together. */
|
|
94
|
+
media?: string[];
|
|
95
|
+
/** `@container`-condition stack, outermost first; conditions AND together. Absent when none. */
|
|
96
|
+
container?: string[];
|
|
97
|
+
layered?: boolean;
|
|
98
|
+
spec: number;
|
|
99
|
+
order: number;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* 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).
|
|
105
|
+
*/
|
|
106
|
+
interface ViewCapture {
|
|
107
|
+
/** Viewport width at capture time. */
|
|
108
|
+
width: number;
|
|
109
|
+
min?: number;
|
|
110
|
+
max?: number;
|
|
111
|
+
tree: PlateNode;
|
|
112
|
+
rules: PlateRule[];
|
|
113
|
+
}
|
|
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. */
|
|
119
|
+
breakpoints: number[];
|
|
120
|
+
views: ViewCapture[];
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* A dev-only sticky boolean toggle (get/set/subscribe), persisted per tab.
|
|
124
|
+
* Used for both "light box" (render every `<Skeleton>` regardless of readiness)
|
|
125
|
+
* and "capture" (whether browsing re-captures plates).
|
|
126
|
+
*/
|
|
127
|
+
interface ToggleStore {
|
|
128
|
+
get: () => boolean;
|
|
129
|
+
set: (value: boolean) => void;
|
|
130
|
+
subscribe: (onChange: () => void) => () => void;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Dev-only store of the freshest plate per name, updated in place when a
|
|
134
|
+
* capture lands. `<Skeleton>` reads through it so a new plate re-renders the
|
|
135
|
+
* site rather than reloading the module — which would remount the capture
|
|
136
|
+
* boundary and re-fire the capture, a feedback loop.
|
|
137
|
+
*/
|
|
138
|
+
interface PlateStore {
|
|
139
|
+
get: (name: string) => Plate | undefined;
|
|
140
|
+
subscribe: (name: string, onChange: () => void) => () => void;
|
|
141
|
+
/**
|
|
142
|
+
* Register a plate imported via the ref graph if the store has none yet — so a
|
|
143
|
+
* stitch resolves even before its child is re-captured, and survives a parent
|
|
144
|
+
* hot-swap (whose pushed plate carries no resolved `refs`). Never overwrites a
|
|
145
|
+
* live capture.
|
|
146
|
+
*/
|
|
147
|
+
seed: (name: string, plate: Plate) => void;
|
|
148
|
+
}
|
|
149
|
+
/** A view interval `[min, max)`; an open end is undefined (ADR 0004). */
|
|
150
|
+
interface Span {
|
|
151
|
+
min?: number;
|
|
152
|
+
max?: number;
|
|
153
|
+
}
|
|
154
|
+
/** A plate's detected width thresholds and the spans actually captured, from disk. */
|
|
155
|
+
interface PlateCoverage {
|
|
156
|
+
breakpoints: number[];
|
|
157
|
+
spans: Span[];
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Dev-only per-plate coverage, sourced from the committed plate files (not the
|
|
161
|
+
* session): the breakpoints detected so far and which view spans are captured.
|
|
162
|
+
* The HUD renders every band — covered ones lit, the rest dim — and the
|
|
163
|
+
* capture-all sweep derives its target widths from the breakpoints.
|
|
164
|
+
*/
|
|
165
|
+
interface CoverageStore {
|
|
166
|
+
get: () => ReadonlyMap<string, PlateCoverage>;
|
|
167
|
+
set: (name: string, coverage: PlateCoverage) => void;
|
|
168
|
+
subscribe: (onChange: () => void) => () => void;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Dev-only capture hook, installed by the dev client before the app boots.
|
|
172
|
+
* Absent in production builds, so `<Skeleton>` carries zero capture weight.
|
|
173
|
+
* `captured` returns a dispose callback (stop observing this site).
|
|
174
|
+
*/
|
|
175
|
+
interface CaptureHook {
|
|
176
|
+
captured: (name: string, el: Element, opts?: {
|
|
177
|
+
delay?: number;
|
|
178
|
+
}) => (() => void) | undefined;
|
|
179
|
+
/** Light Box toggle: force every `<Skeleton>` to its skeleton, read by `<Skeleton>`. */
|
|
180
|
+
lightbox?: ToggleStore;
|
|
181
|
+
/** Capture toggle: whether browsing re-captures plates, flipped from the HUD. */
|
|
182
|
+
capture?: ToggleStore;
|
|
183
|
+
/** Read side for `<Skeleton>`. */
|
|
184
|
+
plates?: PlateStore;
|
|
185
|
+
/** Write side for the dev client's HMR-event handler. */
|
|
186
|
+
updatePlate?: (name: string, plate: Plate) => void;
|
|
187
|
+
/** Dev-only: run the HUD-triggered capture-all-views sweep via the extension (ADR 0010). */
|
|
188
|
+
captureAllViews?: () => Promise<void>;
|
|
189
|
+
/** Dev-only per-plate breakpoints + captured spans, from disk; fed by the bootstrap. */
|
|
190
|
+
coverage?: CoverageStore;
|
|
191
|
+
}
|
|
192
|
+
declare global {
|
|
193
|
+
var __XRAY__: CaptureHook | undefined;
|
|
194
|
+
}
|
|
195
|
+
//#endregion
|
|
196
|
+
//#region src/merge.d.ts
|
|
197
|
+
/**
|
|
198
|
+
* Turn the per-view captures in a plate file into the one plate a Skeleton
|
|
199
|
+
* renders.
|
|
200
|
+
*
|
|
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.
|
|
211
|
+
*
|
|
212
|
+
* Pure data-in/data-out — runs in node (plugin load) and in tests.
|
|
213
|
+
*/
|
|
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;
|
|
219
|
+
//#endregion
|
|
220
|
+
//#region src/index.d.ts
|
|
221
|
+
/**
|
|
222
|
+
* Options for `xrayVitePlugin()`.
|
|
223
|
+
*
|
|
224
|
+
* Fixed conventions are intentionally not configurable: Plate names come from
|
|
225
|
+
* `virtual:xray/plates/<name>`, files live under `platesDir`, and responsive
|
|
226
|
+
* breakpoints are derived from captured CSS plus the `matchMedia` queries the
|
|
227
|
+
* page evaluates.
|
|
228
|
+
*/
|
|
229
|
+
interface XrayOptions {
|
|
230
|
+
/** Directory where Plate JSON files are written and read, relative to the Vite root. Default: `plates`. */
|
|
231
|
+
platesDir?: string;
|
|
232
|
+
/** Mount the dev HUD for coverage, Capture, Light Box, and capture-all controls. Default: `false`. */
|
|
233
|
+
hud?: boolean;
|
|
234
|
+
/**
|
|
235
|
+
* Initial state of the browse-time Capture toggle in dev. Keep this off for
|
|
236
|
+
* normal browsing and let the HUD, capture-all flow, or `?xray-capture` turn
|
|
237
|
+
* Capture on per tab. Default: `false`.
|
|
238
|
+
*/
|
|
239
|
+
capture?: boolean;
|
|
240
|
+
/** Settle delay in milliseconds before capturing a ready Skeleton; override one boundary with the `delay` prop. Default: `200`. */
|
|
241
|
+
delay?: number;
|
|
242
|
+
/** Upper bound in milliseconds on waiting for a busy subtree to go quiet before capturing anyway. Default: `5000`. */
|
|
243
|
+
settleCap?: number;
|
|
244
|
+
/** Refuse captures larger than this many nodes (Skeleton too high in the tree). Default: `4000`. */
|
|
245
|
+
maxNodes?: number;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Create the Vite plugins that resolve committed Plates and, during dev,
|
|
249
|
+
* capture new Plates from rendered `<Skeleton>` boundaries.
|
|
250
|
+
*
|
|
251
|
+
* Typical setup:
|
|
252
|
+
*
|
|
253
|
+
* ```ts
|
|
254
|
+
* import { xrayVitePlugin } from '@hueest/xray'
|
|
255
|
+
*
|
|
256
|
+
* export default {
|
|
257
|
+
* plugins: [xrayVitePlugin({ hud: true })],
|
|
258
|
+
* }
|
|
259
|
+
* ```
|
|
260
|
+
*
|
|
261
|
+
* Returns two plugins:
|
|
262
|
+
* - `xray:data` resolves `virtual:xray/plates/<name>` from committed Plate files in dev and build.
|
|
263
|
+
* - `xray:dev` runs only during `vite serve`; it injects the capture client, receives Captures, writes Plate files, and hot-updates the page.
|
|
264
|
+
*/
|
|
265
|
+
declare function xrayVitePlugin(options?: XrayOptions): Plugin[];
|
|
266
|
+
interface CaptureRequest {
|
|
267
|
+
method?: string;
|
|
268
|
+
on(event: 'data' | 'end', listener: (chunk?: Buffer | string) => void): unknown;
|
|
269
|
+
}
|
|
270
|
+
interface CaptureResponse {
|
|
271
|
+
statusCode: number;
|
|
272
|
+
end(chunk?: string): unknown;
|
|
273
|
+
}
|
|
274
|
+
interface PlateBroadcaster {
|
|
275
|
+
send(payload: unknown): void;
|
|
276
|
+
}
|
|
277
|
+
interface CaptureServer {
|
|
278
|
+
ws: PlateBroadcaster;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Per-plate breakpoints + captured spans, read from the committed plate files.
|
|
282
|
+
*
|
|
283
|
+
* @internal
|
|
284
|
+
*/
|
|
285
|
+
declare function readCoverage(dir: string): Record<string, {
|
|
286
|
+
breakpoints: number[];
|
|
287
|
+
spans: {
|
|
288
|
+
min?: number;
|
|
289
|
+
max?: number;
|
|
290
|
+
}[];
|
|
291
|
+
}>;
|
|
292
|
+
/**
|
|
293
|
+
* Fold a posted capture into its plate file and hot-swap the plate in place.
|
|
294
|
+
*
|
|
295
|
+
* @internal
|
|
296
|
+
*/
|
|
297
|
+
declare function handleCapturePost(req: CaptureRequest, res: CaptureResponse, server: CaptureServer, platePath: (name: string) => string): void;
|
|
298
|
+
/**
|
|
299
|
+
* Names come from import specifiers and POST bodies — keep them inside the plates dir.
|
|
300
|
+
*
|
|
301
|
+
* @internal
|
|
302
|
+
*/
|
|
303
|
+
declare function isValidPlateName(name: string): boolean;
|
|
304
|
+
/**
|
|
305
|
+
* Render a plate as the virtual module's source. Each stitch (`ref` node) becomes
|
|
306
|
+
* a real import of that child's virtual module, wired into a `refs` map on the
|
|
307
|
+
* default export — so the bundler builds the actual reference graph and chunks
|
|
308
|
+
* the transitive closure a page uses, with no runtime registry (ADR 0006). A
|
|
309
|
+
* stitchless plate is just its JSON. Self-references are dropped (a module can't
|
|
310
|
+
* import itself); an unknown child resolves to its own empty plate, never a
|
|
311
|
+
* build break.
|
|
312
|
+
*
|
|
313
|
+
* @internal
|
|
314
|
+
*/
|
|
315
|
+
declare function renderPlateModule(merged: MergedPlate): string;
|
|
316
|
+
//#endregion
|
|
317
|
+
export { type LeafKind, type Plate, type PlateFile, type PlateNode, type PlateRule, type ViewCapture, XrayOptions, addCapture, collectRefs, handleCapturePost, isValidPlateName, mergeViews, readCoverage, renderPlateModule, xrayVitePlugin };
|