@embedpdf/engine-core 3.0.0-next.1 → 3.0.0-next.11

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 (62) 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-C1U3XUxP.d.cts} +846 -127
  6. package/dist/{AnnotationListSnapshot-W0RCsyDE.d.ts → AnnotationListSnapshot-C1U3XUxP.d.ts} +846 -127
  7. package/dist/{DocumentSecurityService-Df79fgpe.d.cts → DocumentSecurityService-6rhztZlz.d.cts} +33 -2
  8. package/dist/{DocumentSecurityService-B-tULKNY.d.ts → DocumentSecurityService-7nL9aY75.d.ts} +33 -2
  9. package/dist/{Engine-B7Oe7f5i.d.ts → Engine-CWnAKccD.d.ts} +62 -19
  10. package/dist/{Engine-D9loFZc1.d.cts → Engine-D3868xIU.d.cts} +62 -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-Cdz34nbF.js} +566 -164
  16. package/dist/builders-Cdz34nbF.js.map +1 -0
  17. package/dist/{builders-BvikWRuO.cjs → builders-Do6_4KnS.cjs} +628 -220
  18. package/dist/builders-Do6_4KnS.cjs.map +1 -0
  19. package/dist/conformance.cjs +1039 -204
  20. package/dist/conformance.cjs.map +1 -1
  21. package/dist/conformance.d.cts +130 -6
  22. package/dist/conformance.d.ts +130 -6
  23. package/dist/conformance.js +994 -163
  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 +38 -19
  30. package/dist/index.d.cts +2 -2
  31. package/dist/index.d.ts +2 -2
  32. package/dist/index.js +5 -5
  33. package/dist/{resolver-DiPMTfcy.js → resolver-CHG4waXL.js} +2 -1
  34. package/dist/resolver-CHG4waXL.js.map +1 -0
  35. package/dist/{resolver-DAZWVoJL.cjs → resolver-D-jumvgM.cjs} +2 -1
  36. package/dist/resolver-D-jumvgM.cjs.map +1 -0
  37. package/dist/runtime.cjs +43 -24
  38. package/dist/runtime.cjs.map +1 -1
  39. package/dist/runtime.d.cts +103 -5
  40. package/dist/runtime.d.ts +103 -5
  41. package/dist/runtime.js +6 -6
  42. package/dist/{schemas-B_1VyFsm.cjs → schemas-CS5iPpX5.cjs} +305 -111
  43. package/dist/schemas-CS5iPpX5.cjs.map +1 -0
  44. package/dist/{schemas-9OpSSeM6.js → schemas-XGrNVMDd.js} +250 -92
  45. package/dist/schemas-XGrNVMDd.js.map +1 -0
  46. package/dist/wire.cjs +131 -4
  47. package/dist/wire.cjs.map +1 -1
  48. package/dist/wire.d.cts +277 -20
  49. package/dist/wire.d.ts +277 -20
  50. package/dist/wire.js +120 -5
  51. package/dist/wire.js.map +1 -1
  52. package/package.json +1 -1
  53. package/dist/builders-BvikWRuO.cjs.map +0 -1
  54. package/dist/builders-C548U47M.js.map +0 -1
  55. package/dist/fold-CB4fAOOy.js.map +0 -1
  56. package/dist/fold-IaMph3CO.cjs.map +0 -1
  57. package/dist/resolver-DAZWVoJL.cjs.map +0 -1
  58. package/dist/resolver-DiPMTfcy.js.map +0 -1
  59. package/dist/schemas-9OpSSeM6.js.map +0 -1
  60. package/dist/schemas-B_1VyFsm.cjs.map +0 -1
  61. package/dist/subtype-BvAzLCGC.js.map +0 -1
  62. package/dist/subtype-qejjVr6h.cjs.map +0 -1
@@ -1,8 +1,8 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_subtype = require("./subtype-qejjVr6h.cjs");
3
- const require_fold = require("./fold-IaMph3CO.cjs");
4
- const require_AbortError = require("./AbortError-ByBflvEl.cjs");
5
- const require_schemas = require("./schemas-B_1VyFsm.cjs");
2
+ const require_fold = require("./fold-C7I5GpBz.cjs");
3
+ const require_PageInsertBlankInput = require("./PageInsertBlankInput-CzShuSJI.cjs");
4
+ const require_AbortError = require("./AbortError-CRSooKsk.cjs");
5
+ const require_schemas = require("./schemas-CS5iPpX5.cjs");
6
6
  //#region src/conformance/runMetadataConformance.ts
7
7
  /**
8
8
  * The conformance harness is transport-agnostic. It takes the test runner as
@@ -19,7 +19,7 @@ function runMetadataConformance(runner, opts) {
19
19
  if (engine) await engine.destroy();
20
20
  });
21
21
  test("reads metadata from sample fixture", async () => {
22
- const doc = await openFixture$14(engine, opts);
22
+ const doc = await openFixture$17(engine, opts);
23
23
  try {
24
24
  const meta = await doc.metadata.read();
25
25
  expect(meta).toMatchObject(opts.fixture.expected);
@@ -29,7 +29,7 @@ function runMetadataConformance(runner, opts) {
29
29
  }
30
30
  });
31
31
  test("abort() before completion rejects with AbortError", async () => {
32
- const doc = await openFixture$14(engine, opts);
32
+ const doc = await openFixture$17(engine, opts);
33
33
  try {
34
34
  const p = doc.metadata.read();
35
35
  p.abort("test");
@@ -39,7 +39,7 @@ function runMetadataConformance(runner, opts) {
39
39
  }
40
40
  });
41
41
  test("aborting one read does not affect a concurrent one", async () => {
42
- const doc = await openFixture$14(engine, opts);
42
+ const doc = await openFixture$17(engine, opts);
43
43
  try {
44
44
  const a = doc.metadata.read();
45
45
  const b = doc.metadata.read();
@@ -51,7 +51,7 @@ function runMetadataConformance(runner, opts) {
51
51
  }
52
52
  });
53
53
  test("metadata after close throws DocNotOpen", async () => {
54
- const doc = await openFixture$14(engine, opts);
54
+ const doc = await openFixture$17(engine, opts);
55
55
  await doc.close();
56
56
  let caught;
57
57
  try {
@@ -60,10 +60,10 @@ function runMetadataConformance(runner, opts) {
60
60
  caught = err;
61
61
  }
62
62
  expect(caught).toBeTruthy();
63
- expect(require_fold.EngineError.is(caught, require_subtype.EngineErrorCode.DocNotOpen)).toBe(true);
63
+ expect(require_fold.EngineError.is(caught, require_PageInsertBlankInput.EngineErrorCode.DocNotOpen)).toBe(true);
64
64
  });
65
65
  test("update() sets standard + custom fields and a subsequent read observes them", async () => {
66
- const doc = await openFixture$14(engine, opts);
66
+ const doc = await openFixture$17(engine, opts);
67
67
  try {
68
68
  const result = await doc.metadata.update({
69
69
  title: "Conformance Title",
@@ -82,7 +82,7 @@ function runMetadataConformance(runner, opts) {
82
82
  }
83
83
  });
84
84
  test("update() clears a field with null", async () => {
85
- const doc = await openFixture$14(engine, opts);
85
+ const doc = await openFixture$17(engine, opts);
86
86
  try {
87
87
  await doc.metadata.update({ title: "To Be Cleared" });
88
88
  const set = await doc.metadata.read();
@@ -95,7 +95,7 @@ function runMetadataConformance(runner, opts) {
95
95
  }
96
96
  });
97
97
  test("update() after close throws DocNotOpen", async () => {
98
- const doc = await openFixture$14(engine, opts);
98
+ const doc = await openFixture$17(engine, opts);
99
99
  await doc.close();
100
100
  let caught;
101
101
  try {
@@ -104,11 +104,11 @@ function runMetadataConformance(runner, opts) {
104
104
  caught = err;
105
105
  }
106
106
  expect(caught).toBeTruthy();
107
- expect(require_fold.EngineError.is(caught, require_subtype.EngineErrorCode.DocNotOpen)).toBe(true);
107
+ expect(require_fold.EngineError.is(caught, require_PageInsertBlankInput.EngineErrorCode.DocNotOpen)).toBe(true);
108
108
  });
109
109
  });
110
110
  }
111
- async function openFixture$14(engine, opts) {
111
+ async function openFixture$17(engine, opts) {
112
112
  if (opts.openKind === "bytes") {
113
113
  const bytes = await opts.fixture.bytes();
114
114
  return engine.open({
@@ -135,11 +135,12 @@ function runAnnotationReadConformance(runner, opts) {
135
135
  if (engine) await engine.destroy();
136
136
  });
137
137
  test("listRawAll returns one entry per page with valid PageState", async () => {
138
- const doc = await openFixture$13(engine, opts);
138
+ const doc = await openFixture$16(engine, opts);
139
139
  try {
140
140
  const snap = await doc.annotations.listRawAll();
141
141
  expect(require_schemas.AnnotationListSnapshotAllPagesSchema.safeParse(snap).success).toBe(true);
142
142
  expect(snap.pages.length >= 1).toBe(true);
143
+ if (snap.auditHead !== void 0) expect(Number.isInteger(snap.auditHead) && snap.auditHead >= 0).toBe(true);
143
144
  const target = snap.pages.find((p) => p.pageState.pageObjectNumber === opts.fixture.pageObjectNumber);
144
145
  expect(target !== void 0).toBe(true);
145
146
  expect(target.annotations.length).toBe(opts.fixture.expectedAnnotationCount);
@@ -148,7 +149,7 @@ function runAnnotationReadConformance(runner, opts) {
148
149
  }
149
150
  });
150
151
  test("listRaw on the test page returns the expected counts and DTO shape", async () => {
151
- const doc = await openFixture$13(engine, opts);
152
+ const doc = await openFixture$16(engine, opts);
152
153
  try {
153
154
  const snap = await doc.annotations.listRaw(opts.fixture.pageObjectNumber);
154
155
  expect(require_schemas.AnnotationListPageSnapshotSchema.safeParse(snap).success).toBe(true);
@@ -178,7 +179,7 @@ function runAnnotationReadConformance(runner, opts) {
178
179
  }
179
180
  });
180
181
  test("shape/vertex/line annotations expose their family fields", async () => {
181
- const doc = await openFixture$13(engine, opts);
182
+ const doc = await openFixture$16(engine, opts);
182
183
  try {
183
184
  const snap = await doc.annotations.listRaw(opts.fixture.pageObjectNumber);
184
185
  for (const a of snap.annotations) {
@@ -237,7 +238,7 @@ function runAnnotationReadConformance(runner, opts) {
237
238
  }
238
239
  });
239
240
  test("full page list dispatches per-subtype and matches raw counts", async () => {
240
- const doc = await openFixture$13(engine, opts);
241
+ const doc = await openFixture$16(engine, opts);
241
242
  try {
242
243
  const full = await doc.page(opts.fixture.pageObjectNumber).annotations.list();
243
244
  expect(require_schemas.AnnotationListPageSnapshotSchema.safeParse(full).success).toBe(true);
@@ -247,7 +248,7 @@ function runAnnotationReadConformance(runner, opts) {
247
248
  }
248
249
  });
249
250
  test("every annotation DTO satisfies AnnotationDTOSchema (discriminated union)", async () => {
250
- const doc = await openFixture$13(engine, opts);
251
+ const doc = await openFixture$16(engine, opts);
251
252
  try {
252
253
  const snap = await doc.annotations.listRaw(opts.fixture.pageObjectNumber);
253
254
  for (const a of snap.annotations) {
@@ -287,7 +288,7 @@ function runAnnotationReadConformance(runner, opts) {
287
288
  });
288
289
  if (opts.fixture.expectsWeakAnnotation) {
289
290
  test("weak annotations carry identityQuality: weak and ref.kind: index", async () => {
290
- const doc = await openFixture$13(engine, opts);
291
+ const doc = await openFixture$16(engine, opts);
291
292
  try {
292
293
  const weak = (await doc.annotations.listRaw(opts.fixture.pageObjectNumber)).annotations.find((a) => a.identityQuality === "weak");
293
294
  expect(weak !== void 0).toBe(true);
@@ -297,7 +298,7 @@ function runAnnotationReadConformance(runner, opts) {
297
298
  }
298
299
  });
299
300
  test("a stale revision token throws InvalidReference", async () => {
300
- const doc = await openFixture$13(engine, opts);
301
+ const doc = await openFixture$16(engine, opts);
301
302
  try {
302
303
  const snap = await doc.page(opts.fixture.pageObjectNumber).annotations.list();
303
304
  const weak = snap.annotations.find((a) => a.identityQuality === "weak");
@@ -313,7 +314,7 @@ function runAnnotationReadConformance(runner, opts) {
313
314
  });
314
315
  }
315
316
  test("abort() on listRaw rejects with AbortError", async () => {
316
- const doc = await openFixture$13(engine, opts);
317
+ const doc = await openFixture$16(engine, opts);
317
318
  try {
318
319
  const p = doc.annotations.listRaw(opts.fixture.pageObjectNumber);
319
320
  p.abort("test");
@@ -323,7 +324,7 @@ function runAnnotationReadConformance(runner, opts) {
323
324
  }
324
325
  });
325
326
  test("listRaw on an unknown pageObjectNumber throws NotFound", async () => {
326
- const doc = await openFixture$13(engine, opts);
327
+ const doc = await openFixture$16(engine, opts);
327
328
  try {
328
329
  let caught;
329
330
  try {
@@ -332,14 +333,14 @@ function runAnnotationReadConformance(runner, opts) {
332
333
  caught = err;
333
334
  }
334
335
  expect(caught).toBeTruthy();
335
- expect(require_fold.EngineError.is(caught, require_subtype.EngineErrorCode.NotFound)).toBe(true);
336
+ expect(require_fold.EngineError.is(caught, require_PageInsertBlankInput.EngineErrorCode.NotFound)).toBe(true);
336
337
  } finally {
337
338
  await doc.close();
338
339
  }
339
340
  });
340
341
  });
341
342
  }
342
- async function openFixture$13(engine, opts) {
343
+ async function openFixture$16(engine, opts) {
343
344
  if (opts.openKind === "bytes") {
344
345
  const bytes = await opts.fixture.bytes();
345
346
  return engine.open({
@@ -369,6 +370,13 @@ function emptyFlags() {
369
370
  }
370
371
  //#endregion
371
372
  //#region src/conformance/runActionsConformance.ts
373
+ /** The script payload rides only the javascript/rendition arms. */
374
+ function scriptOf(node) {
375
+ if (!node) return "";
376
+ if (node.type === "javascript") return node.script;
377
+ if (node.type === "rendition") return node.script ?? "";
378
+ return "";
379
+ }
372
380
  /** Action extraction parity. These tests assert data only; no script executes. */
