@pdf-testkit/core 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.
Files changed (81) hide show
  1. package/dist/diff/diff.d.ts +9 -0
  2. package/dist/diff/diff.d.ts.map +1 -0
  3. package/dist/diff/diff.js +139 -0
  4. package/dist/diff/diff.js.map +1 -0
  5. package/dist/diff/match.d.ts +21 -0
  6. package/dist/diff/match.d.ts.map +1 -0
  7. package/dist/diff/match.js +133 -0
  8. package/dist/diff/match.js.map +1 -0
  9. package/dist/events.d.ts +79 -0
  10. package/dist/events.d.ts.map +1 -0
  11. package/dist/events.js +11 -0
  12. package/dist/events.js.map +1 -0
  13. package/dist/extract/finalize.d.ts +16 -0
  14. package/dist/extract/finalize.d.ts.map +1 -0
  15. package/dist/extract/finalize.js +88 -0
  16. package/dist/extract/finalize.js.map +1 -0
  17. package/dist/extract/fromFormeLayout.d.ts +82 -0
  18. package/dist/extract/fromFormeLayout.d.ts.map +1 -0
  19. package/dist/extract/fromFormeLayout.js +324 -0
  20. package/dist/extract/fromFormeLayout.js.map +1 -0
  21. package/dist/extract/fromPdf.d.ts +15 -0
  22. package/dist/extract/fromPdf.d.ts.map +1 -0
  23. package/dist/extract/fromPdf.js +120 -0
  24. package/dist/extract/fromPdf.js.map +1 -0
  25. package/dist/extract/pdfjs/headings.d.ts +16 -0
  26. package/dist/extract/pdfjs/headings.d.ts.map +1 -0
  27. package/dist/extract/pdfjs/headings.js +39 -0
  28. package/dist/extract/pdfjs/headings.js.map +1 -0
  29. package/dist/extract/pdfjs/loadDocument.d.ts +32 -0
  30. package/dist/extract/pdfjs/loadDocument.d.ts.map +1 -0
  31. package/dist/extract/pdfjs/loadDocument.js +45 -0
  32. package/dist/extract/pdfjs/loadDocument.js.map +1 -0
  33. package/dist/extract/pdfjs/overflow.d.ts +16 -0
  34. package/dist/extract/pdfjs/overflow.d.ts.map +1 -0
  35. package/dist/extract/pdfjs/overflow.js +35 -0
  36. package/dist/extract/pdfjs/overflow.js.map +1 -0
  37. package/dist/extract/pdfjs/tables.d.ts +31 -0
  38. package/dist/extract/pdfjs/tables.d.ts.map +1 -0
  39. package/dist/extract/pdfjs/tables.js +142 -0
  40. package/dist/extract/pdfjs/tables.js.map +1 -0
  41. package/dist/extract/pdfjs/textRuns.d.ts +26 -0
  42. package/dist/extract/pdfjs/textRuns.d.ts.map +1 -0
  43. package/dist/extract/pdfjs/textRuns.js +69 -0
  44. package/dist/extract/pdfjs/textRuns.js.map +1 -0
  45. package/dist/extract/raw.d.ts +29 -0
  46. package/dist/extract/raw.d.ts.map +1 -0
  47. package/dist/extract/raw.js +2 -0
  48. package/dist/extract/raw.js.map +1 -0
  49. package/dist/geometry.d.ts +13 -0
  50. package/dist/geometry.d.ts.map +1 -0
  51. package/dist/geometry.js +41 -0
  52. package/dist/geometry.js.map +1 -0
  53. package/dist/index.d.ts +15 -0
  54. package/dist/index.d.ts.map +1 -0
  55. package/dist/index.js +14 -0
  56. package/dist/index.js.map +1 -0
  57. package/dist/snapshot/io.d.ts +4 -0
  58. package/dist/snapshot/io.d.ts.map +1 -0
  59. package/dist/snapshot/io.js +12 -0
  60. package/dist/snapshot/io.js.map +1 -0
  61. package/dist/snapshot/load.d.ts +9 -0
  62. package/dist/snapshot/load.d.ts.map +1 -0
  63. package/dist/snapshot/load.js +25 -0
  64. package/dist/snapshot/load.js.map +1 -0
  65. package/dist/snapshot/serialize.d.ts +14 -0
  66. package/dist/snapshot/serialize.d.ts.map +1 -0
  67. package/dist/snapshot/serialize.js +31 -0
  68. package/dist/snapshot/serialize.js.map +1 -0
  69. package/dist/text/headingLevels.d.ts +11 -0
  70. package/dist/text/headingLevels.d.ts.map +1 -0
  71. package/dist/text/headingLevels.js +40 -0
  72. package/dist/text/headingLevels.js.map +1 -0
  73. package/dist/text/normalize.d.ts +17 -0
  74. package/dist/text/normalize.d.ts.map +1 -0
  75. package/dist/text/normalize.js +67 -0
  76. package/dist/text/normalize.js.map +1 -0
  77. package/dist/types.d.ts +85 -0
  78. package/dist/types.d.ts.map +1 -0
  79. package/dist/types.js +8 -0
  80. package/dist/types.js.map +1 -0
  81. package/package.json +41 -0
