@testivai/witness 1.3.0 → 1.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/commands/report.d.ts.map +1 -1
- package/dist/commands/report.js +3 -1
- package/dist/commands/report.js.map +1 -1
- package/dist/config/local-config.d.ts +17 -0
- package/dist/config/local-config.d.ts.map +1 -1
- package/dist/config/local-config.js +8 -0
- package/dist/config/local-config.js.map +1 -1
- package/dist/diff/diff.d.ts.map +1 -1
- package/dist/diff/diff.js +13 -2
- package/dist/diff/diff.js.map +1 -1
- package/dist/diff/mask.d.ts +98 -0
- package/dist/diff/mask.d.ts.map +1 -0
- package/dist/diff/mask.js +209 -0
- package/dist/diff/mask.js.map +1 -0
- package/dist/diff/regions.d.ts +17 -4
- package/dist/diff/regions.d.ts.map +1 -1
- package/dist/diff/regions.js +70 -5
- package/dist/diff/regions.js.map +1 -1
- package/dist/diff/types.d.ts +13 -0
- package/dist/diff/types.d.ts.map +1 -1
- package/dist/report/compare.d.ts +9 -0
- package/dist/report/compare.d.ts.map +1 -1
- package/dist/report/compare.js +72 -12
- package/dist/report/compare.js.map +1 -1
- package/dist/report/generator.d.ts +12 -1
- package/dist/report/generator.d.ts.map +1 -1
- package/dist/report/generator.js +36 -15
- package/dist/report/generator.js.map +1 -1
- package/dist/report/results.d.ts +12 -0
- package/dist/report/results.d.ts.map +1 -1
- package/dist/report/template.d.ts.map +1 -1
- package/dist/report/template.js +74 -0
- package/dist/report/template.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../src/commands/report.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC;;;;;;;;;;GAUG;AACH,eAAO,MAAM,aAAa,
|
|
1
|
+
{"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../src/commands/report.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC;;;;;;;;;;GAUG;AACH,eAAO,MAAM,aAAa,SA8CtB,CAAC"}
|
package/dist/commands/report.js
CHANGED
|
@@ -34,7 +34,9 @@ exports.reportCommand = new commander_1.Command('report')
|
|
|
34
34
|
reportDir: config.reportDir ?? 'visual-report',
|
|
35
35
|
threshold: config.threshold,
|
|
36
36
|
autoOpen: options.open ?? config.autoOpen,
|
|
37
|
-
|
|
37
|
+
// NOTE: `version` here is the results.json SCHEMA version (a public
|
|
38
|
+
// contract), not the package version — conflating them shipped
|
|
39
|
+
// results.json files labeled 1.3.1. Omitted → schema default.
|
|
38
40
|
});
|
|
39
41
|
const { summary } = report;
|
|
40
42
|
console.log();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/commands/report.ts"],"names":[],"mappings":";;;;;;AAAA,yCAAoC;AACpC,kDAA0B;AAC1B,mDAAqD;AACrD,yDAAyD;AACzD,4CAAyC;AAEzC;;;;;;;;;;GAUG;AACU,QAAA,aAAa,GAAG,IAAI,mBAAO,CAAC,QAAQ,CAAC;KAC/C,WAAW,CACV,oFAAoF;IAClF,8FAA8F,CACjG;KACA,MAAM,CAAC,gBAAgB,EAAE,0EAA0E,CAAC;KACpG,MAAM,CAAC,QAAQ,EAAE,+DAA+D,CAAC;KACjF,MAAM,CAAC,KAAK,EAAE,OAAiD,EAAE,EAAE;IAClE,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;QAClC,MAAM,MAAM,GAAG,IAAA,8BAAe,EAAC,WAAW,CAAC,CAAC;QAE5C,MAAM,MAAM,GAAG,IAAA,0BAAc,EAAC;YAC5B,WAAW;YACX,SAAS,EAAE,MAAM,CAAC,SAAS,IAAI,eAAe;YAC9C,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,QAAQ,EAAE,OAAO,CAAC,IAAI,IAAI,MAAM,CAAC,QAAQ;YACzC,
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/commands/report.ts"],"names":[],"mappings":";;;;;;AAAA,yCAAoC;AACpC,kDAA0B;AAC1B,mDAAqD;AACrD,yDAAyD;AACzD,4CAAyC;AAEzC;;;;;;;;;;GAUG;AACU,QAAA,aAAa,GAAG,IAAI,mBAAO,CAAC,QAAQ,CAAC;KAC/C,WAAW,CACV,oFAAoF;IAClF,8FAA8F,CACjG;KACA,MAAM,CAAC,gBAAgB,EAAE,0EAA0E,CAAC;KACpG,MAAM,CAAC,QAAQ,EAAE,+DAA+D,CAAC;KACjF,MAAM,CAAC,KAAK,EAAE,OAAiD,EAAE,EAAE;IAClE,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;QAClC,MAAM,MAAM,GAAG,IAAA,8BAAe,EAAC,WAAW,CAAC,CAAC;QAE5C,MAAM,MAAM,GAAG,IAAA,0BAAc,EAAC;YAC5B,WAAW;YACX,SAAS,EAAE,MAAM,CAAC,SAAS,IAAI,eAAe;YAC9C,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,QAAQ,EAAE,OAAO,CAAC,IAAI,IAAI,MAAM,CAAC,QAAQ;YACzC,oEAAoE;YACpE,+DAA+D;YAC/D,8DAA8D;SAE/D,CAAC,CAAC;QAEH,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC;QAC3B,OAAO,CAAC,GAAG,EAAE,CAAC;QACd,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAC,CAAC;QACjE,OAAO,CAAC,GAAG,CACT,YAAY,OAAO,CAAC,KAAK,gBAAgB,OAAO,CAAC,MAAM,iBAAiB,OAAO,CAAC,OAAO,aAAa,OAAO,CAAC,YAAY,EAAE,CAC3H,CAAC;QACF,IAAI,OAAO,CAAC,YAAY,GAAG,CAAC,EAAE,CAAC;YAC7B,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,kFAAkF,CAAC,CAAC,CAAC;QAC9G,CAAC;QACD,IAAI,OAAO,CAAC,OAAO,GAAG,CAAC,EAAE,CAAC;YACxB,OAAO,CAAC,GAAG,CACT,eAAK,CAAC,IAAI,CAAC,aAAa,MAAM,CAAC,SAAS,IAAI,eAAe,yDAAyD,CAAC,CACtH,CAAC;QACJ,CAAC;QAED,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,MAAM,CAAC,UAAU,CAAC;QAC3D,IAAI,UAAU,IAAI,OAAO,CAAC,OAAO,GAAG,CAAC,EAAE,CAAC;YACtC,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,eAAM,CAAC,KAAK,CAAC,2BAA2B,EAAE,KAAK,CAAC,CAAC;QACjD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC"}
|
|
@@ -26,6 +26,23 @@ export interface LocalConfig {
|
|
|
26
26
|
* Example: ["[data-testivai-ignore]", ".version-badge", "#live-chat"]
|
|
27
27
|
*/
|
|
28
28
|
ignoreSelectors?: string[];
|
|
29
|
+
/**
|
|
30
|
+
* Masks: page areas excluded from the pixel diff and hatched in the
|
|
31
|
+
* report (auditable — never silent). Each entry is a CSS selector
|
|
32
|
+
* string (geometry captured by the adapter at capture time) or a
|
|
33
|
+
* geometric region: px numbers, 0–1 ratios, "NN%" strings, or a
|
|
34
|
+
* single-edge shorthand like { "top": 24 }.
|
|
35
|
+
*/
|
|
36
|
+
mask?: Array<string | Record<string, number | string>>;
|
|
37
|
+
/**
|
|
38
|
+
* Diff clustering tunables: minSize = noise floor in changed pixels
|
|
39
|
+
* (default 10), mergeDistance = px gap within which nearby regions
|
|
40
|
+
* merge into one (default 12).
|
|
41
|
+
*/
|
|
42
|
+
diffRegions?: {
|
|
43
|
+
minSize?: number;
|
|
44
|
+
mergeDistance?: number;
|
|
45
|
+
};
|
|
29
46
|
/**
|
|
30
47
|
* Pass tolerance: snapshots whose diff percentage (0-100) is at or below
|
|
31
48
|
* this value are reported as passed instead of changed. Default: 0 —
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-config.d.ts","sourceRoot":"","sources":["../../src/config/local-config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,MAAM,WAAW,WAAW;IAC1B,yCAAyC;IACzC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC;IACxB,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAC;IAClB,qEAAqE;IACrE,QAAQ,EAAE,OAAO,CAAC;IAClB,kEAAkE;IAClE,UAAU,EAAE,OAAO,CAAC;IACpB,uFAAuF;IACvF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B;;;;OAIG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;;OAMG;IACH,aAAa,EAAE,OAAO,CAAC;IACvB;;;OAGG;IACH,mBAAmB,EAAE,MAAM,CAAC;IAC5B;;;;OAIG;IACH,SAAS,EAAE,OAAO,CAAC;IACnB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4DAA4D;IAC5D,QAAQ,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAC9C;AAgBD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,WAAW,CAiBhE;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAC/B,WAAW,CAab;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAE9D;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,
|
|
1
|
+
{"version":3,"file":"local-config.d.ts","sourceRoot":"","sources":["../../src/config/local-config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,MAAM,WAAW,WAAW;IAC1B,yCAAyC;IACzC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC;IACxB,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAC;IAClB,qEAAqE;IACrE,QAAQ,EAAE,OAAO,CAAC;IAClB,kEAAkE;IAClE,UAAU,EAAE,OAAO,CAAC;IACpB,uFAAuF;IACvF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC;IACvD;;;;OAIG;IACH,WAAW,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3D;;;;OAIG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;;OAMG;IACH,aAAa,EAAE,OAAO,CAAC;IACvB;;;OAGG;IACH,mBAAmB,EAAE,MAAM,CAAC;IAC5B;;;;OAIG;IACH,SAAS,EAAE,OAAO,CAAC;IACnB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4DAA4D;IAC5D,QAAQ,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAC9C;AAgBD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,WAAW,CAiBhE;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAC/B,WAAW,CAab;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAE9D;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAYxD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,WAAW,CAE9C"}
|
|
@@ -113,6 +113,14 @@ function localConfigExists(projectRoot) {
|
|
|
113
113
|
* Priority: `.testivai/config.json` → check for mode field → default false.
|
|
114
114
|
*/
|
|
115
115
|
function isLocalMode(projectRoot) {
|
|
116
|
+
// Env override first: a repo can host BOTH lanes side by side (the demo
|
|
117
|
+
// app does), and .testivai/config.json { mode: "local" } must not hijack
|
|
118
|
+
// a cloud run. TESTIVAI_MODE=cloud|local wins over the file.
|
|
119
|
+
const envMode = process.env.TESTIVAI_MODE;
|
|
120
|
+
if (envMode === 'local')
|
|
121
|
+
return true;
|
|
122
|
+
if (envMode === 'cloud')
|
|
123
|
+
return false;
|
|
116
124
|
if (!localConfigExists(projectRoot)) {
|
|
117
125
|
return false;
|
|
118
126
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-config.js","sourceRoot":"","sources":["../../src/config/local-config.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"local-config.js","sourceRoot":"","sources":["../../src/config/local-config.ts"],"names":[],"mappings":"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"}
|
package/dist/diff/diff.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../src/diff/diff.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,WAAW,EAAE,UAAU,EAAgB,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../src/diff/diff.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,WAAW,EAAE,UAAU,EAAgB,MAAM,SAAS,CAAC;AAsBhE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,IAAI,CAClB,SAAS,EAAE,UAAU,GAAG,iBAAiB,EACzC,UAAU,EAAE,UAAU,GAAG,iBAAiB,EAC1C,KAAK,EAAE,UAAU,GAAG,iBAAiB,EACrC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,WAAgB,GACxB,UAAU,CA4GZ"}
|
package/dist/diff/diff.js
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
13
|
exports.diff = diff;
|
|
14
14
|
const ignore_1 = require("./ignore");
|
|
15
|
+
const mask_1 = require("./mask");
|
|
15
16
|
const resize_1 = require("./resize");
|
|
16
17
|
const regions_1 = require("./regions");
|
|
17
18
|
const minimap_1 = require("./minimap");
|
|
@@ -89,6 +90,12 @@ function diff(baseline8, candidate8, diff8, width, height, options = {}) {
|
|
|
89
90
|
effectiveCandidate = new Uint8ClampedArray(effectiveCandidate);
|
|
90
91
|
(0, ignore_1.applyIgnoreRegions)(effectiveBaseline, effectiveCandidate, effectiveWidth, effectiveHeight, opts.ignoreRegions);
|
|
91
92
|
}
|
|
93
|
+
// ── Masks: equalize before diffing, hatch the output after ─────────────
|
|
94
|
+
const masks = opts.masks ?? [];
|
|
95
|
+
if (masks.length > 0) {
|
|
96
|
+
effectiveBaseline = new Uint8ClampedArray(effectiveBaseline);
|
|
97
|
+
(0, mask_1.applyMaskRects)(effectiveBaseline, effectiveCandidate, effectiveWidth, effectiveHeight, masks);
|
|
98
|
+
}
|
|
92
99
|
// ── Core Pbd diff loop (basic mode — pixel-level YIQ comparison) ──────
|
|
93
100
|
const raw = pbdDiffCore(effectiveBaseline, effectiveCandidate, diff8, effectiveWidth, effectiveHeight, opts.threshold, opts.cumulatedThreshold, opts.enableMinimap);
|
|
94
101
|
// ── Compute extended result ─────────────────────────────────────────────
|
|
@@ -106,9 +113,13 @@ function diff(baseline8, candidate8, diff8, width, height, options = {}) {
|
|
|
106
113
|
if (sizeMismatch) {
|
|
107
114
|
result.sizeMismatch = sizeMismatch;
|
|
108
115
|
}
|
|
109
|
-
// ── Region detection
|
|
116
|
+
// ── Region detection (before hatching — hatch pixels must not cluster) ──
|
|
110
117
|
if (opts.detectRegions && !isIdentical) {
|
|
111
|
-
result.regions = (0, regions_1.detectRegions)(diff8, effectiveWidth, effectiveHeight);
|
|
118
|
+
result.regions = (0, regions_1.detectRegions)(diff8, effectiveWidth, effectiveHeight, opts.regionOptions ?? {});
|
|
119
|
+
}
|
|
120
|
+
// ── Hatch masked areas in the diff output (auditable, never silent) ─────
|
|
121
|
+
if (masks.length > 0) {
|
|
122
|
+
(0, mask_1.hatchMaskRects)(diff8, effectiveWidth, effectiveHeight, masks);
|
|
112
123
|
}
|
|
113
124
|
return result;
|
|
114
125
|
}
|
package/dist/diff/diff.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diff.js","sourceRoot":"","sources":["../../src/diff/diff.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;
|
|
1
|
+
{"version":3,"file":"diff.js","sourceRoot":"","sources":["../../src/diff/diff.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TestivAI Diff Engine — Masking DSL
|
|
3
|
+
*
|
|
4
|
+
* Masks exclude page areas from the pixel diff while staying auditable:
|
|
5
|
+
* every masked region is hatched in the diff output and listed in the
|
|
6
|
+
* report — silent masking is not acceptable.
|
|
7
|
+
*
|
|
8
|
+
* Two shapes:
|
|
9
|
+
* - CSS selector strings ("#cookie-banner"): geometry is captured by the
|
|
10
|
+
* adapter at capture time (metadata `maskRects`, via
|
|
11
|
+
* getBoundingClientRect) because the DOM snapshot carries no layout.
|
|
12
|
+
* Without captured rects the mask degrades to a warning — never a crash.
|
|
13
|
+
* - Geometric regions: px numbers, 0–1 ratio numbers, "NN%" strings, or a
|
|
14
|
+
* single-edge shorthand like { top: 24 } (full-width/height strip).
|
|
15
|
+
*/
|
|
16
|
+
export type MaskSpec = string | Record<string, number | string>;
|
|
17
|
+
/** A selector's geometry as captured by the adapter (metadata maskRects). */
|
|
18
|
+
export interface CapturedMaskRect {
|
|
19
|
+
selector: string;
|
|
20
|
+
x: number;
|
|
21
|
+
y: number;
|
|
22
|
+
width: number;
|
|
23
|
+
height: number;
|
|
24
|
+
}
|
|
25
|
+
export interface MaskSource {
|
|
26
|
+
type: 'selector' | 'region' | 'edge';
|
|
27
|
+
/** The original spec (selector string or the raw region object). */
|
|
28
|
+
spec: MaskSpec;
|
|
29
|
+
/** Where the mask came from. */
|
|
30
|
+
origin: 'config' | 'call';
|
|
31
|
+
}
|
|
32
|
+
/** A mask resolved to concrete pixels on a specific image. */
|
|
33
|
+
export interface MaskRect {
|
|
34
|
+
x: number;
|
|
35
|
+
y: number;
|
|
36
|
+
width: number;
|
|
37
|
+
height: number;
|
|
38
|
+
source: MaskSource;
|
|
39
|
+
}
|
|
40
|
+
type Parsed = {
|
|
41
|
+
type: 'selector';
|
|
42
|
+
selector: string;
|
|
43
|
+
} | {
|
|
44
|
+
type: 'region';
|
|
45
|
+
x: Dim;
|
|
46
|
+
y: Dim;
|
|
47
|
+
width: Dim;
|
|
48
|
+
height: Dim;
|
|
49
|
+
} | {
|
|
50
|
+
type: 'edge';
|
|
51
|
+
edge: 'top' | 'right' | 'bottom' | 'left';
|
|
52
|
+
size: Dim;
|
|
53
|
+
};
|
|
54
|
+
/** A dimension before resolution: px count or ratio of the image size. */
|
|
55
|
+
type Dim = {
|
|
56
|
+
kind: 'px';
|
|
57
|
+
value: number;
|
|
58
|
+
} | {
|
|
59
|
+
kind: 'ratio';
|
|
60
|
+
value: number;
|
|
61
|
+
};
|
|
62
|
+
export type ParseResult = {
|
|
63
|
+
ok: true;
|
|
64
|
+
mask: Parsed;
|
|
65
|
+
} | {
|
|
66
|
+
ok: false;
|
|
67
|
+
error: string;
|
|
68
|
+
};
|
|
69
|
+
/** Validate a single mask spec. Never throws. */
|
|
70
|
+
export declare function parseMaskSpec(spec: unknown): ParseResult;
|
|
71
|
+
export interface ResolvedMasks {
|
|
72
|
+
rects: MaskRect[];
|
|
73
|
+
/** Human-readable skips (e.g. selector without captured geometry). */
|
|
74
|
+
warnings: string[];
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Resolve mask specs to concrete pixel rects for one image.
|
|
78
|
+
*
|
|
79
|
+
* @param specs Mask specs from config and/or per-call options
|
|
80
|
+
* @param imgWidth Image width in px
|
|
81
|
+
* @param imgHeight Image height in px
|
|
82
|
+
* @param capturedRects Selector geometry captured by the adapter
|
|
83
|
+
* @param origin Where these specs came from (for the audit trail)
|
|
84
|
+
*/
|
|
85
|
+
export declare function resolveMasks(specs: MaskSpec[], imgWidth: number, imgHeight: number, capturedRects?: CapturedMaskRect[], origin?: 'config' | 'call'): ResolvedMasks;
|
|
86
|
+
/**
|
|
87
|
+
* Equalize masked pixels (copy candidate → baseline) so the diff loop sees
|
|
88
|
+
* no difference there. Mutates baseline in place; callers clone first.
|
|
89
|
+
*/
|
|
90
|
+
export declare function applyMaskRects(baseline: Uint8Array | Uint8ClampedArray, candidate: Uint8Array | Uint8ClampedArray, width: number, height: number, rects: MaskRect[]): void;
|
|
91
|
+
/**
|
|
92
|
+
* Draw a diagonal hatch pattern over masked areas of the diff image so
|
|
93
|
+
* masking is always visible in review. Only pixels inside mask rects are
|
|
94
|
+
* touched.
|
|
95
|
+
*/
|
|
96
|
+
export declare function hatchMaskRects(diff8: Uint8Array | Uint8ClampedArray, width: number, height: number, rects: MaskRect[]): void;
|
|
97
|
+
export {};
|
|
98
|
+
//# sourceMappingURL=mask.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mask.d.ts","sourceRoot":"","sources":["../../src/diff/mask.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;AAEhE,6EAA6E;AAC7E,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,UAAU,GAAG,QAAQ,GAAG,MAAM,CAAC;IACrC,oEAAoE;IACpE,IAAI,EAAE,QAAQ,CAAC;IACf,gCAAgC;IAChC,MAAM,EAAE,QAAQ,GAAG,MAAM,CAAC;CAC3B;AAED,8DAA8D;AAC9D,MAAM,WAAW,QAAQ;IACvB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,UAAU,CAAC;CACpB;AAED,KAAK,MAAM,GACP;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACtC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,CAAC,EAAE,GAAG,CAAC;IAAC,CAAC,EAAE,GAAG,CAAC;IAAC,KAAK,EAAE,GAAG,CAAC;IAAC,MAAM,EAAE,GAAG,CAAA;CAAE,GAC3D;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;IAAC,IAAI,EAAE,GAAG,CAAA;CAAE,CAAC;AAE3E,0EAA0E;AAC1E,KAAK,GAAG,GAAG;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5E,MAAM,MAAM,WAAW,GACnB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC1B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAwBjC,iDAAiD;AACjD,wBAAgB,aAAa,CAAC,IAAI,EAAE,OAAO,GAAG,WAAW,CA4DxD;AAkBD,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,sEAAsE;IACtE,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,QAAQ,EAAE,EACjB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,aAAa,CAAC,EAAE,gBAAgB,EAAE,EAClC,MAAM,GAAE,QAAQ,GAAG,MAAiB,GACnC,aAAa,CAqDf;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,UAAU,GAAG,iBAAiB,EACxC,SAAS,EAAE,UAAU,GAAG,iBAAiB,EACzC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,QAAQ,EAAE,GAChB,IAAI,CAaN;AAOD;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,UAAU,GAAG,iBAAiB,EACrC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,QAAQ,EAAE,GAChB,IAAI,CAiBN"}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* TestivAI Diff Engine — Masking DSL
|
|
4
|
+
*
|
|
5
|
+
* Masks exclude page areas from the pixel diff while staying auditable:
|
|
6
|
+
* every masked region is hatched in the diff output and listed in the
|
|
7
|
+
* report — silent masking is not acceptable.
|
|
8
|
+
*
|
|
9
|
+
* Two shapes:
|
|
10
|
+
* - CSS selector strings ("#cookie-banner"): geometry is captured by the
|
|
11
|
+
* adapter at capture time (metadata `maskRects`, via
|
|
12
|
+
* getBoundingClientRect) because the DOM snapshot carries no layout.
|
|
13
|
+
* Without captured rects the mask degrades to a warning — never a crash.
|
|
14
|
+
* - Geometric regions: px numbers, 0–1 ratio numbers, "NN%" strings, or a
|
|
15
|
+
* single-edge shorthand like { top: 24 } (full-width/height strip).
|
|
16
|
+
*/
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.parseMaskSpec = parseMaskSpec;
|
|
19
|
+
exports.resolveMasks = resolveMasks;
|
|
20
|
+
exports.applyMaskRects = applyMaskRects;
|
|
21
|
+
exports.hatchMaskRects = hatchMaskRects;
|
|
22
|
+
const EDGES = ['top', 'right', 'bottom', 'left'];
|
|
23
|
+
const RECT_KEYS = ['x', 'y', 'width', 'height'];
|
|
24
|
+
/**
|
|
25
|
+
* Parse one dimension value: number > =1 → px, 0–1 → ratio, "NN%" → ratio.
|
|
26
|
+
* Returns null for anything else.
|
|
27
|
+
*/
|
|
28
|
+
function parseDim(value, allowNegative = false) {
|
|
29
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
30
|
+
if (!allowNegative && value < 0)
|
|
31
|
+
return null;
|
|
32
|
+
if (value > 0 && value < 1)
|
|
33
|
+
return { kind: 'ratio', value };
|
|
34
|
+
return { kind: 'px', value };
|
|
35
|
+
}
|
|
36
|
+
if (typeof value === 'string') {
|
|
37
|
+
const m = /^(\d+(?:\.\d+)?)%$/.exec(value.trim());
|
|
38
|
+
if (!m)
|
|
39
|
+
return null;
|
|
40
|
+
const pct = Number(m[1]);
|
|
41
|
+
return { kind: 'ratio', value: pct / 100 };
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
/** Validate a single mask spec. Never throws. */
|
|
46
|
+
function parseMaskSpec(spec) {
|
|
47
|
+
if (typeof spec === 'string') {
|
|
48
|
+
if (spec.trim().length === 0) {
|
|
49
|
+
return { ok: false, error: 'mask: selector must be a non-empty string' };
|
|
50
|
+
}
|
|
51
|
+
return { ok: true, mask: { type: 'selector', selector: spec.trim() } };
|
|
52
|
+
}
|
|
53
|
+
if (spec === null || typeof spec !== 'object' || Array.isArray(spec)) {
|
|
54
|
+
return {
|
|
55
|
+
ok: false,
|
|
56
|
+
error: 'mask: expected a CSS selector string or a region object like { x, y, width, height } or { top: 24 }',
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const obj = spec;
|
|
60
|
+
const presentEdges = EDGES.filter((e) => obj[e] !== undefined);
|
|
61
|
+
const presentRect = RECT_KEYS.filter((k) => obj[k] !== undefined);
|
|
62
|
+
if (presentEdges.length > 0) {
|
|
63
|
+
if (presentRect.length > 0) {
|
|
64
|
+
return { ok: false, error: 'mask: cannot mix edge shorthands with x/y/width/height' };
|
|
65
|
+
}
|
|
66
|
+
if (presentEdges.length > 1) {
|
|
67
|
+
return {
|
|
68
|
+
ok: false,
|
|
69
|
+
error: `mask: use exactly one edge per shorthand (got ${presentEdges.join(', ')}) — define separate masks instead`,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
const edge = presentEdges[0];
|
|
73
|
+
const size = parseDim(obj[edge]);
|
|
74
|
+
if (!size || size.value < 0) {
|
|
75
|
+
return { ok: false, error: `mask: { ${edge}: … } must be a positive number of px, a 0–1 ratio, or "NN%"` };
|
|
76
|
+
}
|
|
77
|
+
return { ok: true, mask: { type: 'edge', edge, size } };
|
|
78
|
+
}
|
|
79
|
+
if (presentRect.length !== RECT_KEYS.length) {
|
|
80
|
+
const missing = RECT_KEYS.filter((k) => obj[k] === undefined);
|
|
81
|
+
return { ok: false, error: `mask: region object is missing ${missing.join(', ')}` };
|
|
82
|
+
}
|
|
83
|
+
const dims = {};
|
|
84
|
+
for (const key of RECT_KEYS) {
|
|
85
|
+
// x/y may legitimately be 0; width/height must end up positive after
|
|
86
|
+
// resolution but a 0-width spec is caught by the zero-area drop.
|
|
87
|
+
const dim = parseDim(obj[key]);
|
|
88
|
+
if (!dim) {
|
|
89
|
+
return { ok: false, error: `mask: ${key} must be a number of px, a 0–1 ratio, or "NN%" (got ${JSON.stringify(obj[key])})` };
|
|
90
|
+
}
|
|
91
|
+
if (dim.value < 0) {
|
|
92
|
+
return { ok: false, error: `mask: ${key} cannot be negative` };
|
|
93
|
+
}
|
|
94
|
+
dims[key] = dim;
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
ok: true,
|
|
98
|
+
mask: { type: 'region', x: dims.x, y: dims.y, width: dims.width, height: dims.height },
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function resolveDim(dim, total) {
|
|
102
|
+
return dim.kind === 'ratio' ? Math.round(dim.value * total) : Math.round(dim.value);
|
|
103
|
+
}
|
|
104
|
+
function clampRect(x, y, width, height, imgWidth, imgHeight) {
|
|
105
|
+
const x0 = Math.max(0, Math.min(x, imgWidth));
|
|
106
|
+
const y0 = Math.max(0, Math.min(y, imgHeight));
|
|
107
|
+
const x1 = Math.max(0, Math.min(x + width, imgWidth));
|
|
108
|
+
const y1 = Math.max(0, Math.min(y + height, imgHeight));
|
|
109
|
+
if (x1 <= x0 || y1 <= y0)
|
|
110
|
+
return null; // zero-area after clamping
|
|
111
|
+
return { x: x0, y: y0, width: x1 - x0, height: y1 - y0 };
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Resolve mask specs to concrete pixel rects for one image.
|
|
115
|
+
*
|
|
116
|
+
* @param specs Mask specs from config and/or per-call options
|
|
117
|
+
* @param imgWidth Image width in px
|
|
118
|
+
* @param imgHeight Image height in px
|
|
119
|
+
* @param capturedRects Selector geometry captured by the adapter
|
|
120
|
+
* @param origin Where these specs came from (for the audit trail)
|
|
121
|
+
*/
|
|
122
|
+
function resolveMasks(specs, imgWidth, imgHeight, capturedRects, origin = 'config') {
|
|
123
|
+
const rects = [];
|
|
124
|
+
const warnings = [];
|
|
125
|
+
for (const spec of specs) {
|
|
126
|
+
const parsed = parseMaskSpec(spec);
|
|
127
|
+
if (!parsed.ok) {
|
|
128
|
+
warnings.push(parsed.error);
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
const mask = parsed.mask;
|
|
132
|
+
if (mask.type === 'selector') {
|
|
133
|
+
const matches = (capturedRects ?? []).filter((r) => r.selector === mask.selector);
|
|
134
|
+
if (matches.length === 0) {
|
|
135
|
+
warnings.push(`mask: no captured geometry for selector "${mask.selector}" — ` +
|
|
136
|
+
`the adapter records mask rects at capture time; this capture has none, so the mask was skipped`);
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
for (const m of matches) {
|
|
140
|
+
const rect = clampRect(m.x, m.y, m.width, m.height, imgWidth, imgHeight);
|
|
141
|
+
if (rect) {
|
|
142
|
+
rects.push({ ...rect, source: { type: 'selector', spec: mask.selector, origin } });
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
if (mask.type === 'edge') {
|
|
148
|
+
const size = resolveDim(mask.size, mask.edge === 'top' || mask.edge === 'bottom' ? imgHeight : imgWidth);
|
|
149
|
+
const raw = mask.edge === 'top' ? { x: 0, y: 0, width: imgWidth, height: size } :
|
|
150
|
+
mask.edge === 'bottom' ? { x: 0, y: imgHeight - size, width: imgWidth, height: size } :
|
|
151
|
+
mask.edge === 'left' ? { x: 0, y: 0, width: size, height: imgHeight } :
|
|
152
|
+
{ x: imgWidth - size, y: 0, width: size, height: imgHeight };
|
|
153
|
+
const rect = clampRect(raw.x, raw.y, raw.width, raw.height, imgWidth, imgHeight);
|
|
154
|
+
if (rect)
|
|
155
|
+
rects.push({ ...rect, source: { type: 'edge', spec: spec, origin } });
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
const rect = clampRect(resolveDim(mask.x, imgWidth), resolveDim(mask.y, imgHeight), resolveDim(mask.width, imgWidth), resolveDim(mask.height, imgHeight), imgWidth, imgHeight);
|
|
159
|
+
if (rect)
|
|
160
|
+
rects.push({ ...rect, source: { type: 'region', spec: spec, origin } });
|
|
161
|
+
}
|
|
162
|
+
return { rects, warnings };
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Equalize masked pixels (copy candidate → baseline) so the diff loop sees
|
|
166
|
+
* no difference there. Mutates baseline in place; callers clone first.
|
|
167
|
+
*/
|
|
168
|
+
function applyMaskRects(baseline, candidate, width, height, rects) {
|
|
169
|
+
for (const r of rects) {
|
|
170
|
+
for (let y = r.y; y < r.y + r.height; y++) {
|
|
171
|
+
const rowOffset = y * width * 4;
|
|
172
|
+
for (let x = r.x; x < r.x + r.width; x++) {
|
|
173
|
+
const i = rowOffset + x * 4;
|
|
174
|
+
baseline[i] = candidate[i];
|
|
175
|
+
baseline[i + 1] = candidate[i + 1];
|
|
176
|
+
baseline[i + 2] = candidate[i + 2];
|
|
177
|
+
baseline[i + 3] = candidate[i + 3];
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/** Hatch stripe period in px. */
|
|
183
|
+
const HATCH_PERIOD = 8;
|
|
184
|
+
/** Hatch color: mid gray, ~55% alpha — visible on light and dark diffs. */
|
|
185
|
+
const HATCH_RGBA = [128, 128, 128, 140];
|
|
186
|
+
/**
|
|
187
|
+
* Draw a diagonal hatch pattern over masked areas of the diff image so
|
|
188
|
+
* masking is always visible in review. Only pixels inside mask rects are
|
|
189
|
+
* touched.
|
|
190
|
+
*/
|
|
191
|
+
function hatchMaskRects(diff8, width, height, rects) {
|
|
192
|
+
for (const r of rects) {
|
|
193
|
+
for (let y = r.y; y < r.y + r.height; y++) {
|
|
194
|
+
const rowOffset = y * width * 4;
|
|
195
|
+
for (let x = r.x; x < r.x + r.width; x++) {
|
|
196
|
+
const onStripe = (x + y) % HATCH_PERIOD === 0;
|
|
197
|
+
const onBorder = x === r.x || x === r.x + r.width - 1 || y === r.y || y === r.y + r.height - 1;
|
|
198
|
+
if (!onStripe && !onBorder)
|
|
199
|
+
continue;
|
|
200
|
+
const i = rowOffset + x * 4;
|
|
201
|
+
diff8[i] = HATCH_RGBA[0];
|
|
202
|
+
diff8[i + 1] = HATCH_RGBA[1];
|
|
203
|
+
diff8[i + 2] = HATCH_RGBA[2];
|
|
204
|
+
diff8[i + 3] = HATCH_RGBA[3];
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
//# sourceMappingURL=mask.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mask.js","sourceRoot":"","sources":["../../src/diff/mask.ts"],"names":[],"mappings":"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"}
|
package/dist/diff/regions.d.ts
CHANGED
|
@@ -5,19 +5,32 @@
|
|
|
5
5
|
* and groups them into rectangular bounding boxes using a simple
|
|
6
6
|
* connected-component labeling approach.
|
|
7
7
|
*/
|
|
8
|
-
import { DiffRegion } from './types';
|
|
8
|
+
import { DiffRegion, RegionOptions } from './types';
|
|
9
|
+
/** Defaults for region clustering (config-overridable). */
|
|
10
|
+
export declare const DEFAULT_REGION_OPTIONS: Required<RegionOptions>;
|
|
9
11
|
/**
|
|
10
12
|
* Detect regions of change in the diff buffer.
|
|
11
13
|
*
|
|
12
14
|
* Scans for non-transparent pixels (any pixel with alpha > 0 in
|
|
13
15
|
* the diff output), groups adjacent changed pixels into rectangular
|
|
14
|
-
* bounding boxes,
|
|
16
|
+
* bounding boxes, filters out regions smaller than the noise floor,
|
|
17
|
+
* and merges regions whose bounding boxes are within mergeDistance px
|
|
18
|
+
* of each other (an anti-fragmentation pass — one logical change often
|
|
19
|
+
* rasterizes as several nearby specks).
|
|
15
20
|
*
|
|
16
21
|
* @param diff8 - Diff image pixel buffer (RGBA)
|
|
17
22
|
* @param width - Image width
|
|
18
23
|
* @param height - Image height
|
|
19
|
-
* @param
|
|
24
|
+
* @param options - { minSize, mergeDistance }, or a bare number meaning
|
|
25
|
+
* minSize (legacy signature, mergeDistance 0)
|
|
20
26
|
* @returns Array of DiffRegion bounding boxes
|
|
21
27
|
*/
|
|
22
|
-
export declare function detectRegions(diff8: Uint8Array | Uint8ClampedArray, width: number, height: number,
|
|
28
|
+
export declare function detectRegions(diff8: Uint8Array | Uint8ClampedArray, width: number, height: number, options?: number | RegionOptions): DiffRegion[];
|
|
29
|
+
/**
|
|
30
|
+
* Merge regions whose bounding boxes are within `distance` px of each
|
|
31
|
+
* other (Chebyshev gap: both axis gaps must be within the distance).
|
|
32
|
+
* Merging cascades until stable; pixel counts sum, boxes union.
|
|
33
|
+
* Region counts are small, so the O(n²) pass is fine.
|
|
34
|
+
*/
|
|
35
|
+
export declare function mergeRegions(regions: DiffRegion[], distance: number): DiffRegion[];
|
|
23
36
|
//# sourceMappingURL=regions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"regions.d.ts","sourceRoot":"","sources":["../../src/diff/regions.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"regions.d.ts","sourceRoot":"","sources":["../../src/diff/regions.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAEpD,2DAA2D;AAC3D,eAAO,MAAM,sBAAsB,EAAE,QAAQ,CAAC,aAAa,CAG1D,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,UAAU,GAAG,iBAAiB,EACrC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,MAAM,GAAG,aAAkB,GACnC,UAAU,EAAE,CAyHd;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,UAAU,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU,EAAE,CA4ClF"}
|