@embedpdf/engine-core 3.0.0-next.0 → 3.0.0-next.10

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 (56) hide show
  1. package/dist/{AbortError-CjSasIlL.js → AbortError-8-9JAX1Y.js} +2 -2
  2. package/dist/{AbortError-CjSasIlL.js.map → AbortError-8-9JAX1Y.js.map} +1 -1
  3. package/dist/{AbortError-ByBflvEl.cjs → AbortError-CRSooKsk.cjs} +2 -2
  4. package/dist/{AbortError-ByBflvEl.cjs.map → AbortError-CRSooKsk.cjs.map} +1 -1
  5. package/dist/{AnnotationListSnapshot-W0RCsyDE.d.cts → AnnotationListSnapshot-DDxHpJQU.d.cts} +538 -41
  6. package/dist/{AnnotationListSnapshot-W0RCsyDE.d.ts → AnnotationListSnapshot-DDxHpJQU.d.ts} +538 -41
  7. package/dist/{DocumentSecurityService-Df79fgpe.d.cts → DocumentSecurityService-BV4_YXkU.d.cts} +33 -2
  8. package/dist/{DocumentSecurityService-B-tULKNY.d.ts → DocumentSecurityService-o9aeTHwT.d.ts} +33 -2
  9. package/dist/{Engine-D9loFZc1.d.cts → Engine-D9je6GdU.d.cts} +50 -19
  10. package/dist/{Engine-B7Oe7f5i.d.ts → Engine-DOoVem7_.d.ts} +50 -19
  11. package/dist/{subtype-qejjVr6h.cjs → PageInsertBlankInput-CzShuSJI.cjs} +184 -1
  12. package/dist/PageInsertBlankInput-CzShuSJI.cjs.map +1 -0
  13. package/dist/{subtype-BvAzLCGC.js → PageInsertBlankInput-WKY0tr6S.js} +149 -2
  14. package/dist/PageInsertBlankInput-WKY0tr6S.js.map +1 -0
  15. package/dist/{builders-C548U47M.js → builders-B_KufGJ9.js} +547 -169
  16. package/dist/builders-B_KufGJ9.js.map +1 -0
  17. package/dist/{builders-BvikWRuO.cjs → builders-Cp_rlGob.cjs} +603 -225
  18. package/dist/builders-Cp_rlGob.cjs.map +1 -0
  19. package/dist/conformance.cjs +822 -198
  20. package/dist/conformance.cjs.map +1 -1
  21. package/dist/conformance.d.cts +124 -6
  22. package/dist/conformance.d.ts +124 -6
  23. package/dist/conformance.js +777 -157
  24. package/dist/conformance.js.map +1 -1
  25. package/dist/{fold-CB4fAOOy.js → fold-BfyxxwS6.js} +162 -3
  26. package/dist/fold-BfyxxwS6.js.map +1 -0
  27. package/dist/{fold-IaMph3CO.cjs → fold-C7I5GpBz.cjs} +235 -4
  28. package/dist/fold-C7I5GpBz.cjs.map +1 -0
  29. package/dist/index.cjs +36 -18
  30. package/dist/index.d.cts +2 -2
  31. package/dist/index.d.ts +2 -2
  32. package/dist/index.js +4 -4
  33. package/dist/runtime.cjs +41 -23
  34. package/dist/runtime.cjs.map +1 -1
  35. package/dist/runtime.d.cts +103 -5
  36. package/dist/runtime.d.ts +103 -5
  37. package/dist/runtime.js +5 -5
  38. package/dist/{schemas-B_1VyFsm.cjs → schemas-D1pxEv3t.cjs} +138 -32
  39. package/dist/schemas-D1pxEv3t.cjs.map +1 -0
  40. package/dist/{schemas-9OpSSeM6.js → schemas-DAnu8tuW.js} +95 -13
  41. package/dist/schemas-DAnu8tuW.js.map +1 -0
  42. package/dist/wire.cjs +118 -3
  43. package/dist/wire.cjs.map +1 -1
  44. package/dist/wire.d.cts +139 -27
  45. package/dist/wire.d.ts +139 -27
  46. package/dist/wire.js +111 -4
  47. package/dist/wire.js.map +1 -1
  48. package/package.json +1 -1
  49. package/dist/builders-BvikWRuO.cjs.map +0 -1
  50. package/dist/builders-C548U47M.js.map +0 -1
  51. package/dist/fold-CB4fAOOy.js.map +0 -1
  52. package/dist/fold-IaMph3CO.cjs.map +0 -1
  53. package/dist/schemas-9OpSSeM6.js.map +0 -1
  54. package/dist/schemas-B_1VyFsm.cjs.map +0 -1
  55. package/dist/subtype-BvAzLCGC.js.map +0 -1
  56. package/dist/subtype-qejjVr6h.cjs.map +0 -1
@@ -1,7 +1,7 @@
1
- import { o as EngineErrorCode } from "./subtype-BvAzLCGC.js";
2
- import { i as EngineError, n as foldText } from "./fold-CB4fAOOy.js";
3
- import { t as AbortError } from "./AbortError-CjSasIlL.js";
4
- import { A as FormSetValueResultSchema, At as AnnotationDTOSchema, B as PageDeleteResultSchema, G as PageListSnapshotSchema, H as PageFlattenResultSchema, O as FormImportResultSchema, Q as PageRotateResultSchema, a as AnnotationCreateResultSchema, c as AnnotationListPageSnapshotSchema, d as AnnotationUpdateResultSchema, k as FormRepairResultSchema, l as AnnotationListSnapshotAllPagesSchema, o as AnnotationDeleteResultSchema, q as PageMoveResultSchema, rt as RedactionApplyResultSchema, tt as PageTextSnapshotSchema, u as AnnotationMoveResultSchema, ur as DocumentActionsSnapshotSchema, ut as SearchSliceSchema, wt as FormSnapshotSchema } from "./schemas-9OpSSeM6.js";
1
+ import { f as pdfQuadBounds, i as EngineError, n as foldText, u as isRotatedGeometryRun } from "./fold-BfyxxwS6.js";
2
+ import { f as EngineErrorCode, o as sliceTextByChars } from "./PageInsertBlankInput-WKY0tr6S.js";
3
+ import { t as AbortError } from "./AbortError-8-9JAX1Y.js";
4
+ import { A as FormSetValueResultSchema, B as PageDeleteResultSchema, O as FormImportResultSchema, Ot as FormSnapshotSchema, Pt as AnnotationDTOSchema, U as PageFlattenResultSchema, W as PageGeometrySnapshotSchema, Y as PageListSnapshotSchema, Z as PageMoveResultSchema, a as AnnotationCreateResultSchema, at as PageTextSnapshotSchema, c as AnnotationListPageSnapshotSchema, d as AnnotationUpdateResultSchema, k as FormRepairResultSchema, l as AnnotationListSnapshotAllPagesSchema, mr as DocumentActionsSnapshotSchema, mt as SearchSliceSchema, nt as PageRotateResultSchema, o as AnnotationDeleteResultSchema, st as RedactionApplyResultSchema, u as AnnotationMoveResultSchema } from "./schemas-DAnu8tuW.js";
5
5
  //#region src/conformance/runMetadataConformance.ts