373
381
  function runActionsConformance(runner, opts) {
374
382
  const { describe, test, beforeAll, afterAll, expect } = runner;
@@ -388,10 +396,10 @@ function runActionsConformance(runner, opts) {
388
396
  expect(require_schemas.DocumentActionsSnapshotSchema.safeParse(snapshot).success).toBe(true);
389
397
  expect(snapshot.openAction).toBeNull();
390
398
  expect(snapshot.willSave?.root?.type).toBe("javascript");
391
- expect(snapshot.willSave?.root?.script ?? "").toMatch(/Will Save/);
392
- expect(snapshot.didSave?.root?.script ?? "").toMatch(/Did Save/);
393
- expect(snapshot.willPrint?.root?.script ?? "").toMatch(/Will Print/);
394
- expect(snapshot.didPrint?.root?.script ?? "").toMatch(/Did Print/);
399
+ expect(scriptOf(snapshot.willSave?.root)).toMatch(/Will Save/);
400
+ expect(scriptOf(snapshot.didSave?.root)).toMatch(/Did Save/);
401
+ expect(scriptOf(snapshot.willPrint?.root)).toMatch(/Will Print/);
402
+ expect(scriptOf(snapshot.didPrint?.root)).toMatch(/Did Print/);
395
403
  const form = await doc.forms.list();
396
404
  expect(form.calculationOrder).toEqual([{
397
405
  kind: "objectNumber",
@@ -438,7 +446,7 @@ function runActionsConformance(runner, opts) {
438
446
  expect(link?.subtype).toBe("link");
439
447
  if (link?.subtype === "link") expect(link.target).toEqual({ kind: "javascript" });
440
448
  expect(link?.actions?.activate?.root?.type).toBe("javascript");
441
- expect(link?.actions?.activate?.root?.script ?? "").toMatch(/app\.alert/);
449
+ expect(scriptOf(link?.actions?.activate?.root)).toMatch(/app\.alert/);
442
450
  } finally {
443
451
  await doc.close();
444
452
  }
@@ -449,7 +457,7 @@ function runActionsConformance(runner, opts) {
449
457
  const form = await doc.forms.list();
450
458
  expect(form.fields).toHaveLength(1);
451
459
  expect(form.fields[0].actions?.format?.root?.type).toBe("javascript");
452
- expect(form.fields[0].actions?.format?.root?.script ?? "").toMatch(/AFDate_FormatEx/);
460
+ expect(scriptOf(form.fields[0].actions?.format?.root)).toMatch(/AFDate_FormatEx/);
453
461
  const page = (await doc.pages.list()).pages[0];
454
462
  const widget = (await doc.page(page.pageObjectNumber).annotations.list()).annotations[0];
455
463
  expect(widget.actions).toBe(void 0);
@@ -457,6 +465,210 @@ function runActionsConformance(runner, opts) {
457
465
  await doc.close();
458
466
  }
459
467
  });
468
+ test("carries interpreter payloads on every executable node", async () => {
469
+ const doc = await open(engine, opts, opts.fixtures.payloads);
470
+ try {
471
+ const page = (await doc.pages.list()).pages[0];
472
+ const snapshot = await doc.page(page.pageObjectNumber).annotations.list();
473
+ const pon = page.pageObjectNumber;
474
+ const rootOf = (nm) => {
475
+ const annotation = snapshot.annotations.find((candidate) => candidate.nm === nm);
476
+ expect(Boolean(annotation)).toBe(true);
477
+ return annotation?.actions?.activate?.root ?? null;
478
+ };
479
+ const targetOf = (nm) => {
480
+ const annotation = snapshot.annotations.find((candidate) => candidate.nm === nm);
481
+ return annotation?.subtype === "link" ? annotation.target : null;
482
+ };
483
+ expect(rootOf("goto-fitr")).toMatchObject({
484
+ type: "goto",
485
+ destination: {
486
+ kind: "fitR",
487
+ pageObjectNumber: pon,
488
+ left: 10,
489
+ bottom: 20,
490
+ right: 300,
491
+ top: 400
492
+ }
493
+ });
494
+ expect(targetOf("goto-fitr")).toMatchObject({
495
+ kind: "goto",
496
+ destination: { kind: "fitR" }
497
+ });
498
+ expect(rootOf("uri-map")).toMatchObject({
499
+ type: "uri",
500
+ uri: "https://example.test/map",
501
+ isMap: true
502
+ });
503
+ expect(rootOf("named-next")).toMatchObject({
504
+ type: "named",
505
+ name: "NextPage"
506
+ });
507
+ const mixed = rootOf("hide-mixed");
508
+ expect(mixed).toMatchObject({
509
+ type: "hide",
510
+ hide: false
511
+ });
512
+ const mixedTargets = mixed?.type === "hide" ? mixed.targets : [];
513
+ expect(mixedTargets).toHaveLength(2);
514
+ expect(mixedTargets[0]).toEqual({
515
+ kind: "name",
516
+ name: "note1"
517
+ });
518
+ expect(mixedTargets[1]?.kind === "objectNumber" && mixedTargets[1].objectNumber > 0).toBe(true);
519
+ expect(rootOf("hide-scalar")).toMatchObject({
520
+ type: "hide",
521
+ targets: [{
522
+ kind: "name",
523
+ name: "fieldB"
524
+ }],
525
+ hide: true
526
+ });
527
+ expect(rootOf("reset-include")).toMatchObject({
528
+ type: "reset-form",
529
+ fields: [{
530
+ kind: "name",
531
+ name: "calc1"
532
+ }],
533
+ exclude: true
534
+ });
535
+ expect(rootOf("reset-absent")).toMatchObject({
536
+ type: "reset-form",
537
+ fields: null,
538
+ exclude: true
539
+ });
540
+ expect(rootOf("reset-empty")).toMatchObject({
541
+ type: "reset-form",
542
+ fields: [],
543
+ exclude: false
544
+ });
545
+ expect(rootOf("submit-urlspec")).toMatchObject({
546
+ type: "submit-form",
547
+ payload: {
548
+ url: "https://uf.example.test/submit",
549
+ fields: [{
550
+ kind: "name",
551
+ name: "parent"
552
+ }],
553
+ flags: {
554
+ raw: 0,
555
+ exclude: false,
556
+ format: "fdf",
557
+ method: "post"
558
+ }
559
+ }
560
+ });
561
+ expect(rootOf("submit-compat-xfdf")).toMatchObject({
562
+ type: "submit-form",
563
+ payload: {
564
+ url: "https://example.test/xfdf",
565
+ fields: [{
566
+ kind: "name",
567
+ name: "noexport"
568
+ }, {
569
+ kind: "name",
570
+ name: "plain"
571
+ }],
572
+ flags: {
573
+ raw: 35,
574
+ exclude: true,
575
+ includeNoValueFields: true,
576
+ format: "xfdf",
577
+ method: "post"
578
+ },
579
+ charSet: "utf-8"
580
+ }
581
+ });
582
+ expect(rootOf("submit-pdf-get")).toMatchObject({
583
+ type: "submit-form",
584
+ payload: {
585
+ url: "https://example.test/pdf",
586
+ fields: null,
587
+ flags: {
588
+ raw: 264,
589
+ format: "pdf",
590
+ method: "get"
591
+ }
592
+ }
593
+ });
594
+ expect(rootOf("launch-app")).toMatchObject({
595
+ type: "launch",
596
+ filePath: "app.exe"
597
+ });
598
+ expect(rootOf("gotor-file")).toMatchObject({
599
+ type: "goto-remote",
600
+ filePath: "other.pdf"
601
+ });
602
+ const chain = rootOf("chain-js-goto-hide");
603
+ expect(chain).toMatchObject({
604
+ type: "javascript",
605
+ script: "app.alert('chain');"
606
+ });
607
+ expect(chain?.next[0]).toMatchObject({
608
+ type: "goto",
609
+ destination: {
610
+ kind: "xyz",
611
+ pageObjectNumber: pon,
612
+ left: 5,
613
+ top: 10,
614
+ zoom: 1.25
615
+ }
616
+ });
617
+ expect(chain?.next[0]?.next[0]).toMatchObject({
618
+ type: "hide",
619
+ targets: [{
620
+ kind: "name",
621
+ name: "note1"
622
+ }],
623
+ hide: true
624
+ });
625
+ } finally {
626
+ await doc.close();
627
+ }
628
+ });
629
+ test("degrades unreadable payloads to unknown with payload-dropped", async () => {
630
+ const doc = await open(engine, opts, opts.fixtures.payloads);
631
+ try {
632
+ const page = (await doc.pages.list()).pages[0];
633
+ const snapshot = await doc.page(page.pageObjectNumber).annotations.list();
634
+ for (const [nm, subtype] of [
635
+ ["goto-malformed", "GoTo"],
636
+ ["hide-partial", "Hide"],
637
+ ["submit-not-url", "SubmitForm"],
638
+ ["submit-no-f", "SubmitForm"]
639
+ ]) {
640
+ const annotation = snapshot.annotations.find((candidate) => candidate.nm === nm);
641
+ const tree = annotation?.actions?.activate;
642
+ expect(tree?.root).toMatchObject({
643
+ type: "unknown",
644
+ subtype
645
+ });
646
+ expect(tree?.warnings ?? []).toContain("payload-dropped");
647
+ if (annotation?.subtype === "link") expect(annotation.target).toEqual({ kind: "unsupported" });
648
+ }
649
+ } finally {
650
+ await doc.close();
651
+ }
652
+ });
653
+ test("reads a destination-form /OpenAction as openDestination", async () => {
654
+ const doc = await open(engine, opts, opts.fixtures.openDestination);
655
+ try {
656
+ expect(Boolean(doc.actions)).toBe(true);
657
+ const snapshot = await doc.actions.read();
658
+ expect(require_schemas.DocumentActionsSnapshotSchema.safeParse(snapshot).success).toBe(true);
659
+ expect(snapshot.openAction).toBeNull();
660
+ const pon = (await doc.pages.list()).pages[0].pageObjectNumber;
661
+ expect(snapshot.openDestination).toEqual({
662
+ kind: "xyz",
663
+ pageObjectNumber: pon,
664
+ left: 10,
665
+ top: 700,
666
+ zoom: 1.5
667
+ });
668
+ } finally {
669
+ await doc.close();
670
+ }
671
+ });
460
672
  });
461
673
  }
462
674
  async function open(engine, opts, fixture) {
@@ -597,7 +809,7 @@ function runAnnotationMutationConformance(runner, opts) {
597
809
  if (engine) await engine.destroy();
598
810
  });
599
811
  test("create appends without shifting indices and leaves weak refs valid", async () => {
600
- const doc = await openFixture$12(engine, opts);
812
+ const doc = await openFixture$15(engine, opts);
601
813
  try {
602
814
  const page = doc.page(fix.pageObjectNumber);
603
815
  const before = await page.annotations.list();
@@ -633,7 +845,7 @@ function runAnnotationMutationConformance(runner, opts) {
633
845
  }
634
846
  });
635
847
  test("create + update honor annotation flags (set on create, merge on patch)", async () => {
636
- const doc = await openFixture$12(engine, opts);
848
+ const doc = await openFixture$15(engine, opts);
637
849
  try {
638
850
  const page = doc.page(fix.pageObjectNumber);
639
851
  const draft = {
@@ -668,7 +880,7 @@ function runAnnotationMutationConformance(runner, opts) {
668
880
  }
669
881
  });
670
882
  test("create shape annotations (circle + square) round-trip shape fields", async () => {
671
- const doc = await openFixture$12(engine, opts);
883
+ const doc = await openFixture$15(engine, opts);
672
884
  try {
673
885
  const page = doc.page(fix.pageObjectNumber);
674
886
  const circleDraft = {
@@ -749,7 +961,7 @@ function runAnnotationMutationConformance(runner, opts) {
749
961
  }
750
962
  });
751
963
  test("cloudy border (/BE) + rect differences (/RD) are tri-state", async () => {
752
- const doc = await openFixture$12(engine, opts);
964
+ const doc = await openFixture$15(engine, opts);
753
965
  try {
754
966
  const page = doc.page(fix.pageObjectNumber);
755
967
  const plainDraft = {
@@ -828,7 +1040,7 @@ function runAnnotationMutationConformance(runner, opts) {
828
1040
  }
829
1041
  });
830
1042
  test("update echoes the appearance verdict: moves preserve /AP, restyles re-bake", async () => {
831
- const doc = await openFixture$12(engine, opts);
1043
+ const doc = await openFixture$15(engine, opts);
832
1044
  try {
833
1045
  const page = doc.page(fix.pageObjectNumber);
834
1046
  const draft = {
@@ -903,7 +1115,7 @@ function runAnnotationMutationConformance(runner, opts) {
903
1115
  }
904
1116
  });
905
1117
  test("a partial /DA patch preserves the unpatched font members", async () => {
906
- const doc = await openFixture$12(engine, opts);
1118
+ const doc = await openFixture$15(engine, opts);
907
1119
  try {
908
1120
  const page = doc.page(fix.pageObjectNumber);
909
1121
  const draft = {
@@ -959,7 +1171,7 @@ function runAnnotationMutationConformance(runner, opts) {
959
1171
  }
960
1172
  });
961
1173
  test("box transform is tri-state: rect-only moves keep rotation, null flattens", async () => {
962
- const doc = await openFixture$12(engine, opts);
1174
+ const doc = await openFixture$15(engine, opts);
963
1175
  try {
964
1176
  const page = doc.page(fix.pageObjectNumber);
965
1177
  const draft = {
@@ -1020,7 +1232,7 @@ function runAnnotationMutationConformance(runner, opts) {
1020
1232
  }
1021
1233
  });
1022
1234
  test("create vertex + line annotations (polygon/polyline/line) round-trip geometry", async () => {
1023
- const doc = await openFixture$12(engine, opts);
1235
+ const doc = await openFixture$15(engine, opts);
1024
1236
  try {
1025
1237
  const page = doc.page(fix.pageObjectNumber);
1026
1238
  const polygonDraft = {
@@ -1151,7 +1363,7 @@ function runAnnotationMutationConformance(runner, opts) {
1151
1363
  }
1152
1364
  });
1153
1365
  test("create free-text + callout round-trip text/colour/intent fields", async () => {
1154
- const doc = await openFixture$12(engine, opts);
1366
+ const doc = await openFixture$15(engine, opts);
1155
1367
  try {
1156
1368
  const page = doc.page(fix.pageObjectNumber);
1157
1369
  const freeTextDraft = {
@@ -1247,7 +1459,7 @@ function runAnnotationMutationConformance(runner, opts) {
1247
1459
  }
1248
1460
  });
1249
1461
  test("update a free-text patches alignment + colours and is non-structural", async () => {
1250
- const doc = await openFixture$12(engine, opts);
1462
+ const doc = await openFixture$15(engine, opts);
1251
1463
  try {
1252
1464
  const page = doc.page(fix.pageObjectNumber);
1253
1465
  const created = await page.annotations.create({
@@ -1308,7 +1520,7 @@ function runAnnotationMutationConformance(runner, opts) {
1308
1520
  }
1309
1521
  });
1310
1522
  test("create redact (area + text) round-trips label + colour fields", async () => {
1311
- const doc = await openFixture$12(engine, opts);
1523
+ const doc = await openFixture$15(engine, opts);
1312
1524
  try {
1313
1525
  const page = doc.page(fix.pageObjectNumber);
1314
1526
  const areaDraft = {
@@ -1388,7 +1600,7 @@ function runAnnotationMutationConformance(runner, opts) {
1388
1600
  }
1389
1601
  });
1390
1602
  test("update a redact patches the label and clears it, non-structurally", async () => {
1391
- const doc = await openFixture$12(engine, opts);
1603
+ const doc = await openFixture$15(engine, opts);
1392
1604
  try {
1393
1605
  const page = doc.page(fix.pageObjectNumber);
1394
1606
  const created = await page.annotations.create({
@@ -1465,7 +1677,7 @@ function runAnnotationMutationConformance(runner, opts) {
1465
1677
  }
1466
1678
  });
1467
1679
  test("create + update a caret round-trips color/opacity/rect differences", async () => {
1468
- const doc = await openFixture$12(engine, opts);
1680
+ const doc = await openFixture$15(engine, opts);
1469
1681
  try {
1470
1682
  const page = doc.page(fix.pageObjectNumber);
1471
1683
  const caretDraft = {
@@ -1545,7 +1757,7 @@ function runAnnotationMutationConformance(runner, opts) {
1545
1757
  }
1546
1758
  });
1547
1759
  test("update an ink annotation patches strokes + color and is non-structural", async () => {
1548
- const doc = await openFixture$12(engine, opts);
1760
+ const doc = await openFixture$15(engine, opts);
1549
1761
  try {
1550
1762
  const page = doc.page(fix.pageObjectNumber);
1551
1763
  const created = await page.annotations.create({
@@ -1596,7 +1808,7 @@ function runAnnotationMutationConformance(runner, opts) {
1596
1808
  }
1597
1809
  });
1598
1810
  test("ink highlight intent + blend round-trip and unrelated patches preserve blend", async () => {
1599
- const doc = await openFixture$12(engine, opts);
1811
+ const doc = await openFixture$15(engine, opts);
1600
1812
  try {
1601
1813
  const page = doc.page(fix.pageObjectNumber);
1602
1814
  const created = await page.annotations.create({
@@ -1639,7 +1851,7 @@ function runAnnotationMutationConformance(runner, opts) {
1639
1851
  }
1640
1852
  });
1641
1853
  test("update a polyline patches vertices + line endings and is non-structural", async () => {
1642
- const doc = await openFixture$12(engine, opts);
1854
+ const doc = await openFixture$15(engine, opts);
1643
1855
  try {
1644
1856
  const page = doc.page(fix.pageObjectNumber);
1645
1857
  const created = await page.annotations.create({
@@ -1688,7 +1900,7 @@ function runAnnotationMutationConformance(runner, opts) {
1688
1900
  }
1689
1901
  });
1690
1902
  test("update a shape annotation patches color and is non-structural", async () => {
1691
- const doc = await openFixture$12(engine, opts);
1903
+ const doc = await openFixture$15(engine, opts);
1692
1904
  try {
1693
1905
  const page = doc.page(fix.pageObjectNumber);
1694
1906
  const created = await page.annotations.create({
@@ -1737,7 +1949,7 @@ function runAnnotationMutationConformance(runner, opts) {
1737
1949
  }
1738
1950
  });
1739
1951
  if (opts.supportsAppearanceRasters) test("creating a shape bakes an /AP appearance the reader can rasterize", async () => {
1740
- const doc = await openFixture$12(engine, opts);
1952
+ const doc = await openFixture$15(engine, opts);
1741
1953
  try {
1742
1954
  const page = doc.page(fix.pageObjectNumber);
1743
1955
  const created = await page.annotations.create({
@@ -1767,7 +1979,7 @@ function runAnnotationMutationConformance(runner, opts) {
1767
1979
  }
1768
1980
  });
1769
1981
  test("update on a durable annotation is non-structural and never touches /NM", async () => {
1770
- const doc = await openFixture$12(engine, opts);
1982
+ const doc = await openFixture$15(engine, opts);
1771
1983
  try {
1772
1984
  const page = doc.page(fix.pageObjectNumber);
1773
1985
  const before = await page.annotations.list();
@@ -1793,7 +2005,7 @@ function runAnnotationMutationConformance(runner, opts) {
1793
2005
  }
1794
2006
  });
1795
2007
  if (fix.expectsWeakAnnotation) test("update on a weak annotation stamps a UUID v4 /NM and upgrades the ref", async () => {
1796
- const doc = await openFixture$12(engine, opts);
2008
+ const doc = await openFixture$15(engine, opts);
1797
2009
  try {
1798
2010
  const page = doc.page(fix.pageObjectNumber);
1799
2011
  const before = await page.annotations.list();
@@ -1823,7 +2035,7 @@ function runAnnotationMutationConformance(runner, opts) {
1823
2035
  }
1824
2036
  });
1825
2037
  test("delete by objectNumber removes the annotation and reports a stable id", async () => {
1826
- const doc = await openFixture$12(engine, opts);
2038
+ const doc = await openFixture$15(engine, opts);
1827
2039
  try {
1828
2040
  const page = doc.page(fix.pageObjectNumber);
1829
2041
  const draft = {
@@ -1853,7 +2065,7 @@ function runAnnotationMutationConformance(runner, opts) {
1853
2065
  }
1854
2066
  });
1855
2067
  if (fix.expectsWeakAnnotation) test("delete by index of a weak annotation reports deleted: null and refetch reason", async () => {
1856
- const doc = await openFixture$12(engine, opts);
2068
+ const doc = await openFixture$15(engine, opts);
1857
2069
  try {
1858
2070
  const page = doc.page(fix.pageObjectNumber);
1859
2071
  const weak = (await page.annotations.list()).annotations.find((a) => a.identityQuality === "weak");
@@ -1876,7 +2088,7 @@ function runAnnotationMutationConformance(runner, opts) {
1876
2088
  }
1877
2089
  });
1878
2090
  test("abort on create rejects with AbortError", async () => {
1879
- const doc = await openFixture$12(engine, opts);
2091
+ const doc = await openFixture$15(engine, opts);
1880
2092
  try {
1881
2093
  const page = doc.page(fix.pageObjectNumber);
1882
2094
  const draft = {
@@ -1892,7 +2104,7 @@ function runAnnotationMutationConformance(runner, opts) {
1892
2104
  }
1893
2105
  });
1894
2106
  test("update with a stale index revision throws InvalidReference", async () => {
1895
- const doc = await openFixture$12(engine, opts);
2107
+ const doc = await openFixture$15(engine, opts);
1896
2108
  try {
1897
2109
  const page = doc.page(fix.pageObjectNumber);
1898
2110
  const weak = (await page.annotations.list()).annotations.find((a) => a.identityQuality === "weak");
@@ -1913,13 +2125,13 @@ function runAnnotationMutationConformance(runner, opts) {
1913
2125
  } catch (err) {
1914
2126
  caught = err;
1915
2127
  }
1916
- expect(require_fold.EngineError.is(caught, require_subtype.EngineErrorCode.InvalidReference)).toBe(true);
2128
+ expect(require_fold.EngineError.is(caught, require_PageInsertBlankInput.EngineErrorCode.InvalidReference)).toBe(true);
1917
2129
  } finally {
1918
2130
  await doc.close();
1919
2131
  }
1920
2132
  });
1921
2133
  test("move single durable annotation reorders within the page (single-as-batch)", async () => {
1922
- const doc = await openFixture$12(engine, opts);
2134
+ const doc = await openFixture$15(engine, opts);
1923
2135
  try {
1924
2136
  const page = doc.page(fix.pageObjectNumber);
1925
2137
  const aDraft = {
@@ -1963,7 +2175,7 @@ function runAnnotationMutationConformance(runner, opts) {
1963
2175
  }
1964
2176
  });
1965
2177
  test("move multi-block preserves caller-supplied order at the destination", async () => {
1966
- const doc = await openFixture$12(engine, opts);
2178
+ const doc = await openFixture$15(engine, opts);
1967
2179
  try {
1968
2180
  const page = doc.page(fix.pageObjectNumber);
1969
2181
  const ids = await Promise.all([
@@ -2002,7 +2214,7 @@ function runAnnotationMutationConformance(runner, opts) {
2002
2214
  }
2003
2215
  });
2004
2216
  if (fix.expectsWeakAnnotation) test("move on a weak annotation upgrades it to durable /NM (one rev bump for batch)", async () => {
2005
- const doc = await openFixture$12(engine, opts);
2217
+ const doc = await openFixture$15(engine, opts);
2006
2218
  try {
2007
2219
  const page = doc.page(fix.pageObjectNumber);
2008
2220
  const before = await page.annotations.list();
@@ -2027,7 +2239,7 @@ function runAnnotationMutationConformance(runner, opts) {
2027
2239
  }
2028
2240
  });
2029
2241
  test("move with a stale index revision rejects (locked rev-token guard)", async () => {
2030
- const doc = await openFixture$12(engine, opts);
2242
+ const doc = await openFixture$15(engine, opts);
2031
2243
  try {
2032
2244
  const page = doc.page(fix.pageObjectNumber);
2033
2245
  const a = await page.annotations.create({
@@ -2059,13 +2271,13 @@ function runAnnotationMutationConformance(runner, opts) {
2059
2271
  } finally {
2060
2272
  await weakSession?.release();
2061
2273
  }
2062
- expect(require_fold.EngineError.is(caught, require_subtype.EngineErrorCode.InvalidReference)).toBe(true);
2274
+ expect(require_fold.EngineError.is(caught, require_PageInsertBlankInput.EngineErrorCode.InvalidReference)).toBe(true);
2063
2275
  } finally {
2064
2276
  await doc.close();
2065
2277
  }
2066
2278
  });
2067
2279
  test("move with out-of-range toIndex rejects with InvalidArg", async () => {
2068
- const doc = await openFixture$12(engine, opts);
2280
+ const doc = await openFixture$15(engine, opts);
2069
2281
  try {
2070
2282
  const page = doc.page(fix.pageObjectNumber);
2071
2283
  const a = await page.annotations.create({
@@ -2083,13 +2295,13 @@ function runAnnotationMutationConformance(runner, opts) {
2083
2295
  } finally {
2084
2296
  await weakSession?.release();
2085
2297
  }
2086
- expect(require_fold.EngineError.is(caught, require_subtype.EngineErrorCode.InvalidArg)).toBe(true);
2298
+ expect(require_fold.EngineError.is(caught, require_PageInsertBlankInput.EngineErrorCode.InvalidArg)).toBe(true);
2087
2299
  } finally {
2088
2300
  await doc.close();
2089
2301
  }
2090
2302
  });
2091
2303
  test("move with duplicate refs rejects with InvalidArg", async () => {
2092
- const doc = await openFixture$12(engine, opts);
2304
+ const doc = await openFixture$15(engine, opts);
2093
2305
  try {
2094
2306
  const page = doc.page(fix.pageObjectNumber);
2095
2307
  const a = await page.annotations.create({
@@ -2106,13 +2318,13 @@ function runAnnotationMutationConformance(runner, opts) {
2106
2318
  } finally {
2107
2319
  await weakSession?.release();
2108
2320
  }
2109
- expect(require_fold.EngineError.is(caught, require_subtype.EngineErrorCode.InvalidArg)).toBe(true);
2321
+ expect(require_fold.EngineError.is(caught, require_PageInsertBlankInput.EngineErrorCode.InvalidArg)).toBe(true);
2110
2322
  } finally {
2111
2323
  await doc.close();
2112
2324
  }
2113
2325
  });
2114
2326
  test("abort on move rejects with AbortError", async () => {
2115
- const doc = await openFixture$12(engine, opts);
2327
+ const doc = await openFixture$15(engine, opts);
2116
2328
  try {
2117
2329
  const page = doc.page(fix.pageObjectNumber);
2118
2330
  const a = await page.annotations.create({
@@ -2133,7 +2345,7 @@ function runAnnotationMutationConformance(runner, opts) {
2133
2345
  }
2134
2346
  });
2135
2347
  test("a page move does NOT bump per-page RevisionTokens (weak refs survive reorder)", async () => {
2136
- const doc = await openFixture$12(engine, opts);
2348
+ const doc = await openFixture$15(engine, opts);
2137
2349
  try {
2138
2350
  const list = await doc.pages.list();
2139
2351
  if (list.pages.length < 1) return;
@@ -2148,7 +2360,7 @@ function runAnnotationMutationConformance(runner, opts) {
2148
2360
  }
2149
2361
  });
2150
2362
  test("create a reply links /IRT and defaults /RT to \"reply\", reporting the parent", async () => {
2151
- const doc = await openFixture$12(engine, opts);
2363
+ const doc = await openFixture$15(engine, opts);
2152
2364
  try {
2153
2365
  const page = doc.page(fix.pageObjectNumber);
2154
2366
  const parent = await page.annotations.create({
@@ -2182,7 +2394,7 @@ function runAnnotationMutationConformance(runner, opts) {
2182
2394
  }
2183
2395
  });
2184
2396
  test("create a grouped subordinate writes /RT /Group", async () => {
2185
- const doc = await openFixture$12(engine, opts);
2397
+ const doc = await openFixture$15(engine, opts);
2186
2398
  try {
2187
2399
  const page = doc.page(fix.pageObjectNumber);
2188
2400
  const primary = await page.annotations.create({
@@ -2213,7 +2425,7 @@ function runAnnotationMutationConformance(runner, opts) {
2213
2425
  }
2214
2426
  });
2215
2427
  test("create link annotations round-trip uri, goto/xyz, goto/fitH, and dead targets", async () => {
2216
- const doc = await openFixture$12(engine, opts);
2428
+ const doc = await openFixture$15(engine, opts);
2217
2429
  try {
2218
2430
  const page = doc.page(fix.pageObjectNumber);
2219
2431
  const uri = await page.annotations.create({
@@ -2290,7 +2502,7 @@ function runAnnotationMutationConformance(runner, opts) {
2290
2502
  }
2291
2503
  });
2292
2504
  test("a link patch retargets in both directions (uri→goto, goto→uri) and moves the rect", async () => {
2293
- const doc = await openFixture$12(engine, opts);
2505
+ const doc = await openFixture$15(engine, opts);
2294
2506
  try {
2295
2507
  const page = doc.page(fix.pageObjectNumber);
2296
2508
  const created = await page.annotations.create({
@@ -2345,7 +2557,7 @@ function runAnnotationMutationConformance(runner, opts) {
2345
2557
  }
2346
2558
  });
2347
2559
  test("a link patch clears the target with null — a dead link on re-read", async () => {
2348
- const doc = await openFixture$12(engine, opts);
2560
+ const doc = await openFixture$15(engine, opts);
2349
2561
  try {
2350
2562
  const page = doc.page(fix.pageObjectNumber);
2351
2563
  const created = await page.annotations.create({
@@ -2384,7 +2596,7 @@ function runAnnotationMutationConformance(runner, opts) {
2384
2596
  }
2385
2597
  });
2386
2598
  test("a link grouped to a highlight round-trips /IRT + /RT /Group with its target", async () => {
2387
- const doc = await openFixture$12(engine, opts);
2599
+ const doc = await openFixture$15(engine, opts);
2388
2600
  try {
2389
2601
  const page = doc.page(fix.pageObjectNumber);
2390
2602
  const parent = await page.annotations.create({
@@ -2418,7 +2630,7 @@ function runAnnotationMutationConformance(runner, opts) {
2418
2630
  }
2419
2631
  });
2420
2632
  test("replace-text round-trips /IT and groups StrikeOut under its Caret", async () => {
2421
- const doc = await openFixture$12(engine, opts);
2633
+ const doc = await openFixture$15(engine, opts);
2422
2634
  try {
2423
2635
  const page = doc.page(fix.pageObjectNumber);
2424
2636
  const caret = await page.annotations.create({
@@ -2470,7 +2682,7 @@ function runAnnotationMutationConformance(runner, opts) {
2470
2682
  }
2471
2683
  });
2472
2684
  test("patch inReplyTo: null clears /IRT and /RT (back to top-level)", async () => {
2473
- const doc = await openFixture$12(engine, opts);
2685
+ const doc = await openFixture$15(engine, opts);
2474
2686
  try {
2475
2687
  const page = doc.page(fix.pageObjectNumber);
2476
2688
  const parent = await page.annotations.create({
@@ -2497,7 +2709,7 @@ function runAnnotationMutationConformance(runner, opts) {
2497
2709
  }
2498
2710
  });
2499
2711
  test("create with a cross-page /IRT parent throws InvalidArg", async () => {
2500
- const doc = await openFixture$12(engine, opts);
2712
+ const doc = await openFixture$15(engine, opts);
2501
2713
  try {
2502
2714
  const page = doc.page(fix.pageObjectNumber);
2503
2715
  const parent = await page.annotations.create({
@@ -2522,7 +2734,130 @@ function runAnnotationMutationConformance(runner, opts) {
2522
2734
  } catch (err) {
2523
2735
  caught = err;
2524
2736
  }
2525
- expect(require_fold.EngineError.is(caught, require_subtype.EngineErrorCode.InvalidArg)).toBe(true);
2737
+ expect(require_fold.EngineError.is(caught, require_PageInsertBlankInput.EngineErrorCode.InvalidArg)).toBe(true);
2738
+ } finally {
2739
+ await doc.close();
2740
+ }
2741
+ });
2742
+ test("a review-status reply round-trips /State + /StateModel + /Subj", async () => {
2743
+ const doc = await openFixture$15(engine, opts);
2744
+ try {
2745
+ const page = doc.page(fix.pageObjectNumber);
2746
+ const target = await page.annotations.create({
2747
+ subtype: "highlight",
2748
+ contents: "status target",
2749
+ subject: "Pricing question",
2750
+ quadPoints: quad
2751
+ });
2752
+ expect(target.created.subject).toBe("Pricing question");
2753
+ const status = await page.annotations.create({
2754
+ subtype: "text",
2755
+ rect: shapeRect,
2756
+ inReplyTo: target.created.ref,
2757
+ state: "accepted",
2758
+ stateModel: "review"
2759
+ });
2760
+ expect(require_schemas.AnnotationCreateResultSchema.safeParse(status).success).toBe(true);
2761
+ expect(status.created.subtype).toBe("text");
2762
+ if (status.created.subtype !== "text") return;
2763
+ expect(status.created.state).toBe("accepted");
2764
+ expect(status.created.stateModel).toBe("review");
2765
+ expect(status.created.replyType).toBe("reply");
2766
+ 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);
2767
+ expect(read?.subtype).toBe("text");
2768
+ if (read?.subtype === "text") {
2769
+ expect(read.state).toBe("accepted");
2770
+ expect(read.stateModel).toBe("review");
2771
+ }
2772
+ } finally {
2773
+ await doc.close();
2774
+ }
2775
+ });
2776
+ test("a draft /State without /StateModel is rejected with InvalidArg", async () => {
2777
+ const doc = await openFixture$15(engine, opts);
2778
+ try {
2779
+ const page = doc.page(fix.pageObjectNumber);
2780
+ let caught;
2781
+ try {
2782
+ await page.annotations.create({
2783
+ subtype: "text",
2784
+ rect: shapeRect,
2785
+ state: "accepted"
2786
+ });
2787
+ } catch (err) {
2788
+ caught = err;
2789
+ }
2790
+ expect(require_fold.EngineError.is(caught, require_PageInsertBlankInput.EngineErrorCode.InvalidArg)).toBe(true);
2791
+ } finally {
2792
+ await doc.close();
2793
+ }
2794
+ });
2795
+ test("null-clear patches truly remove /State, /StateModel, /Subj and /Contents", async () => {
2796
+ const doc = await openFixture$15(engine, opts);
2797
+ try {
2798
+ const page = doc.page(fix.pageObjectNumber);
2799
+ const note = await page.annotations.create({
2800
+ subtype: "text",
2801
+ rect: shapeRect,
2802
+ contents: "work in progress",
2803
+ subject: "Draft",
2804
+ state: "none",
2805
+ stateModel: "review"
2806
+ });
2807
+ const cycled = await page.annotations.update(note.created.ref, {
2808
+ subtype: "text",
2809
+ state: "rejected"
2810
+ });
2811
+ expect(cycled.appearance.changed).toBe(false);
2812
+ expect(cycled.updated.subtype).toBe("text");
2813
+ if (cycled.updated.subtype === "text") {
2814
+ expect(cycled.updated.state).toBe("rejected");
2815
+ expect(cycled.updated.stateModel).toBe("review");
2816
+ }
2817
+ const cleared = await page.annotations.update(note.created.ref, {
2818
+ subtype: "text",
2819
+ contents: null,
2820
+ subject: null,
2821
+ state: null,
2822
+ stateModel: null
2823
+ });
2824
+ expect(cleared.appearance.changed).toBe(false);
2825
+ expect(cleared.updated.contents).toBe(null);
2826
+ expect(cleared.updated.subject).toBe(null);
2827
+ if (cleared.updated.subtype === "text") {
2828
+ expect(cleared.updated.state).toBe(null);
2829
+ expect(cleared.updated.stateModel).toBe(null);
2830
+ }
2831
+ 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);
2832
+ expect(read?.contents).toBe(null);
2833
+ expect(read?.subject).toBe(null);
2834
+ if (read?.subtype === "text") {
2835
+ expect(read.state).toBe(null);
2836
+ expect(read.stateModel).toBe(null);
2837
+ }
2838
+ } finally {
2839
+ await doc.close();
2840
+ }
2841
+ });
2842
+ test("custom state models round-trip verbatim", async () => {
2843
+ const doc = await openFixture$15(engine, opts);
2844
+ try {
2845
+ const page = doc.page(fix.pageObjectNumber);
2846
+ const custom = await page.annotations.create({
2847
+ subtype: "text",
2848
+ rect: shapeRect,
2849
+ state: "in-progress",
2850
+ stateModel: "X-ReviewWorkflow"
2851
+ });
2852
+ expect(custom.created.subtype).toBe("text");
2853
+ if (custom.created.subtype !== "text") return;
2854
+ expect(custom.created.state).toBe("in-progress");
2855
+ expect(custom.created.stateModel).toBe("X-ReviewWorkflow");
2856
+ 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);
2857
+ if (read?.subtype === "text") {
2858
+ expect(read.state).toBe("in-progress");
2859
+ expect(read.stateModel).toBe("X-ReviewWorkflow");
2860
+ }
2526
2861
  } finally {
2527
2862
  await doc.close();
2528
2863
  }
@@ -2533,7 +2868,7 @@ async function beginWeakEditIfRequired(doc, pageObjectNumber, fix) {
2533
2868
  if (doc.capabilities.weakAnnotationEditSessions !== "required" || !fix.expectsWeakAnnotation) return null;
2534
2869
  return doc.annotations.beginWeakEdit([pageObjectNumber]);
2535
2870
  }
2536
- async function openFixture$12(engine, opts) {
2871
+ async function openFixture$15(engine, opts) {
2537
2872
  if (opts.openKind === "bytes") {
2538
2873
  const bytes = await opts.fixture.bytes();
2539
2874
  return engine.open({
@@ -2699,7 +3034,7 @@ function runFormConformance(runner, opts) {
2699
3034
  await expect(doc.forms.setValue(radioRef, {
2700
3035
  type: "toggle",
2701
3036
  state: null
2702
- })).rejects.toMatchObject({ code: require_subtype.EngineErrorCode.InvalidArg });
3037
+ })).rejects.toMatchObject({ code: require_PageInsertBlankInput.EngineErrorCode.InvalidArg });
2703
3038
  const check = await doc.forms.setValue({
2704
3039
  kind: "fqn",
2705
3040
  name: "opt_check"
@@ -2727,7 +3062,7 @@ function runFormConformance(runner, opts) {
2727
3062
  }, {
2728
3063
  type: "toggle",
2729
3064
  state: "On"
2730
- })).rejects.toMatchObject({ code: require_subtype.EngineErrorCode.InvalidArg });
3065
+ })).rejects.toMatchObject({ code: require_PageInsertBlankInput.EngineErrorCode.InvalidArg });
2731
3066
  const field = await doc.forms.get({
2732
3067
  kind: "fqn",
2733
3068
  name: "maxlen_text"
@@ -2831,7 +3166,7 @@ function runFormConformance(runner, opts) {
2831
3166
  }, {
2832
3167
  type: "choice",
2833
3168
  values: ["foo", "bar"]
2834
- })).rejects.toMatchObject({ code: require_subtype.EngineErrorCode.InvalidArg });
3169
+ })).rejects.toMatchObject({ code: require_PageInsertBlankInput.EngineErrorCode.InvalidArg });
2835
3170
  } finally {
2836
3171
  await doc.close();
2837
3172
  }
@@ -2901,7 +3236,7 @@ function runFormConformance(runner, opts) {
2901
3236
  test("rejects garbage import payloads", async () => {
2902
3237
  const doc = await open(opts.fixtures.toggleFields);
2903
3238
  try {
2904
- await expect(doc.forms.importData(new TextEncoder().encode("not a form payload"))).rejects.toMatchObject({ code: require_subtype.EngineErrorCode.InvalidArg });
3239
+ await expect(doc.forms.importData(new TextEncoder().encode("not a form payload"))).rejects.toMatchObject({ code: require_PageInsertBlankInput.EngineErrorCode.InvalidArg });
2905
3240
  } finally {
2906
3241
  await doc.close();
2907
3242
  }
@@ -3008,7 +3343,7 @@ function runFormConformance(runner, opts) {
3008
3343
  g: 247,
3009
3344
  b: 219
3010
3345
  });
3011
- await expect(page.annotations.delete(widgetRef)).rejects.toMatchObject({ code: require_subtype.EngineErrorCode.InvalidArg });
3346
+ await expect(page.annotations.delete(widgetRef)).rejects.toMatchObject({ code: require_PageInsertBlankInput.EngineErrorCode.InvalidArg });
3012
3347
  await doc.forms.detachWidget(field.field.ref, {
3013
3348
  annotObjectNumber: widgetRef.annotObjectNumber,
3014
3349
  pageObjectNumber: pon
@@ -3072,7 +3407,7 @@ function runFormConformance(runner, opts) {
3072
3407
  await expect(doc.forms.updateField(created.field.ref, {
3073
3408
  family: "radio",
3074
3409
  name: "unison_radio"
3075
- })).rejects.toMatchObject({ code: require_subtype.EngineErrorCode.InvalidArg });
3410
+ })).rejects.toMatchObject({ code: require_PageInsertBlankInput.EngineErrorCode.InvalidArg });
3076
3411
  const before = await doc.page(pon).annotations.list();
3077
3412
  const removed = await doc.forms.deleteField(created.field.ref);
3078
3413
  expect(removed.removedWidgets.length).toBe(2);
@@ -3081,7 +3416,7 @@ function runFormConformance(runner, opts) {
3081
3416
  await expect(doc.forms.get({
3082
3417
  kind: "fqn",
3083
3418
  name: "renamed_radio"
3084
- })).rejects.toMatchObject({ code: require_subtype.EngineErrorCode.NotFound });
3419
+ })).rejects.toMatchObject({ code: require_PageInsertBlankInput.EngineErrorCode.NotFound });
3085
3420
  } finally {
3086
3421
  await doc.close();
3087
3422
  }
@@ -3092,7 +3427,7 @@ function runFormConformance(runner, opts) {
3092
3427
  await expect(doc.forms.get({
3093
3428
  kind: "fqn",
3094
3429
  name: "no.such.field"
3095
- })).rejects.toMatchObject({ code: require_subtype.EngineErrorCode.NotFound });
3430
+ })).rejects.toMatchObject({ code: require_PageInsertBlankInput.EngineErrorCode.NotFound });
3096
3431
  } finally {
3097
3432
  await doc.close();
3098
3433
  }
@@ -3123,7 +3458,7 @@ function runAnnotationAppearanceConformance(runner, opts) {
3123
3458
  if (engine) await engine.destroy();
3124
3459
  });
3125
3460
  test("renders the expected set of appearances with valid output", async () => {
3126
- const doc = await openFixture$11(engine, opts);
3461
+ const doc = await openFixture$14(engine, opts);
3127
3462
  try {
3128
3463
  const { pageState, appearances } = await collect(doc, opts, useRaw);
3129
3464
  expect(pageState.pageObjectNumber).toBe(opts.fixture.pageObjectNumber);
@@ -3150,7 +3485,7 @@ function runAnnotationAppearanceConformance(runner, opts) {
3150
3485
  }
3151
3486
  });
3152
3487
  if (opts.fixture.expectsWeakAppearance) test("weak (index-only) annotations are emitted, not dropped", async () => {
3153
- const doc = await openFixture$11(engine, opts);
3488
+ const doc = await openFixture$14(engine, opts);
3154
3489
  try {
3155
3490
  const { appearances } = await collect(doc, opts, useRaw);
3156
3491
  const weak = appearances.find((a) => a.ref.kind === "index");
@@ -3160,7 +3495,7 @@ function runAnnotationAppearanceConformance(runner, opts) {
3160
3495
  }
3161
3496
  });
3162
3497
  if (useRaw) test("rendered appearances are not blank (non-zero alpha)", async () => {
3163
- const doc = await openFixture$11(engine, opts);
3498
+ const doc = await openFixture$14(engine, opts);
3164
3499
  try {
3165
3500
  const { appearances } = await collect(doc, opts, useRaw);
3166
3501
  const anyVisible = appearances.some((a) => a.raster !== null && hasOpaquePixel(a.raster));
@@ -3170,7 +3505,7 @@ function runAnnotationAppearanceConformance(runner, opts) {
3170
3505
  }
3171
3506
  });
3172
3507
  test("abort() rejects with AbortError", async () => {
3173
- const doc = await openFixture$11(engine, opts);
3508
+ const doc = await openFixture$14(engine, opts);
3174
3509
  try {
3175
3510
  const page = doc.page(opts.fixture.pageObjectNumber);
3176
3511
  const p = useRaw ? page.annotations.renderAppearances({ scale: 1 }) : page.annotations.renderAppearanceImages({
@@ -3225,7 +3560,7 @@ function hasOpaquePixel(raster) {
3225
3560
  }
3226
3561
  return false;
3227
3562
  }
3228
- async function openFixture$11(engine, opts) {
3563
+ async function openFixture$14(engine, opts) {
3229
3564
  if (opts.openKind === "bytes") {
3230
3565
  const bytes = await opts.fixture.bytes();
3231
3566
  return engine.open({
@@ -3294,7 +3629,7 @@ function runPageReorderConformance(runner, opts) {
3294
3629
  if (engine) await engine.destroy();
3295
3630
  });
3296
3631
  test("pages.list() returns every page in display order, durable PONs only", async () => {
3297
- const doc = await openFixture$10(engine, opts);
3632
+ const doc = await openFixture$13(engine, opts);
3298
3633
  try {
3299
3634
  const list = await doc.pages.list();
3300
3635
  expect(require_schemas.PageListSnapshotSchema.safeParse(list).success).toBe(true);
@@ -3313,7 +3648,7 @@ function runPageReorderConformance(runner, opts) {
3313
3648
  }
3314
3649
  });
3315
3650
  test("pages.move() reorders pages and returns the full post-move order", async () => {
3316
- const doc = await openFixture$10(engine, opts);
3651
+ const doc = await openFixture$13(engine, opts);
3317
3652
  try {
3318
3653
  const before = await doc.pages.list();
3319
3654
  if (before.pages.length < 3) return;
@@ -3337,7 +3672,7 @@ function runPageReorderConformance(runner, opts) {
3337
3672
  }
3338
3673
  });
3339
3674
  test("weak index-based annotation refs survive a page reorder", async () => {
3340
- const doc = await openFixture$10(engine, opts);
3675
+ const doc = await openFixture$13(engine, opts);
3341
3676
  try {
3342
3677
  const list = await doc.pages.list();
3343
3678
  if (list.pages.length < 2) return;
@@ -3374,7 +3709,7 @@ function runPageReorderConformance(runner, opts) {
3374
3709
  }
3375
3710
  });
3376
3711
  test("pages.move() rejects duplicate PONs with InvalidArg", async () => {
3377
- const doc = await openFixture$10(engine, opts);
3712
+ const doc = await openFixture$13(engine, opts);
3378
3713
  try {
3379
3714
  const list = await doc.pages.list();
3380
3715
  if (list.pages.length < 1) return;
@@ -3385,13 +3720,13 @@ function runPageReorderConformance(runner, opts) {
3385
3720
  } catch (err) {
3386
3721
  caught = err;
3387
3722
  }
3388
- expect(require_fold.EngineError.is(caught, require_subtype.EngineErrorCode.InvalidArg)).toBe(true);
3723
+ expect(require_fold.EngineError.is(caught, require_PageInsertBlankInput.EngineErrorCode.InvalidArg)).toBe(true);
3389
3724
  } finally {
3390
3725
  await doc.close();
3391
3726
  }
3392
3727
  });
3393
3728
  test("pages.move() rejects out-of-range destIndex with InvalidArg", async () => {
3394
- const doc = await openFixture$10(engine, opts);
3729
+ const doc = await openFixture$13(engine, opts);
3395
3730
  try {
3396
3731
  const list = await doc.pages.list();
3397
3732
  if (list.pages.length < 1) return;
@@ -3402,13 +3737,13 @@ function runPageReorderConformance(runner, opts) {
3402
3737
  } catch (err) {
3403
3738
  caught = err;
3404
3739
  }
3405
- expect(require_fold.EngineError.is(caught, require_subtype.EngineErrorCode.InvalidArg)).toBe(true);
3740
+ expect(require_fold.EngineError.is(caught, require_PageInsertBlankInput.EngineErrorCode.InvalidArg)).toBe(true);
3406
3741
  } finally {
3407
3742
  await doc.close();
3408
3743
  }
3409
3744
  });
3410
3745
  test("pages.move() rejects unknown PON with NotFound or InvalidArg", async () => {
3411
- const doc = await openFixture$10(engine, opts);
3746
+ const doc = await openFixture$13(engine, opts);
3412
3747
  try {
3413
3748
  const list = await doc.pages.list();
3414
3749
  let bogus = 0;
@@ -3420,13 +3755,13 @@ function runPageReorderConformance(runner, opts) {
3420
3755
  } catch (err) {
3421
3756
  caught = err;
3422
3757
  }
3423
- expect(require_fold.EngineError.is(caught, require_subtype.EngineErrorCode.NotFound) || require_fold.EngineError.is(caught, require_subtype.EngineErrorCode.InvalidArg)).toBe(true);
3758
+ expect(require_fold.EngineError.is(caught, require_PageInsertBlankInput.EngineErrorCode.NotFound) || require_fold.EngineError.is(caught, require_PageInsertBlankInput.EngineErrorCode.InvalidArg)).toBe(true);
3424
3759
  } finally {
3425
3760
  await doc.close();
3426
3761
  }
3427
3762
  });
3428
3763
  test("abort on pages.move() rejects with AbortError", async () => {
3429
- const doc = await openFixture$10(engine, opts);
3764
+ const doc = await openFixture$13(engine, opts);
3430
3765
  try {
3431
3766
  const list = await doc.pages.list();
3432
3767
  if (list.pages.length < 1) return;
@@ -3440,7 +3775,7 @@ function runPageReorderConformance(runner, opts) {
3440
3775
  });
3441
3776
  });
3442
3777
  }
3443
- async function openFixture$10(engine, opts) {
3778
+ async function openFixture$13(engine, opts) {
3444
3779
  if (opts.openKind === "bytes") {
3445
3780
  const bytes = await opts.fixture.bytes();
3446
3781
  return engine.open({
@@ -3514,7 +3849,7 @@ function runPageRotateConformance(runner, opts) {
3514
3849
  if (engine) await engine.destroy();
3515
3850
  });
3516
3851
  test("pages.rotate() sets the absolute rotation and returns the full layout", async () => {
3517
- const doc = await openFixture$9(engine, opts);
3852
+ const doc = await openFixture$12(engine, opts);
3518
3853
  try {
3519
3854
  const before = await doc.pages.list();
3520
3855
  const target = before.pages[0];
@@ -3533,7 +3868,7 @@ function runPageRotateConformance(runner, opts) {
3533
3868
  }
3534
3869
  });
3535
3870
  test("absolute rotation is idempotent: setting the same value twice is a no-op", async () => {
3536
- const doc = await openFixture$9(engine, opts);
3871
+ const doc = await openFixture$12(engine, opts);
3537
3872
  try {
3538
3873
  const pon = (await doc.pages.list()).pages[0].pageObjectNumber;
3539
3874
  const first = await doc.pages.rotate([pon], 180);
@@ -3544,7 +3879,7 @@ function runPageRotateConformance(runner, opts) {
3544
3879
  }
3545
3880
  });
3546
3881
  test("one rotation applies to EVERY listed page (the multi-select gesture)", async () => {
3547
- const doc = await openFixture$9(engine, opts);
3882
+ const doc = await openFixture$12(engine, opts);
3548
3883
  try {
3549
3884
  const list = await doc.pages.list();
3550
3885
  if (list.pages.length < 2) return;
@@ -3556,7 +3891,7 @@ function runPageRotateConformance(runner, opts) {
3556
3891
  }
3557
3892
  });
3558
3893
  test("weak index-based annotation refs survive a rotate (no revision bump)", async () => {
3559
- const doc = await openFixture$9(engine, opts);
3894
+ const doc = await openFixture$12(engine, opts);
3560
3895
  try {
3561
3896
  const hostPon = (await doc.pages.list()).pages[0].pageObjectNumber;
3562
3897
  const hostPage = doc.page(hostPon);
@@ -3586,7 +3921,7 @@ function runPageRotateConformance(runner, opts) {
3586
3921
  }
3587
3922
  });
3588
3923
  test("pages.rotate() rejects an invalid rotation value with InvalidArg", async () => {
3589
- const doc = await openFixture$9(engine, opts);
3924
+ const doc = await openFixture$12(engine, opts);
3590
3925
  try {
3591
3926
  const pon = (await doc.pages.list()).pages[0].pageObjectNumber;
3592
3927
  let caught;
@@ -3595,13 +3930,13 @@ function runPageRotateConformance(runner, opts) {
3595
3930
  } catch (err) {
3596
3931
  caught = err;
3597
3932
  }
3598
- expect(require_fold.EngineError.is(caught, require_subtype.EngineErrorCode.InvalidArg)).toBe(true);
3933
+ expect(require_fold.EngineError.is(caught, require_PageInsertBlankInput.EngineErrorCode.InvalidArg)).toBe(true);
3599
3934
  } finally {
3600
3935
  await doc.close();
3601
3936
  }
3602
3937
  });
3603
3938
  test("pages.rotate() rejects duplicate PONs with InvalidArg", async () => {
3604
- const doc = await openFixture$9(engine, opts);
3939
+ const doc = await openFixture$12(engine, opts);
3605
3940
  try {
3606
3941
  const pon = (await doc.pages.list()).pages[0].pageObjectNumber;
3607
3942
  let caught;
@@ -3610,13 +3945,13 @@ function runPageRotateConformance(runner, opts) {
3610
3945
  } catch (err) {
3611
3946
  caught = err;
3612
3947
  }
3613
- expect(require_fold.EngineError.is(caught, require_subtype.EngineErrorCode.InvalidArg)).toBe(true);
3948
+ expect(require_fold.EngineError.is(caught, require_PageInsertBlankInput.EngineErrorCode.InvalidArg)).toBe(true);
3614
3949
  } finally {
3615
3950
  await doc.close();
3616
3951
  }
3617
3952
  });
3618
3953
  test("pages.rotate() rejects unknown PON with NotFound or InvalidArg", async () => {
3619
- const doc = await openFixture$9(engine, opts);
3954
+ const doc = await openFixture$12(engine, opts);
3620
3955
  try {
3621
3956
  const list = await doc.pages.list();
3622
3957
  let bogus = 0;
@@ -3628,13 +3963,13 @@ function runPageRotateConformance(runner, opts) {
3628
3963
  } catch (err) {
3629
3964
  caught = err;
3630
3965
  }
3631
- expect(require_fold.EngineError.is(caught, require_subtype.EngineErrorCode.NotFound) || require_fold.EngineError.is(caught, require_subtype.EngineErrorCode.InvalidArg)).toBe(true);
3966
+ expect(require_fold.EngineError.is(caught, require_PageInsertBlankInput.EngineErrorCode.NotFound) || require_fold.EngineError.is(caught, require_PageInsertBlankInput.EngineErrorCode.InvalidArg)).toBe(true);
3632
3967
  } finally {
3633
3968
  await doc.close();
3634
3969
  }
3635
3970
  });
3636
3971
  test("abort on pages.rotate() rejects with AbortError", async () => {
3637
- const doc = await openFixture$9(engine, opts);
3972
+ const doc = await openFixture$12(engine, opts);
3638
3973
  try {
3639
3974
  const pon = (await doc.pages.list()).pages[0].pageObjectNumber;
3640
3975
  const p = doc.pages.rotate([pon], 90);
@@ -3646,7 +3981,7 @@ function runPageRotateConformance(runner, opts) {
3646
3981
  });
3647
3982
  });
3648
3983
  }
3649
- async function openFixture$9(engine, opts) {
3984
+ async function openFixture$12(engine, opts) {
3650
3985
  if (opts.openKind === "bytes") {
3651
3986
  const bytes = await opts.fixture.bytes();
3652
3987
  return engine.open({
@@ -3709,7 +4044,7 @@ function runPageDeleteConformance(runner, opts) {
3709
4044
  if (engine) await engine.destroy();
3710
4045
  });
3711
4046
  test("pages.delete() removes the page and returns the surviving layout", async () => {
3712
- const doc = await openFixture$8(engine, opts);
4047
+ const doc = await openFixture$11(engine, opts);
3713
4048
  try {
3714
4049
  const before = await doc.pages.list();
3715
4050
  if (before.pages.length < 2) return;
@@ -3730,7 +4065,7 @@ function runPageDeleteConformance(runner, opts) {
3730
4065
  }
3731
4066
  });
3732
4067
  test("addressing a deleted page afterwards is a clean NotFound", async () => {
3733
- const doc = await openFixture$8(engine, opts);
4068
+ const doc = await openFixture$11(engine, opts);
3734
4069
  try {
3735
4070
  const before = await doc.pages.list();
3736
4071
  if (before.pages.length < 2) return;
@@ -3742,13 +4077,13 @@ function runPageDeleteConformance(runner, opts) {
3742
4077
  } catch (err) {
3743
4078
  caught = err;
3744
4079
  }
3745
- expect(require_fold.EngineError.is(caught, require_subtype.EngineErrorCode.NotFound)).toBe(true);
4080
+ expect(require_fold.EngineError.is(caught, require_PageInsertBlankInput.EngineErrorCode.NotFound)).toBe(true);
3746
4081
  } finally {
3747
4082
  await doc.close();
3748
4083
  }
3749
4084
  });
3750
4085
  test("surviving pages keep their weak index-based annotation refs", async () => {
3751
- const doc = await openFixture$8(engine, opts);
4086
+ const doc = await openFixture$11(engine, opts);
3752
4087
  try {
3753
4088
  const list = await doc.pages.list();
3754
4089
  if (list.pages.length < 2) return;
@@ -3781,7 +4116,7 @@ function runPageDeleteConformance(runner, opts) {
3781
4116
  }
3782
4117
  });
3783
4118
  test("deleting EVERY page is rejected with InvalidArg", async () => {
3784
- const doc = await openFixture$8(engine, opts);
4119
+ const doc = await openFixture$11(engine, opts);
3785
4120
  try {
3786
4121
  const list = await doc.pages.list();
3787
4122
  let caught;
@@ -3790,13 +4125,13 @@ function runPageDeleteConformance(runner, opts) {
3790
4125
  } catch (err) {
3791
4126
  caught = err;
3792
4127
  }
3793
- expect(require_fold.EngineError.is(caught, require_subtype.EngineErrorCode.InvalidArg)).toBe(true);
4128
+ expect(require_fold.EngineError.is(caught, require_PageInsertBlankInput.EngineErrorCode.InvalidArg)).toBe(true);
3794
4129
  } finally {
3795
4130
  await doc.close();
3796
4131
  }
3797
4132
  });
3798
4133
  test("pages.delete() rejects duplicate PONs with InvalidArg", async () => {
3799
- const doc = await openFixture$8(engine, opts);
4134
+ const doc = await openFixture$11(engine, opts);
3800
4135
  try {
3801
4136
  const pon = (await doc.pages.list()).pages[0].pageObjectNumber;
3802
4137
  let caught;
@@ -3805,13 +4140,13 @@ function runPageDeleteConformance(runner, opts) {
3805
4140
  } catch (err) {
3806
4141
  caught = err;
3807
4142
  }
3808
- expect(require_fold.EngineError.is(caught, require_subtype.EngineErrorCode.InvalidArg)).toBe(true);
4143
+ expect(require_fold.EngineError.is(caught, require_PageInsertBlankInput.EngineErrorCode.InvalidArg)).toBe(true);
3809
4144
  } finally {
3810
4145
  await doc.close();
3811
4146
  }
3812
4147
  });
3813
4148
  test("pages.delete() rejects unknown PON with NotFound or InvalidArg", async () => {
3814
- const doc = await openFixture$8(engine, opts);
4149
+ const doc = await openFixture$11(engine, opts);
3815
4150
  try {
3816
4151
  const list = await doc.pages.list();
3817
4152
  let bogus = 0;
@@ -3823,13 +4158,13 @@ function runPageDeleteConformance(runner, opts) {
3823
4158
  } catch (err) {
3824
4159
  caught = err;
3825
4160
  }
3826
- expect(require_fold.EngineError.is(caught, require_subtype.EngineErrorCode.NotFound) || require_fold.EngineError.is(caught, require_subtype.EngineErrorCode.InvalidArg)).toBe(true);
4161
+ expect(require_fold.EngineError.is(caught, require_PageInsertBlankInput.EngineErrorCode.NotFound) || require_fold.EngineError.is(caught, require_PageInsertBlankInput.EngineErrorCode.InvalidArg)).toBe(true);
3827
4162
  } finally {
3828
4163
  await doc.close();
3829
4164
  }
3830
4165
  });
3831
4166
  test("abort on pages.delete() rejects with AbortError", async () => {
3832
- const doc = await openFixture$8(engine, opts);
4167
+ const doc = await openFixture$11(engine, opts);
3833
4168
  try {
3834
4169
  const list = await doc.pages.list();
3835
4170
  if (list.pages.length < 2) return;
@@ -3843,7 +4178,7 @@ function runPageDeleteConformance(runner, opts) {
3843
4178
  });
3844
4179
  });
3845
4180
  }
3846
- async function openFixture$8(engine, opts) {
4181
+ async function openFixture$11(engine, opts) {
3847
4182
  if (opts.openKind === "bytes") {
3848
4183
  const bytes = await opts.fixture.bytes();
3849
4184
  return engine.open({
@@ -3871,7 +4206,7 @@ function runPageFlattenConformance(runner, opts) {
3871
4206
  if (engine) await engine.destroy();
3872
4207
  });
3873
4208
  test("flattens eligible appearances once without changing layout", async () => {
3874
- const doc = await openFixture$7(engine, opts);
4209
+ const doc = await openFixture$10(engine, opts);
3875
4210
  try {
3876
4211
  if (!doc.pages.flatten) return;
3877
4212
  const layoutBefore = await doc.pages.list();
@@ -3898,14 +4233,14 @@ function runPageFlattenConformance(runner, opts) {
3898
4233
  expect(noOp.meta).toBeNull();
3899
4234
  expect(events).toHaveLength(1);
3900
4235
  unsubscribe();
3901
- await expect(doc.pages.flatten([pageObjectNumber, pageObjectNumber])).rejects.toMatchObject({ code: require_subtype.EngineErrorCode.InvalidArg });
4236
+ await expect(doc.pages.flatten([pageObjectNumber, pageObjectNumber])).rejects.toMatchObject({ code: require_PageInsertBlankInput.EngineErrorCode.InvalidArg });
3902
4237
  } finally {
3903
4238
  await doc.close();
3904
4239
  }
3905
4240
  });
3906
4241
  });
3907
4242
  }
3908
- async function openFixture$7(engine, opts) {
4243
+ async function openFixture$10(engine, opts) {
3909
4244
  if (opts.openKind === "bytes") return engine.open({
3910
4245
  kind: "bytes",
3911
4246
  id: opts.fixture.id,
@@ -3960,7 +4295,7 @@ function runRedactionApplyConformance(runner, opts) {
3960
4295
  if (engine) await engine.destroy();
3961
4296
  });
3962
4297
  test("annotations scope applies the marked redaction and counts collateral", async () => {
3963
- const doc = await openFixture$6(engine, opts);
4298
+ const doc = await openFixture$9(engine, opts);
3964
4299
  try {
3965
4300
  if (!doc.redaction) return;
3966
4301
  const layoutBefore = await doc.pages.list();
@@ -4014,7 +4349,7 @@ function runRedactionApplyConformance(runner, opts) {
4014
4349
  }
4015
4350
  });
4016
4351
  test("pages scope applies everything once and validates its input", async () => {
4017
- const doc = await openFixture$6(engine, opts);
4352
+ const doc = await openFixture$9(engine, opts);
4018
4353
  try {
4019
4354
  if (!doc.redaction) return;
4020
4355
  const pageObjectNumber = (await doc.pages.list()).pages[0].pageObjectNumber;
@@ -4050,7 +4385,7 @@ function runRedactionApplyConformance(runner, opts) {
4050
4385
  await expect(doc.redaction.apply({
4051
4386
  kind: "pages",
4052
4387
  pageObjectNumbers: [pageObjectNumber, pageObjectNumber]
4053
- })).rejects.toMatchObject({ code: require_subtype.EngineErrorCode.InvalidArg });
4388
+ })).rejects.toMatchObject({ code: require_PageInsertBlankInput.EngineErrorCode.InvalidArg });
4054
4389
  const notRedact = await page.annotations.create({
4055
4390
  subtype: "highlight",
4056
4391
  quadPoints: COLLATERAL_QUAD
@@ -4058,14 +4393,14 @@ function runRedactionApplyConformance(runner, opts) {
4058
4393
  await expect(doc.redaction.apply({
4059
4394
  kind: "annotations",
4060
4395
  refs: [notRedact.created.ref]
4061
- })).rejects.toMatchObject({ code: require_subtype.EngineErrorCode.InvalidArg });
4396
+ })).rejects.toMatchObject({ code: require_PageInsertBlankInput.EngineErrorCode.InvalidArg });
4062
4397
  } finally {
4063
4398
  await doc.close();
4064
4399
  }
4065
4400
  });
4066
4401
  });
4067
4402
  }
4068
- async function openFixture$6(engine, opts) {
4403
+ async function openFixture$9(engine, opts) {
4069
4404
  if (opts.openKind === "bytes") return engine.open({
4070
4405
  kind: "bytes",
4071
4406
  id: opts.fixture.id,
@@ -4105,19 +4440,14 @@ function runPageExtractConformance(runner, opts) {
4105
4440
  const { describe, test, beforeAll, afterAll, expect } = runner;
4106
4441
  describe(`page extract conformance: ${opts.label}`, () => {
4107
4442
  let engine;
4108
- let supported = false;
4109
4443
  beforeAll(async () => {
4110
4444
  engine = await opts.makeEngine();
4111
- const probe = await openFixture$5(engine, opts);
4112
- supported = probe.pages.extract !== void 0;
4113
- await probe.close();
4114
4445
  });
4115
4446
  afterAll(async () => {
4116
4447
  if (engine) await engine.destroy();
4117
4448
  });
4118
4449
  test("pages.extract() returns standalone PDF bytes and leaves the source untouched", async () => {
4119
- if (!supported) return;
4120
- const doc = await openFixture$5(engine, opts);
4450
+ const doc = await openFixture$8(engine, opts);
4121
4451
  try {
4122
4452
  const before = await doc.pages.list();
4123
4453
  const target = before.pages[before.pages.length - 1];
@@ -4132,8 +4462,8 @@ function runPageExtractConformance(runner, opts) {
4132
4462
  }
4133
4463
  });
4134
4464
  test("extracted bytes re-open as a document with the requested pages, in caller order", async () => {
4135
- if (!supported || opts.openKind !== "bytes") return;
4136
- const doc = await openFixture$5(engine, opts);
4465
+ if (opts.openKind !== "bytes") return;
4466
+ const doc = await openFixture$8(engine, opts);
4137
4467
  let extracted = null;
4138
4468
  try {
4139
4469
  const list = await doc.pages.list();
@@ -4156,8 +4486,7 @@ function runPageExtractConformance(runner, opts) {
4156
4486
  }
4157
4487
  });
4158
4488
  test("pages.extract() rejects empty input with InvalidArg", async () => {
4159
- if (!supported) return;
4160
- const doc = await openFixture$5(engine, opts);
4489
+ const doc = await openFixture$8(engine, opts);
4161
4490
  try {
4162
4491
  let caught;
4163
4492
  try {
@@ -4165,14 +4494,13 @@ function runPageExtractConformance(runner, opts) {
4165
4494
  } catch (err) {
4166
4495
  caught = err;
4167
4496
  }
4168
- expect(require_fold.EngineError.is(caught, require_subtype.EngineErrorCode.InvalidArg)).toBe(true);
4497
+ expect(require_fold.EngineError.is(caught, require_PageInsertBlankInput.EngineErrorCode.InvalidArg)).toBe(true);
4169
4498
  } finally {
4170
4499
  await doc.close();
4171
4500
  }
4172
4501
  });
4173
4502
  test("pages.extract() rejects duplicate PONs with InvalidArg", async () => {
4174
- if (!supported) return;
4175
- const doc = await openFixture$5(engine, opts);
4503
+ const doc = await openFixture$8(engine, opts);
4176
4504
  try {
4177
4505
  const pon = (await doc.pages.list()).pages[0].pageObjectNumber;
4178
4506
  let caught;
@@ -4181,14 +4509,13 @@ function runPageExtractConformance(runner, opts) {
4181
4509
  } catch (err) {
4182
4510
  caught = err;
4183
4511
  }
4184
- expect(require_fold.EngineError.is(caught, require_subtype.EngineErrorCode.InvalidArg)).toBe(true);
4512
+ expect(require_fold.EngineError.is(caught, require_PageInsertBlankInput.EngineErrorCode.InvalidArg)).toBe(true);
4185
4513
  } finally {
4186
4514
  await doc.close();
4187
4515
  }
4188
4516
  });
4189
4517
  test("pages.extract() rejects unknown PON with NotFound or InvalidArg", async () => {
4190
- if (!supported) return;
4191
- const doc = await openFixture$5(engine, opts);
4518
+ const doc = await openFixture$8(engine, opts);
4192
4519
  try {
4193
4520
  const list = await doc.pages.list();
4194
4521
  let bogus = 0;
@@ -4200,14 +4527,13 @@ function runPageExtractConformance(runner, opts) {
4200
4527
  } catch (err) {
4201
4528
  caught = err;
4202
4529
  }
4203
- expect(require_fold.EngineError.is(caught, require_subtype.EngineErrorCode.NotFound) || require_fold.EngineError.is(caught, require_subtype.EngineErrorCode.InvalidArg)).toBe(true);
4530
+ expect(require_fold.EngineError.is(caught, require_PageInsertBlankInput.EngineErrorCode.NotFound) || require_fold.EngineError.is(caught, require_PageInsertBlankInput.EngineErrorCode.InvalidArg)).toBe(true);
4204
4531
  } finally {
4205
4532
  await doc.close();
4206
4533
  }
4207
4534
  });
4208
4535
  test("abort on pages.extract() rejects with AbortError", async () => {
4209
- if (!supported) return;
4210
- const doc = await openFixture$5(engine, opts);
4536
+ const doc = await openFixture$8(engine, opts);
4211
4537
  try {
4212
4538
  const list = await doc.pages.list();
4213
4539
  const p = doc.pages.extract([list.pages[0].pageObjectNumber]);
@@ -4219,7 +4545,7 @@ function runPageExtractConformance(runner, opts) {
4219
4545
  });
4220
4546
  });
4221
4547
  }
4222
- async function openFixture$5(engine, opts) {
4548
+ async function openFixture$8(engine, opts) {
4223
4549
  if (opts.openKind === "bytes") {
4224
4550
  const bytes = await opts.fixture.bytes();
4225
4551
  return engine.open({
@@ -4481,9 +4807,9 @@ function runAttachmentConformance(runner, opts) {
4481
4807
  //#endregion
4482
4808
  //#region src/conformance/runPageInsertConformance.ts
4483
4809
  /**
4484
- * Page insert conformance. `pages.insert?` is optional only until the cloud
4485
- * endpoint ships (it is slated REQUIRED-parity — a cloud viewer must be able
4486
- * to add pages); the suite runs wherever the implementation exists.
4810
+ * Page insert conformance. `pages.insert` is a REQUIRED member — this suite
4811
+ * runs unconditionally on every engine, so an implementation that loses the
4812
+ * verb fails loudly instead of being skipped past.
4487
4813
  *
4488
4814
  * Invariants:
4489
4815
  * 1. Every page of the source bytes is COPIED in at `destIndex` (omitted →
@@ -4499,19 +4825,14 @@ function runPageInsertConformance(runner, opts) {
4499
4825
  const { describe, test, beforeAll, afterAll, expect } = runner;
4500
4826
  describe(`page insert conformance: ${opts.label}`, () => {
4501
4827
  let engine;
4502
- let supported = false;
4503
4828
  beforeAll(async () => {
4504
4829
  engine = await opts.makeEngine();
4505
- const probe = await openFixture$4(engine, opts);
4506
- supported = probe.pages.insert !== void 0 && probe.pages.extract !== void 0;
4507
- await probe.close();
4508
4830
  });
4509
4831
  afterAll(async () => {
4510
4832
  if (engine) await engine.destroy();
4511
4833
  });
4512
4834
  test("appends every source page with fresh PONs; existing pages keep identity", async () => {
4513
- if (!supported) return;
4514
- const doc = await openFixture$4(engine, opts);
4835
+ const doc = await openFixture$7(engine, opts);
4515
4836
  try {
4516
4837
  const before = await doc.pages.list();
4517
4838
  const beforePons = before.pages.map((p) => p.pageObjectNumber);
@@ -4529,8 +4850,7 @@ function runPageInsertConformance(runner, opts) {
4529
4850
  }
4530
4851
  });
4531
4852
  test("destIndex places the block mid-document, in source order", async () => {
4532
- if (!supported) return;
4533
- const doc = await openFixture$4(engine, opts);
4853
+ const doc = await openFixture$7(engine, opts);
4534
4854
  try {
4535
4855
  const before = await doc.pages.list();
4536
4856
  if (before.pages.length < 2) return;
@@ -4547,8 +4867,8 @@ function runPageInsertConformance(runner, opts) {
4547
4867
  }
4548
4868
  });
4549
4869
  test("the inserted pages persist through save → re-open", async () => {
4550
- if (!supported || opts.openKind !== "bytes") return;
4551
- const doc = await openFixture$4(engine, opts);
4870
+ if (opts.openKind !== "bytes") return;
4871
+ const doc = await openFixture$7(engine, opts);
4552
4872
  let reopened = null;
4553
4873
  try {
4554
4874
  const before = await doc.pages.list();
@@ -4568,8 +4888,7 @@ function runPageInsertConformance(runner, opts) {
4568
4888
  }
4569
4889
  });
4570
4890
  test("empty bytes reject with InvalidArg; garbage rejects with MalformedPdf", async () => {
4571
- if (!supported) return;
4572
- const doc = await openFixture$4(engine, opts);
4891
+ const doc = await openFixture$7(engine, opts);
4573
4892
  try {
4574
4893
  let caught;
4575
4894
  try {
@@ -4577,14 +4896,14 @@ function runPageInsertConformance(runner, opts) {
4577
4896
  } catch (err) {
4578
4897
  caught = err;
4579
4898
  }
4580
- expect(require_fold.EngineError.is(caught, require_subtype.EngineErrorCode.InvalidArg)).toBe(true);
4899
+ expect(require_fold.EngineError.is(caught, require_PageInsertBlankInput.EngineErrorCode.InvalidArg)).toBe(true);
4581
4900
  caught = void 0;
4582
4901
  try {
4583
4902
  await doc.pages.insert(new TextEncoder().encode("not a pdf at all"));
4584
4903
  } catch (err) {
4585
4904
  caught = err;
4586
4905
  }
4587
- expect(require_fold.EngineError.is(caught, require_subtype.EngineErrorCode.MalformedPdf)).toBe(true);
4906
+ expect(require_fold.EngineError.is(caught, require_PageInsertBlankInput.EngineErrorCode.MalformedPdf)).toBe(true);
4588
4907
  const list = await doc.pages.list();
4589
4908
  expect(list.pageCount > 0).toBe(true);
4590
4909
  } finally {
@@ -4592,8 +4911,7 @@ function runPageInsertConformance(runner, opts) {
4592
4911
  }
4593
4912
  });
4594
4913
  test("out-of-range destIndex rejects with InvalidArg", async () => {
4595
- if (!supported) return;
4596
- const doc = await openFixture$4(engine, opts);
4914
+ const doc = await openFixture$7(engine, opts);
4597
4915
  try {
4598
4916
  const before = await doc.pages.list();
4599
4917
  const single = await doc.pages.extract([before.pages[0].pageObjectNumber]);
@@ -4603,14 +4921,167 @@ function runPageInsertConformance(runner, opts) {
4603
4921
  } catch (err) {
4604
4922
  caught = err;
4605
4923
  }
4606
- expect(require_fold.EngineError.is(caught, require_subtype.EngineErrorCode.InvalidArg)).toBe(true);
4924
+ expect(require_fold.EngineError.is(caught, require_PageInsertBlankInput.EngineErrorCode.InvalidArg)).toBe(true);
4607
4925
  } finally {
4608
4926
  await doc.close();
4609
4927
  }
4610
4928
  });
4611
4929
  });
4612
4930
  }
4613
- async function openFixture$4(engine, opts) {
4931
+ async function openFixture$7(engine, opts) {
4932
+ if (opts.openKind === "bytes") {
4933
+ const bytes = await opts.fixture.bytes();
4934
+ return engine.open({
4935
+ kind: "bytes",
4936
+ id: opts.fixture.id,
4937
+ bytes
4938
+ });
4939
+ }
4940
+ return engine.open({
4941
+ kind: "id",
4942
+ id: opts.fixture.cloudId ?? opts.fixture.id
4943
+ });
4944
+ }
4945
+ //#endregion
4946
+ //#region src/conformance/runPageInsertBlankConformance.ts
4947
+ /** An on-purpose non-default size so the assertions can't pass by accident. */
4948
+ const SIZE = {
4949
+ width: 396,
4950
+ height: 612
4951
+ };
4952
+ /**
4953
+ * Blank-page insert conformance. `pages.insertBlank` is a REQUIRED member —
4954
+ * the suite runs unconditionally on every engine (no fixture bytes are
4955
+ * needed at all, which is the point of the verb), so an implementation that
4956
+ * loses the verb fails loudly instead of being skipped past.
4957
+ *
4958
+ * Invariants:
4959
+ * 1. `count` blank pages of exactly `size` appear at `destIndex` (omitted
4960
+ * → append), rotation 0; the result lists their fresh PONs in insertion
4961
+ * order and they agree with the returned layout.
4962
+ * 2. Pre-existing pages keep their identity: same PONs before and after,
4963
+ * in the expected positions (an insert never invalidates neighbours).
4964
+ * 3. The mutation persists through save → re-open (bytes engines only).
4965
+ * 4. Non-positive size / count outside [1, PAGE_INSERT_BLANK_MAX_COUNT] /
4966
+ * out-of-range destIndex reject with InvalidArg, leaving the document
4967
+ * untouched.
4968
+ */
4969
+ function runPageInsertBlankConformance(runner, opts) {
4970
+ const { describe, test, beforeAll, afterAll, expect } = runner;
4971
+ describe(`page insert-blank conformance: ${opts.label}`, () => {
4972
+ let engine;
4973
+ beforeAll(async () => {
4974
+ engine = await opts.makeEngine();
4975
+ });
4976
+ afterAll(async () => {
4977
+ if (engine) await engine.destroy();
4978
+ });
4979
+ test("appends one blank page of the requested size with a fresh PON", async () => {
4980
+ const doc = await openFixture$6(engine, opts);
4981
+ try {
4982
+ const before = await doc.pages.list();
4983
+ const beforePons = before.pages.map((p) => p.pageObjectNumber);
4984
+ const result = await doc.pages.insertBlank({ size: SIZE });
4985
+ expect(result.insertedPageObjectNumbers.length).toBe(1);
4986
+ expect(result.layout.pageCount).toBe(before.pageCount + 1);
4987
+ expect(result.layout.pages.slice(0, before.pageCount).map((p) => p.pageObjectNumber)).toEqual(beforePons);
4988
+ const newPon = result.insertedPageObjectNumbers[0];
4989
+ expect(beforePons.includes(newPon)).toBe(false);
4990
+ const appended = result.layout.pages[before.pageCount];
4991
+ expect(appended.pageObjectNumber).toBe(newPon);
4992
+ expect(appended.size).toEqual(SIZE);
4993
+ expect(appended.rotation).toBe(0);
4994
+ } finally {
4995
+ await doc.close();
4996
+ }
4997
+ });
4998
+ test("destIndex + count places the blank block mid-document", async () => {
4999
+ const doc = await openFixture$6(engine, opts);
5000
+ try {
5001
+ const before = await doc.pages.list();
5002
+ if (before.pages.length < 2) return;
5003
+ const beforePons = before.pages.map((p) => p.pageObjectNumber);
5004
+ const result = await doc.pages.insertBlank({
5005
+ size: SIZE,
5006
+ count: 2
5007
+ }, 1);
5008
+ expect(result.insertedPageObjectNumbers.length).toBe(2);
5009
+ expect(result.insertedPageObjectNumbers[0] === result.insertedPageObjectNumbers[1]).toBe(false);
5010
+ const pons = result.layout.pages.map((p) => p.pageObjectNumber);
5011
+ expect(pons[0]).toBe(beforePons[0]);
5012
+ expect(pons.slice(1, 3)).toEqual(result.insertedPageObjectNumbers);
5013
+ expect(pons.slice(3)).toEqual(beforePons.slice(1));
5014
+ expect(result.layout.pages[1].size).toEqual(SIZE);
5015
+ expect(result.layout.pages[2].size).toEqual(SIZE);
5016
+ } finally {
5017
+ await doc.close();
5018
+ }
5019
+ });
5020
+ test("the blank pages persist through save → re-open", async () => {
5021
+ if (opts.openKind !== "bytes") return;
5022
+ const doc = await openFixture$6(engine, opts);
5023
+ let reopened = null;
5024
+ try {
5025
+ const before = await doc.pages.list();
5026
+ await doc.pages.insertBlank({ size: SIZE });
5027
+ const bytes = await doc.download();
5028
+ reopened = await engine.open({
5029
+ kind: "bytes",
5030
+ id: `${opts.fixture.id}-insert-blank-reopen`,
5031
+ bytes
5032
+ });
5033
+ const after = await reopened.pages.list();
5034
+ expect(after.pageCount).toBe(before.pageCount + 1);
5035
+ const tail = after.pages[after.pageCount - 1];
5036
+ expect(Math.round(tail.size.width)).toBe(SIZE.width);
5037
+ expect(Math.round(tail.size.height)).toBe(SIZE.height);
5038
+ } finally {
5039
+ if (reopened) await reopened.close();
5040
+ await doc.close();
5041
+ }
5042
+ });
5043
+ test("non-positive size, bad count, and out-of-range destIndex reject with InvalidArg", async () => {
5044
+ const doc = await openFixture$6(engine, opts);
5045
+ try {
5046
+ const before = await doc.pages.list();
5047
+ const rejects = [
5048
+ () => doc.pages.insertBlank({ size: {
5049
+ width: 0,
5050
+ height: 612
5051
+ } }),
5052
+ () => doc.pages.insertBlank({ size: {
5053
+ width: 396,
5054
+ height: -10
5055
+ } }),
5056
+ () => doc.pages.insertBlank({
5057
+ size: SIZE,
5058
+ count: 0
5059
+ }),
5060
+ () => doc.pages.insertBlank({
5061
+ size: SIZE,
5062
+ count: 101
5063
+ }),
5064
+ () => doc.pages.insertBlank({ size: SIZE }, before.pageCount + 1)
5065
+ ];
5066
+ for (const attempt of rejects) {
5067
+ let caught;
5068
+ try {
5069
+ await attempt();
5070
+ } catch (err) {
5071
+ caught = err;
5072
+ }
5073
+ expect(require_fold.EngineError.is(caught, require_PageInsertBlankInput.EngineErrorCode.InvalidArg)).toBe(true);
5074
+ }
5075
+ const list = await doc.pages.list();
5076
+ expect(list.pageCount).toBe(before.pageCount);
5077
+ expect(list.pages.map((p) => p.pageObjectNumber)).toEqual(before.pages.map((p) => p.pageObjectNumber));
5078
+ } finally {
5079
+ await doc.close();
5080
+ }
5081
+ });
5082
+ });
5083
+ }
5084
+ async function openFixture$6(engine, opts) {
4614
5085
  if (opts.openKind === "bytes") {
4615
5086
  const bytes = await opts.fixture.bytes();
4616
5087
  return engine.open({
@@ -4651,7 +5122,7 @@ function runPieceInfoConformance(runner, opts) {
4651
5122
  let supported = false;
4652
5123
  beforeAll(async () => {
4653
5124
  engine = await opts.makeEngine();
4654
- const probe = await openFixture$3(engine, opts);
5125
+ const probe = await openFixture$5(engine, opts);
4655
5126
  supported = probe.pieceInfo !== void 0;
4656
5127
  await probe.close();
4657
5128
  });
@@ -4660,7 +5131,7 @@ function runPieceInfoConformance(runner, opts) {
4660
5131
  });
4661
5132
  test("doc-level: the full value vocabulary round-trips with tags intact", async () => {
4662
5133
  if (!supported) return;
4663
- const doc = await openFixture$3(engine, opts);
5134
+ const doc = await openFixture$5(engine, opts);
4664
5135
  try {
4665
5136
  await doc.pieceInfo.update(APP, {
4666
5137
  name: "Standard Stamps",
@@ -4700,7 +5171,7 @@ function runPieceInfoConformance(runner, opts) {
4700
5171
  });
4701
5172
  test("page-level: the stamp schema round-trips on a page", async () => {
4702
5173
  if (!supported) return;
4703
- const doc = await openFixture$3(engine, opts);
5174
+ const doc = await openFixture$5(engine, opts);
4704
5175
  try {
4705
5176
  const list = await doc.pages.list();
4706
5177
  const page = doc.page(list.pages[0].pageObjectNumber);
@@ -4726,7 +5197,7 @@ function runPieceInfoConformance(runner, opts) {
4726
5197
  });
4727
5198
  test("writes persist through save → re-open", async () => {
4728
5199
  if (!supported || opts.openKind !== "bytes") return;
4729
- const doc = await openFixture$3(engine, opts);
5200
+ const doc = await openFixture$5(engine, opts);
4730
5201
  let reopened = null;
4731
5202
  try {
4732
5203
  const pon = (await doc.pages.list()).pages[0].pageObjectNumber;
@@ -4759,7 +5230,7 @@ function runPieceInfoConformance(runner, opts) {
4759
5230
  });
4760
5231
  test("null deletes a key; clear removes an entry; siblings survive", async () => {
4761
5232
  if (!supported) return;
4762
- const doc = await openFixture$3(engine, opts);
5233
+ const doc = await openFixture$5(engine, opts);
4763
5234
  try {
4764
5235
  await doc.pieceInfo.update(APP, {
4765
5236
  name: "A",
@@ -4785,7 +5256,7 @@ function runPieceInfoConformance(runner, opts) {
4785
5256
  });
4786
5257
  test("an application that was never written reads as null", async () => {
4787
5258
  if (!supported) return;
4788
- const doc = await openFixture$3(engine, opts);
5259
+ const doc = await openFixture$5(engine, opts);
4789
5260
  try {
4790
5261
  expect(await doc.pieceInfo.read("EMBD_NeverWritten")).toBe(null);
4791
5262
  const list = await doc.pages.list();
@@ -4796,7 +5267,7 @@ function runPieceInfoConformance(runner, opts) {
4796
5267
  });
4797
5268
  });
4798
5269
  }
4799
- async function openFixture$3(engine, opts) {
5270
+ async function openFixture$5(engine, opts) {
4800
5271
  if (opts.openKind === "bytes") {
4801
5272
  const bytes = await opts.fixture.bytes();
4802
5273
  return engine.open({
@@ -4859,7 +5330,7 @@ function runDocumentEventsConformance(runner, opts) {
4859
5330
  if (engine) await engine.destroy();
4860
5331
  });
4861
5332
  test("every confirmed mutation publishes exactly one event, results verbatim", async () => {
4862
- const doc = await openFixture$2(engine, opts);
5333
+ const doc = await openFixture$4(engine, opts);
4863
5334
  try {
4864
5335
  const events = [];
4865
5336
  doc.events.subscribe((event) => events.push(event));
@@ -4906,19 +5377,46 @@ function runDocumentEventsConformance(runner, opts) {
4906
5377
  expect(evDeleted.layout).toEqual(deleted.layout);
4907
5378
  }
4908
5379
  if (evMeta.type === "metadata.updated") expect(evMeta.metadata).toEqual(meta.metadata);
5380
+ const first = events[0];
5381
+ expect(first !== void 0 && "origin" in first).toBe(true);
5382
+ if (first === void 0 || !("origin" in first)) return;
4909
5383
  for (const event of events) {
5384
+ expect("origin" in event).toBe(true);
5385
+ if (!("origin" in event)) continue;
4910
5386
  expect(event.origin.kind).toBe("local");
4911
5387
  expect(typeof event.origin.sessionId).toBe("string");
4912
5388
  expect(event.origin.sessionId.length > 0).toBe(true);
4913
- expect(event.origin.sessionId).toBe(events[0].origin.sessionId);
5389
+ expect(event.origin.sessionId).toBe(first.origin.sessionId);
4914
5390
  expect(typeof event.origin.ts).toBe("number");
4915
5391
  }
4916
5392
  } finally {
4917
5393
  await doc.close();
4918
5394
  }
4919
5395
  });
5396
+ test("pages.inserted rides verbatim", async () => {
5397
+ const doc = await openFixture$4(engine, opts);
5398
+ try {
5399
+ const events = [];
5400
+ doc.events.subscribe((event) => events.push(event));
5401
+ const inserted = await doc.pages.insertBlank({ size: {
5402
+ width: 396,
5403
+ height: 612
5404
+ } }, 0);
5405
+ expect(events.map((event) => event.type)).toEqual(["pages.inserted"]);
5406
+ const [evInserted] = events;
5407
+ if (evInserted.type === "pages.inserted") {
5408
+ expect(evInserted.insertedPageObjectNumbers).toEqual(inserted.insertedPageObjectNumbers);
5409
+ expect(evInserted.layout).toEqual(inserted.layout);
5410
+ expect(evInserted.cache).toEqual(inserted.cache);
5411
+ expect(evInserted.destIndex).toBe(0);
5412
+ expect(evInserted.origin.kind).toBe("local");
5413
+ }
5414
+ } finally {
5415
+ await doc.close();
5416
+ }
5417
+ });
4920
5418
  test("a FAILED mutation publishes nothing (events are ground truth)", async () => {
4921
- const doc = await openFixture$2(engine, opts);
5419
+ const doc = await openFixture$4(engine, opts);
4922
5420
  try {
4923
5421
  const events = [];
4924
5422
  doc.events.subscribe((event) => events.push(event));
@@ -4936,7 +5434,7 @@ function runDocumentEventsConformance(runner, opts) {
4936
5434
  }
4937
5435
  });
4938
5436
  test("unsubscribe stops delivery", async () => {
4939
- const doc = await openFixture$2(engine, opts);
5437
+ const doc = await openFixture$4(engine, opts);
4940
5438
  try {
4941
5439
  const events = [];
4942
5440
  doc.events.subscribe((event) => events.push(event))();
@@ -4949,7 +5447,7 @@ function runDocumentEventsConformance(runner, opts) {
4949
5447
  });
4950
5448
  });
4951
5449
  }
4952
- async function openFixture$2(engine, opts) {
5450
+ async function openFixture$4(engine, opts) {
4953
5451
  if (opts.openKind === "bytes") {
4954
5452
  const bytes = await opts.fixture.bytes();
4955
5453
  return engine.open({
@@ -4976,7 +5474,7 @@ function runPageTextConformance(runner, opts) {
4976
5474
  if (engine) await engine.destroy();
4977
5475
  });
4978
5476
  test("read() returns a PageTextSnapshot with non-empty text", async () => {
4979
- const doc = await openFixture$1(engine, opts);
5477
+ const doc = await openFixture$3(engine, opts);
4980
5478
  try {
4981
5479
  const snap = await doc.page(opts.fixture.pageObjectNumber).text.read();
4982
5480
  expect(require_schemas.PageTextSnapshotSchema.safeParse(snap).success).toBe(true);
@@ -4987,7 +5485,7 @@ function runPageTextConformance(runner, opts) {
4987
5485
  }
4988
5486
  });
4989
5487
  test("extracted text contains the expected substring", async () => {
4990
- const doc = await openFixture$1(engine, opts);
5488
+ const doc = await openFixture$3(engine, opts);
4991
5489
  try {
4992
5490
  const snap = await doc.page(opts.fixture.pageObjectNumber).text.read();
4993
5491
  expect(snap.text.includes(opts.fixture.expectedSubstring)).toBe(true);
@@ -4996,7 +5494,7 @@ function runPageTextConformance(runner, opts) {
4996
5494
  }
4997
5495
  });
4998
5496
  test("read() on an unknown pageObjectNumber throws NotFound", async () => {
4999
- const doc = await openFixture$1(engine, opts);
5497
+ const doc = await openFixture$3(engine, opts);
5000
5498
  try {
5001
5499
  let caught;
5002
5500
  try {
@@ -5005,13 +5503,13 @@ function runPageTextConformance(runner, opts) {
5005
5503
  caught = err;
5006
5504
  }
5007
5505
  expect(caught).toBeTruthy();
5008
- expect(require_fold.EngineError.is(caught, require_subtype.EngineErrorCode.NotFound)).toBe(true);
5506
+ expect(require_fold.EngineError.is(caught, require_PageInsertBlankInput.EngineErrorCode.NotFound)).toBe(true);
5009
5507
  } finally {
5010
5508
  await doc.close();
5011
5509
  }
5012
5510
  });
5013
5511
  test("abort() on read rejects with AbortError", async () => {
5014
- const doc = await openFixture$1(engine, opts);
5512
+ const doc = await openFixture$3(engine, opts);
5015
5513
  try {
5016
5514
  const p = doc.page(opts.fixture.pageObjectNumber).text.read();
5017
5515
  p.abort("test");
@@ -5021,7 +5519,7 @@ function runPageTextConformance(runner, opts) {
5021
5519
  }
5022
5520
  });
5023
5521
  test("read after close throws DocNotOpen", async () => {
5024
- const doc = await openFixture$1(engine, opts);
5522
+ const doc = await openFixture$3(engine, opts);
5025
5523
  const page = doc.page(opts.fixture.pageObjectNumber);
5026
5524
  await doc.close();
5027
5525
  let caught;
@@ -5031,10 +5529,324 @@ function runPageTextConformance(runner, opts) {
5031
5529
  caught = err;
5032
5530
  }
5033
5531
  expect(caught).toBeTruthy();
5034
- expect(require_fold.EngineError.is(caught, require_subtype.EngineErrorCode.DocNotOpen)).toBe(true);
5532
+ expect(require_fold.EngineError.is(caught, require_PageInsertBlankInput.EngineErrorCode.DocNotOpen)).toBe(true);
5533
+ });
5534
+ });
5535
+ }
5536
+ async function openFixture$3(engine, opts) {
5537
+ if (opts.openKind === "bytes") {
5538
+ const bytes = await opts.fixture.bytes();
5539
+ return engine.open({
5540
+ kind: "bytes",
5541
+ id: opts.fixture.id,
5542
+ bytes
5543
+ });
5544
+ }
5545
+ return engine.open({
5546
+ kind: "id",
5547
+ id: opts.fixture.cloudId ?? opts.fixture.id
5548
+ });
5549
+ }
5550
+ //#endregion
5551
+ //#region src/conformance/runTextDivergenceConformance.ts
5552
+ function runTextDivergenceConformance(runner, opts) {
5553
+ const { describe, test, beforeAll, afterAll, expect } = runner;
5554
+ const fixture = opts.fixture;
5555
+ describe(`text divergence conformance: ${opts.label} [${fixture.id}]`, () => {
5556
+ let engine;
5557
+ beforeAll(async () => {
5558
+ engine = await opts.makeEngine();
5559
+ });
5560
+ afterAll(async () => {
5561
+ if (engine) await engine.destroy();
5562
+ });
5563
+ test("snapshot is schema-valid with the exact text, charCount, and charMap", async () => {
5564
+ const doc = await openFixture$2(engine, opts);
5565
+ try {
5566
+ const snap = await doc.page(fixture.pageObjectNumber).text.read();
5567
+ expect(require_schemas.PageTextSnapshotSchema.safeParse(snap).success).toBe(true);
5568
+ expect(snap.text).toBe(fixture.exactText);
5569
+ expect(snap.charCount).toBe(fixture.expectedCharCount);
5570
+ const anchors = snap.charMap ?? null;
5571
+ if (fixture.expectedCharMap === null) expect(anchors === null || anchors.length === 0).toBe(true);
5572
+ else expect(anchors === null ? null : anchors.map((a) => [a[0], a[1]])).toEqual(fixture.expectedCharMap.map((a) => [a[0], a[1]]));
5573
+ } finally {
5574
+ await doc.close();
5575
+ }
5576
+ });
5577
+ test("char-space slicing round-trips through the map", async () => {
5578
+ const doc = await openFixture$2(engine, opts);
5579
+ try {
5580
+ const snap = await doc.page(fixture.pageObjectNumber).text.read();
5581
+ expect(require_PageInsertBlankInput.sliceTextByChars(snap, 0, snap.charCount)).toBe(snap.text);
5582
+ for (const s of fixture.slices ?? []) expect(require_PageInsertBlankInput.sliceTextByChars(snap, s.from, s.to)).toBe(s.text);
5583
+ } finally {
5584
+ await doc.close();
5585
+ }
5035
5586
  });
5587
+ if (fixture.search) {
5588
+ const probe = fixture.search;
5589
+ test("search hits address character space and join the text by slicing", async () => {
5590
+ const doc = await openFixture$2(engine, opts);
5591
+ try {
5592
+ const hit = (await doc.search.query({
5593
+ query: { text: probe.query },
5594
+ mode: "rects"
5595
+ })).matches.find((m) => m.pageObjectNumber === fixture.pageObjectNumber);
5596
+ expect(hit === void 0).toBe(false);
5597
+ expect(hit.charStart).toBe(probe.charStart);
5598
+ expect(hit.charCount).toBe(probe.charCount);
5599
+ expect(hit.segments.length > 0).toBe(true);
5600
+ const snap = await doc.page(fixture.pageObjectNumber).text.read();
5601
+ expect(require_PageInsertBlankInput.sliceTextByChars(snap, hit.charStart, hit.charStart + hit.charCount)).toBe(probe.matchedText);
5602
+ } finally {
5603
+ await doc.close();
5604
+ }
5605
+ });
5606
+ }
5036
5607
  });
5037
5608
  }
5609
+ async function openFixture$2(engine, opts) {
5610
+ if (opts.openKind === "bytes") {
5611
+ const bytes = await opts.fixture.bytes();
5612
+ return engine.open({
5613
+ kind: "bytes",
5614
+ id: opts.fixture.id,
5615
+ bytes
5616
+ });
5617
+ }
5618
+ return engine.open({
5619
+ kind: "id",
5620
+ id: opts.fixture.cloudId ?? opts.fixture.id
5621
+ });
5622
+ }
5623
+ /**
5624
+ * THE pinned divergence cases — one table, consumed by both engines' suites
5625
+ * so expected values cannot drift. Values verified against the wasm runtime
5626
+ * (see each case). All fixtures are single-page; the page's indirect object
5627
+ * number is 3 in each.
5628
+ */
5629
+ const TEXT_DIVERGENCE_CASES = {
5630
+ /** Leading NON-PRINTING character: 31 character slots, 30 text units —
5631
+ * every text offset shifts by one, encoded by the single anchor [1, 0].
5632
+ * The searchex embeddertest for this fixture pins the same +1 shift. */
5633
+ bug1139: {
5634
+ resource: "bug_1139.pdf",
5635
+ pageObjectNumber: 3,
5636
+ exactText: "Hello, world!\r\nGoodbye, world!",
5637
+ expectedCharCount: 31,
5638
+ expectedCharMap: [[1, 0]],
5639
+ slices: [
5640
+ {
5641
+ from: 0,
5642
+ to: 1,
5643
+ text: ""
5644
+ },
5645
+ {
5646
+ from: 1,
5647
+ to: 7,
5648
+ text: "Hello,"
5649
+ },
5650
+ {
5651
+ from: 16,
5652
+ to: 23,
5653
+ text: "Goodbye"
5654
+ }
5655
+ ],
5656
+ search: {
5657
+ query: "Goodbye",
5658
+ charStart: 16,
5659
+ charCount: 7,
5660
+ matchedText: "Goodbye"
5661
+ }
5662
+ },
5663
+ /** `/ActualText` replacement: the span's real glyphs are REPLACED by one
5664
+ * synthetic kPiece character per ActualText character, lockstep with the
5665
+ * text (identity map), with synthesized evenly-divided boxes — so search
5666
+ * segments exist and extraction yields the ActualText, not the glyphs. */
5667
+ actualText: {
5668
+ resource: "bug_384770169.pdf",
5669
+ pageObjectNumber: 3,
5670
+ exactText: "What is my favorite food?",
5671
+ expectedCharCount: 25,
5672
+ expectedCharMap: null,
5673
+ slices: [{
5674
+ from: 11,
5675
+ to: 19,
5676
+ text: "favorite"
5677
+ }],
5678
+ search: {
5679
+ query: "favorite",
5680
+ charStart: 11,
5681
+ charCount: 8,
5682
+ matchedText: "favorite"
5683
+ }
5684
+ },
5685
+ /** Supplementary-plane text via a ToUnicode surrogate pair: PDFium stores
5686
+ * one character-list entry PER SURROGATE HALF in lockstep with the text
5687
+ * (identity map, no divergence in this representation) — and the emoji
5688
+ * SURVIVES extraction end to end. The combined-astral representation
5689
+ * (font-cmap path, one entry per code point → a [+2] anchor) is covered
5690
+ * by the charmap unit tests and the reader's divergent walk. */
5691
+ astralToUnicode: {
5692
+ resource: "embedpdf_astral_tounicode.pdf",
5693
+ pageObjectNumber: 3,
5694
+ exactText: "😀B!",
5695
+ expectedCharCount: 4,
5696
+ expectedCharMap: null,
5697
+ slices: [{
5698
+ from: 0,
5699
+ to: 2,
5700
+ text: "😀"
5701
+ }, {
5702
+ from: 2,
5703
+ to: 3,
5704
+ text: "B"
5705
+ }],
5706
+ search: {
5707
+ query: "B!",
5708
+ charStart: 2,
5709
+ charCount: 2,
5710
+ matchedText: "B!"
5711
+ }
5712
+ }
5713
+ };
5714
+ //#endregion
5715
+ //#region src/conformance/runPageGeometryOrientationConformance.ts
5716
+ const FLAG_EMPTY = 2;
5717
+ /** Absolute tolerance for coordinate assertions (PDF points). */
5718
+ const COORD_TOLERANCE = .001;
5719
+ /** Tolerance for baseline-angle assertions (radians). */
5720
+ const ANGLE_TOLERANCE = .001;
5721
+ function runPageGeometryOrientationConformance(runner, opts) {
5722
+ const { describe, test, beforeAll, afterAll, expect } = runner;
5723
+ describe(`page geometry orientation conformance: ${opts.label} [${opts.fixture.id}]`, () => {
5724
+ let engine;
5725
+ beforeAll(async () => {
5726
+ engine = await opts.makeEngine();
5727
+ });
5728
+ afterAll(async () => {
5729
+ if (engine) await engine.destroy();
5730
+ });
5731
+ const readSnapshot = async () => {
5732
+ const doc = await openFixture$1(engine, opts);
5733
+ return {
5734
+ snapshot: await doc.page(opts.fixture.pageObjectNumber).geometry.read(),
5735
+ close: () => doc.close()
5736
+ };
5737
+ };
5738
+ test("snapshot round-trips the wire schema", async () => {
5739
+ const { snapshot, close } = await readSnapshot();
5740
+ try {
5741
+ const parsed = require_schemas.PageGeometrySnapshotSchema.safeParse(snapshot);
5742
+ expect(parsed.success).toBe(true);
5743
+ if (parsed.success) expect(parsed.data).toEqual(snapshot);
5744
+ } finally {
5745
+ await close();
5746
+ }
5747
+ });
5748
+ test("run charStart indices tile the page glyph sequence", async () => {
5749
+ const { snapshot, close } = await readSnapshot();
5750
+ try {
5751
+ let next = 0;
5752
+ for (const run of snapshot.runs) {
5753
+ expect(run.charStart).toBe(next);
5754
+ next += run.glyphs.length;
5755
+ }
5756
+ } finally {
5757
+ await close();
5758
+ }
5759
+ });
5760
+ test("runs match the fixture orientation expectation", async () => {
5761
+ const { snapshot, close } = await readSnapshot();
5762
+ try {
5763
+ const expectation = opts.fixture.expectation;
5764
+ if (expectation.kind === "upright") {
5765
+ for (const run of snapshot.runs) {
5766
+ expect(require_fold.isRotatedGeometryRun(run)).toBe(false);
5767
+ if (require_fold.isRotatedGeometryRun(run)) continue;
5768
+ for (const glyph of run.glyphs) {
5769
+ expect("looseQuad" in glyph).toBe(false);
5770
+ if (glyph.flags & FLAG_EMPTY) expect(glyph.looseBox).toEqual({
5771
+ left: 0,
5772
+ bottom: 0,
5773
+ right: 0,
5774
+ top: 0
5775
+ });
5776
+ }
5777
+ }
5778
+ return;
5779
+ }
5780
+ if (expectation.kind === "empty-only") {
5781
+ for (const run of snapshot.runs) {
5782
+ expect(require_fold.isRotatedGeometryRun(run)).toBe(false);
5783
+ if (require_fold.isRotatedGeometryRun(run)) continue;
5784
+ for (const glyph of run.glyphs) {
5785
+ expect(glyph.flags & FLAG_EMPTY).toBe(FLAG_EMPTY);
5786
+ expect(glyph.looseBox).toEqual({
5787
+ left: 0,
5788
+ bottom: 0,
5789
+ right: 0,
5790
+ top: 0
5791
+ });
5792
+ }
5793
+ }
5794
+ return;
5795
+ }
5796
+ const rotatedRuns = snapshot.runs.filter(require_fold.isRotatedGeometryRun);
5797
+ expect(rotatedRuns.length >= (expectation.minRotatedRuns ?? 1)).toBe(true);
5798
+ for (const run of rotatedRuns) {
5799
+ expect(expectation.rotations.some((angle) => angleClose(run.rotation, angle, ANGLE_TOLERANCE))).toBe(true);
5800
+ expect(run.ascentFlip).toBe(expectation.ascentFlip);
5801
+ assertRotatedRunGeometry(run);
5802
+ }
5803
+ if (expectation.sheared) {
5804
+ 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);
5805
+ expect(shearedGlyphs.length > 0).toBe(true);
5806
+ }
5807
+ } finally {
5808
+ await close();
5809
+ }
5810
+ });
5811
+ function assertRotatedRunGeometry(run) {
5812
+ for (const glyph of run.glyphs) {
5813
+ if (glyph.flags & FLAG_EMPTY) {
5814
+ expect(glyph.looseQuad).toEqual({
5815
+ p1: {
5816
+ x: 0,
5817
+ y: 0
5818
+ },
5819
+ p2: {
5820
+ x: 0,
5821
+ y: 0
5822
+ },
5823
+ p3: {
5824
+ x: 0,
5825
+ y: 0
5826
+ },
5827
+ p4: {
5828
+ x: 0,
5829
+ y: 0
5830
+ }
5831
+ });
5832
+ continue;
5833
+ }
5834
+ const q = glyph.looseQuad;
5835
+ expect(Math.abs(q.p2.x - q.p1.x - (q.p4.x - q.p3.x)) <= COORD_TOLERANCE).toBe(true);
5836
+ expect(Math.abs(q.p2.y - q.p1.y - (q.p4.y - q.p3.y)) <= COORD_TOLERANCE).toBe(true);
5837
+ const bounds = require_fold.pdfQuadBounds(q);
5838
+ expect(bounds.left >= run.rect.left - COORD_TOLERANCE).toBe(true);
5839
+ expect(bounds.right <= run.rect.right + COORD_TOLERANCE).toBe(true);
5840
+ expect(bounds.bottom >= run.rect.bottom - COORD_TOLERANCE).toBe(true);
5841
+ expect(bounds.top <= run.rect.top + COORD_TOLERANCE).toBe(true);
5842
+ }
5843
+ }
5844
+ });
5845
+ }
5846
+ function angleClose(a, b, tolerance) {
5847
+ const delta = a - b;
5848
+ return Math.abs(Math.atan2(Math.sin(delta), Math.cos(delta))) <= tolerance;
5849
+ }
5038
5850
  async function openFixture$1(engine, opts) {
5039
5851
  if (opts.openKind === "bytes") {
5040
5852
  const bytes = await opts.fixture.bytes();
@@ -5090,10 +5902,27 @@ function runSearchConformance(runner, opts) {
5090
5902
  expect(matches.some((m) => m.pageObjectNumber === fixture.presentPageObjectNumber)).toBe(true);
5091
5903
  for (const m of matches) {
5092
5904
  expect(m.charCount > 0).toBe(true);
5093
- expect(m.rects.length > 0).toBe(true);
5094
- for (const r of m.rects) {
5095
- expect(r.right > r.left).toBe(true);
5096
- expect(r.top > r.bottom).toBe(true);
5905
+ expect(m.segments.length > 0).toBe(true);
5906
+ for (const s of m.segments) {
5907
+ expect(s.rect.right > s.rect.left).toBe(true);
5908
+ expect(s.rect.top > s.rect.bottom).toBe(true);
5909
+ const xs = [
5910
+ s.quad.p1.x,
5911
+ s.quad.p2.x,
5912
+ s.quad.p3.x,
5913
+ s.quad.p4.x
5914
+ ];
5915
+ const ys = [
5916
+ s.quad.p1.y,
5917
+ s.quad.p2.y,
5918
+ s.quad.p3.y,
5919
+ s.quad.p4.y
5920
+ ];
5921
+ expect(Math.abs(s.rect.left - Math.min(...xs)) < .001).toBe(true);
5922
+ expect(Math.abs(s.rect.right - Math.max(...xs)) < .001).toBe(true);
5923
+ expect(Math.abs(s.rect.bottom - Math.min(...ys)) < .001).toBe(true);
5924
+ expect(Math.abs(s.rect.top - Math.max(...ys)) < .001).toBe(true);
5925
+ expect(s.advance === 1 || s.advance === -1).toBe(true);
5097
5926
  }
5098
5927
  }
5099
5928
  } finally {
@@ -5193,7 +6022,7 @@ function runSearchConformance(runner, opts) {
5193
6022
  } catch (err) {
5194
6023
  caught = err;
5195
6024
  }
5196
- expect(require_fold.EngineError.is(caught, require_subtype.EngineErrorCode.InvalidArg)).toBe(true);
6025
+ expect(require_fold.EngineError.is(caught, require_PageInsertBlankInput.EngineErrorCode.InvalidArg)).toBe(true);
5197
6026
  } finally {
5198
6027
  await doc.close();
5199
6028
  }
@@ -5210,7 +6039,7 @@ function runSearchConformance(runner, opts) {
5210
6039
  } catch (err) {
5211
6040
  caught = err;
5212
6041
  }
5213
- expect(require_fold.EngineError.is(caught, require_subtype.EngineErrorCode.InvalidArg)).toBe(true);
6042
+ expect(require_fold.EngineError.is(caught, require_PageInsertBlankInput.EngineErrorCode.InvalidArg)).toBe(true);
5214
6043
  } finally {
5215
6044
  await doc.close();
5216
6045
  }
@@ -5225,7 +6054,7 @@ function runSearchConformance(runner, opts) {
5225
6054
  expect(matches.length > 0).toBe(true);
5226
6055
  for (const m of matches) {
5227
6056
  expect(m.charCount > 0).toBe(true);
5228
- expect(m.rects.length > 0).toBe(true);
6057
+ expect(m.segments.length > 0).toBe(true);
5229
6058
  }
5230
6059
  } finally {
5231
6060
  await doc.close();
@@ -5266,7 +6095,7 @@ function runSearchConformance(runner, opts) {
5266
6095
  } catch (err) {
5267
6096
  caught = err;
5268
6097
  }
5269
- expect(require_fold.EngineError.is(caught, require_subtype.EngineErrorCode.InvalidArg)).toBe(true);
6098
+ expect(require_fold.EngineError.is(caught, require_PageInsertBlankInput.EngineErrorCode.InvalidArg)).toBe(true);
5270
6099
  } finally {
5271
6100
  await doc.close();
5272
6101
  }
@@ -5288,7 +6117,7 @@ function runSearchConformance(runner, opts) {
5288
6117
  } catch (err) {
5289
6118
  caught = err;
5290
6119
  }
5291
- expect(require_fold.EngineError.is(caught, require_subtype.EngineErrorCode.InvalidArg)).toBe(true);
6120
+ expect(require_fold.EngineError.is(caught, require_PageInsertBlankInput.EngineErrorCode.InvalidArg)).toBe(true);
5292
6121
  }
5293
6122
  } finally {
5294
6123
  await doc.close();
@@ -5323,7 +6152,7 @@ function runSearchConformance(runner, opts) {
5323
6152
  } catch (err) {
5324
6153
  caught = err;
5325
6154
  }
5326
- expect(require_fold.EngineError.is(caught, require_subtype.EngineErrorCode.DocNotOpen)).toBe(true);
6155
+ expect(require_fold.EngineError.is(caught, require_PageInsertBlankInput.EngineErrorCode.DocNotOpen)).toBe(true);
5327
6156
  });
5328
6157
  });
5329
6158
  }
@@ -5352,6 +6181,8 @@ async function openFixture(engine, opts) {
5352
6181
  * are disjoint. We do compare `pageObjectNumber` and `generation` (which
5353
6182
  * should be 0 for fresh reads on both sides). Page order is no longer part
5354
6183
  * of `PageState`; it lives in `PageLayout.index` (see `pages.list()`).
6184
+ * `auditHead` is likewise not compared: it is a cloud-only reconciliation
6185
+ * cursor, absent on local snapshots by design.
5355
6186
  */
5356
6187
  function diffAnnotationListSnapshot(a, b) {
5357
6188
  const errs = [];
@@ -5390,6 +6221,7 @@ function diffAnnotation(i, a, b, errs) {
5390
6221
  if (a.nm !== b.nm) errs.push(`annotations[${i}].nm mismatch: ${a.nm} vs ${b.nm}`);
5391
6222
  }
5392
6223
  //#endregion
6224
+ exports.TEXT_DIVERGENCE_CASES = TEXT_DIVERGENCE_CASES;
5393
6225
  exports.diffAnnotationListSnapshot = diffAnnotationListSnapshot;
5394
6226
  exports.diffAnnotationListSnapshotAll = diffAnnotationListSnapshotAll;
5395
6227
  exports.runActionsConformance = runActionsConformance;
@@ -5403,6 +6235,8 @@ exports.runMetadataConformance = runMetadataConformance;
5403
6235
  exports.runPageDeleteConformance = runPageDeleteConformance;
5404
6236
  exports.runPageExtractConformance = runPageExtractConformance;
5405
6237
  exports.runPageFlattenConformance = runPageFlattenConformance;
6238
+ exports.runPageGeometryOrientationConformance = runPageGeometryOrientationConformance;
6239
+ exports.runPageInsertBlankConformance = runPageInsertBlankConformance;
5406
6240
  exports.runPageInsertConformance = runPageInsertConformance;
5407
6241
  exports.runPageReorderConformance = runPageReorderConformance;
5408
6242
  exports.runPageRotateConformance = runPageRotateConformance;
@@ -5410,5 +6244,6 @@ exports.runPageTextConformance = runPageTextConformance;
5410
6244
  exports.runPieceInfoConformance = runPieceInfoConformance;
5411
6245
  exports.runRedactionApplyConformance = runRedactionApplyConformance;
5412
6246
  exports.runSearchConformance = runSearchConformance;
6247
+ exports.runTextDivergenceConformance = runTextDivergenceConformance;
5413
6248
 
5414
6249
  //# sourceMappingURL=conformance.cjs.map