@embedpdf/engine-core 3.0.0-next.14 → 3.0.0-next.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AnnotationList-CkijRHet.d.cts +27163 -0
- package/dist/AnnotationList-CkijRHet.d.ts +27163 -0
- package/dist/Change-CKoxfTNY.js +889 -0
- package/dist/Change-CKoxfTNY.js.map +1 -0
- package/dist/Change-CjImsrN0.cjs +1020 -0
- package/dist/Change-CjImsrN0.cjs.map +1 -0
- package/dist/DocumentRenderService-BfFdFSwI.cjs +120 -0
- package/dist/DocumentRenderService-BfFdFSwI.cjs.map +1 -0
- package/dist/DocumentRenderService-DV3J9lMz.js +85 -0
- package/dist/DocumentRenderService-DV3J9lMz.js.map +1 -0
- package/dist/{DocumentSecurityService-Ca6EK3-3.d.ts → DocumentSecurityService-CU1M6Q7m.d.ts} +100 -112
- package/dist/{DocumentSecurityService-CIx1MT0T.d.cts → DocumentSecurityService-e6vp2de8.d.cts} +100 -112
- package/dist/LocalDocumentHandle-D02FMm-Z.cjs +53 -0
- package/dist/LocalDocumentHandle-D02FMm-Z.cjs.map +1 -0
- package/dist/LocalDocumentHandle-DAPdaOt_.js +30 -0
- package/dist/LocalDocumentHandle-DAPdaOt_.js.map +1 -0
- package/dist/LocalEngine-DFoBm4B3.d.cts +1565 -0
- package/dist/LocalEngine-DPriFujS.d.ts +1565 -0
- package/dist/PageInsertBlankInput-CXHAM7l6.cjs +17 -0
- package/dist/PageInsertBlankInput-CXHAM7l6.cjs.map +1 -0
- package/dist/PageInsertBlankInput-DhVYVJ_S.js +12 -0
- package/dist/PageInsertBlankInput-DhVYVJ_S.js.map +1 -0
- package/dist/authority-B3BklsB4.js +2239 -0
- package/dist/authority-B3BklsB4.js.map +1 -0
- package/dist/{viewport-BWeNHZmb.cjs → authority-DLMNR7ff.cjs} +820 -1647
- package/dist/authority-DLMNR7ff.cjs.map +1 -0
- package/dist/builders-CNj0POeY.js +161 -0
- package/dist/builders-CNj0POeY.js.map +1 -0
- package/dist/builders-Cgdp_YCn.cjs +184 -0
- package/dist/builders-Cgdp_YCn.cjs.map +1 -0
- package/dist/calibrate-7T3fnP8-.js +2404 -0
- package/dist/calibrate-7T3fnP8-.js.map +1 -0
- package/dist/calibrate-DJBXnhLE.cjs +2919 -0
- package/dist/calibrate-DJBXnhLE.cjs.map +1 -0
- package/dist/charmap-Cprti1ta.js +239 -0
- package/dist/charmap-Cprti1ta.js.map +1 -0
- package/dist/charmap-aYlefQP9.cjs +304 -0
- package/dist/charmap-aYlefQP9.cjs.map +1 -0
- package/dist/conformance.cjs +16466 -5247
- package/dist/conformance.cjs.map +1 -1
- package/dist/conformance.d.cts +924 -165
- package/dist/conformance.d.ts +924 -165
- package/dist/conformance.js +16429 -5252
- package/dist/conformance.js.map +1 -1
- package/dist/format-Bp3QFSH1.cjs +574 -0
- package/dist/format-Bp3QFSH1.cjs.map +1 -0
- package/dist/format-Dgg428Kb.js +461 -0
- package/dist/format-Dgg428Kb.js.map +1 -0
- package/dist/index.cjs +323 -116
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +14 -5
- package/dist/pageRefs-B3ur7Ckm.cjs +51 -0
- package/dist/pageRefs-B3ur7Ckm.cjs.map +1 -0
- package/dist/pageRefs-DkbuxSCV.js +40 -0
- package/dist/pageRefs-DkbuxSCV.js.map +1 -0
- package/dist/public.cjs +57 -0
- package/dist/public.d.cts +5 -0
- package/dist/public.d.ts +5 -0
- package/dist/public.js +9 -0
- package/dist/resolveAnnotationDraft-7Ke4nV7F.cjs +3646 -0
- package/dist/resolveAnnotationDraft-7Ke4nV7F.cjs.map +1 -0
- package/dist/resolveAnnotationDraft-CUpf0SMh.js +3167 -0
- package/dist/resolveAnnotationDraft-CUpf0SMh.js.map +1 -0
- package/dist/{resolver-inAVzsWt.js → resolver-CzxmD9Ck.js} +294 -144
- package/dist/resolver-CzxmD9Ck.js.map +1 -0
- package/dist/{resolver-CYVNV4hn.cjs → resolver-D2OCtE8F.cjs} +347 -143
- package/dist/resolver-D2OCtE8F.cjs.map +1 -0
- package/dist/runtime-Cl0v56mm.d.ts +2032 -0
- package/dist/runtime-p1WDzDo4.d.cts +2032 -0
- package/dist/runtime.cjs +726 -297
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.d.cts +5 -1372
- package/dist/runtime.d.ts +5 -1372
- package/dist/runtime.js +396 -179
- package/dist/runtime.js.map +1 -1
- package/dist/schema-C-gU4vj7.cjs +3034 -0
- package/dist/schema-C-gU4vj7.cjs.map +1 -0
- package/dist/schema-DzlUNk38.js +2081 -0
- package/dist/schema-DzlUNk38.js.map +1 -0
- package/dist/schemas-Berf1swF.cjs +2481 -0
- package/dist/schemas-Berf1swF.cjs.map +1 -0
- package/dist/schemas-D6xOGjqg.js +1654 -0
- package/dist/schemas-D6xOGjqg.js.map +1 -0
- package/dist/subtype-CG4WpIRj.js +129 -0
- package/dist/subtype-CG4WpIRj.js.map +1 -0
- package/dist/subtype-aDt5TFmK.cjs +158 -0
- package/dist/subtype-aDt5TFmK.cjs.map +1 -0
- package/dist/validation.cjs +578 -0
- package/dist/validation.cjs.map +1 -0
- package/dist/validation.d.cts +174 -0
- package/dist/validation.d.ts +174 -0
- package/dist/validation.js +571 -0
- package/dist/validation.js.map +1 -0
- package/dist/value-BFS1R8ek.cjs +435 -0
- package/dist/value-BFS1R8ek.cjs.map +1 -0
- package/dist/value-Dhc8eYcx.js +340 -0
- package/dist/value-Dhc8eYcx.js.map +1 -0
- package/dist/viewport-BJW13EGZ.js +540 -0
- package/dist/viewport-BJW13EGZ.js.map +1 -0
- package/dist/viewport-DlL5Bxbg.cjs +671 -0
- package/dist/viewport-DlL5Bxbg.cjs.map +1 -0
- package/dist/wire.cjs +1051 -397
- package/dist/wire.cjs.map +1 -1
- package/dist/wire.d.cts +12403 -790
- package/dist/wire.d.ts +12403 -790
- package/dist/wire.js +821 -242
- package/dist/wire.js.map +1 -1
- package/package.json +10 -1
- package/dist/AbortError-DwuHPK_I.js +0 -27
- package/dist/AbortError-DwuHPK_I.js.map +0 -1
- package/dist/AbortError-P0RU0Mgg.cjs +0 -38
- package/dist/AbortError-P0RU0Mgg.cjs.map +0 -1
- package/dist/AnnotationListSnapshot-D9R5n2Kf.d.cts +0 -7101
- package/dist/AnnotationListSnapshot-D9R5n2Kf.d.ts +0 -7101
- package/dist/Engine-EWWHhEIi.d.ts +0 -1277
- package/dist/Engine-FNaAEjfb.d.cts +0 -1277
- package/dist/derive-BbsDk_Kz.cjs +0 -880
- package/dist/derive-BbsDk_Kz.cjs.map +0 -1
- package/dist/derive-DAFJolWu.js +0 -647
- package/dist/derive-DAFJolWu.js.map +0 -1
- package/dist/format-Bbphf4zR.js +0 -435
- package/dist/format-Bbphf4zR.js.map +0 -1
- package/dist/format-D_epWlob.cjs +0 -518
- package/dist/format-D_epWlob.cjs.map +0 -1
- package/dist/resolver-CYVNV4hn.cjs.map +0 -1
- package/dist/resolver-inAVzsWt.js.map +0 -1
- package/dist/schemas-CFQGvWiu.js +0 -3243
- package/dist/schemas-CFQGvWiu.js.map +0 -1
- package/dist/schemas-D0g6tKa9.cjs +0 -4694
- package/dist/schemas-D0g6tKa9.cjs.map +0 -1
- package/dist/viewport-BWeNHZmb.cjs.map +0 -1
- package/dist/viewport-CvMcadfQ.js +0 -3072
- package/dist/viewport-CvMcadfQ.js.map +0 -1
|
@@ -0,0 +1,540 @@
|
|
|
1
|
+
import { G as assertRowResources, H as assertKnownFields, J as isBundlePageRef, U as assertResourceIds, V as assertBundlePages, W as assertResourcesNamed, Y as isRecord, q as invalidBundle } from "./resolveAnnotationDraft-CUpf0SMh.js";
|
|
2
|
+
import { h as EngineError, u as encodePageKey, v as _defineProperty, y as EngineErrorCode } from "./format-Dgg428Kb.js";
|
|
3
|
+
import { at as DEFAULT_BUNDLE_LIMITS, c as foldText, gt as AbortError, it as bundleCodec, l as toOriginalRange, ot as assertWithinLimit, st as manifestBytesOf } from "./calibrate-7T3fnP8-.js";
|
|
4
|
+
//#region src/promise/AbortablePromise.ts
|
|
5
|
+
let _Symbol$species;
|
|
6
|
+
_Symbol$species = Symbol.species;
|
|
7
|
+
/**
|
|
8
|
+
* Awaitable Promise subclass with abort and progress.
|
|
9
|
+
*
|
|
10
|
+
* Symbol.species returns plain Promise so chained .then/.catch/.finally
|
|
11
|
+
* return regular promises and never try to synthesize a new
|
|
12
|
+
* AbortablePromise (which would have no executor).
|
|
13
|
+
*
|
|
14
|
+
* Settlement contract:
|
|
15
|
+
* - The first settlement wins. resolve/reject handed to the executor
|
|
16
|
+
* are wrapped so that any subsequent call (whether by the executor,
|
|
17
|
+
* by abort(), or by both racing) becomes a silent no-op.
|
|
18
|
+
* - abort() rejects the public promise with AbortError immediately,
|
|
19
|
+
* even if the executor never observes the AbortSignal. The executor
|
|
20
|
+
* still receives the AbortSignal so it can stop real work
|
|
21
|
+
* cooperatively (release a queue slot, abort fetch, post a worker
|
|
22
|
+
* abort, etc.). Anything the executor does after that — including
|
|
23
|
+
* a late resolve(value), a late reject(error), or a rejected inner
|
|
24
|
+
* Promise inside AbortablePromise.run — is dropped silently.
|
|
25
|
+
* - abort() after settlement is a no-op.
|
|
26
|
+
*/
|
|
27
|
+
var AbortablePromise = class AbortablePromise extends Promise {
|
|
28
|
+
static get [_Symbol$species]() {
|
|
29
|
+
return Promise;
|
|
30
|
+
}
|
|
31
|
+
constructor(executor) {
|
|
32
|
+
const ctrl = new AbortController();
|
|
33
|
+
const progressCbs = /* @__PURE__ */ new Set();
|
|
34
|
+
const state = { settled: false };
|
|
35
|
+
let capturedReject;
|
|
36
|
+
super((resolve, reject) => {
|
|
37
|
+
capturedReject = reject;
|
|
38
|
+
const wrappedResolve = (value) => {
|
|
39
|
+
if (state.settled) return;
|
|
40
|
+
state.settled = true;
|
|
41
|
+
resolve(value);
|
|
42
|
+
};
|
|
43
|
+
const wrappedReject = (reason) => {
|
|
44
|
+
if (state.settled) return;
|
|
45
|
+
state.settled = true;
|
|
46
|
+
reject(reason);
|
|
47
|
+
};
|
|
48
|
+
const progress = (p) => {
|
|
49
|
+
if (state.settled) return;
|
|
50
|
+
for (const cb of progressCbs) try {
|
|
51
|
+
cb(p);
|
|
52
|
+
} catch {}
|
|
53
|
+
};
|
|
54
|
+
executor(wrappedResolve, wrappedReject, progress, ctrl.signal);
|
|
55
|
+
});
|
|
56
|
+
_defineProperty(this, "signal", void 0);
|
|
57
|
+
_defineProperty(this, "_ctrl", void 0);
|
|
58
|
+
_defineProperty(this, "_progressCbs", void 0);
|
|
59
|
+
_defineProperty(this, "_state", void 0);
|
|
60
|
+
_defineProperty(this, "_internalReject", void 0);
|
|
61
|
+
this._ctrl = ctrl;
|
|
62
|
+
this.signal = ctrl.signal;
|
|
63
|
+
this._progressCbs = progressCbs;
|
|
64
|
+
this._state = state;
|
|
65
|
+
this._internalReject = capturedReject;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Abort the underlying operation.
|
|
69
|
+
*
|
|
70
|
+
* - If the promise has already settled, this is a no-op.
|
|
71
|
+
* - Otherwise the public promise rejects synchronously with an
|
|
72
|
+
* AbortError (wrapping the supplied reason if it is not already an
|
|
73
|
+
* AbortError) and the AbortSignal fires for any cooperative
|
|
74
|
+
* cancellation the executor wired up.
|
|
75
|
+
*/
|
|
76
|
+
abort(reason) {
|
|
77
|
+
if (this._state.settled) return;
|
|
78
|
+
if (this._ctrl.signal.aborted) return;
|
|
79
|
+
const err = reason instanceof AbortError ? reason : new AbortError(reason);
|
|
80
|
+
this._state.settled = true;
|
|
81
|
+
this._ctrl.abort(err);
|
|
82
|
+
this._internalReject(err);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Abort this call when `signal` aborts, at once if it already has: the
|
|
86
|
+
* bridge to code that cancels with an `AbortController` (a framework's
|
|
87
|
+
* cleanup, a `fetch`). The listener is removed when the call settles.
|
|
88
|
+
* Returns this promise, so `await call().abortWith(signal)` reads as one.
|
|
89
|
+
*/
|
|
90
|
+
abortWith(signal) {
|
|
91
|
+
if (signal.aborted) {
|
|
92
|
+
this.abort(signal.reason);
|
|
93
|
+
return this;
|
|
94
|
+
}
|
|
95
|
+
const onAbort = () => this.abort(signal.reason);
|
|
96
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
97
|
+
const detach = () => signal.removeEventListener("abort", onAbort);
|
|
98
|
+
Promise.prototype.then.call(this, detach, detach);
|
|
99
|
+
return this;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Subscribe to progress events. Returns an unsubscribe function.
|
|
103
|
+
*
|
|
104
|
+
* @internal No engine method reports progress today; this is plumbing for
|
|
105
|
+
* one that will, typed then per method. Not part of the public contract.
|
|
106
|
+
*/
|
|
107
|
+
onProgress(cb) {
|
|
108
|
+
this._progressCbs.add(cb);
|
|
109
|
+
return () => {
|
|
110
|
+
this._progressCbs.delete(cb);
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Wrap a value in an already-resolved AbortablePromise. Mirrors Promise.resolve.
|
|
115
|
+
*/
|
|
116
|
+
static resolveValue(value) {
|
|
117
|
+
return new AbortablePromise((resolve) => resolve(value));
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Wrap a thrown reason in an already-rejected AbortablePromise.
|
|
121
|
+
*/
|
|
122
|
+
static rejectReason(reason) {
|
|
123
|
+
return new AbortablePromise((_resolve, reject) => reject(reason));
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Run an async function as an AbortablePromise so callers get .abort() and .signal.
|
|
127
|
+
* The async function receives the AbortSignal (and a progress emitter) so it can
|
|
128
|
+
* cooperate with the abort.
|
|
129
|
+
*
|
|
130
|
+
* If abort() fires while the inner async function is still running, the
|
|
131
|
+
* public promise rejects with AbortError immediately. The inner async
|
|
132
|
+
* function's eventual settlement (resolve or reject) is funneled through
|
|
133
|
+
* the wrapped settlers and silently dropped — including a late rejection
|
|
134
|
+
* from the underlying work, which prevents an UnhandledRejection because
|
|
135
|
+
* the .then(resolve, reject) below "handles" it via the no-op.
|
|
136
|
+
*/
|
|
137
|
+
static run(fn) {
|
|
138
|
+
return new AbortablePromise((resolve, reject, progress, signal) => {
|
|
139
|
+
fn(signal, progress).then(resolve, reject);
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
//#endregion
|
|
144
|
+
//#region src/identity/ObjectNumbers.ts
|
|
145
|
+
/**
|
|
146
|
+
* The highest object number a document should reach: Acrobat documents
|
|
147
|
+
* 8,388,607 objects per file, and the interoperable rule keeps every object
|
|
148
|
+
* number at or below it. A write that would pass it is refused with
|
|
149
|
+
* `LayerFull`.
|
|
150
|
+
*/
|
|
151
|
+
const OBJECT_NUMBER_CEILING = 8388607;
|
|
152
|
+
/**
|
|
153
|
+
* The last object number handed out to sessions: issuing stops here, which
|
|
154
|
+
* leaves the numbers above it to the objects the engine makes for itself
|
|
155
|
+
* (appearance streams, fonts) until the ceiling.
|
|
156
|
+
*/
|
|
157
|
+
const OBJECT_NUMBER_ISSUE_LIMIT = 8e6;
|
|
158
|
+
/**
|
|
159
|
+
* Runs of numbers as an HTTP header value, inclusive like an HTTP `Range`:
|
|
160
|
+
* `1003-1035, 2040-2042` (`EmbedPDF-Object-Numbers`).
|
|
161
|
+
*/
|
|
162
|
+
function formatObjectNumberRanges(ranges) {
|
|
163
|
+
return ranges.map(({ first, count }) => `${first}-${first + count - 1}`).join(", ");
|
|
164
|
+
}
|
|
165
|
+
/** The runs a {@link formatObjectNumberRanges} header value names; `[]` for one that names none. */
|
|
166
|
+
function parseObjectNumberRanges(value) {
|
|
167
|
+
const ranges = [];
|
|
168
|
+
for (const part of (value ?? "").split(",")) {
|
|
169
|
+
const match = /^\s*(\d+)-(\d+)\s*$/.exec(part);
|
|
170
|
+
if (!match) continue;
|
|
171
|
+
const first = Number(match[1]);
|
|
172
|
+
const last = Number(match[2]);
|
|
173
|
+
if (last >= first) ranges.push({
|
|
174
|
+
first,
|
|
175
|
+
count: last - first + 1
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
return ranges;
|
|
179
|
+
}
|
|
180
|
+
/** Every number in `ranges`, in order. */
|
|
181
|
+
function objectNumbersIn(ranges) {
|
|
182
|
+
const numbers = [];
|
|
183
|
+
for (const { first, count } of ranges) for (let i = 0; i < count; i++) numbers.push(first + i);
|
|
184
|
+
return numbers;
|
|
185
|
+
}
|
|
186
|
+
/** The manifest's keys, in the order a file writes them. */
|
|
187
|
+
const FORM_BUNDLE_KEYS = [
|
|
188
|
+
"format",
|
|
189
|
+
"version",
|
|
190
|
+
"pages",
|
|
191
|
+
"fields",
|
|
192
|
+
"widgets",
|
|
193
|
+
"calculationOrder"
|
|
194
|
+
];
|
|
195
|
+
/** The resource roles a widget names: none yet. */
|
|
196
|
+
const WIDGET_RESOURCE_ROLES = [];
|
|
197
|
+
const BUNDLE_FIELDS = [...FORM_BUNDLE_KEYS, "resources"];
|
|
198
|
+
const FIELD_ROW_FIELDS = ["data"];
|
|
199
|
+
const WIDGET_ROW_FIELDS = ["data", "resources"];
|
|
200
|
+
/**
|
|
201
|
+
* Check a bundle before anything uses it, the checks cheapest first: its
|
|
202
|
+
* format, its counts and sizes against `limits` (`PayloadTooLarge`), its
|
|
203
|
+
* shape and references (`InvalidArg`), then every resource against its id.
|
|
204
|
+
*/
|
|
205
|
+
async function assertFormBundle(bundle, limits = DEFAULT_BUNDLE_LIMITS) {
|
|
206
|
+
const resources = isRecord(bundle?.resources) ? bundle.resources : invalidBundle("form", "no resources");
|
|
207
|
+
const sizes = /* @__PURE__ */ new Map();
|
|
208
|
+
for (const [id, bytes] of Object.entries(resources)) {
|
|
209
|
+
if (!(bytes instanceof Uint8Array)) invalidBundle("form", `resource ${id} is not bytes`);
|
|
210
|
+
sizes.set(id, bytes.length);
|
|
211
|
+
}
|
|
212
|
+
assertFormBundleManifest(bundle, sizes, limits);
|
|
213
|
+
await assertResourceIds("form", resources);
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* The checks that need no resource bytes, only their sizes by id: format
|
|
217
|
+
* and version, counts, sizes, the shape of pages, fields and widgets, and
|
|
218
|
+
* that widgets and resources name each other exactly. That a widget's field
|
|
219
|
+
* is among the bundle's is the import's to decide: one that isn't is left
|
|
220
|
+
* out.
|
|
221
|
+
*/
|
|
222
|
+
function assertFormBundleManifest(manifest, resourceSizes, limits) {
|
|
223
|
+
if (!isRecord(manifest)) invalidBundle("form", "not a form bundle");
|
|
224
|
+
assertKnownFields("form", "the bundle", manifest, BUNDLE_FIELDS);
|
|
225
|
+
if (manifest.format !== "embedpdf/form") invalidBundle("form", `unknown format ${JSON.stringify(manifest.format)}`);
|
|
226
|
+
if (manifest.version !== 1) invalidBundle("form", `unknown version ${JSON.stringify(manifest.version)}`);
|
|
227
|
+
const { pages, fields, widgets, calculationOrder } = manifest;
|
|
228
|
+
if (!Array.isArray(pages)) invalidBundle("form", "pages is not a list");
|
|
229
|
+
if (!Array.isArray(fields)) invalidBundle("form", "fields is not a list");
|
|
230
|
+
if (!Array.isArray(widgets)) invalidBundle("form", "widgets is not a list");
|
|
231
|
+
if (!Array.isArray(calculationOrder)) invalidBundle("form", "calculationOrder is not a list");
|
|
232
|
+
assertWithinLimit("form", limits, "items", fields.length + widgets.length);
|
|
233
|
+
assertWithinLimit("form", limits, "pages", pages.length);
|
|
234
|
+
assertWithinLimit("form", limits, "resources", resourceSizes.size);
|
|
235
|
+
let bundleBytes = manifestBytesOf({
|
|
236
|
+
pages,
|
|
237
|
+
fields,
|
|
238
|
+
widgets,
|
|
239
|
+
calculationOrder
|
|
240
|
+
});
|
|
241
|
+
assertWithinLimit("form", limits, "manifestBytes", bundleBytes);
|
|
242
|
+
for (const size of resourceSizes.values()) {
|
|
243
|
+
assertWithinLimit("form", limits, "resourceBytes", size);
|
|
244
|
+
bundleBytes += size;
|
|
245
|
+
}
|
|
246
|
+
assertWithinLimit("form", limits, "bundleBytes", bundleBytes);
|
|
247
|
+
const pageKeys = assertBundlePages("form", pages);
|
|
248
|
+
fields.forEach((row, index) => {
|
|
249
|
+
if (!isRecord(row) || !isRecord(row.data) || typeof row.data.family !== "string" || typeof row.data.name !== "string" || !isRecord(row.data.ref)) invalidBundle("form", `field ${index} has no field data`);
|
|
250
|
+
assertKnownFields("form", `field ${index}`, row, FIELD_ROW_FIELDS);
|
|
251
|
+
});
|
|
252
|
+
const named = /* @__PURE__ */ new Set();
|
|
253
|
+
widgets.forEach((row, index) => {
|
|
254
|
+
if (!isRecord(row) || !isRecord(row.data) || row.data.subtype !== "widget") invalidBundle("form", `widget ${index} has no widget data`);
|
|
255
|
+
assertKnownFields("form", `widget ${index}`, row, WIDGET_ROW_FIELDS);
|
|
256
|
+
const ref = row.data.ref;
|
|
257
|
+
const page = isRecord(ref) ? ref.page : void 0;
|
|
258
|
+
if (!isBundlePageRef(page) || !pageKeys.has(encodePageKey(page))) invalidBundle("form", `widget ${index} is on a page the bundle doesn't list`);
|
|
259
|
+
assertRowResources("form", `widget ${index}`, row.resources, WIDGET_RESOURCE_ROLES, resourceSizes, named);
|
|
260
|
+
});
|
|
261
|
+
calculationOrder.forEach((ref, index) => {
|
|
262
|
+
if (!isRecord(ref)) invalidBundle("form", `calculation order entry ${index} is not a field ref`);
|
|
263
|
+
});
|
|
264
|
+
assertResourcesNamed("form", "widget", resourceSizes, named);
|
|
265
|
+
}
|
|
266
|
+
//#endregion
|
|
267
|
+
//#region src/transfer/FormTransfer.ts
|
|
268
|
+
/**
|
|
269
|
+
* A form bundle as one JSON file, each resource in base64: a bundle whole,
|
|
270
|
+
* to store or download. HTTP carries bundles as multipart instead, without
|
|
271
|
+
* the third base64 adds.
|
|
272
|
+
*/
|
|
273
|
+
const FormTransfer = bundleCodec({
|
|
274
|
+
kind: "form",
|
|
275
|
+
keys: FORM_BUNDLE_KEYS,
|
|
276
|
+
assertManifest: assertFormBundleManifest
|
|
277
|
+
});
|
|
278
|
+
//#endregion
|
|
279
|
+
//#region src/search/literal.ts
|
|
280
|
+
/**
|
|
281
|
+
* The fold flavor a literal query needs. Haystack and needle must be
|
|
282
|
+
* folded with the same options; corpus caches key their pre-folded text
|
|
283
|
+
* on this (the default `{}` fold is the persistable one — non-default
|
|
284
|
+
* queries fold the original page text at query time).
|
|
285
|
+
*/
|
|
286
|
+
function foldOptionsFor(query) {
|
|
287
|
+
return {
|
|
288
|
+
keepCase: !!query.matchCase,
|
|
289
|
+
keepMarks: !!query.matchDiacritics,
|
|
290
|
+
dropWhitespace: !!query.ignoreWhitespace
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
const WORD_UNIT = /[\p{L}\p{N}_]/u;
|
|
294
|
+
const MARK = /\p{M}/u;
|
|
295
|
+
/** Whether the code point ending at `index` (exclusive) is a word char. */
|
|
296
|
+
function wordBefore(text, index) {
|
|
297
|
+
if (index <= 0) return false;
|
|
298
|
+
const unit = text.charCodeAt(index - 1);
|
|
299
|
+
const start = unit >= 56320 && unit <= 57343 && index >= 2 ? index - 2 : index - 1;
|
|
300
|
+
return WORD_UNIT.test(String.fromCodePoint(text.codePointAt(start)));
|
|
301
|
+
}
|
|
302
|
+
/** Whether the code point starting at `index` is a word char. */
|
|
303
|
+
function wordAt(text, index) {
|
|
304
|
+
if (index >= text.length) return false;
|
|
305
|
+
return WORD_UNIT.test(String.fromCodePoint(text.codePointAt(index)));
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Whether the folded hit at `at` sits on word boundaries. Checked on the
|
|
309
|
+
* folded plane, except under `ignoreWhitespace`: dropping whitespace glues
|
|
310
|
+
* neighbouring words together ("i n v o i c e 42" folds to "invoice42"), so there the
|
|
311
|
+
* boundaries are read off the original text around the mapped range.
|
|
312
|
+
*/
|
|
313
|
+
function isWholeWordHit(haystack, at, needleLength, query) {
|
|
314
|
+
if (!query.ignoreWhitespace) return !wordBefore(haystack.folded, at) && !wordAt(haystack.folded, at + needleLength);
|
|
315
|
+
const { start, count } = toOriginalRange(haystack, at, needleLength);
|
|
316
|
+
return !wordBefore(haystack.original, start) && !wordAt(haystack.original, start + count);
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* All non-overlapping literal matches, in original code-unit space.
|
|
320
|
+
* `haystack` must have been folded with `foldOptionsFor(query)`.
|
|
321
|
+
*/
|
|
322
|
+
function matchLiteral(haystack, query) {
|
|
323
|
+
const needle = foldText(query.text, foldOptionsFor(query)).folded;
|
|
324
|
+
if (needle.trim().length === 0) return [];
|
|
325
|
+
const out = [];
|
|
326
|
+
let from = 0;
|
|
327
|
+
while (from <= haystack.folded.length - needle.length) {
|
|
328
|
+
const at = haystack.folded.indexOf(needle, from);
|
|
329
|
+
if (at < 0) break;
|
|
330
|
+
if (query.wholeWord && !isWholeWordHit(haystack, at, needle.length, query)) {
|
|
331
|
+
from = at + 1;
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
if (query.matchDiacritics && at + needle.length < haystack.folded.length) {
|
|
335
|
+
const after = haystack.folded.codePointAt(at + needle.length);
|
|
336
|
+
if (MARK.test(String.fromCodePoint(after))) {
|
|
337
|
+
from = at + 1;
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
out.push(toOriginalRange(haystack, at, needle.length));
|
|
342
|
+
from = at + needle.length;
|
|
343
|
+
}
|
|
344
|
+
return out;
|
|
345
|
+
}
|
|
346
|
+
//#endregion
|
|
347
|
+
//#region src/search/regex.ts
|
|
348
|
+
/**
|
|
349
|
+
* The portable search-regex dialect: JavaScript `u`-mode syntax minus the
|
|
350
|
+
* features RE2 refuses (backreferences, lookaround). The local engine
|
|
351
|
+
* executes patterns with JS `RegExp`; the server executes the same
|
|
352
|
+
* pattern with RE2, whose linear-time guarantee is the ReDoS defense.
|
|
353
|
+
* Restricting both sides to the common subset keeps one query string
|
|
354
|
+
* valid — and equally powerful — everywhere.
|
|
355
|
+
*/
|
|
356
|
+
const SEARCH_REGEX_MAX_LENGTH = 512;
|
|
357
|
+
/**
|
|
358
|
+
* Validate a pattern against the dialect. Cheap and dependency-free —
|
|
359
|
+
* viewers call it on keystroke for early feedback; engines re-validate
|
|
360
|
+
* and reject bad patterns with `InvalidArg`.
|
|
361
|
+
*/
|
|
362
|
+
function validateSearchRegex(pattern) {
|
|
363
|
+
if (pattern.length === 0) return {
|
|
364
|
+
ok: false,
|
|
365
|
+
issue: "empty",
|
|
366
|
+
message: "Pattern is empty."
|
|
367
|
+
};
|
|
368
|
+
if (pattern.length > 512) return {
|
|
369
|
+
ok: false,
|
|
370
|
+
issue: "too-long",
|
|
371
|
+
message: `Pattern exceeds 512 characters.`
|
|
372
|
+
};
|
|
373
|
+
let inClass = false;
|
|
374
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
375
|
+
const ch = pattern[i];
|
|
376
|
+
if (ch === "\\") {
|
|
377
|
+
const next = pattern[i + 1] ?? "";
|
|
378
|
+
if (!inClass && next >= "1" && next <= "9") return {
|
|
379
|
+
ok: false,
|
|
380
|
+
issue: "backreference",
|
|
381
|
+
message: `Backreference \\${next} is not supported.`
|
|
382
|
+
};
|
|
383
|
+
if (!inClass && next === "k" && pattern[i + 2] === "<") return {
|
|
384
|
+
ok: false,
|
|
385
|
+
issue: "backreference",
|
|
386
|
+
message: "Named backreferences (\\k<…>) are not supported."
|
|
387
|
+
};
|
|
388
|
+
i++;
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
if (inClass) {
|
|
392
|
+
if (ch === "]") inClass = false;
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
395
|
+
if (ch === "[") {
|
|
396
|
+
inClass = true;
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
399
|
+
if (ch === "(" && pattern[i + 1] === "?") {
|
|
400
|
+
const tag = pattern[i + 2] ?? "";
|
|
401
|
+
if (tag === "=" || tag === "!") return {
|
|
402
|
+
ok: false,
|
|
403
|
+
issue: "lookaround",
|
|
404
|
+
message: "Lookahead ((?=…), (?!…)) is not supported."
|
|
405
|
+
};
|
|
406
|
+
if (tag === "<" && (pattern[i + 3] === "=" || pattern[i + 3] === "!")) return {
|
|
407
|
+
ok: false,
|
|
408
|
+
issue: "lookaround",
|
|
409
|
+
message: "Lookbehind ((?<=…), (?<!…)) is not supported."
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
try {
|
|
414
|
+
new RegExp(pattern, "mu");
|
|
415
|
+
} catch (error) {
|
|
416
|
+
return {
|
|
417
|
+
ok: false,
|
|
418
|
+
issue: "syntax",
|
|
419
|
+
message: error.message
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
return { ok: true };
|
|
423
|
+
}
|
|
424
|
+
function validateSearchQuery(query) {
|
|
425
|
+
if (!query.regex) return { ok: true };
|
|
426
|
+
if (query.matchDiacritics) return {
|
|
427
|
+
ok: false,
|
|
428
|
+
issue: "diacritics-with-regex",
|
|
429
|
+
message: "Diacritic-sensitive matching is not available for regex patterns (regex runs on the raw text plane)."
|
|
430
|
+
};
|
|
431
|
+
if (query.ignoreWhitespace) return {
|
|
432
|
+
ok: false,
|
|
433
|
+
issue: "ignore-whitespace-with-regex",
|
|
434
|
+
message: "Whitespace-insensitive matching is not available for regex patterns (regex runs on the raw text plane; use \\s* in the pattern)."
|
|
435
|
+
};
|
|
436
|
+
return validateSearchRegex(query.text);
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* All matches of a dialect-valid pattern over the raw page text (regex
|
|
440
|
+
* does not run against folded text — case handling is the `i` flag, and
|
|
441
|
+
* diacritic folding never applies). `wholeWord` is a boundary POST-filter
|
|
442
|
+
* using the same Unicode word test as the literal path — never `\b`,
|
|
443
|
+
* whose ASCII word set would give the toggle different semantics in regex
|
|
444
|
+
* mode ("caf" whole-word matching inside "café"). Filtering after the
|
|
445
|
+
* match also survives the move to RE2 server-side. Zero-length matches
|
|
446
|
+
* are skipped: search UI cannot highlight nothing, and skipping them is
|
|
447
|
+
* also the infinite-loop guard for patterns like `a*`.
|
|
448
|
+
*/
|
|
449
|
+
function matchRegex(text, query) {
|
|
450
|
+
const valid = validateSearchQuery({
|
|
451
|
+
...query,
|
|
452
|
+
regex: true
|
|
453
|
+
});
|
|
454
|
+
if (!valid.ok) throw new Error(`Invalid search query (${valid.issue}): ${valid.message}`);
|
|
455
|
+
const re = new RegExp(query.text, query.matchCase ? "gmu" : "gimu");
|
|
456
|
+
const out = [];
|
|
457
|
+
let m;
|
|
458
|
+
while ((m = re.exec(text)) !== null) {
|
|
459
|
+
if (m[0].length === 0) {
|
|
460
|
+
const unit = text.charCodeAt(re.lastIndex);
|
|
461
|
+
re.lastIndex += unit >= 55296 && unit <= 56319 ? 2 : 1;
|
|
462
|
+
continue;
|
|
463
|
+
}
|
|
464
|
+
if (query.wholeWord && (wordBefore(text, m.index) || wordAt(text, m.index + m[0].length))) continue;
|
|
465
|
+
out.push({
|
|
466
|
+
start: m.index,
|
|
467
|
+
count: m[0].length
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
return out;
|
|
471
|
+
}
|
|
472
|
+
//#endregion
|
|
473
|
+
//#region src/mutation/ListPosition.ts
|
|
474
|
+
/** The neighbour a position names, or `null` for `'start'` and `'end'`. */
|
|
475
|
+
function anchorOf(position) {
|
|
476
|
+
if (position === "start" || position === "end") return null;
|
|
477
|
+
return "before" in position ? position.before : position.after;
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Where `position` falls in `order`: the index the first row placed there
|
|
481
|
+
* gets. The neighbour must be in `order` (`NotFound` otherwise).
|
|
482
|
+
*/
|
|
483
|
+
function positionIndex(order, position, keyOf) {
|
|
484
|
+
if (position === "start") return 0;
|
|
485
|
+
if (position === "end") return order.length;
|
|
486
|
+
const anchor = anchorOf(position);
|
|
487
|
+
const at = order.findIndex((ref) => keyOf(ref) === keyOf(anchor));
|
|
488
|
+
if (at < 0) throw new EngineError(EngineErrorCode.NotFound, `no neighbour ${keyOf(anchor)} in the list`, { details: { field: "position" } });
|
|
489
|
+
return "before" in position ? at : at + 1;
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* A list's order once `moved` go to `position`, together and in the order
|
|
493
|
+
* given: what a reorder answers, worked out as the engine does. Every moved
|
|
494
|
+
* row must be in `order`, once (`InvalidArg` otherwise), and the neighbour
|
|
495
|
+
* can't be one of them (`InvalidArg`).
|
|
496
|
+
*/
|
|
497
|
+
function reorderedList(order, moved, position, keyOf) {
|
|
498
|
+
const movedKeys = new Set(moved.map(keyOf));
|
|
499
|
+
if (movedKeys.size !== moved.length) throw new EngineError(EngineErrorCode.InvalidArg, "a row is named twice", { details: { field: "refs" } });
|
|
500
|
+
const present = new Set(order.map(keyOf));
|
|
501
|
+
const missing = moved.find((ref) => !present.has(keyOf(ref)));
|
|
502
|
+
if (missing !== void 0) throw new EngineError(EngineErrorCode.NotFound, `no row ${keyOf(missing)} in the list`, { details: { field: "refs" } });
|
|
503
|
+
const anchor = anchorOf(position);
|
|
504
|
+
if (anchor !== null && movedKeys.has(keyOf(anchor))) throw new EngineError(EngineErrorCode.InvalidArg, `the neighbour ${keyOf(anchor)} is one of the rows that move`, { details: { field: "position" } });
|
|
505
|
+
const staying = order.filter((ref) => !movedKeys.has(keyOf(ref)));
|
|
506
|
+
const at = positionIndex(staying, position, keyOf);
|
|
507
|
+
return [
|
|
508
|
+
...staying.slice(0, at),
|
|
509
|
+
...moved,
|
|
510
|
+
...staying.slice(at)
|
|
511
|
+
];
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* `list` with the items `inPart` picks put in the order `order` gives them,
|
|
515
|
+
* by key, in the slots those items have: every other item keeps its place.
|
|
516
|
+
* How a list holding several pages takes one page's new order. Items of the
|
|
517
|
+
* part `order` doesn't name follow the named ones, in their order.
|
|
518
|
+
*/
|
|
519
|
+
function reorderPart(list, inPart, order, keyOf) {
|
|
520
|
+
const part = list.filter(inPart);
|
|
521
|
+
const byKey = new Map(part.map((item) => [keyOf(item), item]));
|
|
522
|
+
const named = order.flatMap((key) => byKey.has(key) ? [byKey.get(key)] : []);
|
|
523
|
+
const placed = new Set(named);
|
|
524
|
+
const next = [...named, ...part.filter((item) => !placed.has(item))];
|
|
525
|
+
let slot = 0;
|
|
526
|
+
return list.map((item) => inPart(item) ? next[slot++] : item);
|
|
527
|
+
}
|
|
528
|
+
//#endregion
|
|
529
|
+
//#region src/measure/viewport.ts
|
|
530
|
+
/**
|
|
531
|
+
* The last viewport whose box holds `point` (the one drawn on top), or
|
|
532
|
+
* `undefined`. A foreign measure still wins; never guess a fallback.
|
|
533
|
+
*/
|
|
534
|
+
function viewportForPoint(viewports, point) {
|
|
535
|
+
return [...viewports].reverse().find(({ bbox: b }) => point.x >= b.x && point.x <= b.x + b.width && point.y >= b.y && point.y <= b.y + b.height);
|
|
536
|
+
}
|
|
537
|
+
//#endregion
|
|
538
|
+
export { AbortablePromise as S, OBJECT_NUMBER_CEILING as _, reorderedList as a, objectNumbersIn as b, validateSearchQuery as c, matchLiteral as d, wordAt as f, assertFormBundleManifest as g, assertFormBundle as h, reorderPart as i, validateSearchRegex as l, FormTransfer as m, anchorOf as n, SEARCH_REGEX_MAX_LENGTH as o, wordBefore as p, positionIndex as r, matchRegex as s, viewportForPoint as t, foldOptionsFor as u, OBJECT_NUMBER_ISSUE_LIMIT as v, parseObjectNumberRanges as x, formatObjectNumberRanges as y };
|
|
539
|
+
|
|
540
|
+
//# sourceMappingURL=viewport-BJW13EGZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"viewport-BJW13EGZ.js","names":[],"sources":["../src/promise/AbortablePromise.ts","../src/identity/ObjectNumbers.ts","../src/transfer/FormBundle.ts","../src/transfer/FormTransfer.ts","../src/search/literal.ts","../src/search/regex.ts","../src/mutation/ListPosition.ts","../src/measure/viewport.ts"],"sourcesContent":["import { AbortError } from './AbortError';\n\nexport type AbortableExecutor<T, P> = (\n resolve: (value: T | PromiseLike<T>) => void,\n reject: (reason: unknown) => void,\n progress: (p: P) => void,\n signal: AbortSignal,\n) => void;\n\n/**\n * Awaitable Promise subclass with abort and progress.\n *\n * Symbol.species returns plain Promise so chained .then/.catch/.finally\n * return regular promises and never try to synthesize a new\n * AbortablePromise (which would have no executor).\n *\n * Settlement contract:\n * - The first settlement wins. resolve/reject handed to the executor\n * are wrapped so that any subsequent call (whether by the executor,\n * by abort(), or by both racing) becomes a silent no-op.\n * - abort() rejects the public promise with AbortError immediately,\n * even if the executor never observes the AbortSignal. The executor\n * still receives the AbortSignal so it can stop real work\n * cooperatively (release a queue slot, abort fetch, post a worker\n * abort, etc.). Anything the executor does after that — including\n * a late resolve(value), a late reject(error), or a rejected inner\n * Promise inside AbortablePromise.run — is dropped silently.\n * - abort() after settlement is a no-op.\n */\nexport class AbortablePromise<T, P = never> extends Promise<T> {\n static override get [Symbol.species](): PromiseConstructor {\n return Promise;\n }\n\n readonly signal: AbortSignal;\n\n private readonly _ctrl: AbortController;\n private readonly _progressCbs: Set<(p: P) => void>;\n private readonly _state: { settled: boolean };\n private readonly _internalReject: (reason: unknown) => void;\n\n constructor(executor: AbortableExecutor<T, P>) {\n const ctrl = new AbortController();\n const progressCbs = new Set<(p: P) => void>();\n const state = { settled: false };\n let capturedReject!: (reason: unknown) => void;\n\n super((resolve, reject) => {\n capturedReject = reject;\n\n const wrappedResolve = (value: T | PromiseLike<T>) => {\n if (state.settled) return;\n state.settled = true;\n resolve(value);\n };\n const wrappedReject = (reason: unknown) => {\n if (state.settled) return;\n state.settled = true;\n reject(reason);\n };\n const progress = (p: P) => {\n if (state.settled) return;\n for (const cb of progressCbs) {\n try {\n cb(p);\n } catch {\n // ignore subscriber errors\n }\n }\n };\n\n executor(wrappedResolve, wrappedReject, progress, ctrl.signal);\n });\n\n this._ctrl = ctrl;\n this.signal = ctrl.signal;\n this._progressCbs = progressCbs;\n this._state = state;\n this._internalReject = capturedReject;\n }\n\n /**\n * Abort the underlying operation.\n *\n * - If the promise has already settled, this is a no-op.\n * - Otherwise the public promise rejects synchronously with an\n * AbortError (wrapping the supplied reason if it is not already an\n * AbortError) and the AbortSignal fires for any cooperative\n * cancellation the executor wired up.\n */\n abort(reason?: unknown): void {\n if (this._state.settled) return;\n if (this._ctrl.signal.aborted) return;\n\n const err = reason instanceof AbortError ? reason : new AbortError(reason);\n\n // Mark settled before signalling so any abort listener the executor\n // attached cannot race a late resolve/reject through the wrapped\n // settlers (they all consult state.settled).\n this._state.settled = true;\n this._ctrl.abort(err);\n this._internalReject(err);\n }\n\n /**\n * Abort this call when `signal` aborts, at once if it already has: the\n * bridge to code that cancels with an `AbortController` (a framework's\n * cleanup, a `fetch`). The listener is removed when the call settles.\n * Returns this promise, so `await call().abortWith(signal)` reads as one.\n */\n abortWith(signal: AbortSignal): this {\n if (signal.aborted) {\n this.abort(signal.reason);\n return this;\n }\n const onAbort = () => this.abort(signal.reason);\n signal.addEventListener('abort', onAbort, { once: true });\n const detach = () => signal.removeEventListener('abort', onAbort);\n Promise.prototype.then.call(this, detach, detach);\n return this;\n }\n\n /**\n * Subscribe to progress events. Returns an unsubscribe function.\n *\n * @internal No engine method reports progress today; this is plumbing for\n * one that will, typed then per method. Not part of the public contract.\n */\n onProgress(cb: (p: P) => void): () => void {\n this._progressCbs.add(cb);\n return () => {\n this._progressCbs.delete(cb);\n };\n }\n\n /**\n * Wrap a value in an already-resolved AbortablePromise. Mirrors Promise.resolve.\n */\n static resolveValue<T>(value: T | PromiseLike<T>): AbortablePromise<T> {\n return new AbortablePromise<T>((resolve) => resolve(value));\n }\n\n /**\n * Wrap a thrown reason in an already-rejected AbortablePromise.\n */\n static rejectReason<T = never>(reason: unknown): AbortablePromise<T> {\n return new AbortablePromise<T>((_resolve, reject) => reject(reason));\n }\n\n /**\n * Run an async function as an AbortablePromise so callers get .abort() and .signal.\n * The async function receives the AbortSignal (and a progress emitter) so it can\n * cooperate with the abort.\n *\n * If abort() fires while the inner async function is still running, the\n * public promise rejects with AbortError immediately. The inner async\n * function's eventual settlement (resolve or reject) is funneled through\n * the wrapped settlers and silently dropped — including a late rejection\n * from the underlying work, which prevents an UnhandledRejection because\n * the .then(resolve, reject) below \"handles\" it via the no-op.\n */\n static run<T, P = never>(\n fn: (signal: AbortSignal, progress: (p: P) => void) => Promise<T>,\n ): AbortablePromise<T, P> {\n return new AbortablePromise<T, P>((resolve, reject, progress, signal) => {\n fn(signal, progress).then(resolve, reject);\n });\n }\n}\n","import type { AbortablePromise } from '../promise/AbortablePromise';\n\n/**\n * The highest object number a document should reach: Acrobat documents\n * 8,388,607 objects per file, and the interoperable rule keeps every object\n * number at or below it. A write that would pass it is refused with\n * `LayerFull`.\n */\nexport const OBJECT_NUMBER_CEILING = 8_388_607;\n\n/**\n * The last object number handed out to sessions: issuing stops here, which\n * leaves the numbers above it to the objects the engine makes for itself\n * (appearance streams, fonts) until the ceiling.\n */\nexport const OBJECT_NUMBER_ISSUE_LIMIT = 8_000_000;\n\n/** `count` consecutive object numbers starting at `first`. */\nexport interface ObjectNumberRange {\n readonly first: number;\n readonly count: number;\n}\n\n/**\n * Runs of numbers as an HTTP header value, inclusive like an HTTP `Range`:\n * `1003-1035, 2040-2042` (`EmbedPDF-Object-Numbers`).\n */\nexport function formatObjectNumberRanges(ranges: readonly ObjectNumberRange[]): string {\n return ranges.map(({ first, count }) => `${first}-${first + count - 1}`).join(', ');\n}\n\n/** The runs a {@link formatObjectNumberRanges} header value names; `[]` for one that names none. */\nexport function parseObjectNumberRanges(value: string | null | undefined): ObjectNumberRange[] {\n const ranges: ObjectNumberRange[] = [];\n for (const part of (value ?? '').split(',')) {\n const match = /^\\s*(\\d+)-(\\d+)\\s*$/.exec(part);\n if (!match) continue;\n const first = Number(match[1]);\n const last = Number(match[2]);\n if (last >= first) ranges.push({ first, count: last - first + 1 });\n }\n return ranges;\n}\n\n/** Every number in `ranges`, in order. */\nexport function objectNumbersIn(ranges: readonly ObjectNumberRange[]): number[] {\n const numbers: number[] = [];\n for (const { first, count } of ranges) {\n for (let i = 0; i < count; i++) numbers.push(first + i);\n }\n return numbers;\n}\n\n/**\n * A cloud editing session, as `/access` tells a client that may create:\n * `'new'`, `'revived'` (it had expired, and its numbers came back with it\n * unless another session took them) or `'live'`; the seconds until it\n * expires without a sign of life; and the numbers handed out now.\n */\nexport interface EditSessionAccess {\n readonly session: 'new' | 'revived' | 'live';\n readonly expiresIn: number;\n readonly objectNumbers: readonly ObjectNumberRange[];\n}\n\n/**\n * The event stream's `session` event: every number the editing session\n * holds now, and the seconds until it expires without a sign of life.\n */\nexport interface EditSessionStatus {\n readonly held: readonly ObjectNumberRange[];\n readonly expiresIn: number;\n}\n\n/** Numbers a session can no longer use, and why. */\nexport interface ObjectNumbersLost {\n readonly numbers: readonly number[];\n /**\n * `'reclaimed'`: the session expired and its numbers went to another one.\n * `'versioned'`: a new version was published, which numbers objects anew.\n */\n readonly reason: 'reclaimed' | 'versioned';\n}\n\n/**\n * This session's reserved object numbers: final names for objects it is\n * about to create. A create that names one of them (`objectNumber`) makes\n * its object at exactly that number, so its ref is known before the engine\n * answers. Numbers are this session's alone; another session never gets\n * them.\n */\nexport interface ObjectNumberPool {\n /** One held number, removed from the pool; `null` when none is held (then `reserve`). */\n take(): number | null;\n /** How many are held now. */\n readonly held: number;\n /** Resolves once at least `count` are held: one request, only when the pool is short. */\n reserve(count: number): AbortablePromise<void>;\n /**\n * Called with numbers this session took or held that it can no longer\n * use. Creates naming them are refused; a view showing objects at them\n * should drop those. Returns the unsubscriber.\n */\n onLost(listener: (lost: ObjectNumbersLost) => void): () => void;\n}\n","import {\n assertBundlePages,\n assertKnownFields,\n assertResourceIds,\n assertResourcesNamed,\n assertRowResources,\n invalidBundle,\n isBundlePageRef,\n isRecord,\n type BundlePage,\n type ResourceId,\n} from './bundle';\nimport {\n assertWithinLimit,\n DEFAULT_BUNDLE_LIMITS,\n manifestBytesOf,\n type BundleLimits,\n} from './bundleLimits';\nimport type { WidgetAnnotation } from '../annotation/kinds/widget';\nimport type { FormFieldDTO } from '../forms/field';\nimport type { FormFieldRef } from '../identity/FormFieldRef';\nimport { encodePageKey } from '../identity/PageRef';\nimport type { Coordinates, PageCoordinates } from '../pageSpace/coordinates';\n\n/**\n * Form fields and their widgets, taken out of a document to go into this one\n * or another: the design (`doc.forms.import`) or only the values\n * (`doc.forms.importValues`). Each row is what a read returns: a field\n * whole, with its value and who made and filled it, and each of its\n * widgets as the form's rows read them. Any set of whole fields, their\n * widgets and the resources these name is a bundle.\n */\nexport interface FormBundle<C extends Coordinates = PageCoordinates> {\n readonly format: 'embedpdf/form';\n readonly version: 1;\n /** Every page a widget is on or points at, in document order. */\n readonly pages: readonly BundlePage[];\n /** Whole fields, in the form's order. */\n readonly fields: readonly FormBundleField<C>[];\n /** The fields' widgets, in their fields' order. */\n readonly widgets: readonly FormBundleWidget<C>[];\n /** The calculation order among these fields. */\n readonly calculationOrder: readonly FormFieldRef[];\n readonly resources: Readonly<Record<ResourceId, Uint8Array>>;\n}\n\nexport interface FormBundleField<C extends Coordinates = PageCoordinates> {\n /**\n * The field as a read returns it. A field the form doesn't export\n * (`noExport`) comes without its value or who filled it, and a signed\n * signature field without its signature: a signature never travels.\n */\n readonly data: FormFieldDTO<C>;\n}\n\nexport interface FormBundleWidget<C extends Coordinates = PageCoordinates> {\n readonly data: WidgetAnnotation<C>;\n /** None yet: a widget is drawn from its data. A push button's icon will be one. */\n readonly resources: Readonly<Record<string, ResourceId>>;\n}\n\n/** A bundle as a worker returns it: each resource an `ArrayBuffer` on the transfer list. */\nexport interface WireFormBundle<C extends Coordinates = PageCoordinates> extends Omit<\n FormBundle<C>,\n 'resources'\n> {\n readonly resources: Readonly<Record<ResourceId, ArrayBuffer>>;\n}\n\nexport const FORM_BUNDLE_FORMAT = 'embedpdf/form';\nexport const FORM_BUNDLE_VERSION = 1;\n\n/** The manifest's keys, in the order a file writes them. */\nexport const FORM_BUNDLE_KEYS = [\n 'format',\n 'version',\n 'pages',\n 'fields',\n 'widgets',\n 'calculationOrder',\n] as const;\n\n/** The resource roles a widget names: none yet. */\nconst WIDGET_RESOURCE_ROLES: readonly string[] = [];\n\nconst BUNDLE_FIELDS = [...FORM_BUNDLE_KEYS, 'resources'] as const;\nconst FIELD_ROW_FIELDS = ['data'] as const;\nconst WIDGET_ROW_FIELDS = ['data', 'resources'] as const;\n\n/**\n * Check a bundle before anything uses it, the checks cheapest first: its\n * format, its counts and sizes against `limits` (`PayloadTooLarge`), its\n * shape and references (`InvalidArg`), then every resource against its id.\n */\nexport async function assertFormBundle(\n bundle: FormBundle,\n limits: BundleLimits = DEFAULT_BUNDLE_LIMITS,\n): Promise<void> {\n const resources = isRecord(bundle?.resources)\n ? bundle.resources\n : invalidBundle('form', 'no resources');\n const sizes = new Map<string, number>();\n for (const [id, bytes] of Object.entries(resources)) {\n if (!(bytes instanceof Uint8Array)) invalidBundle('form', `resource ${id} is not bytes`);\n sizes.set(id, bytes.length);\n }\n assertFormBundleManifest(bundle, sizes, limits);\n await assertResourceIds('form', resources);\n}\n\n/**\n * The checks that need no resource bytes, only their sizes by id: format\n * and version, counts, sizes, the shape of pages, fields and widgets, and\n * that widgets and resources name each other exactly. That a widget's field\n * is among the bundle's is the import's to decide: one that isn't is left\n * out.\n */\nexport function assertFormBundleManifest(\n manifest: unknown,\n resourceSizes: ReadonlyMap<string, number>,\n limits: BundleLimits,\n): asserts manifest is Omit<FormBundle, 'resources'> {\n if (!isRecord(manifest)) invalidBundle('form', 'not a form bundle');\n assertKnownFields('form', 'the bundle', manifest, BUNDLE_FIELDS);\n if (manifest.format !== FORM_BUNDLE_FORMAT) {\n invalidBundle('form', `unknown format ${JSON.stringify(manifest.format)}`);\n }\n if (manifest.version !== FORM_BUNDLE_VERSION) {\n invalidBundle('form', `unknown version ${JSON.stringify(manifest.version)}`);\n }\n const { pages, fields, widgets, calculationOrder } = manifest;\n if (!Array.isArray(pages)) invalidBundle('form', 'pages is not a list');\n if (!Array.isArray(fields)) invalidBundle('form', 'fields is not a list');\n if (!Array.isArray(widgets)) invalidBundle('form', 'widgets is not a list');\n if (!Array.isArray(calculationOrder)) invalidBundle('form', 'calculationOrder is not a list');\n\n assertWithinLimit('form', limits, 'items', fields.length + widgets.length);\n assertWithinLimit('form', limits, 'pages', pages.length);\n assertWithinLimit('form', limits, 'resources', resourceSizes.size);\n let bundleBytes = manifestBytesOf({ pages, fields, widgets, calculationOrder });\n assertWithinLimit('form', limits, 'manifestBytes', bundleBytes);\n for (const size of resourceSizes.values()) {\n assertWithinLimit('form', limits, 'resourceBytes', size);\n bundleBytes += size;\n }\n assertWithinLimit('form', limits, 'bundleBytes', bundleBytes);\n\n const pageKeys = assertBundlePages('form', pages);\n fields.forEach((row, index) => {\n if (\n !isRecord(row) ||\n !isRecord(row.data) ||\n typeof row.data.family !== 'string' ||\n typeof row.data.name !== 'string' ||\n !isRecord(row.data.ref)\n ) {\n invalidBundle('form', `field ${index} has no field data`);\n }\n assertKnownFields('form', `field ${index}`, row, FIELD_ROW_FIELDS);\n });\n const named = new Set<string>();\n widgets.forEach((row, index) => {\n if (!isRecord(row) || !isRecord(row.data) || row.data.subtype !== 'widget') {\n invalidBundle('form', `widget ${index} has no widget data`);\n }\n assertKnownFields('form', `widget ${index}`, row, WIDGET_ROW_FIELDS);\n const ref = row.data.ref;\n const page = isRecord(ref) ? ref.page : undefined;\n if (!isBundlePageRef(page) || !pageKeys.has(encodePageKey(page))) {\n invalidBundle('form', `widget ${index} is on a page the bundle doesn't list`);\n }\n assertRowResources(\n 'form',\n `widget ${index}`,\n row.resources,\n WIDGET_RESOURCE_ROLES,\n resourceSizes,\n named,\n );\n });\n calculationOrder.forEach((ref, index) => {\n if (!isRecord(ref))\n invalidBundle('form', `calculation order entry ${index} is not a field ref`);\n });\n assertResourcesNamed('form', 'widget', resourceSizes, named);\n}\n","import { bundleCodec } from './codec';\nimport { assertFormBundleManifest, FORM_BUNDLE_KEYS, type FormBundle } from './FormBundle';\n\n/**\n * A form bundle as one JSON file, each resource in base64: a bundle whole,\n * to store or download. HTTP carries bundles as multipart instead, without\n * the third base64 adds.\n */\nexport const FormTransfer = bundleCodec<FormBundle>({\n kind: 'form',\n keys: FORM_BUNDLE_KEYS,\n assertManifest: assertFormBundleManifest,\n});\n","import { foldText, toOriginalRange } from './fold';\nimport type { FoldedText, FoldOptions } from './fold';\nimport type { TextRange } from '../text/TextRange';\nimport type { SearchQuery } from './types';\n\n/**\n * The fold flavor a literal query needs. Haystack and needle must be\n * folded with the same options; corpus caches key their pre-folded text\n * on this (the default `{}` fold is the persistable one — non-default\n * queries fold the original page text at query time).\n */\nexport function foldOptionsFor(query: SearchQuery): FoldOptions {\n return {\n keepCase: !!query.matchCase,\n keepMarks: !!query.matchDiacritics,\n dropWhitespace: !!query.ignoreWhitespace,\n };\n}\n\n// One definition of \"word character\" for the whole search subsystem —\n// Unicode letters/digits, not JS \\b's ASCII set — shared by the literal\n// boundary check and the regex wholeWord post-filter, so the wholeWord\n// toggle means the same thing in both modes (\"caf\" never whole-word\n// matches inside \"café\", regex or not).\nconst WORD_UNIT = /[\\p{L}\\p{N}_]/u;\nconst MARK = /\\p{M}/u;\n\n/** Whether the code point ending at `index` (exclusive) is a word char. */\nexport function wordBefore(text: string, index: number): boolean {\n if (index <= 0) return false;\n // Step back over a low surrogate to test the full code point.\n const unit = text.charCodeAt(index - 1);\n const start = unit >= 0xdc00 && unit <= 0xdfff && index >= 2 ? index - 2 : index - 1;\n return WORD_UNIT.test(String.fromCodePoint(text.codePointAt(start)!));\n}\n\n/** Whether the code point starting at `index` is a word char. */\nexport function wordAt(text: string, index: number): boolean {\n if (index >= text.length) return false;\n return WORD_UNIT.test(String.fromCodePoint(text.codePointAt(index)!));\n}\n\n/**\n * Whether the folded hit at `at` sits on word boundaries. Checked on the\n * folded plane, except under `ignoreWhitespace`: dropping whitespace glues\n * neighbouring words together (\"i n v o i c e 42\" folds to \"invoice42\"), so there the\n * boundaries are read off the original text around the mapped range.\n */\nfunction isWholeWordHit(\n haystack: FoldedText,\n at: number,\n needleLength: number,\n query: SearchQuery,\n): boolean {\n if (!query.ignoreWhitespace) {\n return !wordBefore(haystack.folded, at) && !wordAt(haystack.folded, at + needleLength);\n }\n const { start, count } = toOriginalRange(haystack, at, needleLength);\n return !wordBefore(haystack.original, start) && !wordAt(haystack.original, start + count);\n}\n\n/**\n * All non-overlapping literal matches, in original code-unit space.\n * `haystack` must have been folded with `foldOptionsFor(query)`.\n */\nexport function matchLiteral(haystack: FoldedText, query: SearchQuery): TextRange[] {\n const needle = foldText(query.text, foldOptionsFor(query)).folded;\n // Nothing searchable: empty or whitespace-only needles would \"match\"\n // every collapsed space.\n if (needle.trim().length === 0) return [];\n\n const out: TextRange[] = [];\n let from = 0;\n while (from <= haystack.folded.length - needle.length) {\n const at = haystack.folded.indexOf(needle, from);\n if (at < 0) break;\n if (query.wholeWord && !isWholeWordHit(haystack, at, needle.length, query)) {\n from = at + 1;\n continue;\n }\n // Diacritic-sensitive folds are decomposed (base + marks), so a bare\n // needle would otherwise match just the base letters of an accented\n // char — \"cafe\" ending right before the U+0301 of \"café\". A match may\n // not end mid-character.\n if (query.matchDiacritics && at + needle.length < haystack.folded.length) {\n const after = haystack.folded.codePointAt(at + needle.length)!;\n if (MARK.test(String.fromCodePoint(after))) {\n from = at + 1;\n continue;\n }\n }\n out.push(toOriginalRange(haystack, at, needle.length));\n from = at + needle.length;\n }\n return out;\n}\n","import type { TextRange } from '../text/TextRange';\nimport { wordAt, wordBefore } from './literal';\nimport type { SearchQuery } from './types';\n\n/**\n * The portable search-regex dialect: JavaScript `u`-mode syntax minus the\n * features RE2 refuses (backreferences, lookaround). The local engine\n * executes patterns with JS `RegExp`; the server executes the same\n * pattern with RE2, whose linear-time guarantee is the ReDoS defense.\n * Restricting both sides to the common subset keeps one query string\n * valid — and equally powerful — everywhere.\n */\nexport const SEARCH_REGEX_MAX_LENGTH = 512;\n\nexport type SearchRegexIssue = 'empty' | 'too-long' | 'backreference' | 'lookaround' | 'syntax';\n\nexport type SearchRegexValidation =\n | { ok: true }\n | { ok: false; issue: SearchRegexIssue; message: string };\n\n/**\n * Validate a pattern against the dialect. Cheap and dependency-free —\n * viewers call it on keystroke for early feedback; engines re-validate\n * and reject bad patterns with `InvalidArg`.\n */\nexport function validateSearchRegex(pattern: string): SearchRegexValidation {\n if (pattern.length === 0) {\n return { ok: false, issue: 'empty', message: 'Pattern is empty.' };\n }\n if (pattern.length > SEARCH_REGEX_MAX_LENGTH) {\n return {\n ok: false,\n issue: 'too-long',\n message: `Pattern exceeds ${SEARCH_REGEX_MAX_LENGTH} characters.`,\n };\n }\n\n // One pass tracking escape/char-class state: backreferences and\n // lookaround only mean what they mean outside a character class.\n let inClass = false;\n for (let i = 0; i < pattern.length; i++) {\n const ch = pattern[i];\n if (ch === '\\\\') {\n const next = pattern[i + 1] ?? '';\n if (!inClass && next >= '1' && next <= '9') {\n return {\n ok: false,\n issue: 'backreference',\n message: `Backreference \\\\${next} is not supported.`,\n };\n }\n if (!inClass && next === 'k' && pattern[i + 2] === '<') {\n return {\n ok: false,\n issue: 'backreference',\n message: 'Named backreferences (\\\\k<…>) are not supported.',\n };\n }\n i++; // consume the escaped char\n continue;\n }\n if (inClass) {\n if (ch === ']') inClass = false;\n continue;\n }\n if (ch === '[') {\n inClass = true;\n continue;\n }\n if (ch === '(' && pattern[i + 1] === '?') {\n const tag = pattern[i + 2] ?? '';\n if (tag === '=' || tag === '!') {\n return {\n ok: false,\n issue: 'lookaround',\n message: 'Lookahead ((?=…), (?!…)) is not supported.',\n };\n }\n // \"(?<\" is lookbehind when followed by = or !, a named group otherwise.\n if (tag === '<' && (pattern[i + 3] === '=' || pattern[i + 3] === '!')) {\n return {\n ok: false,\n issue: 'lookaround',\n message: 'Lookbehind ((?<=…), (?<!…)) is not supported.',\n };\n }\n }\n }\n\n try {\n // `u` is the strict common ground with RE2's syntax; `m` because ^/$\n // meaning line boundaries is what page-text search wants.\n new RegExp(pattern, 'mu');\n } catch (error) {\n return { ok: false, issue: 'syntax', message: (error as Error).message };\n }\n return { ok: true };\n}\n\n/**\n * Every flag combination has defined semantics — most by behavior, one by\n * loud rejection (see the {@link SearchQuery} table). The single validator\n * a UI needs: call it on keystroke to grey out the diacritics toggle in\n * regex mode and flag bad patterns early; engines run the same check and\n * reject with `InvalidArg`. Literal queries are always valid (an empty\n * literal simply finds nothing).\n */\nexport type SearchQueryIssue =\n | SearchRegexIssue\n | 'diacritics-with-regex'\n | 'ignore-whitespace-with-regex';\n\nexport type SearchQueryValidation =\n | { ok: true }\n | { ok: false; issue: SearchQueryIssue; message: string };\n\nexport function validateSearchQuery(query: SearchQuery): SearchQueryValidation {\n if (!query.regex) return { ok: true };\n if (query.matchDiacritics) {\n return {\n ok: false,\n issue: 'diacritics-with-regex',\n message:\n 'Diacritic-sensitive matching is not available for regex patterns (regex runs on the raw text plane).',\n };\n }\n if (query.ignoreWhitespace) {\n return {\n ok: false,\n issue: 'ignore-whitespace-with-regex',\n message:\n 'Whitespace-insensitive matching is not available for regex patterns (regex runs on the raw text plane; use \\\\s* in the pattern).',\n };\n }\n return validateSearchRegex(query.text);\n}\n\n/**\n * All matches of a dialect-valid pattern over the raw page text (regex\n * does not run against folded text — case handling is the `i` flag, and\n * diacritic folding never applies). `wholeWord` is a boundary POST-filter\n * using the same Unicode word test as the literal path — never `\\b`,\n * whose ASCII word set would give the toggle different semantics in regex\n * mode (\"caf\" whole-word matching inside \"café\"). Filtering after the\n * match also survives the move to RE2 server-side. Zero-length matches\n * are skipped: search UI cannot highlight nothing, and skipping them is\n * also the infinite-loop guard for patterns like `a*`.\n */\nexport function matchRegex(text: string, query: SearchQuery): TextRange[] {\n const valid = validateSearchQuery({ ...query, regex: true });\n if (!valid.ok) throw new Error(`Invalid search query (${valid.issue}): ${valid.message}`);\n\n const re = new RegExp(query.text, query.matchCase ? 'gmu' : 'gimu');\n const out: TextRange[] = [];\n let m: RegExpExecArray | null;\n while ((m = re.exec(text)) !== null) {\n if (m[0].length === 0) {\n // Advance one full code point past the empty match.\n const unit = text.charCodeAt(re.lastIndex);\n re.lastIndex += unit >= 0xd800 && unit <= 0xdbff ? 2 : 1;\n continue;\n }\n if (query.wholeWord && (wordBefore(text, m.index) || wordAt(text, m.index + m[0].length))) {\n continue;\n }\n out.push({ start: m.index, count: m[0].length });\n }\n return out;\n}\n","import { EngineError } from '../errors/EngineError';\nimport { EngineErrorCode } from '../errors/EngineErrorCode';\nimport type { AnnotationRef } from '../identity/AnnotationRef';\nimport type { FormFieldRef } from '../identity/FormFieldRef';\nimport type { PageRef } from '../identity/PageRef';\n\n/**\n * Where rows go in their list: right before or after a neighbour, or at the\n * start or the end. A position names a neighbour, never a number, so a change\n * keeps its meaning when other rows come and go before it applies, and a\n * caller only names rows it can see.\n *\n * Each list is in its own order: a document's pages in display order; a\n * page's annotations, and its widgets, each in paint order, later over\n * earlier. Widgets always paint above annotations, so the two are separate\n * lists and a position names a neighbour of the same kind.\n */\nexport type ListPosition<Ref> =\n | { readonly before: Ref }\n | { readonly after: Ref }\n | 'start'\n | 'end';\n\n/** Where pages go among the document's pages. */\nexport type PagePosition = ListPosition<PageRef>;\n\n/** Where annotations go among their page's annotations, or widgets among its widgets. */\nexport type AnnotationPosition = ListPosition<AnnotationRef>;\n\n/** Where fields go in the form's calculation order. */\nexport type FieldPosition = ListPosition<FormFieldRef>;\n\n/** The neighbour a position names, or `null` for `'start'` and `'end'`. */\nexport function anchorOf<Ref>(position: ListPosition<Ref>): Ref | null {\n if (position === 'start' || position === 'end') return null;\n return 'before' in position ? position.before : position.after;\n}\n\n/**\n * Where `position` falls in `order`: the index the first row placed there\n * gets. The neighbour must be in `order` (`NotFound` otherwise).\n */\nexport function positionIndex<Ref>(\n order: readonly Ref[],\n position: ListPosition<Ref>,\n keyOf: (ref: Ref) => string,\n): number {\n if (position === 'start') return 0;\n if (position === 'end') return order.length;\n const anchor = anchorOf(position)!;\n const at = order.findIndex((ref) => keyOf(ref) === keyOf(anchor));\n if (at < 0) {\n throw new EngineError(EngineErrorCode.NotFound, `no neighbour ${keyOf(anchor)} in the list`, {\n details: { field: 'position' },\n });\n }\n return 'before' in position ? at : at + 1;\n}\n\n/**\n * A list's order once `moved` go to `position`, together and in the order\n * given: what a reorder answers, worked out as the engine does. Every moved\n * row must be in `order`, once (`InvalidArg` otherwise), and the neighbour\n * can't be one of them (`InvalidArg`).\n */\nexport function reorderedList<Ref>(\n order: readonly Ref[],\n moved: readonly Ref[],\n position: ListPosition<Ref>,\n keyOf: (ref: Ref) => string,\n): Ref[] {\n const movedKeys = new Set(moved.map(keyOf));\n if (movedKeys.size !== moved.length) {\n throw new EngineError(EngineErrorCode.InvalidArg, 'a row is named twice', {\n details: { field: 'refs' },\n });\n }\n const present = new Set(order.map(keyOf));\n const missing = moved.find((ref) => !present.has(keyOf(ref)));\n if (missing !== undefined) {\n throw new EngineError(EngineErrorCode.NotFound, `no row ${keyOf(missing)} in the list`, {\n details: { field: 'refs' },\n });\n }\n const anchor = anchorOf(position);\n if (anchor !== null && movedKeys.has(keyOf(anchor))) {\n throw new EngineError(\n EngineErrorCode.InvalidArg,\n `the neighbour ${keyOf(anchor)} is one of the rows that move`,\n { details: { field: 'position' } },\n );\n }\n const staying = order.filter((ref) => !movedKeys.has(keyOf(ref)));\n const at = positionIndex(staying, position, keyOf);\n return [...staying.slice(0, at), ...moved, ...staying.slice(at)];\n}\n\n/**\n * `list` with the items `inPart` picks put in the order `order` gives them,\n * by key, in the slots those items have: every other item keeps its place.\n * How a list holding several pages takes one page's new order. Items of the\n * part `order` doesn't name follow the named ones, in their order.\n */\nexport function reorderPart<Item, Key>(\n list: readonly Item[],\n inPart: (item: Item) => boolean,\n order: readonly Key[],\n keyOf: (item: Item) => Key,\n): Item[] {\n const part = list.filter(inPart);\n const byKey = new Map(part.map((item) => [keyOf(item), item]));\n const named: Item[] = order.flatMap((key) => (byKey.has(key) ? [byKey.get(key)!] : []));\n const placed = new Set<Item>(named);\n const next = [...named, ...part.filter((item) => !placed.has(item))];\n let slot = 0;\n return list.map((item) => (inPart(item) ? next[slot++]! : item));\n}\n","import type { PdfViewport } from '../dto/Measure';\nimport type { PagePoint } from '../geometry/pageSpace';\n\n/**\n * The last viewport whose box holds `point` (the one drawn on top), or\n * `undefined`. A foreign measure still wins; never guess a fallback.\n */\nexport function viewportForPoint<T extends PdfViewport>(\n viewports: readonly T[],\n point: PagePoint,\n): T | undefined {\n return [...viewports]\n .reverse()\n .find(\n ({ bbox: b }) =>\n point.x >= b.x && point.x <= b.x + b.width && point.y >= b.y && point.y <= b.y + b.height,\n );\n}\n"],"mappings":"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"}
|