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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/dist/{AbortError-CjSasIlL.js → AbortError-8-9JAX1Y.js} +2 -2
  2. package/dist/{AbortError-CjSasIlL.js.map → AbortError-8-9JAX1Y.js.map} +1 -1
  3. package/dist/{AbortError-ByBflvEl.cjs → AbortError-CRSooKsk.cjs} +2 -2
  4. package/dist/{AbortError-ByBflvEl.cjs.map → AbortError-CRSooKsk.cjs.map} +1 -1
  5. package/dist/{AnnotationListSnapshot-W0RCsyDE.d.cts → AnnotationListSnapshot-DDxHpJQU.d.cts} +538 -41
  6. package/dist/{AnnotationListSnapshot-W0RCsyDE.d.ts → AnnotationListSnapshot-DDxHpJQU.d.ts} +538 -41
  7. package/dist/{DocumentSecurityService-Df79fgpe.d.cts → DocumentSecurityService-BV4_YXkU.d.cts} +33 -2
  8. package/dist/{DocumentSecurityService-B-tULKNY.d.ts → DocumentSecurityService-o9aeTHwT.d.ts} +33 -2
  9. package/dist/{Engine-D9loFZc1.d.cts → Engine-D9je6GdU.d.cts} +50 -19
  10. package/dist/{Engine-B7Oe7f5i.d.ts → Engine-DOoVem7_.d.ts} +50 -19
  11. package/dist/{subtype-qejjVr6h.cjs → PageInsertBlankInput-CzShuSJI.cjs} +184 -1
  12. package/dist/PageInsertBlankInput-CzShuSJI.cjs.map +1 -0
  13. package/dist/{subtype-BvAzLCGC.js → PageInsertBlankInput-WKY0tr6S.js} +149 -2
  14. package/dist/PageInsertBlankInput-WKY0tr6S.js.map +1 -0
  15. package/dist/{builders-C548U47M.js → builders-B_KufGJ9.js} +547 -169
  16. package/dist/builders-B_KufGJ9.js.map +1 -0
  17. package/dist/{builders-BvikWRuO.cjs → builders-Cp_rlGob.cjs} +603 -225
  18. package/dist/builders-Cp_rlGob.cjs.map +1 -0
  19. package/dist/conformance.cjs +822 -198
  20. package/dist/conformance.cjs.map +1 -1
  21. package/dist/conformance.d.cts +124 -6
  22. package/dist/conformance.d.ts +124 -6
  23. package/dist/conformance.js +777 -157
  24. package/dist/conformance.js.map +1 -1
  25. package/dist/{fold-CB4fAOOy.js → fold-BfyxxwS6.js} +162 -3
  26. package/dist/fold-BfyxxwS6.js.map +1 -0
  27. package/dist/{fold-IaMph3CO.cjs → fold-C7I5GpBz.cjs} +235 -4
  28. package/dist/fold-C7I5GpBz.cjs.map +1 -0
  29. package/dist/index.cjs +36 -18
  30. package/dist/index.d.cts +2 -2
  31. package/dist/index.d.ts +2 -2
  32. package/dist/index.js +4 -4
  33. package/dist/runtime.cjs +41 -23
  34. package/dist/runtime.cjs.map +1 -1
  35. package/dist/runtime.d.cts +103 -5
  36. package/dist/runtime.d.ts +103 -5
  37. package/dist/runtime.js +5 -5
  38. package/dist/{schemas-B_1VyFsm.cjs → schemas-D1pxEv3t.cjs} +138 -32
  39. package/dist/schemas-D1pxEv3t.cjs.map +1 -0
  40. package/dist/{schemas-9OpSSeM6.js → schemas-DAnu8tuW.js} +95 -13
  41. package/dist/schemas-DAnu8tuW.js.map +1 -0
  42. package/dist/wire.cjs +118 -3
  43. package/dist/wire.cjs.map +1 -1
  44. package/dist/wire.d.cts +139 -27
  45. package/dist/wire.d.ts +139 -27
  46. package/dist/wire.js +111 -4
  47. package/dist/wire.js.map +1 -1
  48. package/package.json +1 -1
  49. package/dist/builders-BvikWRuO.cjs.map +0 -1
  50. package/dist/builders-C548U47M.js.map +0 -1
  51. package/dist/fold-CB4fAOOy.js.map +0 -1
  52. package/dist/fold-IaMph3CO.cjs.map +0 -1
  53. package/dist/schemas-9OpSSeM6.js.map +0 -1
  54. package/dist/schemas-B_1VyFsm.cjs.map +0 -1
  55. package/dist/subtype-BvAzLCGC.js.map +0 -1
  56. package/dist/subtype-qejjVr6h.cjs.map +0 -1
