@hueest/xray 0.2.0 → 0.4.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 +34 -18
- package/dist/client.js +236 -82
- package/dist/core.d.ts +15 -27
- package/dist/core.js +34 -210
- package/dist/index.d.ts +98 -59
- package/dist/index.js +240 -601
- package/dist/{plate-DoE1HEXp.js → plate-BRR6d8Se.js} +8 -4
- package/dist/project-BZosujs9.js +10250 -0
- package/dist/{react.core-IyFy2b_8.js → react.core-DMIhXHZF.js} +6 -1
- package/dist/{react.core-BzMG_cDy.d.ts → react.core-NhWc9qan.d.ts} +13 -3
- package/dist/react.d.ts +1 -1
- package/dist/react.dev.d.ts +1 -1
- package/dist/react.dev.js +34 -9
- package/dist/react.js +1 -1
- package/dist/{plate-Bv6W-GkA.d.ts → serialize-BgdGt34A.d.ts} +193 -30
- package/package.json +2 -1
- package/dist/breakpoints-CMoFUUOv.js +0 -1393
- package/dist/serialize-B--oY2bV.d.ts +0 -143
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as ROOT_ATTR, o as ROOT_CLASS, s as ROOT_INSTANT_ATTR, t as BASE_CSS } from "./plate-
|
|
1
|
+
import { a as ROOT_ATTR, o as ROOT_CLASS, s as ROOT_INSTANT_ATTR, t as BASE_CSS } from "./plate-BRR6d8Se.js";
|
|
2
2
|
import { t as escapeStyleText } from "./css-escape-N7bOusGW.js";
|
|
3
3
|
import { Fragment, useEffect, useRef, useState } from "react";
|
|
4
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -74,6 +74,7 @@ function collectStitchNames(chunks) {
|
|
|
74
74
|
const walk = (cs) => {
|
|
75
75
|
for (const chunk of cs) {
|
|
76
76
|
if (typeof chunk === "string") continue;
|
|
77
|
+
if ("t" in chunk) continue;
|
|
77
78
|
if ("r" in chunk) names.push(chunk.r);
|
|
78
79
|
else walk(chunk.k);
|
|
79
80
|
}
|
|
@@ -256,6 +257,10 @@ function renderChunks(chunks, resolveStitch) {
|
|
|
256
257
|
style: DISPLAY_CONTENTS,
|
|
257
258
|
dangerouslySetInnerHTML: { __html: chunk }
|
|
258
259
|
}, i);
|
|
260
|
+
if ("t" in chunk) return /* @__PURE__ */ jsx("div", {
|
|
261
|
+
style: DISPLAY_CONTENTS,
|
|
262
|
+
dangerouslySetInnerHTML: { __html: chunk.t.repeat(chunk.n) }
|
|
263
|
+
}, i);
|
|
259
264
|
if ("r" in chunk) return /* @__PURE__ */ jsx(Fragment, { children: resolveStitch(chunk.r) }, i);
|
|
260
265
|
return /* @__PURE__ */ jsx("div", {
|
|
261
266
|
className: chunk.c,
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { o as XrayCaptureWalker } from "./serialize-B--oY2bV.js";
|
|
1
|
+
import { c as XrayCaptureWalker, f as Plate } from "./serialize-BgdGt34A.js";
|
|
3
2
|
import { ReactNode } from "react";
|
|
4
3
|
|
|
5
4
|
//#region src/react.core.d.ts
|
|
@@ -44,10 +43,21 @@ interface SkeletonBase {
|
|
|
44
43
|
* The escape hatch beyond the declarative `data-xr-*` annotations: a closure
|
|
45
44
|
* that decides, per element, whether to ignore it, collapse it to one Bone, or
|
|
46
45
|
* defer to the built-in walk. Threaded through the dev adapter into
|
|
47
|
-
* `
|
|
46
|
+
* `captureView`; PRODUCTION ignores it entirely (it never captures). On both
|
|
48
47
|
* `root` variants of the union, so a manual root may carry one too.
|
|
49
48
|
*/
|
|
50
49
|
captureWalker?: XrayCaptureWalker;
|
|
50
|
+
/**
|
|
51
|
+
* Per-Skeleton Collect limits (ADR 0022, dev only): override the plugin's
|
|
52
|
+
* `collect` defaults for THIS boundary's capture. Flat primitives — not a nested
|
|
53
|
+
* object — so they stay referentially stable across renders and never churn the
|
|
54
|
+
* dev capture effect's deps. `maxNodes` caps total surviving nodes, `maxBreadth`
|
|
55
|
+
* the widest level, `maxDepth` the nesting depth; an omitted `maxNodes` derives
|
|
56
|
+
* from the resolved breadth × depth. PRODUCTION ignores them (it never captures).
|
|
57
|
+
*/
|
|
58
|
+
maxNodes?: number;
|
|
59
|
+
maxBreadth?: number;
|
|
60
|
+
maxDepth?: number;
|
|
51
61
|
}
|
|
52
62
|
/**
|
|
53
63
|
* Props accepted by the React `<Skeleton>` component, generic over the optional
|
package/dist/react.d.ts
CHANGED
package/dist/react.dev.d.ts
CHANGED
package/dist/react.dev.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { n as BOUNDARY_ATTR } from "./plate-
|
|
2
|
-
import { a as renderPlate, i as markStitchesLive, n as isRenderProp, o as resolveContent, r as isStitchLive, s as useSkeletonTiming, t as collectStitchNames } from "./react.core-
|
|
1
|
+
import { n as BOUNDARY_ATTR } from "./plate-BRR6d8Se.js";
|
|
2
|
+
import { a as renderPlate, i as markStitchesLive, n as isRenderProp, o as resolveContent, r as isStitchLive, s as useSkeletonTiming, t as collectStitchNames } from "./react.core-DMIhXHZF.js";
|
|
3
3
|
import { Suspense, useCallback, useEffect, useRef, useState, useSyncExternalStore } from "react";
|
|
4
4
|
import { Fragment as Fragment$1, jsx } from "react/jsx-runtime";
|
|
5
5
|
//#region src/react.dev.tsx
|
|
@@ -88,7 +88,7 @@ function useLivePlate(plate) {
|
|
|
88
88
|
* `react.tsx`.
|
|
89
89
|
*/
|
|
90
90
|
function Skeleton(props) {
|
|
91
|
-
const { plate, loading = false, suspense = false, fallback, delay, children, data, captureWalker } = props;
|
|
91
|
+
const { plate, loading = false, suspense = false, fallback, delay, children, data, captureWalker, maxNodes, maxBreadth, maxDepth } = props;
|
|
92
92
|
const manual = props.root === "manual";
|
|
93
93
|
const lightBoxOn = useLightBox();
|
|
94
94
|
const livePlate = useLivePlate(plate);
|
|
@@ -112,6 +112,9 @@ function Skeleton(props) {
|
|
|
112
112
|
manual,
|
|
113
113
|
data,
|
|
114
114
|
captureWalker,
|
|
115
|
+
maxNodes,
|
|
116
|
+
maxBreadth,
|
|
117
|
+
maxDepth,
|
|
115
118
|
children
|
|
116
119
|
});
|
|
117
120
|
if (suspense) return /* @__PURE__ */ jsx(Suspense, {
|
|
@@ -128,6 +131,20 @@ function Content(props) {
|
|
|
128
131
|
return /* @__PURE__ */ jsx(CaptureBoundary, { ...props });
|
|
129
132
|
}
|
|
130
133
|
/**
|
|
134
|
+
* Assemble the per-site Collect limits (ADR 0022) from the flat `<Skeleton>` props
|
|
135
|
+
* into the nested shape `captured` expects, or `undefined` when none are set (so
|
|
136
|
+
* the client's plugin defaults apply). Called inside the capture effect, keyed on
|
|
137
|
+
* the flat primitives, so it never builds an object during render.
|
|
138
|
+
*/
|
|
139
|
+
function collectLimits(maxNodes, maxBreadth, maxDepth) {
|
|
140
|
+
if (maxNodes === void 0 && maxBreadth === void 0 && maxDepth === void 0) return void 0;
|
|
141
|
+
return {
|
|
142
|
+
maxNodes,
|
|
143
|
+
maxBreadth,
|
|
144
|
+
maxDepth
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
131
148
|
* `root="auto"` (default): the existing `display:contents` boundary wrapper marks
|
|
132
149
|
* the capture seam so a parent capture stops here and references this plate by
|
|
133
150
|
* name rather than re-serializing the content (ADR 0006). A render prop is called
|
|
@@ -135,7 +152,7 @@ function Content(props) {
|
|
|
135
152
|
* element, carries the marker); its result renders inside the wrapper exactly as
|
|
136
153
|
* plain children do.
|
|
137
154
|
*/
|
|
138
|
-
function CaptureBoundary({ plate, delay, children, data, captureWalker }) {
|
|
155
|
+
function CaptureBoundary({ plate, delay, children, data, captureWalker, maxNodes, maxBreadth, maxDepth }) {
|
|
139
156
|
const ref = useRef(null);
|
|
140
157
|
useEffect(() => {
|
|
141
158
|
const el = ref.current;
|
|
@@ -143,12 +160,16 @@ function CaptureBoundary({ plate, delay, children, data, captureWalker }) {
|
|
|
143
160
|
return globalThis.__XRAY__?.captured(plate.name, el, {
|
|
144
161
|
delay,
|
|
145
162
|
walker: captureWalker,
|
|
146
|
-
mode: "auto"
|
|
163
|
+
mode: "auto",
|
|
164
|
+
collect: collectLimits(maxNodes, maxBreadth, maxDepth)
|
|
147
165
|
});
|
|
148
166
|
}, [
|
|
149
167
|
plate.name,
|
|
150
168
|
delay,
|
|
151
|
-
captureWalker
|
|
169
|
+
captureWalker,
|
|
170
|
+
maxNodes,
|
|
171
|
+
maxBreadth,
|
|
172
|
+
maxDepth
|
|
152
173
|
]);
|
|
153
174
|
const effective = useFixtureData(plate.name, data, isRenderProp(children));
|
|
154
175
|
return /* @__PURE__ */ jsx("div", {
|
|
@@ -171,7 +192,7 @@ function CaptureBoundary({ plate, delay, children, data, captureWalker }) {
|
|
|
171
192
|
* can produce. A dev-time effect asserts the ref fired and loudly `console.error`s
|
|
172
193
|
* when it did not.
|
|
173
194
|
*/
|
|
174
|
-
function ManualBoundary({ plate, delay, children, data, captureWalker }) {
|
|
195
|
+
function ManualBoundary({ plate, delay, children, data, captureWalker, maxNodes, maxBreadth, maxDepth }) {
|
|
175
196
|
const disposeRef = useRef(void 0);
|
|
176
197
|
const attachedRef = useRef(false);
|
|
177
198
|
const ref = useCallback((el) => {
|
|
@@ -183,12 +204,16 @@ function ManualBoundary({ plate, delay, children, data, captureWalker }) {
|
|
|
183
204
|
disposeRef.current = globalThis.__XRAY__?.captured(plate.name, el, {
|
|
184
205
|
delay,
|
|
185
206
|
walker: captureWalker,
|
|
186
|
-
mode: "manual"
|
|
207
|
+
mode: "manual",
|
|
208
|
+
collect: collectLimits(maxNodes, maxBreadth, maxDepth)
|
|
187
209
|
});
|
|
188
210
|
}, [
|
|
189
211
|
plate.name,
|
|
190
212
|
delay,
|
|
191
|
-
captureWalker
|
|
213
|
+
captureWalker,
|
|
214
|
+
maxNodes,
|
|
215
|
+
maxBreadth,
|
|
216
|
+
maxDepth
|
|
192
217
|
]);
|
|
193
218
|
useEffect(() => () => disposeRef.current?.(), []);
|
|
194
219
|
useEffect(() => {
|
package/dist/react.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as renderPlate, i as markStitchesLive, o as resolveContent, r as isStitchLive, s as useSkeletonTiming, t as collectStitchNames } from "./react.core-
|
|
1
|
+
import { a as renderPlate, i as markStitchesLive, o as resolveContent, r as isStitchLive, s as useSkeletonTiming, t as collectStitchNames } from "./react.core-DMIhXHZF.js";
|
|
2
2
|
import { Suspense, useEffect, useRef, useState } from "react";
|
|
3
3
|
import { Fragment as Fragment$1, jsx } from "react/jsx-runtime";
|
|
4
4
|
//#region src/react.tsx
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* browser console (`client.ts`) and the Vite server output (`index.ts`) print.
|
|
6
6
|
*
|
|
7
7
|
* Motivation: the serializer silently swallows a handful of capture omissions
|
|
8
|
-
* (an unreadable cross-origin stylesheet, skipped pseudo-elements,
|
|
9
|
-
*
|
|
8
|
+
* (an unreadable cross-origin stylesheet, skipped pseudo-elements, off-screen
|
|
9
|
+
* bones dropped at the fold, an over-the-limit subtree). Early adopters asking "why does this
|
|
10
10
|
* skeleton look wrong?" had to read the serializer source to find out. These
|
|
11
11
|
* diagnostics surface those omissions as data the capture process returns, so a
|
|
12
12
|
* caller can log them — without the core performing any console side effects.
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* Strictly DEV-ONLY (see the plan's decisions and ADR 0008): this type is NOT a
|
|
15
15
|
* public package export and MUST NEVER reach the persisted Plate JSON, the
|
|
16
16
|
* `MergedPlate`, or the production adapter. It rides on the TRANSIENT
|
|
17
|
-
* `
|
|
17
|
+
* `ViewIR.diagnostics` field only, which `captureView` populates and the
|
|
18
18
|
* dev client strips before posting (so committed `plates/<name>.json` stays
|
|
19
19
|
* byte-identical to a capture that produced no diagnostics).
|
|
20
20
|
*
|
|
@@ -44,10 +44,10 @@ interface CaptureDiagnostic {
|
|
|
44
44
|
* serializer (or an existing oversized-capture warning folded into the same
|
|
45
45
|
* vocabulary). Kept as a const union so the message table is exhaustive.
|
|
46
46
|
*/
|
|
47
|
-
type DiagnosticCode = 'unreadable-stylesheet' | 'unreadable-import' | 'skipped-dynamic-pseudo' | 'skipped-pseudo-element' | 'unsupported-rule' | 'dropped-tag' | '
|
|
47
|
+
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';
|
|
48
48
|
//#endregion
|
|
49
49
|
//#region src/plate.d.ts
|
|
50
|
-
type LeafKind = 'text' | 'media' | 'box';
|
|
50
|
+
type LeafKind = 'text' | 'text-block' | 'media' | 'box';
|
|
51
51
|
/**
|
|
52
52
|
* The shipped structural capture of a component's rendered DOM — the data a
|
|
53
53
|
* Skeleton renders from (see CONTEXT.md). Self-contained: pre-serialized chunks
|
|
@@ -81,21 +81,27 @@ interface Plate {
|
|
|
81
81
|
/**
|
|
82
82
|
* One serialized child of a plate root (or of a stitch-bearing node): a
|
|
83
83
|
* stitch-free HTML run (`string`), a stitch to a child plate (`{ r: name }`),
|
|
84
|
-
*
|
|
85
|
-
* (`{ c: className, k: chunk[] }`)
|
|
86
|
-
*
|
|
84
|
+
* a stitch-bearing element kept real so its inner stitches can mount
|
|
85
|
+
* (`{ c: className, k: chunk[] }`), or a templated uniform run (`{ t: html, n }`):
|
|
86
|
+
* the cell's HTML once plus a repeat count the adapter expands to N copies (Phase
|
|
87
|
+
* 2). The framework-neutral render contract: an adapter injects the strings,
|
|
88
|
+
* mounts a child plate at each stitch, and repeats each template (ADR 0008).
|
|
87
89
|
*/
|
|
88
90
|
type Chunk = string | {
|
|
89
91
|
r: string;
|
|
90
92
|
} | {
|
|
91
93
|
c: string;
|
|
92
94
|
k: Chunk[];
|
|
95
|
+
} | {
|
|
96
|
+
t: string;
|
|
97
|
+
n: number;
|
|
93
98
|
};
|
|
94
99
|
/**
|
|
95
|
-
* A plate after
|
|
96
|
-
* build-time RenderNode tree plus its scoped
|
|
97
|
-
*
|
|
98
|
-
*
|
|
100
|
+
* A plate after the in-browser Serialize (emit + merge/gate + classify) but
|
|
101
|
+
* before chunk serialization: the build-time RenderNode tree plus its scoped
|
|
102
|
+
* CSS. Produced by `serializePlateIR` (the structural part of `StoredPlate`) and
|
|
103
|
+
* the single-View `capture`, then serialized into the shipped `Plate` (chunks) at
|
|
104
|
+
* load (`serializePlate`). Never shipped to the client.
|
|
99
105
|
*/
|
|
100
106
|
interface MergedPlate {
|
|
101
107
|
v: number;
|
|
@@ -114,6 +120,12 @@ interface PlateNode {
|
|
|
114
120
|
* against `Plate.refs` (prod) or the live store (dev).
|
|
115
121
|
*/
|
|
116
122
|
ref?: string;
|
|
123
|
+
/**
|
|
124
|
+
* A templated uniform run (Phase 2): render this node's subtree `count` times.
|
|
125
|
+
* The serializer emits the subtree's HTML once plus the count; the adapter
|
|
126
|
+
* expands it to N copies at render. Absent on ordinary nodes.
|
|
127
|
+
*/
|
|
128
|
+
count?: number;
|
|
117
129
|
kids?: PlateNode[];
|
|
118
130
|
}
|
|
119
131
|
/**
|
|
@@ -126,6 +138,8 @@ interface RenderNode {
|
|
|
126
138
|
leaf?: LeafKind;
|
|
127
139
|
ref?: string;
|
|
128
140
|
cls?: string;
|
|
141
|
+
/** A templated uniform run (Phase 2): serialize this subtree once + a count the adapter repeats. */
|
|
142
|
+
count?: number;
|
|
129
143
|
kids?: RenderNode[];
|
|
130
144
|
}
|
|
131
145
|
/**
|
|
@@ -146,8 +160,11 @@ interface PlateRule {
|
|
|
146
160
|
}
|
|
147
161
|
/**
|
|
148
162
|
* One capture of a component within one viewport view — `[min, max)` in px,
|
|
149
|
-
* either side open-ended when undefined.
|
|
150
|
-
*
|
|
163
|
+
* either side open-ended when undefined. The id-form `{ tree, rules }` shape
|
|
164
|
+
* `emit` projects a measured View into (ADR 0004). NO LONGER the disk/wire shape
|
|
165
|
+
* after the ADR 0022 cutover — `StoredPlate` (below) is. Kept as the
|
|
166
|
+
* framework-neutral per-View capture shape, exported for consumers reading the
|
|
167
|
+
* id-form projection directly.
|
|
151
168
|
*/
|
|
152
169
|
interface ViewCapture {
|
|
153
170
|
/** Viewport width at capture time. */
|
|
@@ -158,23 +175,31 @@ interface ViewCapture {
|
|
|
158
175
|
rules: PlateRule[];
|
|
159
176
|
/**
|
|
160
177
|
* TRANSIENT, dev-only capture diagnostics (why this view may be lower
|
|
161
|
-
* fidelity).
|
|
162
|
-
* it and posts it
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
* prod adapter ships no capture). See
|
|
178
|
+
* fidelity). Captured in the browser (mirrored onto `ViewIR.diagnostics`); the
|
|
179
|
+
* dev client logs it and posts it alongside the `StoredPlate` (so the Vite
|
|
180
|
+
* server can print the same text). It MUST NEVER be persisted: it is not part
|
|
181
|
+
* of the committed
|
|
182
|
+
* `StoredPlate` disk shape, the validator does not carry it, and the POST
|
|
183
|
+
* envelope keeps it in a SIBLING field that never reaches the stored artifact
|
|
184
|
+
* (ADR 0008). Absent in production (the prod adapter ships no capture). See
|
|
185
|
+
* diagnostics.ts and ADR 0008.
|
|
168
186
|
*/
|
|
169
187
|
diagnostics?: CaptureDiagnostic[];
|
|
170
188
|
}
|
|
171
|
-
/**
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
189
|
+
/**
|
|
190
|
+
* The committed `plates/<name>.json` shape AFTER the ADR 0022 cutover: the whole
|
|
191
|
+
* Plate already projected in the browser (the in-browser Serialize folds emit +
|
|
192
|
+
* the multi-View merge/gate + classify in one session), so disk holds ONE gated,
|
|
193
|
+
* content-addressed tree + css instead of per-View captures merged at load. It is
|
|
194
|
+
* a `MergedPlate` (the structural `{tree, css}` `serializePlate` consumes) PLUS the
|
|
195
|
+
* `breakpoints` the HUD coverage reads — the analysis-only geometry never crosses
|
|
196
|
+
* the wire (ADR 0021). The gen-side runs `serializePlate(stored) → Plate` (chunks)
|
|
197
|
+
* at module-load, leaving the Tailwind/Bundle passthrough a clean gen-side seam.
|
|
198
|
+
* Replaces `PlateFile` (+ `ViewCapture`) on disk and on the `/__xray` wire.
|
|
199
|
+
*/
|
|
200
|
+
interface StoredPlate extends MergedPlate {
|
|
201
|
+
/** Width thresholds (view boundaries); the HUD coverage derives its bands from these. */
|
|
176
202
|
breakpoints: number[];
|
|
177
|
-
views: ViewCapture[];
|
|
178
203
|
}
|
|
179
204
|
/** The (unserialized) plate an import resolves to before anything has been captured. */
|
|
180
205
|
declare function emptyPlate(name: string): MergedPlate;
|
|
@@ -298,6 +323,11 @@ interface CaptureHook {
|
|
|
298
323
|
delay?: number;
|
|
299
324
|
walker?: unknown;
|
|
300
325
|
mode?: 'auto' | 'manual';
|
|
326
|
+
collect?: {
|
|
327
|
+
maxNodes?: number;
|
|
328
|
+
maxBreadth?: number;
|
|
329
|
+
maxDepth?: number;
|
|
330
|
+
};
|
|
301
331
|
}) => (() => void) | undefined;
|
|
302
332
|
/** Light Box toggle: force every `<Skeleton>` to its skeleton, read by `<Skeleton>`. */
|
|
303
333
|
lightbox?: ToggleStore;
|
|
@@ -305,8 +335,8 @@ interface CaptureHook {
|
|
|
305
335
|
capture?: ToggleStore;
|
|
306
336
|
/** Read side for `<Skeleton>`. */
|
|
307
337
|
plates?: PlateStore;
|
|
308
|
-
/** Write side for the dev client's HMR-event handler. */
|
|
309
|
-
updatePlate?: (name: string, plate: Plate) =>
|
|
338
|
+
/** Write side for the dev client's HMR-event handler. Returns whether the plate changed. */
|
|
339
|
+
updatePlate?: (name: string, plate: Plate) => boolean;
|
|
310
340
|
/** Dev-only: run the HUD-triggered capture-all-views sweep via the extension (ADR 0010). */
|
|
311
341
|
captureAllViews?: () => Promise<void>;
|
|
312
342
|
/** Dev-only per-plate breakpoints + captured spans, from disk; fed by the bootstrap. */
|
|
@@ -335,4 +365,137 @@ declare global {
|
|
|
335
365
|
var __XRAY__: CaptureHook | undefined;
|
|
336
366
|
}
|
|
337
367
|
//#endregion
|
|
338
|
-
|
|
368
|
+
//#region src/serialize.d.ts
|
|
369
|
+
/**
|
|
370
|
+
* Browser-side capture: walk a live rendered subtree into a Plate — one node
|
|
371
|
+
* tree plus one self-contained, scoped CSS string (ADR 0003). The CSS is
|
|
372
|
+
* lifted from the author rules that matched each node, rewritten to
|
|
373
|
+
* plate-local selectors — it carries `@media` for free and keeps layout live
|
|
374
|
+
* (the M1 bake-off winner; ADR 0005).
|
|
375
|
+
*/
|
|
376
|
+
interface CaptureOptions {
|
|
377
|
+
/** Plate name; comes from the virtual-module specifier. */
|
|
378
|
+
name: string;
|
|
379
|
+
/** Collect limits (ADR 0022): refuse a capture whose measured tree exceeds any axis. */
|
|
380
|
+
collect?: CollectLimits;
|
|
381
|
+
/**
|
|
382
|
+
* Optional programmable capture walker (ADR 0018). The declarative `data-xr-*`
|
|
383
|
+
* annotations cover the 90%; this is the escape hatch for the 10% a markup
|
|
384
|
+
* attribute cannot express. Capture-only: it shapes the resulting `PlateNode`
|
|
385
|
+
* tree but is never persisted into the Plate, and the production adapter never
|
|
386
|
+
* captures so it ignores this entirely.
|
|
387
|
+
*/
|
|
388
|
+
walker?: XrayCaptureWalker;
|
|
389
|
+
/**
|
|
390
|
+
* Whether the top-level capture roots ARE this plate's own boundary, so the
|
|
391
|
+
* stitch guard must be SKIPPED for them (ADR 0006/0014, finding #6). True ONLY
|
|
392
|
+
* for a MANUAL root: there `site.el` is the consumer's own element and its
|
|
393
|
+
* `data-xr-boundary` marks the boundary of THIS plate, so the root must be
|
|
394
|
+
* serialized as a real Entry (its box/classes/padding/border) rather than a
|
|
395
|
+
* stitch-to-itself. Defaults to `false` — the auto path, where the top-level
|
|
396
|
+
* roots are the wrapper's CHILDREN and any boundary marker on a direct-child
|
|
397
|
+
* root is a NESTED `<Skeleton>` that MUST stitch (the capture-leak guard).
|
|
398
|
+
*/
|
|
399
|
+
captureRootIsBoundary?: boolean;
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* What a capture walker decides for one element (ADR 0018; ADR 0022 §5). The
|
|
403
|
+
* `applyWalker` PASS reads the decision and applies it to the measured IR node —
|
|
404
|
+
* a walker NEVER hand-builds a `PlateNode`, so xray keeps ownership of the Plate
|
|
405
|
+
* format. `ignore` drops the element + its subtree; `bone` collapses it to one
|
|
406
|
+
* Bone leaf (inferring the kind when omitted); `keep` defers to the default
|
|
407
|
+
* classifier (`markInk`).
|
|
408
|
+
*/
|
|
409
|
+
type WalkerDecision = {
|
|
410
|
+
type: 'ignore';
|
|
411
|
+
} | {
|
|
412
|
+
type: 'bone';
|
|
413
|
+
kind?: LeafKind;
|
|
414
|
+
} | {
|
|
415
|
+
type: 'keep';
|
|
416
|
+
};
|
|
417
|
+
/**
|
|
418
|
+
* The context handed to a capture walker's `element` hook (ADR 0018). The
|
|
419
|
+
* helpers are PURE — each returns a `WalkerDecision` the `applyWalker` pass
|
|
420
|
+
* applies; they do NOT mutate. `el` is the element under consideration.
|
|
421
|
+
*/
|
|
422
|
+
interface CaptureWalkerContext {
|
|
423
|
+
/** The element currently being classified. */
|
|
424
|
+
el: Element;
|
|
425
|
+
/** Drop this element AND its whole subtree (no bone). */
|
|
426
|
+
ignore(): WalkerDecision;
|
|
427
|
+
/**
|
|
428
|
+
* Collapse this element's subtree into ONE Bone leaf. `kind` omitted shares
|
|
429
|
+
* the default classifier's kind-inference heuristic (`inferLeafKind`; ADR 0018,
|
|
430
|
+
* Q4 — ONE code path).
|
|
431
|
+
*/
|
|
432
|
+
bone(opts?: {
|
|
433
|
+
kind?: LeafKind;
|
|
434
|
+
}): WalkerDecision;
|
|
435
|
+
/** Defer to the default classifier (`markInk`) — recurse into the subtree. */
|
|
436
|
+
keep(): WalkerDecision;
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* The programmable capture walker (ADR 0018; ADR 0022 §5). `element` is offered
|
|
440
|
+
* each non-boundary element TOP-DOWN over the measured IR (the `applyWalker`
|
|
441
|
+
* pass, NOT the DOM walk) and returns a `WalkerDecision` the pass applies. The
|
|
442
|
+
* declarative `data-xr-*` annotations cover the 90%; this is the 10% escape
|
|
443
|
+
* hatch for what a markup attribute cannot express.
|
|
444
|
+
*/
|
|
445
|
+
interface XrayCaptureWalker {
|
|
446
|
+
/**
|
|
447
|
+
* Called for every non-boundary element node (a stitch carries no element and
|
|
448
|
+
* is skipped — ADR 0006 — so a boundary never reaches here). Return one of
|
|
449
|
+
* `ctx.ignore()`, `ctx.bone()`, or `ctx.keep()`. An `ignore`/`bone` decision
|
|
450
|
+
* stops the descent (the subtree lowers); `keep` recurses.
|
|
451
|
+
*/
|
|
452
|
+
element?(ctx: CaptureWalkerContext): WalkerDecision;
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Typed identity wrapper for authoring a capture walker (ADR 0018) — the
|
|
456
|
+
* `defineConfig` pattern. It returns its argument unchanged at runtime; its only
|
|
457
|
+
* job is to infer and check the `XrayCaptureWalker` shape at the call site so an
|
|
458
|
+
* author gets completion and a typo in a hook name is caught.
|
|
459
|
+
*/
|
|
460
|
+
declare function defineXrayCaptureWalker(walker: XrayCaptureWalker): XrayCaptureWalker;
|
|
461
|
+
/**
|
|
462
|
+
* Thrown when a subtree has more nodes than the capture limit — almost always
|
|
463
|
+
* a `<Skeleton>` sitting too high in the tree. Raised after the cheap walk and
|
|
464
|
+
* before the O(rules × nodes) CSS extraction, so an oversized capture is
|
|
465
|
+
* refused without freezing the main thread on it.
|
|
466
|
+
*/
|
|
467
|
+
/** The measured size of a capture's surviving tree, one number per Collect axis (ADR 0022). */
|
|
468
|
+
type CaptureSize = {
|
|
469
|
+
nodes: number;
|
|
470
|
+
breadth: number;
|
|
471
|
+
depth: number;
|
|
472
|
+
};
|
|
473
|
+
/** Which Collect axis a too-large capture tripped. */
|
|
474
|
+
type CollectAxis = 'nodes' | 'breadth' | 'depth';
|
|
475
|
+
declare class CaptureTooLargeError extends Error {
|
|
476
|
+
/** The full measured size of the surviving tree that tripped the gate. */
|
|
477
|
+
readonly size: CaptureSize;
|
|
478
|
+
/** Which axis exceeded its cap, and the cap it exceeded. */
|
|
479
|
+
readonly axis: CollectAxis;
|
|
480
|
+
readonly max: number;
|
|
481
|
+
/** Back-compat alias for `size.nodes` (the sole axis before breadth/depth). */
|
|
482
|
+
readonly nodeCount: number;
|
|
483
|
+
/** Folds the oversized-capture warning into the shared diagnostics vocabulary (diagnostics.ts). */
|
|
484
|
+
readonly code: "too-large";
|
|
485
|
+
constructor(size: CaptureSize, axis: CollectAxis, max: number);
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* Collect-stage size limits (ADR 0022): refuse to capture a boundary whose
|
|
489
|
+
* measured tree exceeds any axis. `maxNodes` caps the total surviving-node count;
|
|
490
|
+
* `maxBreadth` the widest level (most nodes at any one depth); `maxDepth` the
|
|
491
|
+
* nesting depth. Each axis is enforced only when its cap is defined. The defaults
|
|
492
|
+
* and the `maxNodes = round(maxBreadth × maxDepth / 4)` derivation live in the
|
|
493
|
+
* client (where the other defaults live); this core only enforces what it is handed.
|
|
494
|
+
*/
|
|
495
|
+
type CollectLimits = {
|
|
496
|
+
maxNodes?: number;
|
|
497
|
+
maxBreadth?: number;
|
|
498
|
+
maxDepth?: number;
|
|
499
|
+
};
|
|
500
|
+
//#endregion
|
|
501
|
+
export { emptyPlate as _, CollectAxis as a, XrayCaptureWalker as c, MergedPlate as d, Plate as f, ViewCapture as g, StoredPlate as h, CaptureWalkerContext as i, defineXrayCaptureWalker as l, RenderNode as m, CaptureSize as n, CollectLimits as o, PlateNode as p, CaptureTooLargeError as r, WalkerDecision as s, CaptureOptions as t, LeafKind as u, CaptureDiagnostic as v };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hueest/xray",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Auto-captured skeleton loading screens for Vite — capture the structure, ship the plate.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"loading",
|
|
@@ -58,6 +58,7 @@
|
|
|
58
58
|
"access": "public"
|
|
59
59
|
},
|
|
60
60
|
"dependencies": {
|
|
61
|
+
"@chenglou/pretext": "0.0.8",
|
|
61
62
|
"devalue": "5.8.1",
|
|
62
63
|
"valibot": "1.4.1"
|
|
63
64
|
},
|