@michaelmagan/dnumb 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/AGENTS.md +180 -0
- package/LICENSE +21 -0
- package/README.md +297 -0
- package/dist/analysis/cluster.cjs +114 -0
- package/dist/analysis/cluster.d.cts +68 -0
- package/dist/analysis/cluster.d.ts +68 -0
- package/dist/analysis/cluster.js +109 -0
- package/dist/analysis/distances.cjs +124 -0
- package/dist/analysis/distances.d.cts +58 -0
- package/dist/analysis/distances.d.ts +58 -0
- package/dist/analysis/distances.js +119 -0
- package/dist/analysis/geometry.cjs +60 -0
- package/dist/analysis/geometry.d.cts +34 -0
- package/dist/analysis/geometry.d.ts +34 -0
- package/dist/analysis/geometry.js +53 -0
- package/dist/analysis/layout-tree.cjs +245 -0
- package/dist/analysis/layout-tree.d.cts +45 -0
- package/dist/analysis/layout-tree.d.ts +45 -0
- package/dist/analysis/layout-tree.js +242 -0
- package/dist/analysis/region-query.cjs +71 -0
- package/dist/analysis/region-query.d.cts +25 -0
- package/dist/analysis/region-query.d.ts +25 -0
- package/dist/analysis/region-query.js +67 -0
- package/dist/analysis/types.cjs +7 -0
- package/dist/analysis/types.d.cts +143 -0
- package/dist/analysis/types.d.ts +143 -0
- package/dist/analysis/types.js +6 -0
- package/dist/bundle.cjs +182 -0
- package/dist/bundle.d.cts +91 -0
- package/dist/bundle.d.ts +91 -0
- package/dist/bundle.js +175 -0
- package/dist/cli.cjs +755 -0
- package/dist/cli.d.cts +2 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +719 -0
- package/dist/diff/changes.cjs +656 -0
- package/dist/diff/changes.d.cts +2 -0
- package/dist/diff/changes.d.ts +2 -0
- package/dist/diff/changes.js +653 -0
- package/dist/diff/identity.cjs +167 -0
- package/dist/diff/identity.d.cts +62 -0
- package/dist/diff/identity.d.ts +62 -0
- package/dist/diff/identity.js +159 -0
- package/dist/diff/index.cjs +84 -0
- package/dist/diff/index.d.cts +76 -0
- package/dist/diff/index.d.ts +76 -0
- package/dist/diff/index.js +68 -0
- package/dist/diff/match.cjs +156 -0
- package/dist/diff/match.d.cts +17 -0
- package/dist/diff/match.d.ts +17 -0
- package/dist/diff/match.js +152 -0
- package/dist/diff/render.cjs +540 -0
- package/dist/diff/render.d.cts +2 -0
- package/dist/diff/render.d.ts +2 -0
- package/dist/diff/render.js +537 -0
- package/dist/diff/types.cjs +2 -0
- package/dist/diff/types.d.cts +211 -0
- package/dist/diff/types.d.ts +211 -0
- package/dist/diff/types.js +1 -0
- package/dist/digest/index.cjs +8 -0
- package/dist/digest/index.d.cts +4 -0
- package/dist/digest/index.d.ts +4 -0
- package/dist/digest/index.js +2 -0
- package/dist/digest/parse.cjs +163 -0
- package/dist/digest/parse.d.cts +80 -0
- package/dist/digest/parse.d.ts +80 -0
- package/dist/digest/parse.js +160 -0
- package/dist/digest/render.cjs +1575 -0
- package/dist/digest/render.d.cts +41 -0
- package/dist/digest/render.d.ts +41 -0
- package/dist/digest/render.js +1571 -0
- package/dist/digest/types.cjs +10 -0
- package/dist/digest/types.d.cts +86 -0
- package/dist/digest/types.d.ts +86 -0
- package/dist/digest/types.js +9 -0
- package/dist/index.cjs +61 -0
- package/dist/index.d.cts +27 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +19 -0
- package/dist/page.cjs +35 -0
- package/dist/page.d.cts +40 -0
- package/dist/page.d.ts +40 -0
- package/dist/page.js +31 -0
- package/dist/playwright.cjs +138 -0
- package/dist/playwright.d.cts +69 -0
- package/dist/playwright.d.ts +69 -0
- package/dist/playwright.js +131 -0
- package/dist/query.cjs +1152 -0
- package/dist/query.d.cts +47 -0
- package/dist/query.d.ts +47 -0
- package/dist/query.js +1146 -0
- package/dist/snapshot/capture.cjs +80 -0
- package/dist/snapshot/capture.d.cts +34 -0
- package/dist/snapshot/capture.d.ts +34 -0
- package/dist/snapshot/capture.js +77 -0
- package/dist/snapshot/cdp.cjs +450 -0
- package/dist/snapshot/cdp.d.cts +36 -0
- package/dist/snapshot/cdp.d.ts +36 -0
- package/dist/snapshot/cdp.js +447 -0
- package/dist/snapshot/clip.cjs +100 -0
- package/dist/snapshot/clip.d.cts +26 -0
- package/dist/snapshot/clip.d.ts +26 -0
- package/dist/snapshot/clip.js +96 -0
- package/dist/snapshot/collect.cjs +358 -0
- package/dist/snapshot/collect.d.cts +18 -0
- package/dist/snapshot/collect.d.ts +18 -0
- package/dist/snapshot/collect.js +355 -0
- package/dist/snapshot/color.cjs +1039 -0
- package/dist/snapshot/color.d.cts +316 -0
- package/dist/snapshot/color.d.ts +316 -0
- package/dist/snapshot/color.js +1026 -0
- package/dist/snapshot/facts.cjs +167 -0
- package/dist/snapshot/facts.d.cts +48 -0
- package/dist/snapshot/facts.d.ts +48 -0
- package/dist/snapshot/facts.js +161 -0
- package/dist/snapshot/limits.cjs +69 -0
- package/dist/snapshot/limits.d.cts +59 -0
- package/dist/snapshot/limits.d.ts +59 -0
- package/dist/snapshot/limits.js +65 -0
- package/dist/snapshot/not-measured.cjs +44 -0
- package/dist/snapshot/not-measured.d.cts +14 -0
- package/dist/snapshot/not-measured.d.ts +14 -0
- package/dist/snapshot/not-measured.js +41 -0
- package/dist/snapshot/types.cjs +2 -0
- package/dist/snapshot/types.d.cts +238 -0
- package/dist/snapshot/types.d.ts +238 -0
- package/dist/snapshot/types.js +1 -0
- package/dist/spec/index.cjs +9 -0
- package/dist/spec/index.d.cts +15 -0
- package/dist/spec/index.d.ts +15 -0
- package/dist/spec/index.js +6 -0
- package/package.json +141 -0
- package/skills/dnumb/SKILL.md +136 -0
package/dist/bundle.js
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { SPEC_VERSION } from "./spec/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* The four color-capture string fields and the two overlap-signal fields are
|
|
4
|
+
* required on `ElementRecord` (`snapshot/types.ts`) -- both real capture
|
|
5
|
+
* paths always set all six, the same as `rect` or `overflowX`. A bundle
|
|
6
|
+
* written before they existed simply has no key for them at all in its JSON,
|
|
7
|
+
* though, which is not the same violation as a caller hand-building a record
|
|
8
|
+
* and skipping one: this is a format-migration problem, not a construction
|
|
9
|
+
* error, so it gets a format-migration fix here rather than the clear thrown
|
|
10
|
+
* error `snapshot/color.ts#requireColorField` gives a hand-built record that
|
|
11
|
+
* is missing a required field. Reading `undefined.trim()` or
|
|
12
|
+
* `undefined.indexOf(...)` off an unmigrated old record would throw deep
|
|
13
|
+
* inside `snapshot/color.ts` regardless of which of the two situations
|
|
14
|
+
* produced it, which is exactly the failure this function exists to
|
|
15
|
+
* pre-empt for the bundle case specifically.
|
|
16
|
+
*
|
|
17
|
+
* `textShadow`/`textStrokeWidth`/`textStrokeColor` are backfilled with the
|
|
18
|
+
* same defaults a fresh capture with nothing to report would itself have
|
|
19
|
+
* written -- `'none'`/`'0px'`/opaque black -- because a missing value and a
|
|
20
|
+
* genuine "no such style" both render identically for these three (each is
|
|
21
|
+
* an optional counter-fact token, printed only when present; see
|
|
22
|
+
* `snapshot/color.ts#colorFactFor`'s tail tokens), so there is no
|
|
23
|
+
* fabrication risk in picking the harmless default.
|
|
24
|
+
*
|
|
25
|
+
* `backgroundImage` does NOT get that treatment (A3, fixed here): it feeds
|
|
26
|
+
* `resolveBackdrop`'s gradient/image detection directly, so defaulting a
|
|
27
|
+
* genuinely-uncaptured value to `'none'` used to manufacture the positive
|
|
28
|
+
* claim "this element has no background image" -- which then silently
|
|
29
|
+
* terminated the compositing walk and printed a specific, fabricated ratio.
|
|
30
|
+
* `null` here means exactly what it means on `hitStackIds`/`paintOrder`
|
|
31
|
+
* below: this specific capture never measured this field, and
|
|
32
|
+
* `snapshot/color.ts` refuses to guess past it (reports
|
|
33
|
+
* `unmeasured (not-captured ...)` instead) -- the same "backfill the honest
|
|
34
|
+
* unknown, not a false positive" discipline `NO_CAPTURE_NOT_MEASURED` below
|
|
35
|
+
* uses for shadow roots and iframes.
|
|
36
|
+
*
|
|
37
|
+
* Every record `parseBundle` returns satisfies the required contract by
|
|
38
|
+
* construction; `requireColorField` exists for the different case of a
|
|
39
|
+
* record that never went through this function at all.
|
|
40
|
+
*/
|
|
41
|
+
function normalizeElement(raw) {
|
|
42
|
+
const el = raw;
|
|
43
|
+
return {
|
|
44
|
+
...el,
|
|
45
|
+
backgroundImage: el.backgroundImage ?? null,
|
|
46
|
+
textShadow: el.textShadow ?? 'none',
|
|
47
|
+
textStrokeWidth: el.textStrokeWidth ?? '0px',
|
|
48
|
+
textStrokeColor: el.textStrokeColor ?? 'rgb(0, 0, 0)',
|
|
49
|
+
hitStackIds: el.hitStackIds ?? null,
|
|
50
|
+
paintOrder: el.paintOrder ?? null,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Stand-in for `notMeasured` when a `CaptureResult` has none -- both real
|
|
55
|
+
* capture paths always set it, so this only fires for a hand-built result
|
|
56
|
+
* that skipped them entirely (a test fixture, typically). `closed: 'unknown'`
|
|
57
|
+
* because that is true regardless of how the result was built; the rest
|
|
58
|
+
* default to zero rather than to a matching 'unknown', which is a real
|
|
59
|
+
* "no measurement was taken" gap this constant papers over for a
|
|
60
|
+
* non-production caller, not a claim about the page.
|
|
61
|
+
*/
|
|
62
|
+
const NO_CAPTURE_NOT_MEASURED = {
|
|
63
|
+
shadowRoots: { open: 0, closed: 'unknown' },
|
|
64
|
+
iframes: { sameOrigin: 0, crossOrigin: 0, unknownOrigin: 0 },
|
|
65
|
+
};
|
|
66
|
+
export function buildBundle(capture, options) {
|
|
67
|
+
return {
|
|
68
|
+
spec: SPEC_VERSION,
|
|
69
|
+
name: options.name,
|
|
70
|
+
url: options.url,
|
|
71
|
+
capturedAt: options.capturedAt,
|
|
72
|
+
viewport: options.viewportName
|
|
73
|
+
? { name: options.viewportName, ...capture.viewport }
|
|
74
|
+
: capture.viewport,
|
|
75
|
+
strategy: capture.strategy,
|
|
76
|
+
truncated: capture.truncated,
|
|
77
|
+
...(capture.cdpFellBack ? { cdpFellBack: true } : {}),
|
|
78
|
+
notMeasured: capture.notMeasured ?? NO_CAPTURE_NOT_MEASURED,
|
|
79
|
+
documentBackground: capture.documentBackground ?? null,
|
|
80
|
+
elements: capture.elements,
|
|
81
|
+
...(options.labels ? { labels: options.labels } : {}),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
export class BundleFormatError extends Error {
|
|
85
|
+
name = 'BundleFormatError';
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Parse and validate. Structural, not schema-library: the check exists so a
|
|
89
|
+
* consumer gets "this is not a dnumb bundle" rather than `undefined is not an
|
|
90
|
+
* object` fourteen frames deep, and that does not need a dependency.
|
|
91
|
+
*/
|
|
92
|
+
export function parseBundle(text, source = '<input>') {
|
|
93
|
+
let value;
|
|
94
|
+
try {
|
|
95
|
+
value = JSON.parse(text);
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
throw new BundleFormatError(`${source} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
99
|
+
}
|
|
100
|
+
if (typeof value !== 'object' || value === null) {
|
|
101
|
+
throw new BundleFormatError(`${source} is not a dnumb bundle (expected a JSON object)`);
|
|
102
|
+
}
|
|
103
|
+
const bundle = value;
|
|
104
|
+
if (typeof bundle.spec !== 'number') {
|
|
105
|
+
throw new BundleFormatError(`${source} is not a dnumb bundle (no "spec" field)`);
|
|
106
|
+
}
|
|
107
|
+
if (bundle.spec > SPEC_VERSION) {
|
|
108
|
+
throw new BundleFormatError(`${source} was written by a newer dnumb (spec ${bundle.spec}; this build reads ${SPEC_VERSION}). Upgrade dnumb.`);
|
|
109
|
+
}
|
|
110
|
+
if (!Array.isArray(bundle.elements)) {
|
|
111
|
+
throw new BundleFormatError(`${source} has no "elements" array`);
|
|
112
|
+
}
|
|
113
|
+
if (typeof bundle.viewport !== 'object' || bundle.viewport === null) {
|
|
114
|
+
throw new BundleFormatError(`${source} has no "viewport"`);
|
|
115
|
+
}
|
|
116
|
+
// `Bundle.strategy`'s own doc comment: "Recorded, never inferred" (B4).
|
|
117
|
+
// `bundle.strategy ?? 'walk'` used to assert a specific capture path a
|
|
118
|
+
// bundle never actually recorded -- a `cdp` bundle whose `"strategy"` key
|
|
119
|
+
// happened to be missing (an old writer, a hand-edited file) would parse
|
|
120
|
+
// back claiming to be `walk`, which is a different, real fact this bundle
|
|
121
|
+
// never measured. Neither value is an honest default for "don't know", so
|
|
122
|
+
// this refuses to parse rather than guess one -- the same register the
|
|
123
|
+
// `notMeasured` check just below uses for the identical shape of problem.
|
|
124
|
+
if (bundle.strategy !== 'cdp' && bundle.strategy !== 'walk') {
|
|
125
|
+
throw new BundleFormatError(`${source} has no valid "strategy" (must be "cdp" or "walk", got ` +
|
|
126
|
+
`${JSON.stringify(bundle.strategy)}): this build cannot honestly say which capture path ` +
|
|
127
|
+
'produced these records, and defaulting to "walk" would assert a specific path this ' +
|
|
128
|
+
'bundle never recorded. Re-capture the page with a current dnumb.');
|
|
129
|
+
}
|
|
130
|
+
// `NotMeasured`'s own doc comment: "every other field here is always a
|
|
131
|
+
// real, counted number." `NO_CAPTURE_NOT_MEASURED`'s zeroed shape is that
|
|
132
|
+
// promise's one sanctioned exception, and it is scoped narrowly -- "a
|
|
133
|
+
// hand-built [CaptureResult] that skipped them entirely (a test fixture,
|
|
134
|
+
// typically)", i.e. `buildBundle`'s in-memory case just above, where no
|
|
135
|
+
// capture happened at all and nothing claims otherwise. A bundle read back
|
|
136
|
+
// from disk is different: it is a *real* prior capture, and reusing that
|
|
137
|
+
// same all-zero shape for one that predates `notMeasured` used to print a
|
|
138
|
+
// specific, confident zero cross-origin-iframe count a page might not
|
|
139
|
+
// actually have (B4) -- exactly the "always a real, counted number"
|
|
140
|
+
// promise broken. There is no honest partial value this format can hand
|
|
141
|
+
// back for that case (unlike `strategy`, `NotMeasured`'s numeric fields
|
|
142
|
+
// have no `| undefined`/`'unknown'` escape hatch), so this refuses outright
|
|
143
|
+
// rather than fabricate one: re-capture with a current dnumb instead.
|
|
144
|
+
if (bundle.notMeasured === undefined) {
|
|
145
|
+
throw new BundleFormatError(`${source} predates capture accounting ("notMeasured") and cannot be read by this build: ` +
|
|
146
|
+
'it has no honest way to report shadow-DOM/iframe coverage for a bundle this old, and ' +
|
|
147
|
+
'silently reporting zero would claim a specific count this bundle never measured. ' +
|
|
148
|
+
're-capture the page with a current dnumb to get a bundle this build can read.');
|
|
149
|
+
}
|
|
150
|
+
return {
|
|
151
|
+
// The value this bundle actually claims, preserved -- never rewritten to
|
|
152
|
+
// this build's own `SPEC_VERSION` (B4). Already validated above: a
|
|
153
|
+
// number, and no greater than `SPEC_VERSION`.
|
|
154
|
+
spec: bundle.spec,
|
|
155
|
+
name: bundle.name ?? 'capture',
|
|
156
|
+
url: bundle.url ?? '',
|
|
157
|
+
capturedAt: bundle.capturedAt ?? '',
|
|
158
|
+
viewport: bundle.viewport,
|
|
159
|
+
// Already validated above: exactly `'cdp'` or `'walk'`, never inferred.
|
|
160
|
+
strategy: bundle.strategy,
|
|
161
|
+
truncated: bundle.truncated ?? false,
|
|
162
|
+
...(bundle.cdpFellBack ? { cdpFellBack: true } : {}),
|
|
163
|
+
notMeasured: bundle.notMeasured,
|
|
164
|
+
// `null` on a bundle old enough to predate color capture entirely (before
|
|
165
|
+
// `documentBackground` existed) -- an honest "never measured", not a
|
|
166
|
+
// guessed canvas color. See `DocumentBackground`.
|
|
167
|
+
documentBackground: bundle.documentBackground ?? null,
|
|
168
|
+
elements: bundle.elements.map(normalizeElement),
|
|
169
|
+
...(bundle.labels ? { labels: bundle.labels } : {}),
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
/** The bundle as it is written to disk. Stable key order, one trailing newline. */
|
|
173
|
+
export function serializeBundle(bundle) {
|
|
174
|
+
return `${JSON.stringify(bundle, null, 2)}\n`;
|
|
175
|
+
}
|