@@ -1,126 +1,11 @@
1
- const require_subtype = require("./subtype-qejjVr6h.cjs");
2
- const require_fold = require("./fold-IaMph3CO.cjs");
3
- //#region src/geometry/convert.ts
4
- /**
5
- * Normalize a `PdfRect` to the y-up invariant (`left <= right`, `bottom <= top`),
6
- * regardless of how the PDF ordered the corners (a `/Rect` array or `FS_RECTF`
7
- * may carry them in any order). THE enforcement point for the "PdfRect is always
8
- * normalized" rule: every producer that reads a raw box from PDFium passes it
9
- * through here, so the invariant holds once, centrally — downstream `width =
10
- * right - left` is never negative.
11
- */
12
- function normalizePdfRect(r) {
13
- return {
14
- left: Math.min(r.left, r.right),
15
- right: Math.max(r.left, r.right),
16
- bottom: Math.min(r.top, r.bottom),
17
- top: Math.max(r.top, r.bottom)
18
- };
19
- }
20
- function pdfRectWidth(r) {
21
- return r.right - r.left;
22
- }
23
- function pdfRectHeight(r) {
24
- return r.top - r.bottom;
25
- }
26
- function pdfRectSize(r) {
27
- return {
28
- width: r.right - r.left,
29
- height: r.top - r.bottom
30
- };
31
- }
32
- /** Convert edges -> origin+size, staying y-up (origin at bottom-left). */
33
- function pdfRectToOriginSize(r) {
34
- return {
35
- x: r.left,
36
- y: r.bottom,
37
- width: r.right - r.left,
38
- height: r.top - r.bottom
39
- };
40
- }
41
- /** Convert y-up origin+size back to edges. */
42
- function pdfRectFromOriginSize(o) {
43
- return {
44
- left: o.x,
45
- bottom: o.y,
46
- right: o.x + o.width,
47
- top: o.y + o.height
48
- };
49
- }
50
- /**
51
- * Enclosing axis-aligned box of a quad, in PDF user space. Orientation
52
- * agnostic (correct for rotated/skewed quads) — this is what highlight
53
- * rendering and hit-testing want.
54
- */
55
- function pdfQuadBounds(q) {
56
- const xs = [
57
- q.p1.x,
58
- q.p2.x,
59
- q.p3.x,
60
- q.p4.x
61
- ];
62
- const ys = [
63
- q.p1.y,
64
- q.p2.y,
65
- q.p3.y,
66
- q.p4.y
67
- ];
68
- return {
69
- left: Math.min(...xs),
70
- right: Math.max(...xs),
71
- bottom: Math.min(...ys),
72
- top: Math.max(...ys)
73
- };
74
- }
75
- /**
76
- * Axis-aligned named-corner interpretation of a quad.
77
- *
78
- * VALID ONLY for upright (non-rotated, non-skewed) quads: it is derived from
79
- * the enclosing bounds, so for rotated/skewed quads it returns the bounding
80
- * box corners, not the true geometric corners. For arbitrary quads use the
81
- * positional `p1..p4` or `pdfQuadBounds` directly.
82
- */
83
- function pdfQuadCorners(q) {
84
- const b = pdfQuadBounds(q);
85
- return {
86
- topLeft: {
87
- x: b.left,
88
- y: b.top
89
- },
90
- topRight: {
91
- x: b.right,
92
- y: b.top
93
- },
94
- bottomLeft: {
95
- x: b.left,
96
- y: b.bottom
97
- },
98
- bottomRight: {
99
- x: b.right,
100
- y: b.bottom
101
- }
102
- };
103
- }
104
- /**
105
- * Build a quad from named corners, in PDFium `FS_QUADPOINTSF` slot order
106
- * (`p1 = topLeft`, `p2 = topRight`, `p3 = bottomLeft`, `p4 = bottomRight`).
107
- * The inverse of `pdfQuadCorners` for upright quads.
108
- */
109
- function pdfQuadFromCorners(c) {
110
- return {
111
- p1: c.topLeft,
112
- p2: c.topRight,
113
- p3: c.bottomLeft,
114
- p4: c.bottomRight
115
- };
116
- }
117
- //#endregion
1
+ const require_fold = require("./fold-C7I5GpBz.cjs");
2
+ const require_PageInsertBlankInput = require("./PageInsertBlankInput-CzShuSJI.cjs");
118
3
  //#region src/dto/PageRender.ts