6
6
  /**
7
7
  * The conformance harness is transport-agnostic. It takes the test runner as
@@ -18,7 +18,7 @@ function runMetadataConformance(runner, opts) {
18
18
  if (engine) await engine.destroy();
19
19
  });
20
20
  test("reads metadata from sample fixture", async () => {
21
- const doc = await openFixture$14(engine, opts);
21
+ const doc = await openFixture$17(engine, opts);
22
22
  try {
23
23
  const meta = await doc.metadata.read();
24
24
  expect(meta).toMatchObject(opts.fixture.expected);
@@ -28,7 +28,7 @@ function runMetadataConformance(runner, opts) {
28
28
  }
29
29
  });
30
30
  test("abort() before completion rejects with AbortError", async () => {
31
- const doc = await openFixture$14(engine, opts);
31
+ const doc = await openFixture$17(engine, opts);
32
32
  try {
33
33
  const p = doc.metadata.read();
34
34
  p.abort("test");
@@ -38,7 +38,7 @@ function runMetadataConformance(runner, opts) {
38
38
  }
39
39
  });
40
40
  test("aborting one read does not affect a concurrent one", async () => {
41
- const doc = await openFixture$14(engine, opts);
41
+ const doc = await openFixture$17(engine, opts);
42
42
  try {
43
43
  const a = doc.metadata.read();
44
44
  const b = doc.metadata.read();
@@ -50,7 +50,7 @@ function runMetadataConformance(runner, opts) {
50
50
  }
51
51
  });
52
52
  test("metadata after close throws DocNotOpen", async () => {
53
- const doc = await openFixture$14(engine, opts);
53
+ const doc = await openFixture$17(engine, opts);
54
54
  await doc.close();
55
55
  let caught;
56
56
  try {
@@ -62,7 +62,7 @@ function runMetadataConformance(runner, opts) {
62
62
  expect(EngineError.is(caught, EngineErrorCode.DocNotOpen)).toBe(true);
63
63
  });
64
64
  test("update() sets standard + custom fields and a subsequent read observes them", async () => {
65
- const doc = await openFixture$14(engine, opts);
65
+ const doc = await openFixture$17(engine, opts);
66
66
  try {
67
67
  const result = await doc.metadata.update({
68
68
  title: "Conformance Title",
@@ -81,7 +81,7 @@ function runMetadataConformance(runner, opts) {
81
81
  }
82
82
  });
83
83
  test("update() clears a field with null", async () => {
84
- const doc = await openFixture$14(engine, opts);
84
+ const doc = await openFixture$17(engine, opts);
85
85
  try {
86
86
  await doc.metadata.update({ title: "To Be Cleared" });
87
87
  const set = await doc.metadata.read();
@@ -94,7 +94,7 @@ function runMetadataConformance(runner, opts) {
94
94
  }
95
95
  });
96
96
  test("update() after close throws DocNotOpen", async () => {
97
- const doc = await openFixture$14(engine, opts);
97
+ const doc = await openFixture$17(engine, opts);
98
98
  await doc.close();
99
99
  let caught;
100
100
  try {
@@ -107,7 +107,7 @@ function runMetadataConformance(runner, opts) {
107
107
  });
108
108
  });
109
109
  }
110
- async function openFixture$14(engine, opts) {
110
+ async function openFixture$17(engine, opts) {
111
111
  if (opts.openKind === "bytes") {
112
112
  const bytes = await opts.fixture.bytes();
113
113
  return engine.open({
@@ -134,11 +134,12 @@ function runAnnotationReadConformance(runner, opts) {
134
134
  if (engine) await engine.destroy();
135
135
  });
136
136
  test("listRawAll returns one entry per page with valid PageState", async () => {
137
- const doc = await openFixture$13(engine, opts);
137
+ const doc = await openFixture$16(engine, opts);
138
138
  try {
139
139
  const snap = await doc.annotations.listRawAll();
140
140
  expect(AnnotationListSnapshotAllPagesSchema.safeParse(snap).success).toBe(true);
141
141
  expect(snap.pages.length >= 1).toBe(true);
142
+ if (snap.auditHead !== void 0) expect(Number.isInteger(snap.auditHead) && snap.auditHead >= 0).toBe(true);
142
143
  const target = snap.pages.find((p) => p.pageState.pageObjectNumber === opts.fixture.pageObjectNumber);
143
144
  expect(target !== void 0).toBe(true);
144
145
  expect(target.annotations.length).toBe(opts.fixture.expectedAnnotationCount);
@@ -147,7 +148,7 @@ function runAnnotationReadConformance(runner, opts) {
147
148
  }
148
149
  });
149
150
  test("listRaw on the test page returns the expected counts and DTO shape", async () => {
150
- const doc = await openFixture$13(engine, opts);
151
+ const doc = await openFixture$16(engine, opts);
151
152
  try {
152
153
  const snap = await doc.annotations.listRaw(opts.fixture.pageObjectNumber);
153
154
  expect(AnnotationListPageSnapshotSchema.safeParse(snap).success).toBe(true);
@@ -177,7 +178,7 @@ function runAnnotationReadConformance(runner, opts) {
177
178
  }
178
179
  });
179
180
  test("shape/vertex/line annotations expose their family fields", async () => {
180
- const doc = await openFixture$13(engine, opts);
181
+ const doc = await openFixture$16(engine, opts);
181
182
  try {
182
183
  const snap = await doc.annotations.listRaw(opts.fixture.pageObjectNumber);
183
184
  for (const a of snap.annotations) {
@@ -236,7 +237,7 @@ function runAnnotationReadConformance(runner, opts) {
236
237
  }
237
238
  });
238
239
  test("full page list dispatches per-subtype and matches raw counts", async () => {
239
- const doc = await openFixture$13(engine, opts);
240
+ const doc = await openFixture$16(engine, opts);
240
241
  try {
241
242
  const full = await doc.page(opts.fixture.pageObjectNumber).annotations.list();
242
243
  expect(AnnotationListPageSnapshotSchema.safeParse(full).success).toBe(true);
@@ -246,7 +247,7 @@ function runAnnotationReadConformance(runner, opts) {
246
247
  }
247
248
  });
248
249
  test("every annotation DTO satisfies AnnotationDTOSchema (discriminated union)", async () => {
249
- const doc = await openFixture$13(engine, opts);
250
+ const doc = await openFixture$16(engine, opts);
250
251
  try {
251
252
  const snap = await doc.annotations.listRaw(opts.fixture.pageObjectNumber);
252
253
  for (const a of snap.annotations) {
@@ -286,7 +287,7 @@ function runAnnotationReadConformance(runner, opts) {
286
287
  });
287
288
  if (opts.fixture.expectsWeakAnnotation) {
288
289
  test("weak annotations carry identityQuality: weak and ref.kind: index", async () => {
289
- const doc = await openFixture$13(engine, opts);
290
+ const doc = await openFixture$16(engine, opts);
290
291
  try {
291
292
  const weak = (await doc.annotations.listRaw(opts.fixture.pageObjectNumber)).annotations.find((a) => a.identityQuality === "weak");
292
293
  expect(weak !== void 0).toBe(true);
@@ -296,7 +297,7 @@ function runAnnotationReadConformance(runner, opts) {
296
297
  }
297
298
  });
298
299
  test("a stale revision token throws InvalidReference", async () => {
299
- const doc = await openFixture$13(engine, opts);
300
+ const doc = await openFixture$16(engine, opts);
300
301
  try {
301
302
  const snap = await doc.page(opts.fixture.pageObjectNumber).annotations.list();
302
303
  const weak = snap.annotations.find((a) => a.identityQuality === "weak");
@@ -312,7 +313,7 @@ function runAnnotationReadConformance(runner, opts) {
312
313
  });
313
314
  }
314
315
  test("abort() on listRaw rejects with AbortError", async () => {
315
- const doc = await openFixture$13(engine, opts);
316
+ const doc = await openFixture$16(engine, opts);
316
317
  try {
317
318
  const p = doc.annotations.listRaw(opts.fixture.pageObjectNumber);
318
319
  p.abort("test");
@@ -322,7 +323,7 @@ function runAnnotationReadConformance(runner, opts) {
322
323
  }
323
324
  });
324
325
  test("listRaw on an unknown pageObjectNumber throws NotFound", async () => {
325
- const doc = await openFixture$13(engine, opts);
326
+ const doc = await openFixture$16(engine, opts);
326
327
  try {
327
328
  let caught;
328
329
  try {
@@ -338,7 +339,7 @@ function runAnnotationReadConformance(runner, opts) {
338
339
  });
339
340
  });
340
341
  }
341
- async function openFixture$13(engine, opts) {
342
+ async function openFixture$16(engine, opts) {
342
343
  if (opts.openKind === "bytes") {
343
344
  const bytes = await opts.fixture.bytes();
344
345
  return engine.open({
@@ -596,7 +597,7 @@ function runAnnotationMutationConformance(runner, opts) {
596
597
  if (engine) await engine.destroy();
597
598
  });
598
599
  test("create appends without shifting indices and leaves weak refs valid", async () => {
599
- const doc = await openFixture$12(engine, opts);
600
+ const doc = await openFixture$15(engine, opts);
600
601
  try {
601
602
  const page = doc.page(fix.pageObjectNumber);
602
603
  const before = await page.annotations.list();
@@ -632,7 +633,7 @@ function runAnnotationMutationConformance(runner, opts) {
632
633
  }
633
634
  });
634
635
  test("create + update honor annotation flags (set on create, merge on patch)", async () => {
635
- const doc = await openFixture$12(engine, opts);
636
+ const doc = await openFixture$15(engine, opts);
636
637
  try {
637
638
  const page = doc.page(fix.pageObjectNumber);
638
639
  const draft = {
@@ -667,7 +668,7 @@ function runAnnotationMutationConformance(runner, opts) {
667
668
  }
668
669
  });
669
670
  test("create shape annotations (circle + square) round-trip shape fields", async () => {
670
- const doc = await openFixture$12(engine, opts);
671
+ const doc = await openFixture$15(engine, opts);
671
672
  try {
672
673
  const page = doc.page(fix.pageObjectNumber);
673
674
  const circleDraft = {
@@ -748,7 +749,7 @@ function runAnnotationMutationConformance(runner, opts) {
748
749
  }
749
750
  });
750
751
  test("cloudy border (/BE) + rect differences (/RD) are tri-state", async () => {
751
- const doc = await openFixture$12(engine, opts);
752
+ const doc = await openFixture$15(engine, opts);
752
753
  try {
753
754
  const page = doc.page(fix.pageObjectNumber);
754
755
  const plainDraft = {
@@ -827,7 +828,7 @@ function runAnnotationMutationConformance(runner, opts) {
827
828
  }
828
829
  });
829
830
  test("update echoes the appearance verdict: moves preserve /AP, restyles re-bake", async () => {
830
- const doc = await openFixture$12(engine, opts);
831
+ const doc = await openFixture$15(engine, opts);
831
832
  try {
832
833
  const page = doc.page(fix.pageObjectNumber);
833
834
  const draft = {
@@ -902,7 +903,7 @@ function runAnnotationMutationConformance(runner, opts) {
902
903
  }
903
904
  });
904
905
  test("a partial /DA patch preserves the unpatched font members", async () => {
905
- const doc = await openFixture$12(engine, opts);
906
+ const doc = await openFixture$15(engine, opts);
906
907
  try {
907
908
  const page = doc.page(fix.pageObjectNumber);
908
909
  const draft = {
@@ -958,7 +959,7 @@ function runAnnotationMutationConformance(runner, opts) {
958
959
  }
959
960
  });
960
961
  test("box transform is tri-state: rect-only moves keep rotation, null flattens", async () => {
961
- const doc = await openFixture$12(engine, opts);
962
+ const doc = await openFixture$15(engine, opts);
962
963
  try {
963
964
  const page = doc.page(fix.pageObjectNumber);
964
965
  const draft = {
@@ -1019,7 +1020,7 @@ function runAnnotationMutationConformance(runner, opts) {
1019
1020
  }
1020
1021
  });
1021
1022
  test("create vertex + line annotations (polygon/polyline/line) round-trip geometry", async () => {
1022
- const doc = await openFixture$12(engine, opts);
1023
+ const doc = await openFixture$15(engine, opts);
1023
1024
  try {
1024
1025
  const page = doc.page(fix.pageObjectNumber);
1025
1026
  const polygonDraft = {
@@ -1150,7 +1151,7 @@ function runAnnotationMutationConformance(runner, opts) {
1150
1151
  }
1151
1152
  });
1152
1153
  test("create free-text + callout round-trip text/colour/intent fields", async () => {
1153
- const doc = await openFixture$12(engine, opts);
1154
+ const doc = await openFixture$15(engine, opts);
1154
1155
  try {
1155
1156
  const page = doc.page(fix.pageObjectNumber);
1156
1157
  const freeTextDraft = {
@@ -1246,7 +1247,7 @@ function runAnnotationMutationConformance(runner, opts) {
1246
1247
  }
1247
1248
  });
1248
1249
  test("update a free-text patches alignment + colours and is non-structural", async () => {
1249
- const doc = await openFixture$12(engine, opts);
1250
+ const doc = await openFixture$15(engine, opts);
1250
1251
  try {
1251
1252
  const page = doc.page(fix.pageObjectNumber);
1252
1253
  const created = await page.annotations.create({
@@ -1307,7 +1308,7 @@ function runAnnotationMutationConformance(runner, opts) {
1307
1308
  }
1308
1309
  });
1309
1310
  test("create redact (area + text) round-trips label + colour fields", async () => {
1310
- const doc = await openFixture$12(engine, opts);
1311
+ const doc = await openFixture$15(engine, opts);
1311
1312
  try {
1312
1313
  const page = doc.page(fix.pageObjectNumber);
1313
1314
  const areaDraft = {
@@ -1387,7 +1388,7 @@ function runAnnotationMutationConformance(runner, opts) {
1387
1388
  }
1388
1389
  });
1389
1390
  test("update a redact patches the label and clears it, non-structurally", async () => {
1390
- const doc = await openFixture$12(engine, opts);
1391
+ const doc = await openFixture$15(engine, opts);
1391
1392
  try {
1392
1393
  const page = doc.page(fix.pageObjectNumber);
1393
1394
  const created = await page.annotations.create({
@@ -1464,7 +1465,7 @@ function runAnnotationMutationConformance(runner, opts) {
1464
1465
  }
1465
1466
  });
1466
1467
  test("create + update a caret round-trips color/opacity/rect differences", async () => {
1467
- const doc = await openFixture$12(engine, opts);
1468
+ const doc = await openFixture$15(engine, opts);
1468
1469
  try {
1469
1470
  const page = doc.page(fix.pageObjectNumber);
1470
1471
  const caretDraft = {
@@ -1544,7 +1545,7 @@ function runAnnotationMutationConformance(runner, opts) {
1544
1545
  }
1545
1546
  });
1546
1547
  test("update an ink annotation patches strokes + color and is non-structural", async () => {
1547
- const doc = await openFixture$12(engine, opts);
1548
+ const doc = await openFixture$15(engine, opts);
1548
1549
  try {
1549
1550
  const page = doc.page(fix.pageObjectNumber);
1550
1551
  const created = await page.annotations.create({
@@ -1595,7 +1596,7 @@ function runAnnotationMutationConformance(runner, opts) {
1595
1596
  }
1596
1597
  });
1597
1598
  test("ink highlight intent + blend round-trip and unrelated patches preserve blend", async () => {
1598
- const doc = await openFixture$12(engine, opts);
1599
+ const doc = await openFixture$15(engine, opts);
1599
1600
  try {
1600
1601
  const page = doc.page(fix.pageObjectNumber);
1601
1602
  const created = await page.annotations.create({
@@ -1638,7 +1639,7 @@ function runAnnotationMutationConformance(runner, opts) {
1638
1639
  }
1639
1640
  });
1640
1641
  test("update a polyline patches vertices + line endings and is non-structural", async () => {
1641
- const doc = await openFixture$12(engine, opts);
1642
+ const doc = await openFixture$15(engine, opts);
1642
1643
  try {
1643
1644
  const page = doc.page(fix.pageObjectNumber);
1644
1645
  const created = await page.annotations.create({
@@ -1687,7 +1688,7 @@ function runAnnotationMutationConformance(runner, opts) {
1687
1688
  }
1688
1689
  });
1689
1690
  test("update a shape annotation patches color and is non-structural", async () => {
1690
- const doc = await openFixture$12(engine, opts);
1691
+ const doc = await openFixture$15(engine, opts);
1691
1692
  try {
1692
1693
  const page = doc.page(fix.pageObjectNumber);
1693
1694
  const created = await page.annotations.create({
@@ -1736,7 +1737,7 @@ function runAnnotationMutationConformance(runner, opts) {
1736
1737
  }
1737
1738
  });
1738
1739
  if (opts.supportsAppearanceRasters) test("creating a shape bakes an /AP appearance the reader can rasterize", async () => {
1739
- const doc = await openFixture$12(engine, opts);
1740
+ const doc = await openFixture$15(engine, opts);
1740
1741
  try {
1741
1742
  const page = doc.page(fix.pageObjectNumber);
1742
1743
  const created = await page.annotations.create({
@@ -1766,7 +1767,7 @@ function runAnnotationMutationConformance(runner, opts) {
1766
1767
  }
1767
1768
  });
1768
1769
  test("update on a durable annotation is non-structural and never touches /NM", async () => {
1769
- const doc = await openFixture$12(engine, opts);
1770
+ const doc = await openFixture$15(engine, opts);
1770
1771
  try {
1771
1772
  const page = doc.page(fix.pageObjectNumber);
1772
1773
  const before = await page.annotations.list();
@@ -1792,7 +1793,7 @@ function runAnnotationMutationConformance(runner, opts) {
1792
1793
  }
1793
1794
  });
1794
1795
  if (fix.expectsWeakAnnotation) test("update on a weak annotation stamps a UUID v4 /NM and upgrades the ref", async () => {
1795
- const doc = await openFixture$12(engine, opts);
1796
+ const doc = await openFixture$15(engine, opts);
1796
1797
  try {
1797
1798
  const page = doc.page(fix.pageObjectNumber);
1798
1799
  const before = await page.annotations.list();
@@ -1822,7 +1823,7 @@ function runAnnotationMutationConformance(runner, opts) {
1822
1823
  }
1823
1824
  });
1824
1825
  test("delete by objectNumber removes the annotation and reports a stable id", async () => {
1825
- const doc = await openFixture$12(engine, opts);
1826
+ const doc = await openFixture$15(engine, opts);
1826
1827
  try {
1827
1828
  const page = doc.page(fix.pageObjectNumber);
1828
1829
  const draft = {
@@ -1852,7 +1853,7 @@ function runAnnotationMutationConformance(runner, opts) {
1852
1853
  }
1853
1854
  });
1854
1855
  if (fix.expectsWeakAnnotation) test("delete by index of a weak annotation reports deleted: null and refetch reason", async () => {
1855
- const doc = await openFixture$12(engine, opts);
1856
+ const doc = await openFixture$15(engine, opts);
1856
1857
  try {
1857
1858
  const page = doc.page(fix.pageObjectNumber);
1858
1859
  const weak = (await page.annotations.list()).annotations.find((a) => a.identityQuality === "weak");
@@ -1875,7 +1876,7 @@ function runAnnotationMutationConformance(runner, opts) {
1875
1876
  }
1876
1877
  });
1877
1878
  test("abort on create rejects with AbortError", async () => {
1878
- const doc = await openFixture$12(engine, opts);
1879
+ const doc = await openFixture$15(engine, opts);
1879
1880
  try {
1880
1881
  const page = doc.page(fix.pageObjectNumber);
1881
1882
  const draft = {
@@ -1891,7 +1892,7 @@ function runAnnotationMutationConformance(runner, opts) {
1891
1892
  }
1892
1893
  });
1893
1894
  test("update with a stale index revision throws InvalidReference", async () => {
1894
- const doc = await openFixture$12(engine, opts);
1895
+ const doc = await openFixture$15(engine, opts);
1895
1896
  try {
1896
1897
  const page = doc.page(fix.pageObjectNumber);
1897
1898
  const weak = (await page.annotations.list()).annotations.find((a) => a.identityQuality === "weak");
@@ -1918,7 +1919,7 @@ function runAnnotationMutationConformance(runner, opts) {
1918
1919
  }
1919
1920
  });
1920
1921
  test("move single durable annotation reorders within the page (single-as-batch)", async () => {
1921
- const doc = await openFixture$12(engine, opts);
1922
+ const doc = await openFixture$15(engine, opts);
1922
1923
  try {
1923
1924
  const page = doc.page(fix.pageObjectNumber);
1924
1925
  const aDraft = {
@@ -1962,7 +1963,7 @@ function runAnnotationMutationConformance(runner, opts) {
1962
1963
  }
1963
1964
  });
1964
1965
  test("move multi-block preserves caller-supplied order at the destination", async () => {
1965
- const doc = await openFixture$12(engine, opts);
1966
+ const doc = await openFixture$15(engine, opts);
1966
1967
  try {
1967
1968
  const page = doc.page(fix.pageObjectNumber);
1968
1969
  const ids = await Promise.all([
@@ -2001,7 +2002,7 @@ function runAnnotationMutationConformance(runner, opts) {
2001
2002
  }
2002
2003
  });
2003
2004
  if (fix.expectsWeakAnnotation) test("move on a weak annotation upgrades it to durable /NM (one rev bump for batch)", async () => {
2004
- const doc = await openFixture$12(engine, opts);
2005
+ const doc = await openFixture$15(engine, opts);
2005
2006
  try {
2006
2007
  const page = doc.page(fix.pageObjectNumber);
2007
2008
  const before = await page.annotations.list();
@@ -2026,7 +2027,7 @@ function runAnnotationMutationConformance(runner, opts) {
2026
2027
  }
2027
2028
  });
2028
2029
  test("move with a stale index revision rejects (locked rev-token guard)", async () => {
2029
- const doc = await openFixture$12(engine, opts);
2030
+ const doc = await openFixture$15(engine, opts);
2030
2031
  try {
2031
2032
  const page = doc.page(fix.pageObjectNumber);
2032
2033
  const a = await page.annotations.create({
@@ -2064,7 +2065,7 @@ function runAnnotationMutationConformance(runner, opts) {
2064
2065
  }
2065
2066
  });
2066
2067
  test("move with out-of-range toIndex rejects with InvalidArg", async () => {
2067
- const doc = await openFixture$12(engine, opts);
2068
+ const doc = await openFixture$15(engine, opts);
2068
2069
  try {
2069
2070
  const page = doc.page(fix.pageObjectNumber);
2070
2071
  const a = await page.annotations.create({
@@ -2088,7 +2089,7 @@ function runAnnotationMutationConformance(runner, opts) {
2088
2089
  }
2089
2090
  });
2090
2091
  test("move with duplicate refs rejects with InvalidArg", async () => {
2091
- const doc = await openFixture$12(engine, opts);
2092
+ const doc = await openFixture$15(engine, opts);
2092
2093
  try {
2093
2094
  const page = doc.page(fix.pageObjectNumber);
2094
2095
  const a = await page.annotations.create({
@@ -2111,7 +2112,7 @@ function runAnnotationMutationConformance(runner, opts) {
2111
2112
  }
2112
2113
  });
2113
2114
  test("abort on move rejects with AbortError", async () => {
2114
- const doc = await openFixture$12(engine, opts);
2115
+ const doc = await openFixture$15(engine, opts);
2115
2116
  try {
2116
2117
  const page = doc.page(fix.pageObjectNumber);
2117
2118
  const a = await page.annotations.create({
@@ -2132,7 +2133,7 @@ function runAnnotationMutationConformance(runner, opts) {
2132
2133
  }
2133
2134
  });
2134
2135
  test("a page move does NOT bump per-page RevisionTokens (weak refs survive reorder)", async () => {
2135
- const doc = await openFixture$12(engine, opts);
2136
+ const doc = await openFixture$15(engine, opts);
2136
2137
  try {
2137
2138
  const list = await doc.pages.list();
2138
2139
  if (list.pages.length < 1) return;
@@ -2147,7 +2148,7 @@ function runAnnotationMutationConformance(runner, opts) {
2147
2148
  }
2148
2149
  });
2149
2150
  test("create a reply links /IRT and defaults /RT to \"reply\", reporting the parent", async () => {
2150
- const doc = await openFixture$12(engine, opts);
2151
+ const doc = await openFixture$15(engine, opts);
2151
2152
  try {
2152
2153
  const page = doc.page(fix.pageObjectNumber);
2153
2154
  const parent = await page.annotations.create({
@@ -2181,7 +2182,7 @@ function runAnnotationMutationConformance(runner, opts) {
2181
2182
  }
2182
2183
  });
2183
2184
  test("create a grouped subordinate writes /RT /Group", async () => {
2184
- const doc = await openFixture$12(engine, opts);
2185
+ const doc = await openFixture$15(engine, opts);
2185
2186
  try {
2186
2187
  const page = doc.page(fix.pageObjectNumber);
2187
2188
  const primary = await page.annotations.create({
@@ -2212,7 +2213,7 @@ function runAnnotationMutationConformance(runner, opts) {
2212
2213
  }
2213
2214
  });
2214
2215
  test("create link annotations round-trip uri, goto/xyz, goto/fitH, and dead targets", async () => {
2215
- const doc = await openFixture$12(engine, opts);
2216
+ const doc = await openFixture$15(engine, opts);
2216
2217
  try {
2217
2218
  const page = doc.page(fix.pageObjectNumber);
2218
2219
  const uri = await page.annotations.create({
@@ -2289,7 +2290,7 @@ function runAnnotationMutationConformance(runner, opts) {
2289
2290
  }
2290
2291
  });
2291
2292
  test("a link patch retargets in both directions (uri→goto, goto→uri) and moves the rect", async () => {
2292
- const doc = await openFixture$12(engine, opts);
2293
+ const doc = await openFixture$15(engine, opts);
2293
2294
  try {
2294
2295
  const page = doc.page(fix.pageObjectNumber);
2295
2296
  const created = await page.annotations.create({
@@ -2344,7 +2345,7 @@ function runAnnotationMutationConformance(runner, opts) {
2344
2345
  }
2345
2346
  });
2346
2347
  test("a link patch clears the target with null — a dead link on re-read", async () => {
2347
- const doc = await openFixture$12(engine, opts);
2348
+ const doc = await openFixture$15(engine, opts);
2348
2349
  try {
2349
2350
  const page = doc.page(fix.pageObjectNumber);
2350
2351
  const created = await page.annotations.create({
@@ -2383,7 +2384,7 @@ function runAnnotationMutationConformance(runner, opts) {
2383
2384
  }
2384
2385
  });
2385
2386
  test("a link grouped to a highlight round-trips /IRT + /RT /Group with its target", async () => {
2386
- const doc = await openFixture$12(engine, opts);
2387
+ const doc = await openFixture$15(engine, opts);
2387
2388
  try {
2388
2389
  const page = doc.page(fix.pageObjectNumber);
2389
2390
  const parent = await page.annotations.create({
@@ -2417,7 +2418,7 @@ function runAnnotationMutationConformance(runner, opts) {
2417
2418
  }
2418
2419
  });
2419
2420
  test("replace-text round-trips /IT and groups StrikeOut under its Caret", async () => {
2420
- const doc = await openFixture$12(engine, opts);
2421
+ const doc = await openFixture$15(engine, opts);
2421
2422
  try {
2422
2423
  const page = doc.page(fix.pageObjectNumber);
2423
2424
  const caret = await page.annotations.create({
@@ -2469,7 +2470,7 @@ function runAnnotationMutationConformance(runner, opts) {
2469
2470
  }
2470
2471
  });
2471
2472
  test("patch inReplyTo: null clears /IRT and /RT (back to top-level)", async () => {
2472
- const doc = await openFixture$12(engine, opts);
2473
+ const doc = await openFixture$15(engine, opts);
2473
2474
  try {
2474
2475
  const page = doc.page(fix.pageObjectNumber);
2475
2476
  const parent = await page.annotations.create({
@@ -2496,7 +2497,7 @@ function runAnnotationMutationConformance(runner, opts) {
2496
2497
  }
2497
2498
  });
2498
2499
  test("create with a cross-page /IRT parent throws InvalidArg", async () => {
2499
- const doc = await openFixture$12(engine, opts);
2500
+ const doc = await openFixture$15(engine, opts);
2500
2501
  try {
2501
2502
  const page = doc.page(fix.pageObjectNumber);
2502
2503
  const parent = await page.annotations.create({
@@ -2526,13 +2527,136 @@ function runAnnotationMutationConformance(runner, opts) {
2526
2527
  await doc.close();
2527
2528
  }
2528
2529
  });
2530
+ test("a review-status reply round-trips /State + /StateModel + /Subj", async () => {
2531
+ const doc = await openFixture$15(engine, opts);
2532
+ try {
2533
+ const page = doc.page(fix.pageObjectNumber);
2534
+ const target = await page.annotations.create({
2535
+ subtype: "highlight",
2536
+ contents: "status target",
2537
+ subject: "Pricing question",
2538
+ quadPoints: quad
2539
+ });
2540
+ expect(target.created.subject).toBe("Pricing question");
2541
+ const status = await page.annotations.create({
2542
+ subtype: "text",
2543
+ rect: shapeRect,
2544
+ inReplyTo: target.created.ref,
2545
+ state: "accepted",
2546
+ stateModel: "review"
2547
+ });
2548
+ expect(AnnotationCreateResultSchema.safeParse(status).success).toBe(true);
2549
+ expect(status.created.subtype).toBe("text");
2550
+ if (status.created.subtype !== "text") return;
2551
+ expect(status.created.state).toBe("accepted");
2552
+ expect(status.created.stateModel).toBe("review");
2553
+ expect(status.created.replyType).toBe("reply");
2554
+ const read = (await page.annotations.list()).annotations.find((a) => a.ref.kind === "objectNumber" && status.created.ref.kind === "objectNumber" && a.ref.annotObjectNumber === status.created.ref.annotObjectNumber);
2555
+ expect(read?.subtype).toBe("text");
2556
+ if (read?.subtype === "text") {
2557
+ expect(read.state).toBe("accepted");
2558
+ expect(read.stateModel).toBe("review");
2559
+ }
2560
+ } finally {
2561
+ await doc.close();
2562
+ }
2563
+ });
2564
+ test("a draft /State without /StateModel is rejected with InvalidArg", async () => {
2565
+ const doc = await openFixture$15(engine, opts);
2566
+ try {
2567
+ const page = doc.page(fix.pageObjectNumber);
2568
+ let caught;
2569
+ try {
2570
+ await page.annotations.create({
2571
+ subtype: "text",
2572
+ rect: shapeRect,
2573
+ state: "accepted"
2574
+ });
2575
+ } catch (err) {
2576
+ caught = err;
2577
+ }
2578
+ expect(EngineError.is(caught, EngineErrorCode.InvalidArg)).toBe(true);
2579
+ } finally {
2580
+ await doc.close();
2581
+ }
2582
+ });
2583
+ test("null-clear patches truly remove /State, /StateModel, /Subj and /Contents", async () => {
2584
+ const doc = await openFixture$15(engine, opts);
2585
+ try {
2586
+ const page = doc.page(fix.pageObjectNumber);
2587
+ const note = await page.annotations.create({
2588
+ subtype: "text",
2589
+ rect: shapeRect,
2590
+ contents: "work in progress",
2591
+ subject: "Draft",
2592
+ state: "none",
2593
+ stateModel: "review"
2594
+ });
2595
+ const cycled = await page.annotations.update(note.created.ref, {
2596
+ subtype: "text",
2597
+ state: "rejected"
2598
+ });
2599
+ expect(cycled.appearance.changed).toBe(false);
2600
+ expect(cycled.updated.subtype).toBe("text");
2601
+ if (cycled.updated.subtype === "text") {
2602
+ expect(cycled.updated.state).toBe("rejected");
2603
+ expect(cycled.updated.stateModel).toBe("review");
2604
+ }
2605
+ const cleared = await page.annotations.update(note.created.ref, {
2606
+ subtype: "text",
2607
+ contents: null,
2608
+ subject: null,
2609
+ state: null,
2610
+ stateModel: null
2611
+ });
2612
+ expect(cleared.appearance.changed).toBe(false);
2613
+ expect(cleared.updated.contents).toBe(null);
2614
+ expect(cleared.updated.subject).toBe(null);
2615
+ if (cleared.updated.subtype === "text") {
2616
+ expect(cleared.updated.state).toBe(null);
2617
+ expect(cleared.updated.stateModel).toBe(null);
2618
+ }
2619
+ const read = (await page.annotations.list()).annotations.find((a) => a.ref.kind === "objectNumber" && note.created.ref.kind === "objectNumber" && a.ref.annotObjectNumber === note.created.ref.annotObjectNumber);
2620
+ expect(read?.contents).toBe(null);
2621
+ expect(read?.subject).toBe(null);
2622
+ if (read?.subtype === "text") {
2623
+ expect(read.state).toBe(null);
2624
+ expect(read.stateModel).toBe(null);
2625
+ }
2626
+ } finally {
2627
+ await doc.close();
2628
+ }
2629
+ });
2630
+ test("custom state models round-trip verbatim", async () => {
2631
+ const doc = await openFixture$15(engine, opts);
2632
+ try {
2633
+ const page = doc.page(fix.pageObjectNumber);
2634
+ const custom = await page.annotations.create({
2635
+ subtype: "text",
2636
+ rect: shapeRect,
2637
+ state: "in-progress",
2638
+ stateModel: "X-ReviewWorkflow"
2639
+ });
2640
+ expect(custom.created.subtype).toBe("text");
2641
+ if (custom.created.subtype !== "text") return;
2642
+ expect(custom.created.state).toBe("in-progress");
2643
+ expect(custom.created.stateModel).toBe("X-ReviewWorkflow");
2644
+ const read = (await page.annotations.list()).annotations.find((a) => a.ref.kind === "objectNumber" && custom.created.ref.kind === "objectNumber" && a.ref.annotObjectNumber === custom.created.ref.annotObjectNumber);
2645
+ if (read?.subtype === "text") {
2646
+ expect(read.state).toBe("in-progress");
2647
+ expect(read.stateModel).toBe("X-ReviewWorkflow");
2648
+ }
2649
+ } finally {
2650
+ await doc.close();
2651
+ }
2652
+ });
2529
2653
  });
2530
2654
  }
2531
2655
  async function beginWeakEditIfRequired(doc, pageObjectNumber, fix) {
2532
2656
  if (doc.capabilities.weakAnnotationEditSessions !== "required" || !fix.expectsWeakAnnotation) return null;
2533
2657
  return doc.annotations.beginWeakEdit([pageObjectNumber]);
2534
2658
  }
2535
- async function openFixture$12(engine, opts) {
2659
+ async function openFixture$15(engine, opts) {
2536
2660
  if (opts.openKind === "bytes") {
2537
2661
  const bytes = await opts.fixture.bytes();
2538
2662
  return engine.open({
@@ -3122,7 +3246,7 @@ function runAnnotationAppearanceConformance(runner, opts) {
3122
3246
  if (engine) await engine.destroy();
3123
3247
  });
3124
3248
  test("renders the expected set of appearances with valid output", async () => {
3125
- const doc = await openFixture$11(engine, opts);
3249
+ const doc = await openFixture$14(engine, opts);
3126
3250
  try {
3127
3251
  const { pageState, appearances } = await collect(doc, opts, useRaw);
3128
3252
  expect(pageState.pageObjectNumber).toBe(opts.fixture.pageObjectNumber);
@@ -3149,7 +3273,7 @@ function runAnnotationAppearanceConformance(runner, opts) {
3149
3273
  }
3150
3274
  });
3151
3275
  if (opts.fixture.expectsWeakAppearance) test("weak (index-only) annotations are emitted, not dropped", async () => {
3152
- const doc = await openFixture$11(engine, opts);
3276
+ const doc = await openFixture$14(engine, opts);
3153
3277
  try {
3154
3278
  const { appearances } = await collect(doc, opts, useRaw);
3155
3279
  const weak = appearances.find((a) => a.ref.kind === "index");
@@ -3159,7 +3283,7 @@ function runAnnotationAppearanceConformance(runner, opts) {
3159
3283
  }
3160
3284
  });
3161
3285
  if (useRaw) test("rendered appearances are not blank (non-zero alpha)", async () => {
3162
- const doc = await openFixture$11(engine, opts);
3286
+ const doc = await openFixture$14(engine, opts);
3163
3287
  try {
3164
3288
  const { appearances } = await collect(doc, opts, useRaw);
3165
3289
  const anyVisible = appearances.some((a) => a.raster !== null && hasOpaquePixel(a.raster));
@@ -3169,7 +3293,7 @@ function runAnnotationAppearanceConformance(runner, opts) {
3169
3293
  }
3170
3294
  });
3171
3295
  test("abort() rejects with AbortError", async () => {
3172
- const doc = await openFixture$11(engine, opts);
3296
+ const doc = await openFixture$14(engine, opts);
3173
3297
  try {
3174
3298
  const page = doc.page(opts.fixture.pageObjectNumber);
3175
3299
  const p = useRaw ? page.annotations.renderAppearances({ scale: 1 }) : page.annotations.renderAppearanceImages({
@@ -3224,7 +3348,7 @@ function hasOpaquePixel(raster) {
3224
3348
  }
3225
3349
  return false;
3226
3350
  }
3227
- async function openFixture$11(engine, opts) {
3351
+ async function openFixture$14(engine, opts) {
3228
3352
  if (opts.openKind === "bytes") {
3229
3353
  const bytes = await opts.fixture.bytes();
3230
3354
  return engine.open({
@@ -3293,7 +3417,7 @@ function runPageReorderConformance(runner, opts) {
3293
3417
  if (engine) await engine.destroy();
3294
3418
  });
3295
3419
  test("pages.list() returns every page in display order, durable PONs only", async () => {
3296
- const doc = await openFixture$10(engine, opts);
3420
+ const doc = await openFixture$13(engine, opts);
3297
3421
  try {
3298
3422
  const list = await doc.pages.list();
3299
3423
  expect(PageListSnapshotSchema.safeParse(list).success).toBe(true);
@@ -3312,7 +3436,7 @@ function runPageReorderConformance(runner, opts) {
3312
3436
  }
3313
3437
  });
3314
3438
  test("pages.move() reorders pages and returns the full post-move order", async () => {
3315
- const doc = await openFixture$10(engine, opts);
3439
+ const doc = await openFixture$13(engine, opts);
3316
3440
  try {
3317
3441
  const before = await doc.pages.list();
3318
3442
  if (before.pages.length < 3) return;
@@ -3336,7 +3460,7 @@ function runPageReorderConformance(runner, opts) {
3336
3460
  }
3337
3461
  });
3338
3462
  test("weak index-based annotation refs survive a page reorder", async () => {
3339
- const doc = await openFixture$10(engine, opts);
3463
+ const doc = await openFixture$13(engine, opts);
3340
3464
  try {
3341
3465
  const list = await doc.pages.list();
3342
3466
  if (list.pages.length < 2) return;
@@ -3373,7 +3497,7 @@ function runPageReorderConformance(runner, opts) {
3373
3497
  }
3374
3498
  });
3375
3499
  test("pages.move() rejects duplicate PONs with InvalidArg", async () => {
3376
- const doc = await openFixture$10(engine, opts);
3500
+ const doc = await openFixture$13(engine, opts);
3377
3501
  try {
3378
3502
  const list = await doc.pages.list();
3379
3503
  if (list.pages.length < 1) return;
@@ -3390,7 +3514,7 @@ function runPageReorderConformance(runner, opts) {
3390
3514
  }
3391
3515
  });
3392
3516
  test("pages.move() rejects out-of-range destIndex with InvalidArg", async () => {
3393
- const doc = await openFixture$10(engine, opts);
3517
+ const doc = await openFixture$13(engine, opts);
3394
3518
  try {
3395
3519
  const list = await doc.pages.list();
3396
3520
  if (list.pages.length < 1) return;
@@ -3407,7 +3531,7 @@ function runPageReorderConformance(runner, opts) {
3407
3531
  }
3408
3532
  });
3409
3533
  test("pages.move() rejects unknown PON with NotFound or InvalidArg", async () => {
3410
- const doc = await openFixture$10(engine, opts);
3534
+ const doc = await openFixture$13(engine, opts);
3411
3535
  try {
3412
3536
  const list = await doc.pages.list();
3413
3537
  let bogus = 0;
@@ -3425,7 +3549,7 @@ function runPageReorderConformance(runner, opts) {
3425
3549
  }
3426
3550
  });
3427
3551
  test("abort on pages.move() rejects with AbortError", async () => {
3428
- const doc = await openFixture$10(engine, opts);
3552
+ const doc = await openFixture$13(engine, opts);
3429
3553
  try {
3430
3554
  const list = await doc.pages.list();
3431
3555
  if (list.pages.length < 1) return;
@@ -3439,7 +3563,7 @@ function runPageReorderConformance(runner, opts) {
3439
3563
  });
3440
3564
  });
3441
3565
  }
3442
- async function openFixture$10(engine, opts) {
3566
+ async function openFixture$13(engine, opts) {
3443
3567
  if (opts.openKind === "bytes") {
3444
3568
  const bytes = await opts.fixture.bytes();
3445
3569
  return engine.open({
@@ -3513,7 +3637,7 @@ function runPageRotateConformance(runner, opts) {
3513
3637
  if (engine) await engine.destroy();
3514
3638
  });
3515
3639
  test("pages.rotate() sets the absolute rotation and returns the full layout", async () => {
3516
- const doc = await openFixture$9(engine, opts);
3640
+ const doc = await openFixture$12(engine, opts);
3517
3641
  try {
3518
3642
  const before = await doc.pages.list();
3519
3643
  const target = before.pages[0];
@@ -3532,7 +3656,7 @@ function runPageRotateConformance(runner, opts) {
3532
3656
  }
3533
3657
  });
3534
3658
  test("absolute rotation is idempotent: setting the same value twice is a no-op", async () => {
3535
- const doc = await openFixture$9(engine, opts);
3659
+ const doc = await openFixture$12(engine, opts);
3536
3660
  try {
3537
3661
  const pon = (await doc.pages.list()).pages[0].pageObjectNumber;
3538
3662
  const first = await doc.pages.rotate([pon], 180);
@@ -3543,7 +3667,7 @@ function runPageRotateConformance(runner, opts) {
3543
3667
  }
3544
3668
  });
3545
3669
  test("one rotation applies to EVERY listed page (the multi-select gesture)", async () => {
3546
- const doc = await openFixture$9(engine, opts);
3670
+ const doc = await openFixture$12(engine, opts);
3547
3671
  try {
3548
3672
  const list = await doc.pages.list();
3549
3673
  if (list.pages.length < 2) return;
@@ -3555,7 +3679,7 @@ function runPageRotateConformance(runner, opts) {
3555
3679
  }
3556
3680
  });
3557
3681
  test("weak index-based annotation refs survive a rotate (no revision bump)", async () => {
3558
- const doc = await openFixture$9(engine, opts);
3682
+ const doc = await openFixture$12(engine, opts);
3559
3683
  try {
3560
3684
  const hostPon = (await doc.pages.list()).pages[0].pageObjectNumber;
3561
3685
  const hostPage = doc.page(hostPon);
@@ -3585,7 +3709,7 @@ function runPageRotateConformance(runner, opts) {
3585
3709
  }
3586
3710
  });
3587
3711
  test("pages.rotate() rejects an invalid rotation value with InvalidArg", async () => {
3588
- const doc = await openFixture$9(engine, opts);
3712
+ const doc = await openFixture$12(engine, opts);
3589
3713
  try {
3590
3714
  const pon = (await doc.pages.list()).pages[0].pageObjectNumber;
3591
3715
  let caught;
@@ -3600,7 +3724,7 @@ function runPageRotateConformance(runner, opts) {
3600
3724
  }
3601
3725
  });
3602
3726
  test("pages.rotate() rejects duplicate PONs with InvalidArg", async () => {
3603
- const doc = await openFixture$9(engine, opts);
3727
+ const doc = await openFixture$12(engine, opts);
3604
3728
  try {
3605
3729
  const pon = (await doc.pages.list()).pages[0].pageObjectNumber;
3606
3730
  let caught;
@@ -3615,7 +3739,7 @@ function runPageRotateConformance(runner, opts) {
3615
3739
  }
3616
3740
  });
3617
3741
  test("pages.rotate() rejects unknown PON with NotFound or InvalidArg", async () => {
3618
- const doc = await openFixture$9(engine, opts);
3742
+ const doc = await openFixture$12(engine, opts);
3619
3743
  try {
3620
3744
  const list = await doc.pages.list();
3621
3745
  let bogus = 0;
@@ -3633,7 +3757,7 @@ function runPageRotateConformance(runner, opts) {
3633
3757
  }
3634
3758
  });
3635
3759
  test("abort on pages.rotate() rejects with AbortError", async () => {
3636
- const doc = await openFixture$9(engine, opts);
3760
+ const doc = await openFixture$12(engine, opts);
3637
3761
  try {
3638
3762
  const pon = (await doc.pages.list()).pages[0].pageObjectNumber;
3639
3763
  const p = doc.pages.rotate([pon], 90);
@@ -3645,7 +3769,7 @@ function runPageRotateConformance(runner, opts) {
3645
3769
  });
3646
3770
  });
3647
3771
  }
3648
- async function openFixture$9(engine, opts) {
3772
+ async function openFixture$12(engine, opts) {
3649
3773
  if (opts.openKind === "bytes") {
3650
3774
  const bytes = await opts.fixture.bytes();
3651
3775
  return engine.open({
@@ -3708,7 +3832,7 @@ function runPageDeleteConformance(runner, opts) {
3708
3832
  if (engine) await engine.destroy();
3709
3833
  });
3710
3834
  test("pages.delete() removes the page and returns the surviving layout", async () => {
3711
- const doc = await openFixture$8(engine, opts);
3835
+ const doc = await openFixture$11(engine, opts);
3712
3836
  try {
3713
3837
  const before = await doc.pages.list();
3714
3838
  if (before.pages.length < 2) return;
@@ -3729,7 +3853,7 @@ function runPageDeleteConformance(runner, opts) {
3729
3853
  }
3730
3854
  });
3731
3855
  test("addressing a deleted page afterwards is a clean NotFound", async () => {
3732
- const doc = await openFixture$8(engine, opts);
3856
+ const doc = await openFixture$11(engine, opts);
3733
3857
  try {
3734
3858
  const before = await doc.pages.list();
3735
3859
  if (before.pages.length < 2) return;
@@ -3747,7 +3871,7 @@ function runPageDeleteConformance(runner, opts) {
3747
3871
  }
3748
3872
  });
3749
3873
  test("surviving pages keep their weak index-based annotation refs", async () => {
3750
- const doc = await openFixture$8(engine, opts);
3874
+ const doc = await openFixture$11(engine, opts);
3751
3875
  try {
3752
3876
  const list = await doc.pages.list();
3753
3877
  if (list.pages.length < 2) return;
@@ -3780,7 +3904,7 @@ function runPageDeleteConformance(runner, opts) {
3780
3904
  }
3781
3905
  });
3782
3906
  test("deleting EVERY page is rejected with InvalidArg", async () => {
3783
- const doc = await openFixture$8(engine, opts);
3907
+ const doc = await openFixture$11(engine, opts);
3784
3908
  try {
3785
3909
  const list = await doc.pages.list();
3786
3910
  let caught;
@@ -3795,7 +3919,7 @@ function runPageDeleteConformance(runner, opts) {
3795
3919
  }
3796
3920
  });
3797
3921
  test("pages.delete() rejects duplicate PONs with InvalidArg", async () => {
3798
- const doc = await openFixture$8(engine, opts);
3922
+ const doc = await openFixture$11(engine, opts);
3799
3923
  try {
3800
3924
  const pon = (await doc.pages.list()).pages[0].pageObjectNumber;
3801
3925
  let caught;
@@ -3810,7 +3934,7 @@ function runPageDeleteConformance(runner, opts) {
3810
3934
  }
3811
3935
  });
3812
3936
  test("pages.delete() rejects unknown PON with NotFound or InvalidArg", async () => {
3813
- const doc = await openFixture$8(engine, opts);
3937
+ const doc = await openFixture$11(engine, opts);
3814
3938
  try {
3815
3939
  const list = await doc.pages.list();
3816
3940
  let bogus = 0;
@@ -3828,7 +3952,7 @@ function runPageDeleteConformance(runner, opts) {
3828
3952
  }
3829
3953
  });
3830
3954
  test("abort on pages.delete() rejects with AbortError", async () => {
3831
- const doc = await openFixture$8(engine, opts);
3955
+ const doc = await openFixture$11(engine, opts);
3832
3956
  try {
3833
3957
  const list = await doc.pages.list();
3834
3958
  if (list.pages.length < 2) return;
@@ -3842,7 +3966,7 @@ function runPageDeleteConformance(runner, opts) {
3842
3966
  });
3843
3967
  });
3844
3968
  }
3845
- async function openFixture$8(engine, opts) {
3969
+ async function openFixture$11(engine, opts) {
3846
3970
  if (opts.openKind === "bytes") {
3847
3971
  const bytes = await opts.fixture.bytes();
3848
3972
  return engine.open({
@@ -3870,7 +3994,7 @@ function runPageFlattenConformance(runner, opts) {
3870
3994
  if (engine) await engine.destroy();
3871
3995
  });
3872
3996
  test("flattens eligible appearances once without changing layout", async () => {
3873
- const doc = await openFixture$7(engine, opts);
3997
+ const doc = await openFixture$10(engine, opts);
3874
3998
  try {
3875
3999
  if (!doc.pages.flatten) return;
3876
4000
  const layoutBefore = await doc.pages.list();
@@ -3904,7 +4028,7 @@ function runPageFlattenConformance(runner, opts) {
3904
4028
  });
3905
4029
  });
3906
4030
  }
3907
- async function openFixture$7(engine, opts) {
4031
+ async function openFixture$10(engine, opts) {
3908
4032
  if (opts.openKind === "bytes") return engine.open({
3909
4033
  kind: "bytes",
3910
4034
  id: opts.fixture.id,
@@ -3959,7 +4083,7 @@ function runRedactionApplyConformance(runner, opts) {
3959
4083
  if (engine) await engine.destroy();
3960
4084
  });
3961
4085
  test("annotations scope applies the marked redaction and counts collateral", async () => {
3962
- const doc = await openFixture$6(engine, opts);
4086
+ const doc = await openFixture$9(engine, opts);
3963
4087
  try {
3964
4088
  if (!doc.redaction) return;
3965
4089
  const layoutBefore = await doc.pages.list();
@@ -4013,7 +4137,7 @@ function runRedactionApplyConformance(runner, opts) {
4013
4137
  }
4014
4138
  });
4015
4139
  test("pages scope applies everything once and validates its input", async () => {
4016
- const doc = await openFixture$6(engine, opts);
4140
+ const doc = await openFixture$9(engine, opts);
4017
4141
  try {
4018
4142
  if (!doc.redaction) return;
4019
4143
  const pageObjectNumber = (await doc.pages.list()).pages[0].pageObjectNumber;
@@ -4064,7 +4188,7 @@ function runRedactionApplyConformance(runner, opts) {
4064
4188
  });
4065
4189
  });
4066
4190
  }
4067
- async function openFixture$6(engine, opts) {
4191
+ async function openFixture$9(engine, opts) {
4068
4192
  if (opts.openKind === "bytes") return engine.open({
4069
4193
  kind: "bytes",
4070
4194
  id: opts.fixture.id,
@@ -4104,19 +4228,14 @@ function runPageExtractConformance(runner, opts) {
4104
4228
  const { describe, test, beforeAll, afterAll, expect } = runner;
4105
4229
  describe(`page extract conformance: ${opts.label}`, () => {
4106
4230
  let engine;
4107
- let supported = false;
4108
4231
  beforeAll(async () => {
4109
4232
  engine = await opts.makeEngine();
4110
- const probe = await openFixture$5(engine, opts);
4111
- supported = probe.pages.extract !== void 0;
4112
- await probe.close();
4113
4233
  });
4114
4234
  afterAll(async () => {
4115
4235
  if (engine) await engine.destroy();
4116
4236
  });
4117
4237
  test("pages.extract() returns standalone PDF bytes and leaves the source untouched", async () => {
4118
- if (!supported) return;
4119
- const doc = await openFixture$5(engine, opts);
4238
+ const doc = await openFixture$8(engine, opts);
4120
4239
  try {
4121
4240
  const before = await doc.pages.list();
4122
4241
  const target = before.pages[before.pages.length - 1];
@@ -4131,8 +4250,8 @@ function runPageExtractConformance(runner, opts) {
4131
4250
  }
4132
4251
  });
4133
4252
  test("extracted bytes re-open as a document with the requested pages, in caller order", async () => {
4134
- if (!supported || opts.openKind !== "bytes") return;
4135
- const doc = await openFixture$5(engine, opts);
4253
+ if (opts.openKind !== "bytes") return;
4254
+ const doc = await openFixture$8(engine, opts);
4136
4255
  let extracted = null;
4137
4256
  try {
4138
4257
  const list = await doc.pages.list();
@@ -4155,8 +4274,7 @@ function runPageExtractConformance(runner, opts) {
4155
4274
  }
4156
4275
  });
4157
4276
  test("pages.extract() rejects empty input with InvalidArg", async () => {
4158
- if (!supported) return;
4159
- const doc = await openFixture$5(engine, opts);
4277
+ const doc = await openFixture$8(engine, opts);
4160
4278
  try {
4161
4279
  let caught;
4162
4280
  try {
@@ -4170,8 +4288,7 @@ function runPageExtractConformance(runner, opts) {
4170
4288
  }
4171
4289
  });
4172
4290
  test("pages.extract() rejects duplicate PONs with InvalidArg", async () => {
4173
- if (!supported) return;
4174
- const doc = await openFixture$5(engine, opts);
4291
+ const doc = await openFixture$8(engine, opts);
4175
4292
  try {
4176
4293
  const pon = (await doc.pages.list()).pages[0].pageObjectNumber;
4177
4294
  let caught;
@@ -4186,8 +4303,7 @@ function runPageExtractConformance(runner, opts) {
4186
4303
  }
4187
4304
  });
4188
4305
  test("pages.extract() rejects unknown PON with NotFound or InvalidArg", async () => {
4189
- if (!supported) return;
4190
- const doc = await openFixture$5(engine, opts);
4306
+ const doc = await openFixture$8(engine, opts);
4191
4307
  try {
4192
4308
  const list = await doc.pages.list();
4193
4309
  let bogus = 0;
@@ -4205,8 +4321,7 @@ function runPageExtractConformance(runner, opts) {
4205
4321
  }
4206
4322
  });
4207
4323
  test("abort on pages.extract() rejects with AbortError", async () => {
4208
- if (!supported) return;
4209
- const doc = await openFixture$5(engine, opts);
4324
+ const doc = await openFixture$8(engine, opts);
4210
4325
  try {
4211
4326
  const list = await doc.pages.list();
4212
4327
  const p = doc.pages.extract([list.pages[0].pageObjectNumber]);
@@ -4218,7 +4333,7 @@ function runPageExtractConformance(runner, opts) {
4218
4333
  });
4219
4334
  });
4220
4335
  }
4221
- async function openFixture$5(engine, opts) {
4336
+ async function openFixture$8(engine, opts) {
4222
4337
  if (opts.openKind === "bytes") {
4223
4338
  const bytes = await opts.fixture.bytes();
4224
4339
  return engine.open({
@@ -4480,9 +4595,9 @@ function runAttachmentConformance(runner, opts) {
4480
4595
  //#endregion
4481
4596
  //#region src/conformance/runPageInsertConformance.ts
4482
4597
  /**
4483
- * Page insert conformance. `pages.insert?` is optional only until the cloud
4484
- * endpoint ships (it is slated REQUIRED-parity — a cloud viewer must be able
4485
- * to add pages); the suite runs wherever the implementation exists.
4598
+ * Page insert conformance. `pages.insert` is a REQUIRED member — this suite
4599
+ * runs unconditionally on every engine, so an implementation that loses the
4600
+ * verb fails loudly instead of being skipped past.
4486
4601
  *
4487
4602
  * Invariants:
4488
4603
  * 1. Every page of the source bytes is COPIED in at `destIndex` (omitted →
@@ -4498,19 +4613,14 @@ function runPageInsertConformance(runner, opts) {
4498
4613
  const { describe, test, beforeAll, afterAll, expect } = runner;
4499
4614
  describe(`page insert conformance: ${opts.label}`, () => {
4500
4615
  let engine;
4501
- let supported = false;
4502
4616
  beforeAll(async () => {
4503
4617
  engine = await opts.makeEngine();
4504
- const probe = await openFixture$4(engine, opts);
4505
- supported = probe.pages.insert !== void 0 && probe.pages.extract !== void 0;
4506
- await probe.close();
4507
4618
  });
4508
4619
  afterAll(async () => {
4509
4620
  if (engine) await engine.destroy();
4510
4621
  });
4511
4622
  test("appends every source page with fresh PONs; existing pages keep identity", async () => {
4512
- if (!supported) return;
4513
- const doc = await openFixture$4(engine, opts);
4623
+ const doc = await openFixture$7(engine, opts);
4514
4624
  try {
4515
4625
  const before = await doc.pages.list();
4516
4626
  const beforePons = before.pages.map((p) => p.pageObjectNumber);
@@ -4528,8 +4638,7 @@ function runPageInsertConformance(runner, opts) {
4528
4638
  }
4529
4639
  });
4530
4640
  test("destIndex places the block mid-document, in source order", async () => {
4531
- if (!supported) return;
4532
- const doc = await openFixture$4(engine, opts);
4641
+ const doc = await openFixture$7(engine, opts);
4533
4642
  try {
4534
4643
  const before = await doc.pages.list();
4535
4644
  if (before.pages.length < 2) return;
@@ -4546,8 +4655,8 @@ function runPageInsertConformance(runner, opts) {
4546
4655
  }
4547
4656
  });
4548
4657
  test("the inserted pages persist through save → re-open", async () => {
4549
- if (!supported || opts.openKind !== "bytes") return;
4550
- const doc = await openFixture$4(engine, opts);
4658
+ if (opts.openKind !== "bytes") return;
4659
+ const doc = await openFixture$7(engine, opts);
4551
4660
  let reopened = null;
4552
4661
  try {
4553
4662
  const before = await doc.pages.list();
@@ -4567,8 +4676,7 @@ function runPageInsertConformance(runner, opts) {
4567
4676
  }
4568
4677
  });
4569
4678
  test("empty bytes reject with InvalidArg; garbage rejects with MalformedPdf", async () => {
4570
- if (!supported) return;
4571
- const doc = await openFixture$4(engine, opts);
4679
+ const doc = await openFixture$7(engine, opts);
4572
4680
  try {
4573
4681
  let caught;
4574
4682
  try {
@@ -4591,8 +4699,7 @@ function runPageInsertConformance(runner, opts) {
4591
4699
  }
4592
4700
  });
4593
4701
  test("out-of-range destIndex rejects with InvalidArg", async () => {
4594
- if (!supported) return;
4595
- const doc = await openFixture$4(engine, opts);
4702
+ const doc = await openFixture$7(engine, opts);
4596
4703
  try {
4597
4704
  const before = await doc.pages.list();
4598
4705
  const single = await doc.pages.extract([before.pages[0].pageObjectNumber]);
@@ -4609,7 +4716,160 @@ function runPageInsertConformance(runner, opts) {
4609
4716
  });
4610
4717
  });
4611
4718
  }
4612
- async function openFixture$4(engine, opts) {
4719
+ async function openFixture$7(engine, opts) {
4720
+ if (opts.openKind === "bytes") {
4721
+ const bytes = await opts.fixture.bytes();
4722
+ return engine.open({
4723
+ kind: "bytes",
4724
+ id: opts.fixture.id,
4725
+ bytes
4726
+ });
4727
+ }
4728
+ return engine.open({
4729
+ kind: "id",
4730
+ id: opts.fixture.cloudId ?? opts.fixture.id
4731
+ });
4732
+ }
4733
+ //#endregion
4734
+ //#region src/conformance/runPageInsertBlankConformance.ts
4735
+ /** An on-purpose non-default size so the assertions can't pass by accident. */
4736
+ const SIZE = {
4737
+ width: 396,
4738
+ height: 612
4739
+ };
4740
+ /**
4741
+ * Blank-page insert conformance. `pages.insertBlank` is a REQUIRED member —
4742
+ * the suite runs unconditionally on every engine (no fixture bytes are
4743
+ * needed at all, which is the point of the verb), so an implementation that
4744
+ * loses the verb fails loudly instead of being skipped past.
4745
+ *
4746
+ * Invariants:
4747
+ * 1. `count` blank pages of exactly `size` appear at `destIndex` (omitted
4748
+ * → append), rotation 0; the result lists their fresh PONs in insertion
4749
+ * order and they agree with the returned layout.
4750
+ * 2. Pre-existing pages keep their identity: same PONs before and after,
4751
+ * in the expected positions (an insert never invalidates neighbours).
4752
+ * 3. The mutation persists through save → re-open (bytes engines only).
4753
+ * 4. Non-positive size / count outside [1, PAGE_INSERT_BLANK_MAX_COUNT] /
4754
+ * out-of-range destIndex reject with InvalidArg, leaving the document
4755
+ * untouched.
4756
+ */
4757
+ function runPageInsertBlankConformance(runner, opts) {
4758
+ const { describe, test, beforeAll, afterAll, expect } = runner;
4759
+ describe(`page insert-blank conformance: ${opts.label}`, () => {
4760
+ let engine;
4761
+ beforeAll(async () => {
4762
+ engine = await opts.makeEngine();
4763
+ });
4764
+ afterAll(async () => {
4765
+ if (engine) await engine.destroy();
4766
+ });
4767
+ test("appends one blank page of the requested size with a fresh PON", async () => {
4768
+ const doc = await openFixture$6(engine, opts);
4769
+ try {
4770
+ const before = await doc.pages.list();
4771
+ const beforePons = before.pages.map((p) => p.pageObjectNumber);
4772
+ const result = await doc.pages.insertBlank({ size: SIZE });
4773
+ expect(result.insertedPageObjectNumbers.length).toBe(1);
4774
+ expect(result.layout.pageCount).toBe(before.pageCount + 1);
4775
+ expect(result.layout.pages.slice(0, before.pageCount).map((p) => p.pageObjectNumber)).toEqual(beforePons);
4776
+ const newPon = result.insertedPageObjectNumbers[0];
4777
+ expect(beforePons.includes(newPon)).toBe(false);
4778
+ const appended = result.layout.pages[before.pageCount];
4779
+ expect(appended.pageObjectNumber).toBe(newPon);
4780
+ expect(appended.size).toEqual(SIZE);
4781
+ expect(appended.rotation).toBe(0);
4782
+ } finally {
4783
+ await doc.close();
4784
+ }
4785
+ });
4786
+ test("destIndex + count places the blank block mid-document", async () => {
4787
+ const doc = await openFixture$6(engine, opts);
4788
+ try {
4789
+ const before = await doc.pages.list();
4790
+ if (before.pages.length < 2) return;
4791
+ const beforePons = before.pages.map((p) => p.pageObjectNumber);
4792
+ const result = await doc.pages.insertBlank({
4793
+ size: SIZE,
4794
+ count: 2
4795
+ }, 1);
4796
+ expect(result.insertedPageObjectNumbers.length).toBe(2);
4797
+ expect(result.insertedPageObjectNumbers[0] === result.insertedPageObjectNumbers[1]).toBe(false);
4798
+ const pons = result.layout.pages.map((p) => p.pageObjectNumber);
4799
+ expect(pons[0]).toBe(beforePons[0]);
4800
+ expect(pons.slice(1, 3)).toEqual(result.insertedPageObjectNumbers);
4801
+ expect(pons.slice(3)).toEqual(beforePons.slice(1));
4802
+ expect(result.layout.pages[1].size).toEqual(SIZE);
4803
+ expect(result.layout.pages[2].size).toEqual(SIZE);
4804
+ } finally {
4805
+ await doc.close();
4806
+ }
4807
+ });
4808
+ test("the blank pages persist through save → re-open", async () => {
4809
+ if (opts.openKind !== "bytes") return;
4810
+ const doc = await openFixture$6(engine, opts);
4811
+ let reopened = null;
4812
+ try {
4813
+ const before = await doc.pages.list();
4814
+ await doc.pages.insertBlank({ size: SIZE });
4815
+ const bytes = await doc.download();
4816
+ reopened = await engine.open({
4817
+ kind: "bytes",
4818
+ id: `${opts.fixture.id}-insert-blank-reopen`,
4819
+ bytes
4820
+ });
4821
+ const after = await reopened.pages.list();
4822
+ expect(after.pageCount).toBe(before.pageCount + 1);
4823
+ const tail = after.pages[after.pageCount - 1];
4824
+ expect(Math.round(tail.size.width)).toBe(SIZE.width);
4825
+ expect(Math.round(tail.size.height)).toBe(SIZE.height);
4826
+ } finally {
4827
+ if (reopened) await reopened.close();
4828
+ await doc.close();
4829
+ }
4830
+ });
4831
+ test("non-positive size, bad count, and out-of-range destIndex reject with InvalidArg", async () => {
4832
+ const doc = await openFixture$6(engine, opts);
4833
+ try {
4834
+ const before = await doc.pages.list();
4835
+ const rejects = [
4836
+ () => doc.pages.insertBlank({ size: {
4837
+ width: 0,
4838
+ height: 612
4839
+ } }),
4840
+ () => doc.pages.insertBlank({ size: {
4841
+ width: 396,
4842
+ height: -10
4843
+ } }),
4844
+ () => doc.pages.insertBlank({
4845
+ size: SIZE,
4846
+ count: 0
4847
+ }),
4848
+ () => doc.pages.insertBlank({
4849
+ size: SIZE,
4850
+ count: 101
4851
+ }),
4852
+ () => doc.pages.insertBlank({ size: SIZE }, before.pageCount + 1)
4853
+ ];
4854
+ for (const attempt of rejects) {
4855
+ let caught;
4856
+ try {
4857
+ await attempt();
4858
+ } catch (err) {
4859
+ caught = err;
4860
+ }
4861
+ expect(EngineError.is(caught, EngineErrorCode.InvalidArg)).toBe(true);
4862
+ }
4863
+ const list = await doc.pages.list();
4864
+ expect(list.pageCount).toBe(before.pageCount);
4865
+ expect(list.pages.map((p) => p.pageObjectNumber)).toEqual(before.pages.map((p) => p.pageObjectNumber));
4866
+ } finally {
4867
+ await doc.close();
4868
+ }
4869
+ });
4870
+ });
4871
+ }
4872
+ async function openFixture$6(engine, opts) {
4613
4873
  if (opts.openKind === "bytes") {
4614
4874
  const bytes = await opts.fixture.bytes();
4615
4875
  return engine.open({
@@ -4650,7 +4910,7 @@ function runPieceInfoConformance(runner, opts) {
4650
4910
  let supported = false;
4651
4911
  beforeAll(async () => {
4652
4912
  engine = await opts.makeEngine();
4653
- const probe = await openFixture$3(engine, opts);
4913
+ const probe = await openFixture$5(engine, opts);
4654
4914
  supported = probe.pieceInfo !== void 0;
4655
4915
  await probe.close();
4656
4916
  });
@@ -4659,7 +4919,7 @@ function runPieceInfoConformance(runner, opts) {
4659
4919
  });
4660
4920
  test("doc-level: the full value vocabulary round-trips with tags intact", async () => {
4661
4921
  if (!supported) return;
4662
- const doc = await openFixture$3(engine, opts);
4922
+ const doc = await openFixture$5(engine, opts);
4663
4923
  try {
4664
4924
  await doc.pieceInfo.update(APP, {
4665
4925
  name: "Standard Stamps",
@@ -4699,7 +4959,7 @@ function runPieceInfoConformance(runner, opts) {
4699
4959
  });
4700
4960
  test("page-level: the stamp schema round-trips on a page", async () => {
4701
4961
  if (!supported) return;
4702
- const doc = await openFixture$3(engine, opts);
4962
+ const doc = await openFixture$5(engine, opts);
4703
4963
  try {
4704
4964
  const list = await doc.pages.list();
4705
4965
  const page = doc.page(list.pages[0].pageObjectNumber);
@@ -4725,7 +4985,7 @@ function runPieceInfoConformance(runner, opts) {
4725
4985
  });
4726
4986
  test("writes persist through save → re-open", async () => {
4727
4987
  if (!supported || opts.openKind !== "bytes") return;
4728
- const doc = await openFixture$3(engine, opts);
4988
+ const doc = await openFixture$5(engine, opts);
4729
4989
  let reopened = null;
4730
4990
  try {
4731
4991
  const pon = (await doc.pages.list()).pages[0].pageObjectNumber;
@@ -4758,7 +5018,7 @@ function runPieceInfoConformance(runner, opts) {
4758
5018
  });
4759
5019
  test("null deletes a key; clear removes an entry; siblings survive", async () => {
4760
5020
  if (!supported) return;
4761
- const doc = await openFixture$3(engine, opts);
5021
+ const doc = await openFixture$5(engine, opts);
4762
5022
  try {
4763
5023
  await doc.pieceInfo.update(APP, {
4764
5024
  name: "A",
@@ -4784,7 +5044,7 @@ function runPieceInfoConformance(runner, opts) {
4784
5044
  });
4785
5045
  test("an application that was never written reads as null", async () => {
4786
5046
  if (!supported) return;
4787
- const doc = await openFixture$3(engine, opts);
5047
+ const doc = await openFixture$5(engine, opts);
4788
5048
  try {
4789
5049
  expect(await doc.pieceInfo.read("EMBD_NeverWritten")).toBe(null);
4790
5050
  const list = await doc.pages.list();
@@ -4795,7 +5055,7 @@ function runPieceInfoConformance(runner, opts) {
4795
5055
  });
4796
5056
  });
4797
5057
  }
4798
- async function openFixture$3(engine, opts) {
5058
+ async function openFixture$5(engine, opts) {
4799
5059
  if (opts.openKind === "bytes") {
4800
5060
  const bytes = await opts.fixture.bytes();
4801
5061
  return engine.open({
@@ -4858,7 +5118,7 @@ function runDocumentEventsConformance(runner, opts) {
4858
5118
  if (engine) await engine.destroy();
4859
5119
  });
4860
5120
  test("every confirmed mutation publishes exactly one event, results verbatim", async () => {
4861
- const doc = await openFixture$2(engine, opts);
5121
+ const doc = await openFixture$4(engine, opts);
4862
5122
  try {
4863
5123
  const events = [];
4864
5124
  doc.events.subscribe((event) => events.push(event));
@@ -4905,19 +5165,46 @@ function runDocumentEventsConformance(runner, opts) {
4905
5165
  expect(evDeleted.layout).toEqual(deleted.layout);
4906
5166
  }
4907
5167
  if (evMeta.type === "metadata.updated") expect(evMeta.metadata).toEqual(meta.metadata);
5168
+ const first = events[0];
5169
+ expect(first !== void 0 && "origin" in first).toBe(true);
5170
+ if (first === void 0 || !("origin" in first)) return;
4908
5171
  for (const event of events) {
5172
+ expect("origin" in event).toBe(true);
5173
+ if (!("origin" in event)) continue;
4909
5174
  expect(event.origin.kind).toBe("local");
4910
5175
  expect(typeof event.origin.sessionId).toBe("string");
4911
5176
  expect(event.origin.sessionId.length > 0).toBe(true);
4912
- expect(event.origin.sessionId).toBe(events[0].origin.sessionId);
5177
+ expect(event.origin.sessionId).toBe(first.origin.sessionId);
4913
5178
  expect(typeof event.origin.ts).toBe("number");
4914
5179
  }
4915
5180
  } finally {
4916
5181
  await doc.close();
4917
5182
  }
4918
5183
  });
5184
+ test("pages.inserted rides verbatim", async () => {
5185
+ const doc = await openFixture$4(engine, opts);
5186
+ try {
5187
+ const events = [];
5188
+ doc.events.subscribe((event) => events.push(event));
5189
+ const inserted = await doc.pages.insertBlank({ size: {
5190
+ width: 396,
5191
+ height: 612
5192
+ } }, 0);
5193
+ expect(events.map((event) => event.type)).toEqual(["pages.inserted"]);
5194
+ const [evInserted] = events;
5195
+ if (evInserted.type === "pages.inserted") {
5196
+ expect(evInserted.insertedPageObjectNumbers).toEqual(inserted.insertedPageObjectNumbers);
5197
+ expect(evInserted.layout).toEqual(inserted.layout);
5198
+ expect(evInserted.cache).toEqual(inserted.cache);
5199
+ expect(evInserted.destIndex).toBe(0);
5200
+ expect(evInserted.origin.kind).toBe("local");
5201
+ }
5202
+ } finally {
5203
+ await doc.close();
5204
+ }
5205
+ });
4919
5206
  test("a FAILED mutation publishes nothing (events are ground truth)", async () => {
4920
- const doc = await openFixture$2(engine, opts);
5207
+ const doc = await openFixture$4(engine, opts);
4921
5208
  try {
4922
5209
  const events = [];
4923
5210
  doc.events.subscribe((event) => events.push(event));
@@ -4935,7 +5222,7 @@ function runDocumentEventsConformance(runner, opts) {
4935
5222
  }
4936
5223
  });
4937
5224
  test("unsubscribe stops delivery", async () => {
4938
- const doc = await openFixture$2(engine, opts);
5225
+ const doc = await openFixture$4(engine, opts);
4939
5226
  try {
4940
5227
  const events = [];
4941
5228
  doc.events.subscribe((event) => events.push(event))();
@@ -4948,7 +5235,7 @@ function runDocumentEventsConformance(runner, opts) {
4948
5235
  });
4949
5236
  });
4950
5237
  }
4951
- async function openFixture$2(engine, opts) {
5238
+ async function openFixture$4(engine, opts) {
4952
5239
  if (opts.openKind === "bytes") {
4953
5240
  const bytes = await opts.fixture.bytes();
4954
5241
  return engine.open({
@@ -4975,7 +5262,7 @@ function runPageTextConformance(runner, opts) {
4975
5262
  if (engine) await engine.destroy();
4976
5263
  });
4977
5264
  test("read() returns a PageTextSnapshot with non-empty text", async () => {
4978
- const doc = await openFixture$1(engine, opts);
5265
+ const doc = await openFixture$3(engine, opts);
4979
5266
  try {
4980
5267
  const snap = await doc.page(opts.fixture.pageObjectNumber).text.read();
4981
5268
  expect(PageTextSnapshotSchema.safeParse(snap).success).toBe(true);
@@ -4986,7 +5273,7 @@ function runPageTextConformance(runner, opts) {
4986
5273
  }
4987
5274
  });
4988
5275
  test("extracted text contains the expected substring", async () => {
4989
- const doc = await openFixture$1(engine, opts);
5276
+ const doc = await openFixture$3(engine, opts);
4990
5277
  try {
4991
5278
  const snap = await doc.page(opts.fixture.pageObjectNumber).text.read();
4992
5279
  expect(snap.text.includes(opts.fixture.expectedSubstring)).toBe(true);
@@ -4995,7 +5282,7 @@ function runPageTextConformance(runner, opts) {
4995
5282
  }
4996
5283
  });
4997
5284
  test("read() on an unknown pageObjectNumber throws NotFound", async () => {
4998
- const doc = await openFixture$1(engine, opts);
5285
+ const doc = await openFixture$3(engine, opts);
4999
5286
  try {
5000
5287
  let caught;
5001
5288
  try {
@@ -5010,7 +5297,7 @@ function runPageTextConformance(runner, opts) {
5010
5297
  }
5011
5298
  });
5012
5299
  test("abort() on read rejects with AbortError", async () => {
5013
- const doc = await openFixture$1(engine, opts);
5300
+ const doc = await openFixture$3(engine, opts);
5014
5301
  try {
5015
5302
  const p = doc.page(opts.fixture.pageObjectNumber).text.read();
5016
5303
  p.abort("test");
@@ -5020,7 +5307,7 @@ function runPageTextConformance(runner, opts) {
5020
5307
  }
5021
5308
  });
5022
5309
  test("read after close throws DocNotOpen", async () => {
5023
- const doc = await openFixture$1(engine, opts);
5310
+ const doc = await openFixture$3(engine, opts);
5024
5311
  const page = doc.page(opts.fixture.pageObjectNumber);
5025
5312
  await doc.close();
5026
5313
  let caught;
@@ -5034,6 +5321,320 @@ function runPageTextConformance(runner, opts) {
5034
5321
  });
5035
5322
  });
5036
5323
  }
5324
+ async function openFixture$3(engine, opts) {
5325
+ if (opts.openKind === "bytes") {
5326
+ const bytes = await opts.fixture.bytes();
5327
+ return engine.open({
5328
+ kind: "bytes",
5329
+ id: opts.fixture.id,
5330
+ bytes
5331
+ });
5332
+ }
5333
+ return engine.open({
5334
+ kind: "id",
5335
+ id: opts.fixture.cloudId ?? opts.fixture.id
5336
+ });
5337
+ }
5338
+ //#endregion
5339
+ //#region src/conformance/runTextDivergenceConformance.ts
5340
+ function runTextDivergenceConformance(runner, opts) {
5341
+ const { describe, test, beforeAll, afterAll, expect } = runner;
5342
+ const fixture = opts.fixture;
5343
+ describe(`text divergence conformance: ${opts.label} [${fixture.id}]`, () => {
5344
+ let engine;
5345
+ beforeAll(async () => {
5346
+ engine = await opts.makeEngine();
5347
+ });
5348
+ afterAll(async () => {
5349
+ if (engine) await engine.destroy();
5350
+ });
5351
+ test("snapshot is schema-valid with the exact text, charCount, and charMap", async () => {
5352
+ const doc = await openFixture$2(engine, opts);
5353
+ try {
5354
+ const snap = await doc.page(fixture.pageObjectNumber).text.read();
5355
+ expect(PageTextSnapshotSchema.safeParse(snap).success).toBe(true);
5356
+ expect(snap.text).toBe(fixture.exactText);
5357
+ expect(snap.charCount).toBe(fixture.expectedCharCount);
5358
+ const anchors = snap.charMap ?? null;
5359
+ if (fixture.expectedCharMap === null) expect(anchors === null || anchors.length === 0).toBe(true);
5360
+ else expect(anchors === null ? null : anchors.map((a) => [a[0], a[1]])).toEqual(fixture.expectedCharMap.map((a) => [a[0], a[1]]));
5361
+ } finally {
5362
+ await doc.close();
5363
+ }
5364
+ });
5365
+ test("char-space slicing round-trips through the map", async () => {
5366
+ const doc = await openFixture$2(engine, opts);
5367
+ try {
5368
+ const snap = await doc.page(fixture.pageObjectNumber).text.read();
5369
+ expect(sliceTextByChars(snap, 0, snap.charCount)).toBe(snap.text);
5370
+ for (const s of fixture.slices ?? []) expect(sliceTextByChars(snap, s.from, s.to)).toBe(s.text);
5371
+ } finally {
5372
+ await doc.close();
5373
+ }
5374
+ });
5375
+ if (fixture.search) {
5376
+ const probe = fixture.search;
5377
+ test("search hits address character space and join the text by slicing", async () => {
5378
+ const doc = await openFixture$2(engine, opts);
5379
+ try {
5380
+ const hit = (await doc.search.query({
5381
+ query: { text: probe.query },
5382
+ mode: "rects"
5383
+ })).matches.find((m) => m.pageObjectNumber === fixture.pageObjectNumber);
5384
+ expect(hit === void 0).toBe(false);
5385
+ expect(hit.charStart).toBe(probe.charStart);
5386
+ expect(hit.charCount).toBe(probe.charCount);
5387
+ expect(hit.segments.length > 0).toBe(true);
5388
+ const snap = await doc.page(fixture.pageObjectNumber).text.read();
5389
+ expect(sliceTextByChars(snap, hit.charStart, hit.charStart + hit.charCount)).toBe(probe.matchedText);
5390
+ } finally {
5391
+ await doc.close();
5392
+ }
5393
+ });
5394
+ }
5395
+ });
5396
+ }
5397
+ async function openFixture$2(engine, opts) {
5398
+ if (opts.openKind === "bytes") {
5399
+ const bytes = await opts.fixture.bytes();
5400
+ return engine.open({
5401
+ kind: "bytes",
5402
+ id: opts.fixture.id,
5403
+ bytes
5404
+ });
5405
+ }
5406
+ return engine.open({
5407
+ kind: "id",
5408
+ id: opts.fixture.cloudId ?? opts.fixture.id
5409
+ });
5410
+ }
5411
+ /**
5412
+ * THE pinned divergence cases — one table, consumed by both engines' suites
5413
+ * so expected values cannot drift. Values verified against the wasm runtime
5414
+ * (see each case). All fixtures are single-page; the page's indirect object
5415
+ * number is 3 in each.
5416
+ */
5417
+ const TEXT_DIVERGENCE_CASES = {
5418
+ /** Leading NON-PRINTING character: 31 character slots, 30 text units —
5419
+ * every text offset shifts by one, encoded by the single anchor [1, 0].
5420
+ * The searchex embeddertest for this fixture pins the same +1 shift. */
5421
+ bug1139: {
5422
+ resource: "bug_1139.pdf",
5423
+ pageObjectNumber: 3,
5424
+ exactText: "Hello, world!\r\nGoodbye, world!",
5425
+ expectedCharCount: 31,
5426
+ expectedCharMap: [[1, 0]],
5427
+ slices: [
5428
+ {
5429
+ from: 0,
5430
+ to: 1,
5431
+ text: ""
5432
+ },
5433
+ {
5434
+ from: 1,
5435
+ to: 7,
5436
+ text: "Hello,"
5437
+ },
5438
+ {
5439
+ from: 16,
5440
+ to: 23,
5441
+ text: "Goodbye"
5442
+ }
5443
+ ],
5444
+ search: {
5445
+ query: "Goodbye",
5446
+ charStart: 16,
5447
+ charCount: 7,
5448
+ matchedText: "Goodbye"
5449
+ }
5450
+ },
5451
+ /** `/ActualText` replacement: the span's real glyphs are REPLACED by one
5452
+ * synthetic kPiece character per ActualText character, lockstep with the
5453
+ * text (identity map), with synthesized evenly-divided boxes — so search
5454
+ * segments exist and extraction yields the ActualText, not the glyphs. */
5455
+ actualText: {
5456
+ resource: "bug_384770169.pdf",
5457
+ pageObjectNumber: 3,
5458
+ exactText: "What is my favorite food?",
5459
+ expectedCharCount: 25,
5460
+ expectedCharMap: null,
5461
+ slices: [{
5462
+ from: 11,
5463
+ to: 19,
5464
+ text: "favorite"
5465
+ }],
5466
+ search: {
5467
+ query: "favorite",
5468
+ charStart: 11,
5469
+ charCount: 8,
5470
+ matchedText: "favorite"
5471
+ }
5472
+ },
5473
+ /** Supplementary-plane text via a ToUnicode surrogate pair: PDFium stores
5474
+ * one character-list entry PER SURROGATE HALF in lockstep with the text
5475
+ * (identity map, no divergence in this representation) — and the emoji
5476
+ * SURVIVES extraction end to end. The combined-astral representation
5477
+ * (font-cmap path, one entry per code point → a [+2] anchor) is covered
5478
+ * by the charmap unit tests and the reader's divergent walk. */
5479
+ astralToUnicode: {
5480
+ resource: "embedpdf_astral_tounicode.pdf",
5481
+ pageObjectNumber: 3,
5482
+ exactText: "😀B!",
5483
+ expectedCharCount: 4,
5484
+ expectedCharMap: null,
5485
+ slices: [{
5486
+ from: 0,
5487
+ to: 2,
5488
+ text: "😀"
5489
+ }, {
5490
+ from: 2,
5491
+ to: 3,
5492
+ text: "B"
5493
+ }],
5494
+ search: {
5495
+ query: "B!",
5496
+ charStart: 2,
5497
+ charCount: 2,
5498
+ matchedText: "B!"
5499
+ }
5500
+ }
5501
+ };
5502
+ //#endregion
5503
+ //#region src/conformance/runPageGeometryOrientationConformance.ts
5504
+ const FLAG_EMPTY = 2;
5505
+ /** Absolute tolerance for coordinate assertions (PDF points). */
5506
+ const COORD_TOLERANCE = .001;
5507
+ /** Tolerance for baseline-angle assertions (radians). */
5508
+ const ANGLE_TOLERANCE = .001;
5509
+ function runPageGeometryOrientationConformance(runner, opts) {
5510
+ const { describe, test, beforeAll, afterAll, expect } = runner;
5511
+ describe(`page geometry orientation conformance: ${opts.label} [${opts.fixture.id}]`, () => {
5512
+ let engine;
5513
+ beforeAll(async () => {
5514
+ engine = await opts.makeEngine();
5515
+ });
5516
+ afterAll(async () => {
5517
+ if (engine) await engine.destroy();
5518
+ });
5519
+ const readSnapshot = async () => {
5520
+ const doc = await openFixture$1(engine, opts);
5521
+ return {
5522
+ snapshot: await doc.page(opts.fixture.pageObjectNumber).geometry.read(),
5523
+ close: () => doc.close()
5524
+ };
5525
+ };
5526
+ test("snapshot round-trips the wire schema", async () => {
5527
+ const { snapshot, close } = await readSnapshot();
5528
+ try {
5529
+ const parsed = PageGeometrySnapshotSchema.safeParse(snapshot);
5530
+ expect(parsed.success).toBe(true);
5531
+ if (parsed.success) expect(parsed.data).toEqual(snapshot);
5532
+ } finally {
5533
+ await close();
5534
+ }
5535
+ });
5536
+ test("run charStart indices tile the page glyph sequence", async () => {
5537
+ const { snapshot, close } = await readSnapshot();
5538
+ try {
5539
+ let next = 0;
5540
+ for (const run of snapshot.runs) {
5541
+ expect(run.charStart).toBe(next);
5542
+ next += run.glyphs.length;
5543
+ }
5544
+ } finally {
5545
+ await close();
5546
+ }
5547
+ });
5548
+ test("runs match the fixture orientation expectation", async () => {
5549
+ const { snapshot, close } = await readSnapshot();
5550
+ try {
5551
+ const expectation = opts.fixture.expectation;
5552
+ if (expectation.kind === "upright") {
5553
+ for (const run of snapshot.runs) {
5554
+ expect(isRotatedGeometryRun(run)).toBe(false);
5555
+ if (isRotatedGeometryRun(run)) continue;
5556
+ for (const glyph of run.glyphs) {
5557
+ expect("looseQuad" in glyph).toBe(false);
5558
+ if (glyph.flags & FLAG_EMPTY) expect(glyph.looseBox).toEqual({
5559
+ left: 0,
5560
+ bottom: 0,
5561
+ right: 0,
5562
+ top: 0
5563
+ });
5564
+ }
5565
+ }
5566
+ return;
5567
+ }
5568
+ if (expectation.kind === "empty-only") {
5569
+ for (const run of snapshot.runs) {
5570
+ expect(isRotatedGeometryRun(run)).toBe(false);
5571
+ if (isRotatedGeometryRun(run)) continue;
5572
+ for (const glyph of run.glyphs) {
5573
+ expect(glyph.flags & FLAG_EMPTY).toBe(FLAG_EMPTY);
5574
+ expect(glyph.looseBox).toEqual({
5575
+ left: 0,
5576
+ bottom: 0,
5577
+ right: 0,
5578
+ top: 0
5579
+ });
5580
+ }
5581
+ }
5582
+ return;
5583
+ }
5584
+ const rotatedRuns = snapshot.runs.filter(isRotatedGeometryRun);
5585
+ expect(rotatedRuns.length >= (expectation.minRotatedRuns ?? 1)).toBe(true);
5586
+ for (const run of rotatedRuns) {
5587
+ expect(expectation.rotations.some((angle) => angleClose(run.rotation, angle, ANGLE_TOLERANCE))).toBe(true);
5588
+ expect(run.ascentFlip).toBe(expectation.ascentFlip);
5589
+ assertRotatedRunGeometry(run);
5590
+ }
5591
+ if (expectation.sheared) {
5592
+ const shearedGlyphs = rotatedRuns.flatMap((run) => run.glyphs).filter((glyph) => (glyph.flags & FLAG_EMPTY) === 0 && Math.abs(glyph.looseQuad.p3.x - glyph.looseQuad.p1.x) > 1);
5593
+ expect(shearedGlyphs.length > 0).toBe(true);
5594
+ }
5595
+ } finally {
5596
+ await close();
5597
+ }
5598
+ });
5599
+ function assertRotatedRunGeometry(run) {
5600
+ for (const glyph of run.glyphs) {
5601
+ if (glyph.flags & FLAG_EMPTY) {
5602
+ expect(glyph.looseQuad).toEqual({
5603
+ p1: {
5604
+ x: 0,
5605
+ y: 0
5606
+ },
5607
+ p2: {
5608
+ x: 0,
5609
+ y: 0
5610
+ },
5611
+ p3: {
5612
+ x: 0,
5613
+ y: 0
5614
+ },
5615
+ p4: {
5616
+ x: 0,
5617
+ y: 0
5618
+ }
5619
+ });
5620
+ continue;
5621
+ }
5622
+ const q = glyph.looseQuad;
5623
+ expect(Math.abs(q.p2.x - q.p1.x - (q.p4.x - q.p3.x)) <= COORD_TOLERANCE).toBe(true);
5624
+ expect(Math.abs(q.p2.y - q.p1.y - (q.p4.y - q.p3.y)) <= COORD_TOLERANCE).toBe(true);
5625
+ const bounds = pdfQuadBounds(q);
5626
+ expect(bounds.left >= run.rect.left - COORD_TOLERANCE).toBe(true);
5627
+ expect(bounds.right <= run.rect.right + COORD_TOLERANCE).toBe(true);
5628
+ expect(bounds.bottom >= run.rect.bottom - COORD_TOLERANCE).toBe(true);
5629
+ expect(bounds.top <= run.rect.top + COORD_TOLERANCE).toBe(true);
5630
+ }
5631
+ }
5632
+ });
5633
+ }
5634
+ function angleClose(a, b, tolerance) {
5635
+ const delta = a - b;
5636
+ return Math.abs(Math.atan2(Math.sin(delta), Math.cos(delta))) <= tolerance;
5637
+ }
5037
5638
  async function openFixture$1(engine, opts) {
5038
5639
  if (opts.openKind === "bytes") {
5039
5640
  const bytes = await opts.fixture.bytes();
@@ -5089,10 +5690,27 @@ function runSearchConformance(runner, opts) {
5089
5690
  expect(matches.some((m) => m.pageObjectNumber === fixture.presentPageObjectNumber)).toBe(true);
5090
5691
  for (const m of matches) {
5091
5692
  expect(m.charCount > 0).toBe(true);
5092
- expect(m.rects.length > 0).toBe(true);
5093
- for (const r of m.rects) {
5094
- expect(r.right > r.left).toBe(true);
5095
- expect(r.top > r.bottom).toBe(true);
5693
+ expect(m.segments.length > 0).toBe(true);
5694
+ for (const s of m.segments) {
5695
+ expect(s.rect.right > s.rect.left).toBe(true);
5696
+ expect(s.rect.top > s.rect.bottom).toBe(true);
5697
+ const xs = [
5698
+ s.quad.p1.x,
5699
+ s.quad.p2.x,
5700
+ s.quad.p3.x,
5701
+ s.quad.p4.x
5702
+ ];
5703
+ const ys = [
5704
+ s.quad.p1.y,
5705
+ s.quad.p2.y,
5706
+ s.quad.p3.y,
5707
+ s.quad.p4.y
5708
+ ];
5709
+ expect(Math.abs(s.rect.left - Math.min(...xs)) < .001).toBe(true);
5710
+ expect(Math.abs(s.rect.right - Math.max(...xs)) < .001).toBe(true);
5711
+ expect(Math.abs(s.rect.bottom - Math.min(...ys)) < .001).toBe(true);
5712
+ expect(Math.abs(s.rect.top - Math.max(...ys)) < .001).toBe(true);
5713
+ expect(s.advance === 1 || s.advance === -1).toBe(true);
5096
5714
  }
5097
5715
  }
5098
5716
  } finally {
@@ -5224,7 +5842,7 @@ function runSearchConformance(runner, opts) {
5224
5842
  expect(matches.length > 0).toBe(true);
5225
5843
  for (const m of matches) {
5226
5844
  expect(m.charCount > 0).toBe(true);
5227
- expect(m.rects.length > 0).toBe(true);
5845
+ expect(m.segments.length > 0).toBe(true);
5228
5846
  }
5229
5847
  } finally {
5230
5848
  await doc.close();
@@ -5351,6 +5969,8 @@ async function openFixture(engine, opts) {
5351
5969
  * are disjoint. We do compare `pageObjectNumber` and `generation` (which
5352
5970
  * should be 0 for fresh reads on both sides). Page order is no longer part
5353
5971
  * of `PageState`; it lives in `PageLayout.index` (see `pages.list()`).
5972
+ * `auditHead` is likewise not compared: it is a cloud-only reconciliation
5973
+ * cursor, absent on local snapshots by design.
5354
5974
  */
5355
5975
  function diffAnnotationListSnapshot(a, b) {
5356
5976
  const errs = [];
@@ -5389,6 +6009,6 @@ function diffAnnotation(i, a, b, errs) {
5389
6009
  if (a.nm !== b.nm) errs.push(`annotations[${i}].nm mismatch: ${a.nm} vs ${b.nm}`);
5390
6010
  }
5391
6011
  //#endregion
5392
- export { diffAnnotationListSnapshot, diffAnnotationListSnapshotAll, runActionsConformance, runAnnotationAppearanceConformance, runAnnotationMutationConformance, runAnnotationReadConformance, runAttachmentConformance, runDocumentEventsConformance, runFormConformance, runMetadataConformance, runPageDeleteConformance, runPageExtractConformance, runPageFlattenConformance, runPageInsertConformance, runPageReorderConformance, runPageRotateConformance, runPageTextConformance, runPieceInfoConformance, runRedactionApplyConformance, runSearchConformance };
6012
+ export { TEXT_DIVERGENCE_CASES, diffAnnotationListSnapshot, diffAnnotationListSnapshotAll, runActionsConformance, runAnnotationAppearanceConformance, runAnnotationMutationConformance, runAnnotationReadConformance, runAttachmentConformance, runDocumentEventsConformance, runFormConformance, runMetadataConformance, runPageDeleteConformance, runPageExtractConformance, runPageFlattenConformance, runPageGeometryOrientationConformance, runPageInsertBlankConformance, runPageInsertConformance, runPageReorderConformance, runPageRotateConformance, runPageTextConformance, runPieceInfoConformance, runRedactionApplyConformance, runSearchConformance, runTextDivergenceConformance };
5393
6013
 
5394
6014
  //# sourceMappingURL=conformance.js.map