@@ -0,0 +1,16 @@
1
+ import type { BBox, OverflowInfo } from '../../types.js';
2
+ import type { PdfTextRun } from './textRuns.js';
3
+ /**
4
+ * Content box inferred from the bounding box of a page's text. FormePDF gives a
5
+ * real content box; here we approximate it, so overflow from this path is
6
+ * explicitly best-effort (documented in the reliability matrix).
7
+ */
8
+ export declare function inferContentBox(runs: PdfTextRun[], pageWidth: number, pageHeight: number): BBox;
9
+ /**
10
+ * Conservative overflow detector: flags a run whose right edge extends past the
11
+ * expected (mirror-margin) text column by more than a line's worth. Tuned to
12
+ * under-report rather than cry wolf — a false overflow is exactly the noise this
13
+ * tool exists to avoid.
14
+ */
15
+ export declare function buildOverflowModel(runs: PdfTextRun[], pageWidth: number): (run: PdfTextRun) => OverflowInfo | null;
16
+ //# sourceMappingURL=overflow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overflow.d.ts","sourceRoot":"","sources":["../../../src/extract/pdfjs/overflow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEzD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAO/F;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,UAAU,EAAE,EAClB,SAAS,EAAE,MAAM,GAChB,CAAC,GAAG,EAAE,UAAU,KAAK,YAAY,GAAG,IAAI,CAW1C"}
@@ -0,0 +1,35 @@
1
+ import { round1 } from '../../geometry.js';
2
+ /**
3
+ * Content box inferred from the bounding box of a page's text. FormePDF gives a
4
+ * real content box; here we approximate it, so overflow from this path is
5
+ * explicitly best-effort (documented in the reliability matrix).
6
+ */
7
+ export function inferContentBox(runs, pageWidth, pageHeight) {
8
+ if (runs.length === 0)
9
+ return { x: 0, y: 0, width: pageWidth, height: pageHeight };
10
+ const minX = Math.min(...runs.map((r) => r.x));
11
+ const maxX = Math.max(...runs.map((r) => r.x + r.width));
12
+ const minY = Math.min(...runs.map((r) => r.y));
13
+ const maxY = Math.max(...runs.map((r) => r.y + r.height));
14
+ return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
15
+ }
16
+ /**
17
+ * Conservative overflow detector: flags a run whose right edge extends past the
18
+ * expected (mirror-margin) text column by more than a line's worth. Tuned to
19
+ * under-report rather than cry wolf — a false overflow is exactly the noise this
20
+ * tool exists to avoid.
21
+ */
22
+ export function buildOverflowModel(runs, pageWidth) {
23
+ if (runs.length === 0)
24
+ return () => null;
25
+ const leftMargin = Math.min(...runs.map((r) => r.x));
26
+ const rightLimit = pageWidth - leftMargin;
27
+ return (run) => {
28
+ const over = run.x + run.width - rightLimit;
29
+ if (over > Math.max(2, run.fontSize)) {
30
+ return { axis: 'x', overflowPts: round1(over), container: 'page-content' };
31
+ }
32
+ return null;
33
+ };
34
+ }
35
+ //# sourceMappingURL=overflow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overflow.js","sourceRoot":"","sources":["../../../src/extract/pdfjs/overflow.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAG3C;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,IAAkB,EAAE,SAAiB,EAAE,UAAkB;IACvF,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IACnF,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/C,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IACzD,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/C,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;IAC1D,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,GAAG,IAAI,EAAE,MAAM,EAAE,IAAI,GAAG,IAAI,EAAE,CAAC;AACvE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAChC,IAAkB,EAClB,SAAiB;IAEjB,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC;IACzC,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACrD,MAAM,UAAU,GAAG,SAAS,GAAG,UAAU,CAAC;IAC1C,OAAO,CAAC,GAAG,EAAE,EAAE;QACb,MAAM,IAAI,GAAG,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,KAAK,GAAG,UAAU,CAAC;QAC5C,IAAI,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YACrC,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,cAAc,EAAE,CAAC;QAC7E,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,31 @@
1
+ import type { BBox } from '../../types.js';
2
+ import type { PdfTextRun } from './textRuns.js';
3
+ export interface DetectedCell {
4
+ bbox: BBox;
5
+ text: string;
6
+ runs: PdfTextRun[];
7
+ }
8
+ export interface DetectedRow {
9
+ bbox: BBox;
10
+ cells: DetectedCell[];
11
+ }
12
+ export interface DetectedTable {
13
+ bbox: BBox;
14
+ rows: DetectedRow[];
15
+ cols: number;
16
+ }
17
+ /**
18
+ * The single hardest problem in v0.1: PDF has no native concept of a table, so
19
+ * one is inferred from the geometry of text runs. Heuristic (borderless, text-
20
+ * driven — ruled/border-only tables need operator-list parsing and are deferred):
21
+ *
22
+ * 1. Group runs into row bands by y (tolerance from median line height).
23
+ * 2. Cluster run left-edges across the page into columns.
24
+ * 3. A table = a maximal run of >= 3 consecutive bands that each populate
25
+ * >= 2 of the same columns.
26
+ *
27
+ * Emits low-confidence regions (callers tag them ~0.5); false positives/negatives
28
+ * are expected on merged/wrapped cells and are managed via `minConfidence`.
29
+ */
30
+ export declare function detectTables(runs: PdfTextRun[]): DetectedTable[];
31
+ //# sourceMappingURL=tables.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../../../src/extract/pdfjs/tables.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAE3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,IAAI,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,UAAU,EAAE,CAAC;CACpB;AACD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,IAAI,CAAC;IACX,KAAK,EAAE,YAAY,EAAE,CAAC;CACvB;AACD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,IAAI,CAAC;IACX,IAAI,EAAE,WAAW,EAAE,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,aAAa,EAAE,CAsChE"}
@@ -0,0 +1,142 @@
1
+ import { median } from '../../geometry.js';
2
+ /**
3
+ * The single hardest problem in v0.1: PDF has no native concept of a table, so
4
+ * one is inferred from the geometry of text runs. Heuristic (borderless, text-
5
+ * driven — ruled/border-only tables need operator-list parsing and are deferred):
6
+ *
7
+ * 1. Group runs into row bands by y (tolerance from median line height).
8
+ * 2. Cluster run left-edges across the page into columns.
9
+ * 3. A table = a maximal run of >= 3 consecutive bands that each populate
10
+ * >= 2 of the same columns.
11
+ *
12
+ * Emits low-confidence regions (callers tag them ~0.5); false positives/negatives
13
+ * are expected on merged/wrapped cells and are managed via `minConfidence`.
14
+ */
15
+ export function detectTables(runs) {
16
+ if (runs.length < 6)
17
+ return [];
18
+ const heights = runs.map((r) => r.height).filter((h) => h > 0);
19
+ const rowTol = Math.max(2, 0.6 * median(heights));
20
+ const bands = groupBands(runs, rowTol);
21
+ if (bands.length < 3)
22
+ return [];
23
+ const charWidths = runs.map((r) => r.width / Math.max(1, r.text.length)).filter((w) => w > 0);
24
+ const colTol = Math.max(4, (median(charWidths) || median(heights) * 0.5) * 2);
25
+ const columns = clusterValues(bands.flatMap((b) => b.map((r) => r.x)), colTol);
26
+ const colOf = (x) => nearestIndex(columns, x, colTol);
27
+ const bandCols = bands.map((b) => new Set(b.map((r) => colOf(r.x)).filter((i) => i >= 0)));
28
+ const tables = [];
29
+ let i = 0;
30
+ while (i < bands.length) {
31
+ if (bandCols[i].size < 2) {
32
+ i++;
33
+ continue;
34
+ }
35
+ let j = i;
36
+ while (j + 1 < bands.length &&
37
+ bandCols[j + 1].size >= 2 &&
38
+ intersectionSize(bandCols[i], bandCols[j + 1]) >= 2) {
39
+ j++;
40
+ }
41
+ if (j - i + 1 >= 3) {
42
+ tables.push(buildTable(bands.slice(i, j + 1), columns, colTol));
43
+ }
44
+ i = j + 1;
45
+ }
46
+ return tables;
47
+ }
48
+ function groupBands(runs, rowTol) {
49
+ const sorted = [...runs].sort((a, b) => a.y - b.y || a.x - b.x);
50
+ const bands = [];
51
+ let current = [];
52
+ let bandY = -Infinity;
53
+ for (const r of sorted) {
54
+ if (current.length === 0 || Math.abs(r.y - bandY) <= rowTol) {
55
+ current.push(r);
56
+ bandY = current.length === 1 ? r.y : bandY;
57
+ }
58
+ else {
59
+ bands.push(current);
60
+ current = [r];
61
+ bandY = r.y;
62
+ }
63
+ }
64
+ if (current.length)
65
+ bands.push(current);
66
+ return bands.map((b) => b.sort((a, c) => a.x - c.x));
67
+ }
68
+ function buildTable(bands, columns, colTol) {
69
+ const rows = bands.map((band) => {
70
+ const byCol = new Map();
71
+ for (const r of band) {
72
+ const c = nearestIndex(columns, r.x, colTol);
73
+ const key = c >= 0 ? c : columns.length + Math.round(r.x);
74
+ const bucket = byCol.get(key);
75
+ if (bucket)
76
+ bucket.push(r);
77
+ else
78
+ byCol.set(key, [r]);
79
+ }
80
+ const cells = [...byCol.entries()]
81
+ .sort((a, b) => a[0] - b[0])
82
+ .map(([, cellRuns]) => ({
83
+ bbox: unionBBox(cellRuns),
84
+ text: cellRuns
85
+ .sort((a, b) => a.x - b.x)
86
+ .map((r) => r.text)
87
+ .join(' ')
88
+ .trim(),
89
+ runs: cellRuns,
90
+ }));
91
+ return { bbox: unionBBox(band), cells };
92
+ });
93
+ const cols = Math.max(0, ...rows.map((r) => r.cells.length));
94
+ return { bbox: unionBBox(bands.flat()), rows, cols };
95
+ }
96
+ function unionBBox(runs) {
97
+ const minX = Math.min(...runs.map((r) => r.x));
98
+ const maxX = Math.max(...runs.map((r) => r.x + r.width));
99
+ const minY = Math.min(...runs.map((r) => r.y));
100
+ const maxY = Math.max(...runs.map((r) => r.y + r.height));
101
+ return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
102
+ }
103
+ /** 1-D gap clustering: values within `tol` of the running group join it. */
104
+ function clusterValues(values, tol) {
105
+ const sorted = [...values].sort((a, b) => a - b);
106
+ const centers = [];
107
+ let group = [];
108
+ for (const v of sorted) {
109
+ const last = group[group.length - 1];
110
+ if (last !== undefined && v - last > tol) {
111
+ centers.push(mean(group));
112
+ group = [];
113
+ }
114
+ group.push(v);
115
+ }
116
+ if (group.length)
117
+ centers.push(mean(group));
118
+ return centers;
119
+ }
120
+ function nearestIndex(centers, x, tol) {
121
+ let bestI = -1;
122
+ let bestD = tol;
123
+ for (let i = 0; i < centers.length; i++) {
124
+ const d = Math.abs(centers[i] - x);
125
+ if (d <= bestD) {
126
+ bestD = d;
127
+ bestI = i;
128
+ }
129
+ }
130
+ return bestI;
131
+ }
132
+ function intersectionSize(a, b) {
133
+ let n = 0;
134
+ for (const v of a)
135
+ if (b.has(v))
136
+ n++;
137
+ return n;
138
+ }
139
+ function mean(values) {
140
+ return values.reduce((s, v) => s + v, 0) / values.length;
141
+ }
142
+ //# sourceMappingURL=tables.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tables.js","sourceRoot":"","sources":["../../../src/extract/pdfjs/tables.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import type { LoadedPdfPage } from './loadDocument.js';
2
+ /** A merged text run with top-left coordinates and inferred font attributes. */
3
+ export interface PdfTextRun {
4
+ x: number;
5
+ y: number;
6
+ width: number;
7
+ height: number;
8
+ text: string;
9
+ fontSize: number;
10
+ fontName: string;
11
+ bold: boolean;
12
+ italic: boolean;
13
+ charCount: number;
14
+ }
15
+ /**
16
+ * Extract merged text runs from a pdfjs page, converting bottom-left PDF
17
+ * coordinates to the top-left origin the rest of the model uses. pdfjs emits
18
+ * per-glyph-cluster items, so adjacent items on the same baseline are merged
19
+ * into runs to stabilize node identity.
20
+ */
21
+ export declare function extractTextRuns(page: LoadedPdfPage): Promise<{
22
+ runs: PdfTextRun[];
23
+ width: number;
24
+ height: number;
25
+ }>;
26
+ //# sourceMappingURL=textRuns.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"textRuns.d.ts","sourceRoot":"","sources":["../../../src/extract/pdfjs/textRuns.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAe,MAAM,mBAAmB,CAAC;AAEpE,gFAAgF;AAChF,MAAM,WAAW,UAAU;IACzB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAeD;;;;;GAKG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC;IAAE,IAAI,EAAE,UAAU,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CA8BhE"}
@@ -0,0 +1,69 @@
1
+ const BOLD_RE = /bold|black|semibold|heavy/i;
2
+ const ITALIC_RE = /italic|oblique/i;
3
+ /**
4
+ * pdfjs names loaded fonts `g_d{docId}_f{n}`, where `docId` is a process-global
5
+ * counter that increments on every `getDocument` call — so the raw name differs
6
+ * between two loads of the same file and would make snapshots non-deterministic.
7
+ * Strip the volatile doc-id segment, keeping the stable per-document font index.
8
+ */
9
+ function sanitizeFontName(name) {
10
+ return name.replace(/^g_d\d+_/, 'g_');
11
+ }
12
+ /**
13
+ * Extract merged text runs from a pdfjs page, converting bottom-left PDF
14
+ * coordinates to the top-left origin the rest of the model uses. pdfjs emits
15
+ * per-glyph-cluster items, so adjacent items on the same baseline are merged
16
+ * into runs to stabilize node identity.
17
+ */
18
+ export async function extractTextRuns(page) {
19
+ const viewport = page.getViewport({ scale: 1 });
20
+ const content = await page.getTextContent();
21
+ const items = content.items.filter((it) => typeof it?.str === 'string');
22
+ const raw = [];
23
+ for (const it of items) {
24
+ if (it.str.length === 0)
25
+ continue;
26
+ const t = it.transform;
27
+ const x = t[4] ?? 0;
28
+ const yBottom = t[5] ?? 0;
29
+ const fontSize = Math.hypot(t[2] ?? 0, t[3] ?? 0) || Math.abs(t[3] ?? 0) || it.height || 0;
30
+ const height = it.height || fontSize;
31
+ const width = it.width || 0;
32
+ const fontName = sanitizeFontName(it.fontName ?? '');
33
+ raw.push({
34
+ x,
35
+ y: viewport.height - (yBottom + height),
36
+ width,
37
+ height,
38
+ text: it.str,
39
+ fontSize,
40
+ fontName,
41
+ bold: BOLD_RE.test(fontName),
42
+ italic: ITALIC_RE.test(fontName),
43
+ charCount: it.str.length,
44
+ });
45
+ }
46
+ return { runs: mergeRuns(raw), width: viewport.width, height: viewport.height };
47
+ }
48
+ function mergeRuns(items) {
49
+ const sorted = [...items].sort((a, b) => a.y - b.y || a.x - b.x);
50
+ const out = [];
51
+ for (const it of sorted) {
52
+ const prev = out[out.length - 1];
53
+ const sameLine = prev && Math.abs(prev.y - it.y) <= Math.max(1, prev.height * 0.4);
54
+ const sameFont = prev && Math.abs(prev.fontSize - it.fontSize) <= 0.5;
55
+ const gap = prev ? it.x - (prev.x + prev.width) : Infinity;
56
+ const adjacent = prev && gap <= prev.fontSize * 0.6 && it.x >= prev.x - 0.5;
57
+ if (prev && sameLine && sameFont && adjacent) {
58
+ prev.text += (gap > prev.fontSize * 0.2 ? ' ' : '') + it.text;
59
+ prev.width = it.x + it.width - prev.x;
60
+ prev.charCount += it.charCount;
61
+ prev.bold = prev.bold || it.bold;
62
+ }
63
+ else {
64
+ out.push({ ...it });
65
+ }
66
+ }
67
+ return out;
68
+ }
69
+ //# sourceMappingURL=textRuns.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"textRuns.js","sourceRoot":"","sources":["../../../src/extract/pdfjs/textRuns.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import type { BBox, FontInfo, NodeRole, OverflowInfo } from '../types.js';
2
+ /**
3
+ * Intermediate extractor output, before `finalize` assigns stable ids, reading
4
+ * order, normalized text, and the content hash. Extractors reference parents by
5
+ * a temporary key; finalize remaps those to final ids.
6
+ */
7
+ export interface RawNode {
8
+ tempId: string;
9
+ parentTempId: string | null;
10
+ role: NodeRole;
11
+ pageIndex: number;
12
+ bbox: BBox;
13
+ text: string | null;
14
+ font: FontInfo | null;
15
+ headingLevel: number | null;
16
+ overflow: OverflowInfo | null;
17
+ table?: {
18
+ rows: number;
19
+ cols: number;
20
+ };
21
+ confidence: number;
22
+ }
23
+ export interface RawPage {
24
+ index: number;
25
+ width: number;
26
+ height: number;
27
+ contentBox: BBox;
28
+ }
29
+ //# sourceMappingURL=raw.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"raw.d.ts","sourceRoot":"","sources":["../../src/extract/raw.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE1E;;;;GAIG;AACH,MAAM,WAAW,OAAO;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,IAAI,EAAE,QAAQ,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,IAAI,CAAC;IACX,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAC;IACtB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,EAAE,YAAY,GAAG,IAAI,CAAC;IAC9B,KAAK,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACvC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,IAAI,CAAC;CAClB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=raw.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"raw.js","sourceRoot":"","sources":["../../src/extract/raw.ts"],"names":[],"mappings":""}
@@ -0,0 +1,13 @@
1
+ import type { BBox, OverflowInfo } from './types.js';
2
+ /** Round to 0.1pt so trivial float noise never creates a diff. */
3
+ export declare function round1(n: number): number;
4
+ export declare function roundBBox(b: BBox): BBox;
5
+ /** Center-to-center distance between two boxes (used for on-page move detection). */
6
+ export declare function centerDistance(a: BBox, b: BBox): number;
7
+ /**
8
+ * Overflow of `node` past `container`, or null if it fits (within `tol`).
9
+ * Considers all four edges; reports the dominant axis and the max overshoot.
10
+ */
11
+ export declare function computeOverflow(node: BBox, container: BBox, kind: OverflowInfo['container'], tol?: number): OverflowInfo | null;
12
+ export declare function median(values: number[]): number;
13
+ //# sourceMappingURL=geometry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"geometry.d.ts","sourceRoot":"","sources":["../src/geometry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAErD,kEAAkE;AAClE,wBAAgB,MAAM,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAExC;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,IAAI,GAAG,IAAI,CAEvC;AAED,qFAAqF;AACrF,wBAAgB,cAAc,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,GAAG,MAAM,CAMvD;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,IAAI,EACV,SAAS,EAAE,IAAI,EACf,IAAI,EAAE,YAAY,CAAC,WAAW,CAAC,EAC/B,GAAG,SAAM,GACR,YAAY,GAAG,IAAI,CAUrB;AAED,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAO/C"}
@@ -0,0 +1,41 @@
1
+ /** Round to 0.1pt so trivial float noise never creates a diff. */
2
+ export function round1(n) {
3
+ return Math.round((n + Number.EPSILON) * 10) / 10;
4
+ }
5
+ export function roundBBox(b) {
6
+ return { x: round1(b.x), y: round1(b.y), width: round1(b.width), height: round1(b.height) };
7
+ }
8
+ /** Center-to-center distance between two boxes (used for on-page move detection). */
9
+ export function centerDistance(a, b) {
10
+ const ax = a.x + a.width / 2;
11
+ const ay = a.y + a.height / 2;
12
+ const bx = b.x + b.width / 2;
13
+ const by = b.y + b.height / 2;
14
+ return Math.hypot(ax - bx, ay - by);
15
+ }
16
+ /**
17
+ * Overflow of `node` past `container`, or null if it fits (within `tol`).
18
+ * Considers all four edges; reports the dominant axis and the max overshoot.
19
+ */
20
+ export function computeOverflow(node, container, kind, tol = 0.5) {
21
+ const overRight = node.x + node.width - (container.x + container.width);
22
+ const overBottom = node.y + node.height - (container.y + container.height);
23
+ const overLeft = container.x - node.x;
24
+ const overTop = container.y - node.y;
25
+ const xOver = Math.max(overRight, overLeft, 0);
26
+ const yOver = Math.max(overBottom, overTop, 0);
27
+ if (xOver <= tol && yOver <= tol)
28
+ return null;
29
+ const axis = xOver > tol && yOver > tol ? 'both' : xOver > tol ? 'x' : 'y';
30
+ return { axis, overflowPts: round1(Math.max(xOver, yOver)), container: kind };
31
+ }
32
+ export function median(values) {
33
+ if (values.length === 0)
34
+ return 0;
35
+ const sorted = [...values].sort((a, b) => a - b);
36
+ const mid = Math.floor(sorted.length / 2);
37
+ return sorted.length % 2 === 0
38
+ ? (sorted[mid - 1] + sorted[mid]) / 2
39
+ : sorted[mid];
40
+ }
41
+ //# sourceMappingURL=geometry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"geometry.js","sourceRoot":"","sources":["../src/geometry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /** Public API for @pdf-testkit/core. */
2
+ export type { BBox, FontInfo, NodeRole, OverflowInfo, PageSnapshot, Producer, SnapshotSchemaVersion, StructuralNode, StructuralSnapshot, } from './types.js';
3
+ export type { BaseEvent, DiffOptions, DiffResult, ElementAddedEvent, ElementMovedToPageEvent, ElementRemovedEvent, HeadingHierarchyChangedEvent, PageCountChangedEvent, SemanticEvent, SemanticEventType, Severity, TableMovedEvent, TextOverflowedEvent, } from './events.js';
4
+ export { DEFAULT_SEVERITY, DEFAULT_POSITION_THRESHOLD_PTS } from './events.js';
5
+ export { fromFormeLayout, FormeShapeError, FORME_ROLE_BY_NODE_TYPE, FORME_INTENTIONALLY_UNMAPPED, } from './extract/fromFormeLayout.js';
6
+ export type { FormeLayoutInfo, FormePageInfo, FormeElementInfo, } from './extract/fromFormeLayout.js';
7
+ export { fromPdf } from './extract/fromPdf.js';
8
+ export { diffSnapshots } from './diff/diff.js';
9
+ export { matchNodes } from './diff/match.js';
10
+ export type { MatchResult, NodePair } from './diff/match.js';
11
+ export { readSnapshotFile, writeSnapshotFile } from './snapshot/io.js';
12
+ export { loadSnapshotFromFile } from './snapshot/load.js';
13
+ export { serializeSnapshot, computeContentHash, stableStringify } from './snapshot/serialize.js';
14
+ export { normalizeText, textPreview, textSimilarity } from './text/normalize.js';
15
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,wCAAwC;AAGxC,YAAY,EACV,IAAI,EACJ,QAAQ,EACR,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,QAAQ,EACR,qBAAqB,EACrB,cAAc,EACd,kBAAkB,GACnB,MAAM,YAAY,CAAC;AAGpB,YAAY,EACV,SAAS,EACT,WAAW,EACX,UAAU,EACV,iBAAiB,EACjB,uBAAuB,EACvB,mBAAmB,EACnB,4BAA4B,EAC5B,qBAAqB,EACrB,aAAa,EACb,iBAAiB,EACjB,QAAQ,EACR,eAAe,EACf,mBAAmB,GACpB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,gBAAgB,EAAE,8BAA8B,EAAE,MAAM,aAAa,CAAC;AAG/E,OAAO,EACL,eAAe,EACf,eAAe,EACf,uBAAuB,EACvB,4BAA4B,GAC7B,MAAM,8BAA8B,CAAC;AACtC,YAAY,EACV,eAAe,EACf,aAAa,EACb,gBAAgB,GACjB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAG/C,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAG7D,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACvE,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AACjG,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,14 @@
1
+ /** Public API for @pdf-testkit/core. */
2
+ export { DEFAULT_SEVERITY, DEFAULT_POSITION_THRESHOLD_PTS } from './events.js';
3
+ // Extractors
4
+ export { fromFormeLayout, FormeShapeError, FORME_ROLE_BY_NODE_TYPE, FORME_INTENTIONALLY_UNMAPPED, } from './extract/fromFormeLayout.js';
5
+ export { fromPdf } from './extract/fromPdf.js';
6
+ // Diff engine
7
+ export { diffSnapshots } from './diff/diff.js';
8
+ export { matchNodes } from './diff/match.js';
9
+ // Snapshot IO + helpers
10
+ export { readSnapshotFile, writeSnapshotFile } from './snapshot/io.js';
11
+ export { loadSnapshotFromFile } from './snapshot/load.js';
12
+ export { serializeSnapshot, computeContentHash, stableStringify } from './snapshot/serialize.js';
13
+ export { normalizeText, textPreview, textSimilarity } from './text/normalize.js';
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,wCAAwC;AA+BxC,OAAO,EAAE,gBAAgB,EAAE,8BAA8B,EAAE,MAAM,aAAa,CAAC;AAE/E,aAAa;AACb,OAAO,EACL,eAAe,EACf,eAAe,EACf,uBAAuB,EACvB,4BAA4B,GAC7B,MAAM,8BAA8B,CAAC;AAMtC,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAE/C,cAAc;AACd,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAG7C,wBAAwB;AACxB,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACvE,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AACjG,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { StructuralSnapshot } from '../types.js';
2
+ export declare function readSnapshotFile(path: string): Promise<StructuralSnapshot>;
3
+ export declare function writeSnapshotFile(path: string, snapshot: StructuralSnapshot): Promise<void>;
4
+ //# sourceMappingURL=io.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"io.d.ts","sourceRoot":"","sources":["../../src/snapshot/io.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAGtD,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAGhF;AAED,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,kBAAkB,GAC3B,OAAO,CAAC,IAAI,CAAC,CAGf"}
@@ -0,0 +1,12 @@
1
+ import { readFile, writeFile, mkdir } from 'node:fs/promises';
2
+ import { dirname } from 'node:path';
3
+ import { serializeSnapshot } from './serialize.js';
4
+ export async function readSnapshotFile(path) {
5
+ const raw = await readFile(path, 'utf8');
6
+ return JSON.parse(raw);
7
+ }
8
+ export async function writeSnapshotFile(path, snapshot) {
9
+ await mkdir(dirname(path), { recursive: true });
10
+ await writeFile(path, serializeSnapshot(snapshot) + '\n', 'utf8');
11
+ }
12
+ //# sourceMappingURL=io.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"io.js","sourceRoot":"","sources":["../../src/snapshot/io.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEnD,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,IAAY;IACjD,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACzC,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAuB,CAAC;AAC/C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,IAAY,EACZ,QAA4B;IAE5B,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,MAAM,SAAS,CAAC,IAAI,EAAE,iBAAiB,CAAC,QAAQ,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;AACpE,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { StructuralSnapshot } from '../types.js';
2
+ /**
3
+ * Resolve a filesystem path to a structural snapshot. Accepts a `.pdf` (parsed
4
+ * via pdfjs), a `.json` StructuralSnapshot (used as-is), or a `.json` FormePDF
5
+ * LayoutInfo (converted). Shared by the CLI and the GitHub Action so both accept
6
+ * PDFs and snapshots interchangeably.
7
+ */
8
+ export declare function loadSnapshotFromFile(path: string): Promise<StructuralSnapshot>;
9
+ //# sourceMappingURL=load.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load.d.ts","sourceRoot":"","sources":["../../src/snapshot/load.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEtD;;;;;GAKG;AACH,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAapF"}
@@ -0,0 +1,25 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { extname } from 'node:path';
3
+ import { fromFormeLayout } from '../extract/fromFormeLayout.js';
4
+ import { fromPdf } from '../extract/fromPdf.js';
5
+ /**
6
+ * Resolve a filesystem path to a structural snapshot. Accepts a `.pdf` (parsed
7
+ * via pdfjs), a `.json` StructuralSnapshot (used as-is), or a `.json` FormePDF
8
+ * LayoutInfo (converted). Shared by the CLI and the GitHub Action so both accept
9
+ * PDFs and snapshots interchangeably.
10
+ */
11
+ export async function loadSnapshotFromFile(path) {
12
+ if (extname(path).toLowerCase() === '.json') {
13
+ const raw = JSON.parse(await readFile(path, 'utf8'));
14
+ if (raw && raw['contentHash'] && Array.isArray(raw['nodes'])) {
15
+ return raw;
16
+ }
17
+ if (raw && Array.isArray(raw['pages'])) {
18
+ return fromFormeLayout(raw, { source: { name: path } });
19
+ }
20
+ throw new Error(`${path}: JSON is neither a StructuralSnapshot nor a FormePDF LayoutInfo`);
21
+ }
22
+ const bytes = new Uint8Array(await readFile(path));
23
+ return fromPdf(bytes, { source: { name: path } });
24
+ }
25
+ //# sourceMappingURL=load.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load.js","sourceRoot":"","sources":["../../src/snapshot/load.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAGhD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,IAAY;IACrD,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,KAAK,OAAO,EAAE,CAAC;QAC5C,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAA4B,CAAC;QAChF,IAAI,GAAG,IAAI,GAAG,CAAC,aAAa,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;YAC7D,OAAO,GAAoC,CAAC;QAC9C,CAAC;QACD,IAAI,GAAG,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;YACvC,OAAO,eAAe,CAAC,GAAY,EAAE,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;QACnE,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,kEAAkE,CAAC,CAAC;IAC7F,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;IACnD,OAAO,OAAO,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;AACpD,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { StructuralSnapshot } from '../types.js';
2
+ /** Deterministic JSON with recursively sorted object keys. */
3
+ export declare function stableStringify(value: unknown): string;
4
+ /**
5
+ * Content hash over everything that describes the document structure, excluding
6
+ * volatile/derived fields (`createdAt`, `contentHash` itself, and reporting-only
7
+ * `source`). Identical structure -> identical hash, which lets the matcher and
8
+ * CLI short-circuit to "no change".
9
+ */
10
+ export declare function computeContentHash(snapshot: Omit<StructuralSnapshot, 'contentHash'> & {
11
+ contentHash?: string;
12
+ }): string;
13
+ export declare function serializeSnapshot(snapshot: StructuralSnapshot, pretty?: boolean): string;
14
+ //# sourceMappingURL=serialize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serialize.d.ts","sourceRoot":"","sources":["../../src/snapshot/serialize.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEtD,8DAA8D;AAC9D,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEtD;AAcD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,IAAI,CAAC,kBAAkB,EAAE,aAAa,CAAC,GAAG;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GAC3E,MAAM,CAGR;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,kBAAkB,EAAE,MAAM,UAAO,GAAG,MAAM,CAErF"}