119
4
  function createPageImageHandle(result, blobSource) {
120
5
  return {
121
6
  ...result,
122
7
  async objectUrl(signal) {
123
- if (typeof Blob === "undefined" || typeof URL === "undefined" || !URL.createObjectURL) throw new require_fold.EngineError(require_subtype.EngineErrorCode.RuntimeUnavailable, "Object URLs are not available in this environment");
8
+ if (typeof Blob === "undefined" || typeof URL === "undefined" || !URL.createObjectURL) throw new require_fold.EngineError(require_PageInsertBlankInput.EngineErrorCode.RuntimeUnavailable, "Object URLs are not available in this environment");
124
9
  const blob = await blobSource.blob(signal);
125
10
  const url = URL.createObjectURL(blob);
126
11
  return {
@@ -344,7 +229,7 @@ function parseJpeg(buffer) {
344
229
  async function normalizeAttachmentFileSource(file, key) {
345
230
  const resolved = await resolveBinarySource(file.data);
346
231
  const name = file.name ?? resolved.name;
347
- if (!name) throw new require_fold.EngineError(require_subtype.EngineErrorCode.InvalidArg, "attachment file requires a name — pass { data, name } or a File whose name is set");
232
+ if (!name) throw new require_fold.EngineError(require_PageInsertBlankInput.EngineErrorCode.InvalidArg, "attachment file requires a name — pass { data, name } or a File whose name is set");
348
233
  const mimeType = file.mimeType ?? resolved.mimeType;
349
234
  return {
350
235
  wireFile: {
@@ -382,7 +267,7 @@ async function normalizeFileAttachmentDraft(draft, allocateKey) {
382
267
  async function resolveStampSource(source) {
383
268
  const resolved = await resolveBinarySource(source);
384
269
  const meta = sniffBinaryMetadata(resolved.bytes);
385
- if (!meta) throw new require_fold.EngineError(require_subtype.EngineErrorCode.InvalidArg, "stamp source must be PNG, JPEG, or single-page PDF bytes (format is detected from the data, not the declared mime type)");
270
+ if (!meta) throw new require_fold.EngineError(require_PageInsertBlankInput.EngineErrorCode.InvalidArg, "stamp source must be PNG, JPEG, or single-page PDF bytes (format is detected from the data, not the declared mime type)");
386
271
  return {
387
272
  ...resolved,
388
273
  mimeType: meta.mimeType
@@ -524,6 +409,164 @@ function buildThreads(annotations) {
524
409
  return threads;
525
410
  }
526
411
  //#endregion
412
+ //#region src/annotation/comments.ts
413
+ /** Subtypes that never participate in comment threads. `unsupported`
414
+ * covers `/Popup` dictionaries (raw code 16) and unreadable foreign
415
+ * blobs — neither makes a meaningful comment card. */
416
+ const EXCLUDED_SUBTYPES = /* @__PURE__ */ new Set([
417
+ "widget",
418
+ "link",
419
+ "unsupported"
420
+ ]);
421
+ const REVIEW_STATES = /* @__PURE__ */ new Set([
422
+ "accepted",
423
+ "rejected",
424
+ "cancelled",
425
+ "completed",
426
+ "none"
427
+ ]);
428
+ const MARKED_STATES = /* @__PURE__ */ new Set(["marked", "unmarked"]);
429
+ const nonEmpty = (v) => typeof v === "string" && v !== "";
430
+ /**
431
+ * An ISO 32000 §12.5.6.3 state annotation: a text annotation carrying a
432
+ * non-empty `/State` or `/StateModel`. Classification never relies on
433
+ * flags — producers disagree on how state annotations are flagged.
434
+ */
435
+ function isStateAnnotation(a) {
436
+ return a.subtype === "text" && (nonEmpty(a.state) || nonEmpty(a.stateModel));
437
+ }
438
+ function buildCommentThreads(annotations, opts = {}) {
439
+ const eligible = annotations.filter((a) => !EXCLUDED_SUBTYPES.has(a.subtype));
440
+ const byKey = /* @__PURE__ */ new Map();
441
+ for (const a of eligible) {
442
+ const key = refKey(a.ref);
443
+ if (!byKey.has(key)) byKey.set(key, a);
444
+ if (a.nm) {
445
+ const aliasKey = refKey({
446
+ kind: "nm",
447
+ pageObjectNumber: a.pageObjectNumber,
448
+ nm: a.nm
449
+ });
450
+ if (!byKey.has(aliasKey)) byKey.set(aliasKey, a);
451
+ }
452
+ }
453
+ const children = /* @__PURE__ */ new Map();
454
+ for (const a of eligible) {
455
+ if (!a.inReplyTo) continue;
456
+ const parent = byKey.get(refKey(a.inReplyTo));
457
+ if (!parent) continue;
458
+ const parentKey = refKey(parent.ref);
459
+ const list = children.get(parentKey);
460
+ if (list) list.push(a);
461
+ else children.set(parentKey, [a]);
462
+ }
463
+ const visited = /* @__PURE__ */ new Set();
464
+ const threads = [];
465
+ const walk = (root) => {
466
+ const replies = [];
467
+ const groupedParts = [];
468
+ const states = [];
469
+ visited.add(refKey(root.ref));
470
+ const stack = [...children.get(refKey(root.ref)) ?? []];
471
+ while (stack.length > 0) {
472
+ const a = stack.pop();
473
+ const key = refKey(a.ref);
474
+ if (visited.has(key)) continue;
475
+ visited.add(key);
476
+ if (isStateAnnotation(a)) states.push(a);
477
+ else if (classifyRelation(a) === "grouped-subordinate") groupedParts.push(a);
478
+ else replies.push(a);
479
+ stack.push(...children.get(key) ?? []);
480
+ }
481
+ replies.sort(chronological);
482
+ states.sort(chronological);
483
+ threads.push({
484
+ root,
485
+ pageObjectNumber: root.pageObjectNumber,
486
+ replies,
487
+ groupedParts,
488
+ review: computeReview(states, opts)
489
+ });
490
+ };
491
+ for (const a of eligible) if (!a.inReplyTo && !isStateAnnotation(a)) walk(a);
492
+ for (const a of eligible) {
493
+ if (visited.has(refKey(a.ref))) continue;
494
+ if (isStateAnnotation(a)) continue;
495
+ walk(a);
496
+ }
497
+ return threads;
498
+ }
499
+ /** `created ?? modified` ascending; undated last; z-order tiebreak. */
500
+ function chronological(a, b) {
501
+ const at = a.created ?? a.modified;
502
+ const bt = b.created ?? b.modified;
503
+ if (at !== null && bt !== null && at !== bt) return at < bt ? -1 : 1;
504
+ if (at !== null && bt === null) return -1;
505
+ if (at === null && bt !== null) return 1;
506
+ return a.index - b.index;
507
+ }
508
+ function computeReview(states, opts) {
509
+ const byReviewer = {};
510
+ const markedLatest = {};
511
+ let lastChange = null;
512
+ for (const a of states) {
513
+ const status = toReviewStatus(a);
514
+ if (!status) continue;
515
+ if (status.stateModel === "marked") {
516
+ if (status.by !== null && isNewer(status, markedLatest[status.by])) markedLatest[status.by] = status;
517
+ continue;
518
+ }
519
+ if (status.by !== null && isNewer(status, byReviewer[status.by])) byReviewer[status.by] = status;
520
+ if (isNewer(status, lastChange)) lastChange = status;
521
+ }
522
+ const markedBy = Object.keys(markedLatest).filter((key) => markedLatest[key].state === "marked").sort();
523
+ const review = {
524
+ byReviewer,
525
+ lastChange,
526
+ markedBy,
527
+ statusRefs: states.map((a) => a.ref)
528
+ };
529
+ if (opts.currentUserId !== void 0) review.mine = byReviewer[opts.currentUserId] ?? null;
530
+ return review;
531
+ }
532
+ /**
533
+ * Derive a `ReviewStatus` from one state annotation, or null when nothing
534
+ * classifiable can be derived. ISO defaulting happens HERE, not in the
535
+ * engine reader: a known model with an absent state means `none` /
536
+ * `unmarked`; an absent model infers from a known state; a custom model
537
+ * with no state has no derivable status.
538
+ */
539
+ function toReviewStatus(a) {
540
+ if (a.subtype !== "text") return null;
541
+ const rawState = nonEmpty(a.state) ? a.state : null;
542
+ let stateModel = nonEmpty(a.stateModel) ? a.stateModel : null;
543
+ if (stateModel === null && rawState !== null) if (REVIEW_STATES.has(rawState)) stateModel = "review";
544
+ else if (MARKED_STATES.has(rawState)) stateModel = "marked";
545
+ else return null;
546
+ let state = rawState;
547
+ if (state === null) if (stateModel === "review") state = "none";
548
+ else if (stateModel === "marked") state = "unmarked";
549
+ else return null;
550
+ if (stateModel === null) return null;
551
+ return {
552
+ state,
553
+ stateModel,
554
+ by: a.userId ?? (nonEmpty(a.author) ? a.author : null),
555
+ at: a.modified ?? a.created ?? null,
556
+ ref: a.ref
557
+ };
558
+ }
559
+ /** `a` newer than (or supersedes) `b`. `at` decides; dated beats undated.
560
+ * States arrive pre-sorted by `chronological` (date, then z-order), so on
561
+ * equal or missing dates the later entry wins via `>=` / `true`. */
562
+ function isNewer(a, b) {
563
+ if (!b) return true;
564
+ if (a.at !== null && b.at !== null) return a.at >= b.at;
565
+ if (a.at !== null) return true;
566
+ if (b.at !== null) return false;
567
+ return true;
568
+ }
569
+ //#endregion
527
570
  //#region src/mutation/AnnotationMutationImpactPolicy.ts
528
571
  function changesAnnotationList(_kind) {
529
572
  return true;
@@ -547,14 +590,19 @@ function invalidatesWeakIndexRefs(kind, weakStateBefore) {
547
590
  const EPSILON = .001;
548
591
  /**
549
592
  * Keys that never affect `/AP` on any kind: the discriminator, behavioral
550
- * flags, reply relationships, and grouping.
593
+ * flags, reply relationships, grouping, and the conversation-plane
594
+ * entries (`/Subj` subject line, `/State` + `/StateModel` review status —
595
+ * dictionary-only per ISO 32000 §12.5.6.3, never painted).
551
596
  */
552
597
  const INERT_KEYS = /* @__PURE__ */ new Set([
553
598
  "subtype",
554
599
  "flags",
555
600
  "inReplyTo",
556
601
  "replyType",
557
- "groupId"
602
+ "groupId",
603
+ "subject",
604
+ "state",
605
+ "stateModel"
558
606
  ]);
559
607
  /**
560
608
  * Kinds whose `/AP` paints `/Contents`. Everywhere else `contents` is popup
@@ -1007,37 +1055,275 @@ function buildSnippet(text, range, context = 48) {
1007
1055
  };
1008
1056
  }
1009
1057
  //#endregion
1010
- //#region src/search/rects.ts
1011
- /**
1012
- * Match-range → merged line rects, in PDF user space (y-up).
1013
- *
1014
- * This is the same line-merge text selection uses (adapted from Chromium's
1015
- * pdf/pdfium/pdfium_range.cc `MergeAdjacentRects`, BSD-licensed, Copyright
1016
- * 2010 The Chromium Authors), operating directly on the engine's
1017
- * `PageGeometrySnapshot`: glyph loose boxes → sub-runs (split on big
1018
- * intra-run gaps) → one rect per visual line. Search highlights therefore
1019
- * look exactly like selections — never a rect per glyph (the v2 bug).
1020
- *
1021
- * It lives in engine-core because BOTH sides need it: the server computes
1022
- * rects here for `'rects'`-mode responses (geometry never leaves the trust
1023
- * boundary), and the local engine computes them in the worker.
1024
- */
1058
+ //#region src/text/layout.ts
1025
1059
  const FLAG_EMPTY = 2;
1060
+ const isBoundary = (flags) => (flags & 3) !== 0;
1061
+ const isEmpty = (flags) => (flags & FLAG_EMPTY) !== 0;
1026
1062
  const CHAR_DISTANCE_FACTOR = 2.5;
1027
1063
  const FONT_SIZE_RATIO_THRESHOLD = 1.5;
1028
1064
  const VERTICAL_OVERLAP_THRESHOLD = .8;
1065
+ const LINE_OVERLAP_THRESHOLD = .5;
1066
+ /** Baseline directions within ~0.5° share a frame. */
1067
+ const FRAME_DOT_TOLERANCE = Math.cos(.0087);
1068
+ const EDGE_EPSILON = 1e-6;
1069
+ const IDENTITY_FRAME = {
1070
+ baseline: {
1071
+ x: 1,
1072
+ y: 0
1073
+ },
1074
+ ascent: {
1075
+ x: 0,
1076
+ y: 1
1077
+ }
1078
+ };
1079
+ const toFrame = (f, p) => ({
1080
+ x: p.x * f.baseline.x + p.y * f.baseline.y,
1081
+ y: p.x * f.ascent.x + p.y * f.ascent.y
1082
+ });
1083
+ const fromFrame = (f, p) => ({
1084
+ x: f.baseline.x * p.x + f.ascent.x * p.y,
1085
+ y: f.baseline.y * p.x + f.ascent.y * p.y
1086
+ });
1087
+ const ZERO_RECT = {
1088
+ left: 0,
1089
+ bottom: 0,
1090
+ right: 0,
1091
+ top: 0
1092
+ };
1093
+ /** Frame-local AABB of a page-space cell (exact under the frame's rotation;
1094
+ * shear residue is the documented in-frame envelope). */
1095
+ function frameBoxOfQuad(f, q) {
1096
+ const pts = [
1097
+ q.p1,
1098
+ q.p2,
1099
+ q.p3,
1100
+ q.p4
1101
+ ].map((p) => toFrame(f, p));
1102
+ const xs = pts.map((p) => p.x);
1103
+ const ys = pts.map((p) => p.y);
1104
+ return {
1105
+ left: Math.min(...xs),
1106
+ bottom: Math.min(...ys),
1107
+ right: Math.max(...xs),
1108
+ top: Math.max(...ys)
1109
+ };
1110
+ }
1111
+ /** Find-or-create the frame for a rotated run, keyed by the baseline
1112
+ * direction + ascent handedness of its first classifiable glyph's edges. */
1113
+ function frameForRun(frames, run) {
1114
+ const seed = run.glyphs.find((g) => !isEmpty(g.flags));
1115
+ if (!seed) return 0;
1116
+ const q = seed.looseQuad;
1117
+ const bx = q.p2.x - q.p1.x;
1118
+ const by = q.p2.y - q.p1.y;
1119
+ const len = Math.hypot(bx, by);
1120
+ if (len <= EDGE_EPSILON) return 0;
1121
+ const baseline = {
1122
+ x: bx / len,
1123
+ y: by / len
1124
+ };
1125
+ const ax = q.p1.x - q.p3.x;
1126
+ const ay = q.p1.y - q.p3.y;
1127
+ const sign = baseline.x * ay - baseline.y * ax >= 0 ? 1 : -1;
1128
+ const ascent = {
1129
+ x: -baseline.y * sign,
1130
+ y: baseline.x * sign
1131
+ };
1132
+ for (let i = 0; i < frames.length; i++) {
1133
+ const f = frames[i];
1134
+ const dot = f.baseline.x * baseline.x + f.baseline.y * baseline.y;
1135
+ const sameHandedness = f.ascent.x * ascent.x + f.ascent.y * ascent.y > 0;
1136
+ if (dot >= FRAME_DOT_TOLERANCE && sameHandedness) return i;
1137
+ }
1138
+ frames.push({
1139
+ baseline,
1140
+ ascent
1141
+ });
1142
+ return frames.length - 1;
1143
+ }
1029
1144
  /**
1030
- * Merged line rects for the text-page char range
1031
- * `[charStart, charStart + charCount)`. Ranges index the same space as
1032
- * `PageGeometryRun.charStart`; chars outside the snapshot are ignored.
1145
+ * Flatten a page's geometry snapshot into the canonical layout. Upright runs
1146
+ * copy their wire boxes verbatim (frame 0 — byte-identical to the
1147
+ * pre-orientation behavior, wire run rects included); rotated runs project
1148
+ * their glyph cells into their cluster's frame.
1033
1149
  */
1034
- function searchRectsForRange(snapshot, charStart, charCount) {
1035
- if (charCount <= 0) return [];
1036
- const lo = charStart;
1037
- const hi = charStart + charCount - 1;
1038
- const subRuns = [];
1150
+ function buildPageTextLayout(snapshot) {
1151
+ const glyphs = [];
1152
+ const runs = [];
1153
+ const frames = [IDENTITY_FRAME];
1039
1154
  for (const run of snapshot.runs) {
1040
- const runEnd = run.charStart + run.glyphs.length - 1;
1155
+ if (!require_fold.isRotatedGeometryRun(run)) {
1156
+ for (const g of run.glyphs) glyphs.push({
1157
+ loose: g.looseBox,
1158
+ tight: g.tightBox,
1159
+ flags: g.flags
1160
+ });
1161
+ runs.push({
1162
+ charStart: run.charStart,
1163
+ count: run.glyphs.length,
1164
+ rect: run.rect,
1165
+ fontSize: run.fontSize,
1166
+ frame: 0
1167
+ });
1168
+ continue;
1169
+ }
1170
+ const frame = frameForRun(frames, run);
1171
+ const f = frames[frame];
1172
+ let runBox = null;
1173
+ for (const g of run.glyphs) {
1174
+ if (isEmpty(g.flags)) {
1175
+ glyphs.push({
1176
+ loose: ZERO_RECT,
1177
+ flags: g.flags
1178
+ });
1179
+ continue;
1180
+ }
1181
+ const loose = frameBoxOfQuad(f, g.looseQuad);
1182
+ const tight = g.tightQuad ? frameBoxOfQuad(f, g.tightQuad) : void 0;
1183
+ glyphs.push({
1184
+ loose,
1185
+ tight,
1186
+ flags: g.flags
1187
+ });
1188
+ runBox = runBox ? {
1189
+ left: Math.min(runBox.left, loose.left),
1190
+ bottom: Math.min(runBox.bottom, loose.bottom),
1191
+ right: Math.max(runBox.right, loose.right),
1192
+ top: Math.max(runBox.top, loose.top)
1193
+ } : loose;
1194
+ }
1195
+ runs.push({
1196
+ charStart: run.charStart,
1197
+ count: run.glyphs.length,
1198
+ rect: runBox ?? ZERO_RECT,
1199
+ fontSize: run.fontSize,
1200
+ frame
1201
+ });
1202
+ }
1203
+ return {
1204
+ glyphs,
1205
+ runs,
1206
+ frames
1207
+ };
1208
+ }
1209
+ const inRect = (r, p) => p.x >= r.left && p.x <= r.right && p.y >= r.bottom && p.y <= r.top;
1210
+ function avgGlyphHeight(layout) {
1211
+ let total = 0;
1212
+ let count = 0;
1213
+ for (const g of layout.glyphs) {
1214
+ if (isEmpty(g.flags)) continue;
1215
+ total += g.loose.top - g.loose.bottom;
1216
+ count++;
1217
+ }
1218
+ return count === 0 ? 0 : total / count;
1219
+ }
1220
+ /** The point expressed in every frame (index-aligned with `layout.frames`).
1221
+ * Frame 0 returns the point itself — no float round-trip on the fast path. */
1222
+ function pointPerFrame(layout, p) {
1223
+ return layout.frames.map((f, i) => i === 0 ? p : toFrame(f, p));
1224
+ }
1225
+ /**
1226
+ * The glyph (char) index at a page-space point, or null when nothing is near
1227
+ * (so callers can show the pointer cursor off-text). PDFium `GetIndexAtPos`:
1228
+ * exact tight-box containment first, then a tolerance pass (closest by
1229
+ * Manhattan distance within `toleranceFactor × average glyph height`). Each
1230
+ * run tests the point in ITS OWN frame, so rotated text hit-tests exactly.
1231
+ */
1232
+ function textGlyphAt(layout, point, toleranceFactor = 1.5) {
1233
+ const pts = pointPerFrame(layout, point);
1234
+ for (const run of layout.runs) {
1235
+ const q = pts[run.frame];
1236
+ if (!inRect(run.rect, q)) continue;
1237
+ for (let i = 0; i < run.count; i++) {
1238
+ const g = layout.glyphs[run.charStart + i];
1239
+ if (inRect(g.tight ?? g.loose, q)) return run.charStart + i;
1240
+ }
1241
+ }
1242
+ if (toleranceFactor <= 0) return null;
1243
+ const half = avgGlyphHeight(layout) * toleranceFactor / 2;
1244
+ let best = -1;
1245
+ let bestDist = Infinity;
1246
+ for (const run of layout.runs) {
1247
+ const q = pts[run.frame];
1248
+ const r = run.rect;
1249
+ if (q.x < r.left - half || q.x > r.right + half || q.y < r.bottom - half || q.y > r.top + half) continue;
1250
+ for (let i = 0; i < run.count; i++) {
1251
+ const g = layout.glyphs[run.charStart + i];
1252
+ if (isEmpty(g.flags)) continue;
1253
+ const b = g.tight ?? g.loose;
1254
+ if (q.x < b.left - half || q.x > b.right + half || q.y < b.bottom - half || q.y > b.top + half) continue;
1255
+ const dx = Math.min(Math.abs(q.x - b.left), Math.abs(q.x - b.right));
1256
+ const dy = Math.min(Math.abs(q.y - b.bottom), Math.abs(q.y - b.top));
1257
+ if (dx + dy < bestDist) {
1258
+ bestDist = dx + dy;
1259
+ best = run.charStart + i;
1260
+ }
1261
+ }
1262
+ }
1263
+ return best >= 0 ? best : null;
1264
+ }
1265
+ /** Double-click: the word around `glyph` (walk to space/empty glyphs both ways). */
1266
+ function expandTextRangeToWord(layout, glyph) {
1267
+ const n = layout.glyphs.length;
1268
+ if (glyph < 0 || glyph >= n) return [glyph, glyph];
1269
+ let from = glyph;
1270
+ while (from > 0 && !isBoundary(layout.glyphs[from - 1].flags)) from--;
1271
+ let to = glyph;
1272
+ while (to < n - 1 && !isBoundary(layout.glyphs[to + 1].flags)) to++;
1273
+ return [from, to];
1274
+ }
1275
+ /** Triple-click: the full visual line — SAME-FRAME runs whose vertical extent
1276
+ * overlaps the anchor run's. A differently-oriented run is a line boundary. */
1277
+ function expandTextRangeToLine(layout, glyph) {
1278
+ const ri = layout.runs.findIndex((r) => glyph >= r.charStart && glyph < r.charStart + r.count);
1279
+ if (ri < 0) return [glyph, glyph];
1280
+ const anchor = layout.runs[ri];
1281
+ const bottom = anchor.rect.bottom;
1282
+ const top = anchor.rect.top;
1283
+ let from = anchor.charStart;
1284
+ let to = anchor.charStart + anchor.count - 1;
1285
+ for (let r = ri - 1; r >= 0; r--) {
1286
+ const run = layout.runs[r];
1287
+ if (isZero(run.rect)) continue;
1288
+ if (run.frame !== anchor.frame) break;
1289
+ if (!overlapV(run.rect.bottom, run.rect.top, bottom, top)) break;
1290
+ from = run.charStart;
1291
+ }
1292
+ for (let r = ri + 1; r < layout.runs.length; r++) {
1293
+ const run = layout.runs[r];
1294
+ if (isZero(run.rect)) continue;
1295
+ if (run.frame !== anchor.frame) break;
1296
+ if (!overlapV(run.rect.bottom, run.rect.top, bottom, top)) break;
1297
+ to = run.charStart + run.count - 1;
1298
+ }
1299
+ return [from, to];
1300
+ }
1301
+ /**
1302
+ * A single glyph's oriented cell in page space, or null for degenerate
1303
+ * glyphs — the anchor for caret placement at a selection boundary.
1304
+ */
1305
+ function textGlyphQuad(layout, glyph) {
1306
+ const g = layout.glyphs[glyph];
1307
+ if (!g || isEmpty(g.flags)) return null;
1308
+ const b = g.loose;
1309
+ if (b.right <= b.left || b.top <= b.bottom) return null;
1310
+ const run = layout.runs.find((r) => glyph >= r.charStart && glyph < r.charStart + r.count);
1311
+ const f = layout.frames[run?.frame ?? 0];
1312
+ return quadOfFrameRect(f, run?.frame ?? 0, b);
1313
+ }
1314
+ /**
1315
+ * Merged visual-line segments for the half-open char range
1316
+ * `[charStart, charStart + charCount)` — THE canonical segmentation. Chars
1317
+ * outside the layout are ignored.
1318
+ */
1319
+ function textSegmentsForRange(layout, charStart, charCount) {
1320
+ if (charCount <= 0 || layout.glyphs.length === 0) return [];
1321
+ const lo = Math.max(0, charStart);
1322
+ const hi = Math.min(layout.glyphs.length - 1, charStart + charCount - 1);
1323
+ if (hi < lo) return [];
1324
+ const subRuns = [];
1325
+ for (const run of layout.runs) {
1326
+ const runEnd = run.charStart + run.count - 1;
1041
1327
  if (runEnd < lo || run.charStart > hi) continue;
1042
1328
  const s = Math.max(lo, run.charStart);
1043
1329
  const e = Math.min(hi, runEnd);
@@ -1045,63 +1331,141 @@ function searchRectsForRange(snapshot, charStart, charCount) {
1045
1331
  let right = -Infinity;
1046
1332
  let bottom = Infinity;
1047
1333
  let top = -Infinity;
1048
- let count = 0;
1334
+ let charCountAcc = 0;
1049
1335
  let widthSum = 0;
1050
1336
  let prevRight = -Infinity;
1337
+ let firstX = 0;
1338
+ let lastX = 0;
1051
1339
  const flush = () => {
1052
- if (count > 0 && right > left && top > bottom) subRuns.push({
1340
+ if (charCountAcc > 0 && left !== Infinity) subRuns.push({
1053
1341
  rect: {
1054
1342
  left,
1055
1343
  bottom,
1056
1344
  right,
1057
1345
  top
1058
1346
  },
1059
- charCount: count,
1060
- fontSize: run.fontSize
1347
+ charCount: charCountAcc,
1348
+ fontSize: run.fontSize,
1349
+ frame: run.frame,
1350
+ firstX,
1351
+ lastX
1061
1352
  });
1062
1353
  left = Infinity;
1063
1354
  right = -Infinity;
1064
1355
  bottom = Infinity;
1065
1356
  top = -Infinity;
1066
- count = 0;
1357
+ charCountAcc = 0;
1067
1358
  widthSum = 0;
1068
1359
  prevRight = -Infinity;
1069
1360
  };
1070
1361
  for (let ci = s; ci <= e; ci++) {
1071
- const g = run.glyphs[ci - run.charStart];
1072
- if ((g.flags & FLAG_EMPTY) !== 0) continue;
1073
- const b = g.looseBox;
1074
- if (count > 0 && prevRight > -Infinity) {
1075
- const avg = widthSum / count;
1362
+ const g = layout.glyphs[ci];
1363
+ if (isEmpty(g.flags)) continue;
1364
+ const b = g.loose;
1365
+ if (charCountAcc > 0 && prevRight > -Infinity) {
1366
+ const avg = widthSum / charCountAcc;
1076
1367
  if (avg > 0 && Math.abs(b.left - prevRight) > CHAR_DISTANCE_FACTOR * avg) flush();
1077
1368
  }
1369
+ const centerX = (b.left + b.right) / 2;
1370
+ if (charCountAcc === 0) firstX = centerX;
1371
+ lastX = centerX;
1078
1372
  left = Math.min(left, b.left);
1079
1373
  right = Math.max(right, b.right);
1080
1374
  bottom = Math.min(bottom, b.bottom);
1081
1375
  top = Math.max(top, b.top);
1082
- count++;
1376
+ charCountAcc++;
1083
1377
  widthSum += b.right - b.left;
1084
1378
  prevRight = b.right;
1085
1379
  }
1086
1380
  flush();
1087
1381
  }
1088
- return mergeAdjacentRects(subRuns);
1382
+ return mergeAdjacentSubRuns(subRuns).map((m) => materializeSegment(layout, m));
1089
1383
  }
1090
- function mergeAdjacentRects(runs) {
1384
+ function mergeAdjacentSubRuns(runs) {
1091
1385
  const out = [];
1092
1386
  let prev = null;
1093
1387
  let cur = null;
1094
1388
  for (const run of runs) {
1095
- if (prev && cur && shouldMerge(prev, run)) cur = union(cur, run.rect);
1389
+ if (prev && cur && prev.frame === run.frame && shouldMerge(prev, run)) cur = {
1390
+ rect: {
1391
+ left: Math.min(cur.rect.left, run.rect.left),
1392
+ bottom: Math.min(cur.rect.bottom, run.rect.bottom),
1393
+ right: Math.max(cur.rect.right, run.rect.right),
1394
+ top: Math.max(cur.rect.top, run.rect.top)
1395
+ },
1396
+ frame: cur.frame,
1397
+ firstX: cur.firstX,
1398
+ lastX: run.lastX
1399
+ };
1096
1400
  else {
1097
1401
  if (cur) out.push(cur);
1098
- cur = run.rect;
1402
+ cur = {
1403
+ rect: run.rect,
1404
+ frame: run.frame,
1405
+ firstX: run.firstX,
1406
+ lastX: run.lastX
1407
+ };
1099
1408
  }
1100
1409
  prev = run;
1101
1410
  }
1102
- if (cur) out.push(cur);
1411
+ if (cur && cur.rect.right > cur.rect.left && cur.rect.top > cur.rect.bottom) out.push(cur);
1103
1412
  return out;
1104
1413
  }
1414
+ /** Segment quad for a frame-local rect (frame 0: the rect's own corners,
1415
+ * no float round-trip). Slot order US, UE, LS, LE; frame is y-up, so the
1416
+ * upper corners sit at the frame TOP. */
1417
+ function quadOfFrameRect(f, frameIndex, r) {
1418
+ if (frameIndex === 0) return {
1419
+ p1: {
1420
+ x: r.left,
1421
+ y: r.top
1422
+ },
1423
+ p2: {
1424
+ x: r.right,
1425
+ y: r.top
1426
+ },
1427
+ p3: {
1428
+ x: r.left,
1429
+ y: r.bottom
1430
+ },
1431
+ p4: {
1432
+ x: r.right,
1433
+ y: r.bottom
1434
+ }
1435
+ };
1436
+ return {
1437
+ p1: fromFrame(f, {
1438
+ x: r.left,
1439
+ y: r.top
1440
+ }),
1441
+ p2: fromFrame(f, {
1442
+ x: r.right,
1443
+ y: r.top
1444
+ }),
1445
+ p3: fromFrame(f, {
1446
+ x: r.left,
1447
+ y: r.bottom
1448
+ }),
1449
+ p4: fromFrame(f, {
1450
+ x: r.right,
1451
+ y: r.bottom
1452
+ })
1453
+ };
1454
+ }
1455
+ function materializeSegment(layout, m) {
1456
+ const frame = layout.frames[m.frame];
1457
+ const quad = quadOfFrameRect(frame, m.frame, m.rect);
1458
+ return {
1459
+ quad,
1460
+ rect: m.frame === 0 ? m.rect : {
1461
+ left: Math.min(quad.p1.x, quad.p2.x, quad.p3.x, quad.p4.x),
1462
+ bottom: Math.min(quad.p1.y, quad.p2.y, quad.p3.y, quad.p4.y),
1463
+ right: Math.max(quad.p1.x, quad.p2.x, quad.p3.x, quad.p4.x),
1464
+ top: Math.max(quad.p1.y, quad.p2.y, quad.p3.y, quad.p4.y)
1465
+ },
1466
+ advance: m.lastX >= m.firstX ? 1 : -1
1467
+ };
1468
+ }
1105
1469
  function shouldMerge(a, b) {
1106
1470
  if (a.fontSize != null && b.fontSize != null && a.fontSize > 0 && b.fontSize > 0) {
1107
1471
  if (Math.max(a.fontSize, b.fontSize) / Math.min(a.fontSize, b.fontSize) > FONT_SIZE_RATIO_THRESHOLD) return false;
@@ -1114,14 +1478,6 @@ function shouldMerge(a, b) {
1114
1478
  const bL = b.rect.left - bw;
1115
1479
  return aL < b.rect.right + bw && aR > bL;
1116
1480
  }
1117
- function union(a, b) {
1118
- return {
1119
- left: Math.min(a.left, b.left),
1120
- bottom: Math.min(a.bottom, b.bottom),
1121
- right: Math.max(a.right, b.right),
1122
- top: Math.max(a.top, b.top)
1123
- };
1124
- }
1125
1481
  function verticalOverlap(a, b) {
1126
1482
  const ah = a.top - a.bottom;
1127
1483
  const bh = b.top - b.bottom;
@@ -1130,6 +1486,28 @@ function verticalOverlap(a, b) {
1130
1486
  if (u === ah || u === bh) return 1;
1131
1487
  return Math.max(0, Math.min(a.top, b.top) - Math.max(a.bottom, b.bottom)) / u;
1132
1488
  }
1489
+ const isZero = (r) => r.right === r.left && r.top === r.bottom;
1490
+ function overlapV(bottom1, top1, bottom2, top2) {
1491
+ const u = Math.max(top1, top2) - Math.min(bottom1, bottom2);
1492
+ if (u === 0) return false;
1493
+ return Math.max(0, Math.min(top1, top2) - Math.max(bottom1, bottom2)) / u >= LINE_OVERLAP_THRESHOLD;
1494
+ }
1495
+ //#endregion
1496
+ //#region src/search/rects.ts
1497
+ /**
1498
+ * Match-range → canonical visual-line segments. Thin wrappers over the ONE
1499
+ * text layout (`src/text/layout.ts`) — search owns matching and cursors,
1500
+ * never segmentation, so a match highlights exactly like a selection of the
1501
+ * same characters. No frame or merge logic lives here.
1502
+ */
1503
+ /** Canonical segments for `[charStart, charStart + charCount)`. */
1504
+ function searchSegmentsForRange(snapshot, charStart, charCount) {
1505
+ return textSegmentsForRange(buildPageTextLayout(snapshot), charStart, charCount);
1506
+ }
1507
+ /** The segments' AABBs — for consumers that genuinely want boxes. */
1508
+ function searchRectsForRange(snapshot, charStart, charCount) {
1509
+ return searchSegmentsForRange(snapshot, charStart, charCount).map((s) => s.rect);
1510
+ }
1133
1511
  //#endregion
1134
1512
  //#region src/search/epoch.ts
1135
1513
  /**
@@ -1404,6 +1782,18 @@ Object.defineProperty(exports, "appearanceImpactOf", {
1404
1782
  return appearanceImpactOf;
1405
1783
  }
1406
1784
  });
1785
+ Object.defineProperty(exports, "buildCommentThreads", {
1786
+ enumerable: true,
1787
+ get: function() {
1788
+ return buildCommentThreads;
1789
+ }
1790
+ });
1791
+ Object.defineProperty(exports, "buildPageTextLayout", {
1792
+ enumerable: true,
1793
+ get: function() {
1794
+ return buildPageTextLayout;
1795
+ }
1796
+ });
1407
1797
  Object.defineProperty(exports, "buildSnippet", {
1408
1798
  enumerable: true,
1409
1799
  get: function() {
@@ -1482,6 +1872,18 @@ Object.defineProperty(exports, "encodeStableIdKey", {
1482
1872
  return encodeStableIdKey;
1483
1873
  }
1484
1874
  });
1875
+ Object.defineProperty(exports, "expandTextRangeToLine", {
1876
+ enumerable: true,
1877
+ get: function() {
1878
+ return expandTextRangeToLine;
1879
+ }
1880
+ });
1881
+ Object.defineProperty(exports, "expandTextRangeToWord", {
1882
+ enumerable: true,
1883
+ get: function() {
1884
+ return expandTextRangeToWord;
1885
+ }
1886
+ });
1485
1887
  Object.defineProperty(exports, "foldOptionsFor", {
1486
1888
  enumerable: true,
1487
1889
  get: function() {
@@ -1494,6 +1896,12 @@ Object.defineProperty(exports, "invalidatesWeakIndexRefs", {
1494
1896
  return invalidatesWeakIndexRefs;
1495
1897
  }
1496
1898
  });
1899
+ Object.defineProperty(exports, "isStateAnnotation", {
1900
+ enumerable: true,
1901
+ get: function() {
1902
+ return isStateAnnotation;
1903
+ }
1904
+ });
1497
1905
  Object.defineProperty(exports, "isValidPageObjectNumber", {
1498
1906
  enumerable: true,
1499
1907
  get: function() {
@@ -1548,66 +1956,12 @@ Object.defineProperty(exports, "normalizeAttachmentFileSource", {
1548
1956
  return normalizeAttachmentFileSource;
1549
1957
  }
1550
1958
  });
1551
- Object.defineProperty(exports, "normalizePdfRect", {
1552
- enumerable: true,
1553
- get: function() {
1554
- return normalizePdfRect;
1555
- }
1556
- });
1557
1959
  Object.defineProperty(exports, "pdfPermissions", {
1558
1960
  enumerable: true,
1559
1961
  get: function() {
1560
1962
  return pdfPermissions;
1561
1963
  }
1562
1964
  });
1563
- Object.defineProperty(exports, "pdfQuadBounds", {
1564
- enumerable: true,
1565
- get: function() {
1566
- return pdfQuadBounds;
1567
- }
1568
- });
1569
- Object.defineProperty(exports, "pdfQuadCorners", {
1570
- enumerable: true,
1571
- get: function() {
1572
- return pdfQuadCorners;
1573
- }
1574
- });
1575
- Object.defineProperty(exports, "pdfQuadFromCorners", {
1576
- enumerable: true,
1577
- get: function() {
1578
- return pdfQuadFromCorners;
1579
- }
1580
- });
1581
- Object.defineProperty(exports, "pdfRectFromOriginSize", {
1582
- enumerable: true,
1583
- get: function() {
1584
- return pdfRectFromOriginSize;
1585
- }
1586
- });
1587
- Object.defineProperty(exports, "pdfRectHeight", {
1588
- enumerable: true,
1589
- get: function() {
1590
- return pdfRectHeight;
1591
- }
1592
- });
1593
- Object.defineProperty(exports, "pdfRectSize", {
1594
- enumerable: true,
1595
- get: function() {
1596
- return pdfRectSize;
1597
- }
1598
- });
1599
- Object.defineProperty(exports, "pdfRectToOriginSize", {
1600
- enumerable: true,
1601
- get: function() {
1602
- return pdfRectToOriginSize;
1603
- }
1604
- });
1605
- Object.defineProperty(exports, "pdfRectWidth", {
1606
- enumerable: true,
1607
- get: function() {
1608
- return pdfRectWidth;
1609
- }
1610
- });
1611
1965
  Object.defineProperty(exports, "refKey", {
1612
1966
  enumerable: true,
1613
1967
  get: function() {
@@ -1638,6 +1992,12 @@ Object.defineProperty(exports, "searchRectsForRange", {
1638
1992
  return searchRectsForRange;
1639
1993
  }
1640
1994
  });
1995
+ Object.defineProperty(exports, "searchSegmentsForRange", {
1996
+ enumerable: true,
1997
+ get: function() {
1998
+ return searchSegmentsForRange;
1999
+ }
2000
+ });
1641
2001
  Object.defineProperty(exports, "semanticEqual", {
1642
2002
  enumerable: true,
1643
2003
  get: function() {
@@ -1656,6 +2016,24 @@ Object.defineProperty(exports, "sniffBinaryMetadata", {
1656
2016
  return sniffBinaryMetadata;
1657
2017
  }
1658
2018
  });
2019
+ Object.defineProperty(exports, "textGlyphAt", {
2020
+ enumerable: true,
2021
+ get: function() {
2022
+ return textGlyphAt;
2023
+ }
2024
+ });
2025
+ Object.defineProperty(exports, "textGlyphQuad", {
2026
+ enumerable: true,
2027
+ get: function() {
2028
+ return textGlyphQuad;
2029
+ }
2030
+ });
2031
+ Object.defineProperty(exports, "textSegmentsForRange", {
2032
+ enumerable: true,
2033
+ get: function() {
2034
+ return textSegmentsForRange;
2035
+ }
2036
+ });
1659
2037
  Object.defineProperty(exports, "validateSearchQuery", {
1660
2038
  enumerable: true,
1661
2039
  get: function() {
@@ -1681,4 +2059,4 @@ Object.defineProperty(exports, "wordBefore", {
1681
2059
  }
1682
2060
  });
1683
2061
 
1684
- //# sourceMappingURL=builders-BvikWRuO.cjs.map
2062
+ //# sourceMappingURL=builders-Cp_rlGob.cjs.map