@arbidocs/react 0.5.61 → 0.5.63

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 (103) hide show
  1. package/dist/ProductionDialog.d-CpzslFn0.d.cts +51 -0
  2. package/dist/ProductionDialog.d-CpzslFn0.d.ts +51 -0
  3. package/dist/annotations/freeze/index.cjs +14 -14
  4. package/dist/annotations/freeze/index.js +3 -3
  5. package/dist/annotations/index.d.cts +3 -2
  6. package/dist/annotations/index.d.ts +3 -2
  7. package/dist/artifacts/index.cjs +12 -12
  8. package/dist/artifacts/index.js +5 -5
  9. package/dist/bundle-IC65W2AO.cjs +14 -0
  10. package/dist/{bundle-DCWPYNGD.cjs.map → bundle-IC65W2AO.cjs.map} +1 -1
  11. package/dist/bundle-YNOX7VIL.js +5 -0
  12. package/dist/{bundle-JCKKGEJE.js.map → bundle-YNOX7VIL.js.map} +1 -1
  13. package/dist/calls/livekit/index.cjs +54 -54
  14. package/dist/calls/livekit/index.js +6 -6
  15. package/dist/{chunk-S2W3ZOWG.js → chunk-2LV53J46.js} +85 -16
  16. package/dist/chunk-2LV53J46.js.map +1 -0
  17. package/dist/{chunk-H3HYKAS5.cjs → chunk-3BFSALPC.cjs} +3 -3
  18. package/dist/{chunk-H3HYKAS5.cjs.map → chunk-3BFSALPC.cjs.map} +1 -1
  19. package/dist/{chunk-2NFUEC5O.js → chunk-3NBPMR3X.js} +3 -3
  20. package/dist/{chunk-2NFUEC5O.js.map → chunk-3NBPMR3X.js.map} +1 -1
  21. package/dist/chunk-4DTTEUVY.js +1042 -0
  22. package/dist/chunk-4DTTEUVY.js.map +1 -0
  23. package/dist/{chunk-U64WCQP3.js → chunk-AOEJ744V.js} +25 -14
  24. package/dist/chunk-AOEJ744V.js.map +1 -0
  25. package/dist/{chunk-4E737GNX.cjs → chunk-BLBXADTN.cjs} +86 -73
  26. package/dist/chunk-BLBXADTN.cjs.map +1 -0
  27. package/dist/{chunk-X7JXQT6F.cjs → chunk-DVL7EFZZ.cjs} +42 -46
  28. package/dist/chunk-DVL7EFZZ.cjs.map +1 -0
  29. package/dist/{chunk-VAMPYLQ4.cjs → chunk-DZUBZ45Y.cjs} +78 -2
  30. package/dist/chunk-DZUBZ45Y.cjs.map +1 -0
  31. package/dist/{chunk-OSWKXZTJ.js → chunk-ER4LFZG3.js} +12 -16
  32. package/dist/chunk-ER4LFZG3.js.map +1 -0
  33. package/dist/{chunk-5HDC43MF.js → chunk-G4Z3IP4J.js} +72 -3
  34. package/dist/chunk-G4Z3IP4J.js.map +1 -0
  35. package/dist/chunk-GOJHU7RZ.cjs +1122 -0
  36. package/dist/chunk-GOJHU7RZ.cjs.map +1 -0
  37. package/dist/{chunk-G76ASVVH.cjs → chunk-GQAXAZN7.cjs} +25 -14
  38. package/dist/chunk-GQAXAZN7.cjs.map +1 -0
  39. package/dist/{chunk-TUPSNW3B.cjs → chunk-LMW46NBD.cjs} +11 -11
  40. package/dist/{chunk-TUPSNW3B.cjs.map → chunk-LMW46NBD.cjs.map} +1 -1
  41. package/dist/{chunk-KRH55LXW.js → chunk-NCMSQACC.js} +37 -24
  42. package/dist/chunk-NCMSQACC.js.map +1 -0
  43. package/dist/{chunk-A2XSLTIP.js → chunk-NIKPG4PW.js} +3 -3
  44. package/dist/{chunk-A2XSLTIP.js.map → chunk-NIKPG4PW.js.map} +1 -1
  45. package/dist/{chunk-B6I2HRRF.cjs → chunk-PC4O3JDV.cjs} +3 -3
  46. package/dist/{chunk-B6I2HRRF.cjs.map → chunk-PC4O3JDV.cjs.map} +1 -1
  47. package/dist/{chunk-M2SZQBCO.js → chunk-SI7DJWBT.js} +3 -3
  48. package/dist/{chunk-M2SZQBCO.js.map → chunk-SI7DJWBT.js.map} +1 -1
  49. package/dist/{chunk-3QK4VVPG.cjs → chunk-TGSEQGM3.cjs} +3 -3
  50. package/dist/{chunk-3QK4VVPG.cjs.map → chunk-TGSEQGM3.cjs.map} +1 -1
  51. package/dist/{chunk-LCSUCZVX.cjs → chunk-ZNQPUPX6.cjs} +118 -41
  52. package/dist/chunk-ZNQPUPX6.cjs.map +1 -0
  53. package/dist/{chunk-CMNZI4WQ.js → chunk-ZTPJNKF2.js} +4 -4
  54. package/dist/{chunk-CMNZI4WQ.js.map → chunk-ZTPJNKF2.js.map} +1 -1
  55. package/dist/document-tools/core.cjs +408 -0
  56. package/dist/document-tools/core.cjs.map +1 -0
  57. package/dist/document-tools/core.d.cts +957 -0
  58. package/dist/document-tools/core.d.ts +957 -0
  59. package/dist/document-tools/core.js +15 -0
  60. package/dist/document-tools/core.js.map +1 -0
  61. package/dist/document-tools/index.cjs +347 -1130
  62. package/dist/document-tools/index.cjs.map +1 -1
  63. package/dist/document-tools/index.d.cts +12 -950
  64. package/dist/document-tools/index.d.ts +12 -950
  65. package/dist/document-tools/index.js +13 -1034
  66. package/dist/document-tools/index.js.map +1 -1
  67. package/dist/index.cjs +1510 -1255
  68. package/dist/index.cjs.map +1 -1
  69. package/dist/{index.d-BLw5AN0H.d.cts → index.d-AT7wdTWC.d.cts} +50 -2
  70. package/dist/{index.d-BLw5AN0H.d.ts → index.d-AT7wdTWC.d.ts} +50 -2
  71. package/dist/{index.d-DPVj21h1.d.ts → index.d-B0dosFZc.d.cts} +6 -6
  72. package/dist/{index.d-DPVj21h1.d.cts → index.d-B0dosFZc.d.ts} +6 -6
  73. package/dist/index.d.cts +145 -69
  74. package/dist/index.d.ts +145 -69
  75. package/dist/index.js +346 -121
  76. package/dist/index.js.map +1 -1
  77. package/dist/{query-keys.d-DEEMzkLz.d.cts → query-keys.d-BFKQVevT.d.cts} +21 -12
  78. package/dist/{query-keys.d-DEEMzkLz.d.ts → query-keys.d-BFKQVevT.d.ts} +21 -12
  79. package/dist/shell/index.cjs +68 -40
  80. package/dist/shell/index.d.cts +1 -1
  81. package/dist/shell/index.d.ts +1 -1
  82. package/dist/shell/index.js +3 -3
  83. package/dist/store.d-DkFXbWDk.d.ts +35 -0
  84. package/dist/store.d-GADDvC7w.d.cts +35 -0
  85. package/dist/ui/index.cjs +149 -149
  86. package/dist/ui/index.d.cts +1 -1
  87. package/dist/ui/index.d.ts +1 -1
  88. package/dist/ui/index.js +2 -2
  89. package/package.json +11 -3
  90. package/dist/ProductionDialog.d-BDJwBSzj.d.ts +0 -84
  91. package/dist/ProductionDialog.d-DjRQFmQw.d.cts +0 -84
  92. package/dist/bundle-DCWPYNGD.cjs +0 -14
  93. package/dist/bundle-JCKKGEJE.js +0 -5
  94. package/dist/chunk-4E737GNX.cjs.map +0 -1
  95. package/dist/chunk-5HDC43MF.js.map +0 -1
  96. package/dist/chunk-G76ASVVH.cjs.map +0 -1
  97. package/dist/chunk-KRH55LXW.js.map +0 -1
  98. package/dist/chunk-LCSUCZVX.cjs.map +0 -1
  99. package/dist/chunk-OSWKXZTJ.js.map +0 -1
  100. package/dist/chunk-S2W3ZOWG.js.map +0 -1
  101. package/dist/chunk-U64WCQP3.js.map +0 -1
  102. package/dist/chunk-VAMPYLQ4.cjs.map +0 -1
  103. package/dist/chunk-X7JXQT6F.cjs.map +0 -1
@@ -0,0 +1,1042 @@
1
+ import { useDocumentTools, SIGNED_LOCKED_REASON, fetchMarkupPdf, fetchDocumentFile, fetchDocumentPdf } from './chunk-SI7DJWBT.js';
2
+ import { mergeLayers, AnnotationStoreOfflineError } from './chunk-T4CLW3DN.js';
3
+ import { parseLayer, emptyLayer, LAYER_SCHEMA_VERSION } from './chunk-T3USOSVN.js';
4
+ import { documentBytesKey, arbiQueryKeys } from './chunk-AOEJ744V.js';
5
+ import { useArbiHost } from './chunk-R7YNABZY.js';
6
+ import { parseVersionRef } from './chunk-NNWMHMEC.js';
7
+ import { useMemo, useState, useCallback, useEffect, useSyncExternalStore } from 'react';
8
+ import { onlineManager, useQuery, useQueryClient, useMutation } from '@tanstack/react-query';
9
+ import { unlockIdentityWithPassword, signMessage, getUserEncryptionKeypair, sealedBoxDecrypt, sealedBoxEncrypt, initSodium } from '@arbidocs/client';
10
+ import { workspaces } from '@arbidocs/sdk/browser';
11
+
12
+ // src/document-tools/markup-store.ts
13
+ var MAX_MERGE_ATTEMPTS = 5;
14
+ var KEEPALIVE_LIMIT = 6e4;
15
+ var MarkupStoreError = class extends Error {
16
+ constructor(message, status) {
17
+ super(message);
18
+ this.status = status;
19
+ this.name = "MarkupStoreError";
20
+ }
21
+ status;
22
+ };
23
+ var detailOf = (error, fallback) => {
24
+ const detail3 = error?.detail;
25
+ return typeof detail3 === "string" ? detail3 : fallback;
26
+ };
27
+ async function call(request) {
28
+ try {
29
+ return await request();
30
+ } catch (e) {
31
+ if (e instanceof TypeError || typeof navigator !== "undefined" && !navigator.onLine) {
32
+ throw new AnnotationStoreOfflineError();
33
+ }
34
+ throw e;
35
+ }
36
+ }
37
+ function createMarkupStore({ version, client, onSaved }) {
38
+ let revision = 0;
39
+ let count = 0;
40
+ let canEdit = false;
41
+ let base = null;
42
+ const seen = /* @__PURE__ */ new Set();
43
+ const remember = (layer) => {
44
+ for (const a of layer.annotations) seen.add(a.id);
45
+ };
46
+ const query = version ? { version } : {};
47
+ async function read(documentId, ifNoneMatch) {
48
+ const { data, error, response } = await call(
49
+ () => client.GET("/v1/document/{document_ext_id}/markup", {
50
+ params: { path: { document_ext_id: documentId }, query },
51
+ headers: ifNoneMatch === void 0 ? void 0 : { "If-None-Match": `"${ifNoneMatch}"` }
52
+ })
53
+ );
54
+ if (response.status === 304) return null;
55
+ if (error || !data) {
56
+ throw new MarkupStoreError(
57
+ detailOf(error, `The markup could not be loaded (${response.status})`),
58
+ response.status
59
+ );
60
+ }
61
+ const layer = parseLayer({
62
+ schema: LAYER_SCHEMA_VERSION,
63
+ documentId,
64
+ annotations: data.annotations ?? [],
65
+ updatedAt: data.updated_at ?? (/* @__PURE__ */ new Date()).toISOString()
66
+ });
67
+ return { revision: data.revision ?? 0, layer, canEdit: !!data.can_edit };
68
+ }
69
+ function adopt(server) {
70
+ revision = server.revision;
71
+ canEdit = server.canEdit;
72
+ base = server.layer;
73
+ count = server.layer.annotations.length;
74
+ remember(server.layer);
75
+ }
76
+ async function put(layer, from) {
77
+ return call(
78
+ () => client.PUT("/v1/document/{document_ext_id}/markup", {
79
+ params: { path: { document_ext_id: layer.documentId }, query },
80
+ headers: { "If-Match": `"${from}"` },
81
+ body: { annotations: layer.annotations, base_revision: from },
82
+ // A save started as the page closes must outlive it; browsers cap such bodies at 64 KB.
83
+ keepalive: JSON.stringify(layer.annotations).length < KEEPALIVE_LIMIT
84
+ })
85
+ );
86
+ }
87
+ return {
88
+ snapshot: () => ({ revision, count, canEdit }),
89
+ canEdit: () => canEdit,
90
+ async load(documentId) {
91
+ const server = await read(documentId);
92
+ if (!server) throw new MarkupStoreError("The markup could not be loaded");
93
+ adopt(server);
94
+ return server.layer;
95
+ },
96
+ async save(layer) {
97
+ remember(layer);
98
+ let mine = layer;
99
+ for (let attempt = 0; attempt < MAX_MERGE_ATTEMPTS; attempt++) {
100
+ const { data, error, response } = await put(mine, revision);
101
+ if (response.ok && data) {
102
+ revision = data.revision;
103
+ count = data.count;
104
+ base = mine;
105
+ remember(mine);
106
+ onSaved?.({ revision, count, canEdit });
107
+ return mine;
108
+ }
109
+ if (response.status === 409) {
110
+ const latest = await read(layer.documentId);
111
+ if (!latest) throw new MarkupStoreError("The markup could not be re-read");
112
+ const deleted = new Set(
113
+ [...seen].filter((id) => !mine.annotations.some((a) => a.id === id))
114
+ );
115
+ const merged = mergeLayers({ base, mine, theirs: latest.layer, deleted });
116
+ revision = latest.revision;
117
+ canEdit = latest.canEdit;
118
+ base = latest.layer;
119
+ remember(latest.layer);
120
+ mine = merged;
121
+ continue;
122
+ }
123
+ if (response.status === 403) canEdit = false;
124
+ throw new MarkupStoreError(
125
+ detailOf(error, `The markup could not be saved (${response.status})`),
126
+ response.status
127
+ );
128
+ }
129
+ throw new MarkupStoreError("The markup keeps changing; it will be saved on the next edit");
130
+ },
131
+ async pull(documentId) {
132
+ const server = await read(documentId, revision);
133
+ if (!server || server.revision === revision) return null;
134
+ adopt(server);
135
+ return server.layer;
136
+ }
137
+ };
138
+ }
139
+
140
+ // src/document-tools/markup-transfer.ts
141
+ var LEGACY_LAYER_PREFIX = "arbi:annotations:";
142
+ function storage() {
143
+ try {
144
+ return typeof localStorage === "undefined" ? void 0 : localStorage;
145
+ } catch {
146
+ return void 0;
147
+ }
148
+ }
149
+ function readLegacyLayer(documentId) {
150
+ try {
151
+ const raw = storage()?.getItem(LEGACY_LAYER_PREFIX + documentId);
152
+ if (!raw) return null;
153
+ return parseLayer(JSON.parse(raw));
154
+ } catch {
155
+ return null;
156
+ }
157
+ }
158
+ function forgetLegacyLayer(documentId) {
159
+ try {
160
+ storage()?.removeItem(LEGACY_LAYER_PREFIX + documentId);
161
+ } catch {
162
+ }
163
+ }
164
+ function marksByVersion(layer, versions) {
165
+ const sorted = [...versions].sort((a, b) => a.n - b.n);
166
+ const out = /* @__PURE__ */ new Map();
167
+ for (const mark of layer.annotations) {
168
+ const at = Date.parse(mark.createdAt);
169
+ let target = sorted[0]?.n ?? 1;
170
+ for (const v of sorted) {
171
+ const created = v.created_at ? Date.parse(v.created_at) : Number.NaN;
172
+ if (!Number.isNaN(created) && !Number.isNaN(at) && created <= at) target = v.n;
173
+ }
174
+ out.set(target, [...out.get(target) ?? [], mark]);
175
+ }
176
+ return out;
177
+ }
178
+ async function addToMarkup(documentId, version, marks, client) {
179
+ const store = createMarkupStore({ version, client });
180
+ const current = await store.load(documentId);
181
+ const incoming = {
182
+ ...emptyLayer(documentId, (/* @__PURE__ */ new Date()).toISOString()),
183
+ annotations: marks
184
+ };
185
+ const merged = mergeLayers({ base: null, mine: current, theirs: incoming });
186
+ const added = merged.annotations.length - current.annotations.length;
187
+ if (added > 0) await store.save(parseLayer({ ...merged, schema: LAYER_SCHEMA_VERSION }));
188
+ return added;
189
+ }
190
+ async function migrateLegacyLayer(documentId, layer, versions, currentVersion, client) {
191
+ let added = 0;
192
+ for (const [n, marks] of marksByVersion(layer, versions.length ? versions : [{ n: 1 }])) {
193
+ added += await addToMarkup(documentId, n === currentVersion ? void 0 : n, marks, client);
194
+ }
195
+ forgetLegacyLayer(documentId);
196
+ return added;
197
+ }
198
+ async function openThreadsOf(documentId, version, client) {
199
+ const layer = await createMarkupStore({ version, client }).load(documentId);
200
+ return layer.annotations.filter((a) => a.kind === "comment" && !a.resolved);
201
+ }
202
+ async function copyOpenThreads(documentId, fromVersion, client) {
203
+ const threads = await openThreadsOf(documentId, fromVersion, client);
204
+ if (!threads.length) return 0;
205
+ return addToMarkup(documentId, void 0, threads, client);
206
+ }
207
+ function versionsWithMarkup(versions) {
208
+ return (versions ?? []).filter((v) => (v.markup_count ?? 0) > 0);
209
+ }
210
+ var SIGNED_UNCHANGED = "The signed PDF is unchanged: the comments are kept beside it as markup.";
211
+ function useDocumentMarkup(documentData, snapshotVersion, currentSigned = false) {
212
+ const { client, notify, saveFile, onDocumentsChanged } = useDocumentTools();
213
+ const documentId = documentData?.external_id;
214
+ const currentVersion = documentData?.current_version ?? 1;
215
+ const shownVersion = snapshotVersion ?? currentVersion;
216
+ const versions = useMemo(() => documentData?.versions ?? [], [documentData?.versions]);
217
+ const [localCounts, setLocalCounts] = useState({});
218
+ const countOf = useCallback(
219
+ (n) => localCounts[`${documentId}@${n}`] ?? versions.find((v) => v.n === n)?.markup_count ?? 0,
220
+ [localCounts, documentId, versions]
221
+ );
222
+ const hasMarkup = countOf(shownVersion) > 0;
223
+ const [showOriginal, setShowOriginal] = useState(false);
224
+ useEffect(() => setShowOriginal(false), [documentId, snapshotVersion]);
225
+ const refresh = useCallback(() => {
226
+ if (!documentId || !onDocumentsChanged) return;
227
+ Promise.resolve(onDocumentsChanged([documentId], "markup")).catch(
228
+ (e) => console.warn("Could not refresh the document after a markup change", e)
229
+ );
230
+ }, [documentId, onDocumentsChanged]);
231
+ const onMarkupChange = useCallback(
232
+ (snapshot) => {
233
+ if (!documentId) return;
234
+ const key = `${documentId}@${shownVersion}`;
235
+ if (countOf(shownVersion) > 0 !== snapshot.count > 0) refresh();
236
+ setLocalCounts((counts) => ({ ...counts, [key]: snapshot.count }));
237
+ },
238
+ [documentId, shownVersion, countOf, refresh]
239
+ );
240
+ const downloadMarkup = useCallback(async () => {
241
+ if (!documentId) return;
242
+ if (currentSigned) {
243
+ notify(SIGNED_LOCKED_REASON, "info");
244
+ return;
245
+ }
246
+ try {
247
+ const { blob, fileName } = await fetchMarkupPdf(
248
+ client,
249
+ documentId,
250
+ snapshotVersion ? snapshotVersion : void 0
251
+ );
252
+ saveFile(blob, fileName);
253
+ } catch (e) {
254
+ notify(e instanceof Error ? e.message : "The markup could not be downloaded", "error");
255
+ }
256
+ }, [client, documentId, snapshotVersion, saveFile, notify, currentSigned]);
257
+ const [legacyCount, setLegacyCount] = useState(0);
258
+ const [migrating, setMigrating] = useState(false);
259
+ const shared = documentData?.shared;
260
+ const loaded = !!documentData;
261
+ const migrate = useCallback(async () => {
262
+ if (!documentId) return;
263
+ const layer = readLegacyLayer(documentId);
264
+ if (!layer) return;
265
+ setMigrating(true);
266
+ try {
267
+ const added = await migrateLegacyLayer(documentId, layer, versions, currentVersion, client);
268
+ setLegacyCount(0);
269
+ setLocalCounts({});
270
+ refresh();
271
+ if (added > 0) {
272
+ notify(
273
+ `${added} annotation${added === 1 ? "" : "s"} from this browser saved to the document`,
274
+ "success",
275
+ {
276
+ description: shared ? "They are now part of the shared markup." : "They are saved with the document, visible only to you."
277
+ }
278
+ );
279
+ }
280
+ } catch (e) {
281
+ notify(
282
+ e instanceof Error ? e.message : "The annotations from this browser could not be saved",
283
+ "error",
284
+ { description: "They are still kept in this browser; open the document again to retry." }
285
+ );
286
+ } finally {
287
+ setMigrating(false);
288
+ }
289
+ }, [client, documentId, versions, currentVersion, refresh, shared, notify]);
290
+ useEffect(() => {
291
+ if (!documentId || !loaded) return;
292
+ const layer = readLegacyLayer(documentId);
293
+ if (!layer) {
294
+ setLegacyCount(0);
295
+ return;
296
+ }
297
+ if (layer.annotations.length === 0) {
298
+ forgetLegacyLayer(documentId);
299
+ setLegacyCount(0);
300
+ return;
301
+ }
302
+ if (shared) setLegacyCount(layer.annotations.length);
303
+ else void migrate();
304
+ }, [documentId, loaded, shared]);
305
+ const legacyPrompt = legacyCount > 0 && !snapshotVersion ? {
306
+ count: legacyCount,
307
+ busy: migrating,
308
+ add: () => void migrate(),
309
+ discard: () => {
310
+ if (documentId) forgetLegacyLayer(documentId);
311
+ setLegacyCount(0);
312
+ }
313
+ } : null;
314
+ const [copying, setCopying] = useState(false);
315
+ const olderWithMarkup = versionsWithMarkup(versions).filter((v) => v.n !== currentVersion).sort((a, b) => b.n - a.n)[0];
316
+ const olderNote = !snapshotVersion && !hasMarkup && olderWithMarkup ? {
317
+ version: olderWithMarkup.n,
318
+ busy: copying,
319
+ signed: currentSigned,
320
+ copy: () => {
321
+ if (!documentId) return;
322
+ setCopying(true);
323
+ copyOpenThreads(documentId, olderWithMarkup.n, client).then((n) => {
324
+ if (n > 0) {
325
+ setLocalCounts((c) => ({ ...c, [`${documentId}@${currentVersion}`]: n }));
326
+ refresh();
327
+ }
328
+ notify(
329
+ n > 0 ? `${n} open comment thread${n === 1 ? "" : "s"} copied to version ${currentVersion}` : `Version ${olderWithMarkup.n} has no open comments`,
330
+ n > 0 ? "success" : "info",
331
+ // The markup is stored beside the version, so the sealed bytes do not change.
332
+ n > 0 && currentSigned ? { description: SIGNED_UNCHANGED } : void 0
333
+ );
334
+ }).catch(
335
+ (e) => notify(e instanceof Error ? e.message : "The comments could not be copied", "error")
336
+ ).finally(() => setCopying(false));
337
+ }
338
+ } : null;
339
+ return {
340
+ hasMarkup,
341
+ showOriginal,
342
+ setShowOriginal,
343
+ onMarkupChange,
344
+ downloadMarkup,
345
+ legacyPrompt,
346
+ olderNote
347
+ };
348
+ }
349
+
350
+ // src/document-tools/frozen-copy.ts
351
+ var baseName = (fileName) => (fileName || "document").replace(/\.pdf$/i, "");
352
+ function frozenCopyName(fileName, redacted) {
353
+ return `${baseName(fileName)} (${redacted ? "redacted" : "annotated"}).pdf`;
354
+ }
355
+ function redactionLogName(fileName) {
356
+ return `${baseName(fileName)} (redaction log).csv`;
357
+ }
358
+ async function uploadFrozenCopy(upload, bytes, source, redacted) {
359
+ const file = new File([bytes], frozenCopyName(source.fileName, redacted), {
360
+ type: "application/pdf"
361
+ });
362
+ const query = {
363
+ derived_from: [source.documentId],
364
+ derived_role: redacted ? "redaction" : "annotated",
365
+ shared: source.shared
366
+ };
367
+ if (source.folder) query.folder = source.folder;
368
+ if (source.workspaceId) query.workspace_ext_id = source.workspaceId;
369
+ const response = await upload({ file, query });
370
+ return response?.doc_ext_ids?.[0];
371
+ }
372
+
373
+ // src/document-tools/can-annotate.ts
374
+ function canAnnotate(s) {
375
+ return s.activeTab === "pdf" && s.hasPdf && !s.isImage && !s.isVideo && !s.isAudio && !s.isDownloadOnly && !s.isDraft && !s.isStreaming && !s.isSnapshot && !s.isComparing;
376
+ }
377
+ function useIsOnline() {
378
+ return useSyncExternalStore(
379
+ (onChange) => onlineManager.subscribe(onChange),
380
+ () => onlineManager.isOnline(),
381
+ () => true
382
+ );
383
+ }
384
+ function useDocumentBytes(documentRef, options) {
385
+ const { client } = useDocumentTools();
386
+ const { version, kind, enabled = true, fallback } = options;
387
+ return useQuery({
388
+ queryKey: documentBytesKey(documentRef, version ?? null, kind),
389
+ queryFn: async ({ signal }) => {
390
+ if (fallback && typeof navigator !== "undefined" && navigator.onLine === false) {
391
+ const offline = await fallback();
392
+ if (offline) return offline;
393
+ }
394
+ try {
395
+ return await (kind === "original" ? fetchDocumentFile : fetchDocumentPdf)(
396
+ client,
397
+ documentRef,
398
+ { signal }
399
+ );
400
+ } catch (error) {
401
+ const offline = await fallback?.();
402
+ if (offline) return offline;
403
+ throw error;
404
+ }
405
+ },
406
+ enabled: enabled && !!documentRef,
407
+ // 'always' so it still runs offline: the fallback serves a copy kept on this device.
408
+ networkMode: "always",
409
+ staleTime: Infinity
410
+ });
411
+ }
412
+ function intentText(signer) {
413
+ return `I, ${signer.name.trim()}, sign this document electronically. I intend this electronic signature to have the same effect as my handwritten signature.`;
414
+ }
415
+ async function sha256Hex(blob) {
416
+ const digest = await crypto.subtle.digest("SHA-256", new Uint8Array(await blob.arrayBuffer()));
417
+ return Array.from(new Uint8Array(digest), (b) => b.toString(16).padStart(2, "0")).join("");
418
+ }
419
+ function toAppearance(box) {
420
+ return { page: box.page - 1, x: box.rect.x, y: box.rect.y, width: box.rect.w, height: box.rect.h };
421
+ }
422
+ var MIN_SIGNATURE_BOX = { width: 0.3, height: 0.07 };
423
+ function isLegibleSignatureBox(rect) {
424
+ return rect.w >= MIN_SIGNATURE_BOX.width && rect.h >= MIN_SIGNATURE_BOX.height;
425
+ }
426
+ var SIGNATURES_PATH = "/v1/document/{document_ext_id}/signatures";
427
+ function errorDetail(error, fallback) {
428
+ const detail3 = error?.detail;
429
+ return typeof detail3 === "string" ? detail3 : fallback;
430
+ }
431
+ async function fetchDocumentSignatures(client, documentRef) {
432
+ const { docId, version } = parseVersionRef(documentRef);
433
+ const { data, error } = await client.GET(SIGNATURES_PATH, {
434
+ params: { path: { document_ext_id: docId }, ...version ? { query: { version } } : {} }
435
+ });
436
+ if (!data) throw new Error(errorDetail(error, "The signatures could not be read"));
437
+ return data;
438
+ }
439
+ function useDocumentSignatures(documentId, enabled = true) {
440
+ const { client } = useDocumentTools();
441
+ const { docId, version } = parseVersionRef(documentId);
442
+ return useQuery({
443
+ queryKey: arbiQueryKeys.documentSignatures(docId ?? "", version),
444
+ queryFn: () => fetchDocumentSignatures(client, documentId),
445
+ enabled: enabled && !!docId,
446
+ staleTime: 6e4
447
+ });
448
+ }
449
+ var SigningError = class extends Error {
450
+ reason;
451
+ constructor(message, reason) {
452
+ super(message);
453
+ this.name = "SigningError";
454
+ this.reason = reason;
455
+ }
456
+ };
457
+ function signingErrorReason(detail3) {
458
+ if (/still being prepared/i.test(detail3)) return "preparing";
459
+ if (/no longer the current version/i.test(detail3)) return "stale";
460
+ return "failed";
461
+ }
462
+ function signingError(error, fallback) {
463
+ const detail3 = errorDetail(error, fallback);
464
+ return new SigningError(detail3, signingErrorReason(detail3));
465
+ }
466
+ var PREPARE_RETRIES = 3;
467
+ var PREPARE_RETRY_DELAY_MS = 1500;
468
+ var wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
469
+ async function signDocument(client, deploymentDomain, {
470
+ documentId,
471
+ pdfBlob,
472
+ email,
473
+ password,
474
+ signer,
475
+ appearance,
476
+ includeDate = false,
477
+ version
478
+ }) {
479
+ const path = { document_ext_id: documentId };
480
+ const body = {
481
+ signer: { name: signer.name },
482
+ ...appearance ? { appearance } : {},
483
+ include_date: includeDate,
484
+ ...version ? { version } : {}
485
+ };
486
+ const prepare = () => client.POST("/v1/document/{document_ext_id}/sign/prepare", { params: { path }, body });
487
+ let prepared = await prepare();
488
+ for (let attempt = 0; !prepared.data && attempt < PREPARE_RETRIES; attempt++) {
489
+ if (signingError(prepared.error, "").reason !== "preparing") break;
490
+ await wait(PREPARE_RETRY_DELAY_MS);
491
+ prepared = await prepare();
492
+ }
493
+ if (!prepared.data) throw signingError(prepared.error, "Could not prepare signing");
494
+ const { statement, intent, source_sha256, timestamp } = prepared.data;
495
+ if (await sha256Hex(pdfBlob) !== source_sha256) {
496
+ throw new Error("The document changed since it was opened. Reopen it and sign again.");
497
+ }
498
+ if (intent !== intentText(signer)) throw new Error("Signing statement mismatch; not signed.");
499
+ const identity = await unlockIdentityWithPassword(email, password, deploymentDomain);
500
+ const signed = await client.POST("/v1/document/{document_ext_id}/sign", {
501
+ params: { path },
502
+ body: { ...body, timestamp, signature: signMessage(statement, identity) }
503
+ });
504
+ if (!signed.data) throw signingError(signed.error, "Signing failed");
505
+ return signed.data;
506
+ }
507
+ function useSignDocument() {
508
+ const env = useDocumentTools();
509
+ const queryClient = useQueryClient();
510
+ return useMutation({
511
+ mutationFn: (vars) => {
512
+ if (!env.deploymentDomain) throw new Error("Signing is not set up: no deployment domain");
513
+ return signDocument(env.client, env.deploymentDomain, vars);
514
+ },
515
+ onSuccess: async (result) => {
516
+ await env.onDocumentsChanged?.([result.doc_ext_id], "signed");
517
+ await queryClient.invalidateQueries({ queryKey: arbiQueryKeys.documentSignatures() });
518
+ }
519
+ });
520
+ }
521
+ var VERIFY_PATH = "/v1/sign/verify/{reference}";
522
+ var REFERENCE = /^sig-[0-9a-f]{32}$/;
523
+ function isSignatureReference(reference) {
524
+ return !!reference && REFERENCE.test(reference);
525
+ }
526
+ var SignatureVerificationError = class extends Error {
527
+ constructor(message, status) {
528
+ super(message);
529
+ this.status = status;
530
+ this.name = "SignatureVerificationError";
531
+ }
532
+ status;
533
+ };
534
+ var VERIFY_ERRORS = {
535
+ 404: "No signature has this reference.",
536
+ 429: "Too many checks, try again in a minute."
537
+ };
538
+ async function fetchSignatureVerification(client, reference) {
539
+ if (!isSignatureReference(reference)) {
540
+ throw new SignatureVerificationError("This is not a signature reference.");
541
+ }
542
+ const { data, response } = await client.GET(VERIFY_PATH, {
543
+ params: { path: { reference } }
544
+ });
545
+ if (!response.ok || !data) {
546
+ throw new SignatureVerificationError(
547
+ VERIFY_ERRORS[response.status] ?? "The signature could not be checked right now.",
548
+ response.status
549
+ );
550
+ }
551
+ return data;
552
+ }
553
+ function useSignatureVerification(reference) {
554
+ const { client } = useDocumentTools();
555
+ return useQuery({
556
+ queryKey: arbiQueryKeys.signatureVerification(reference ?? ""),
557
+ queryFn: () => fetchSignatureVerification(client, reference),
558
+ enabled: !!reference,
559
+ retry: false
560
+ });
561
+ }
562
+ async function matchSignedCopy(file, verification) {
563
+ const hash = await sha256Hex(file);
564
+ if (hash === verification.document_sha256.toLowerCase()) return { kind: "current" };
565
+ const signature = verification.signatures.find((s) => s.sha256.toLowerCase() === hash);
566
+ return signature ? { kind: "earlier", signature } : { kind: "none" };
567
+ }
568
+ function useOpenWorkspaceRead() {
569
+ const arbi = useArbiHost().arbi;
570
+ const workspaceId = arbi?.hasWorkspace ? arbi.getWorkspaceId() : void 0;
571
+ const read = (run) => arbi && workspaceId ? arbi.readInWorkspace(workspaceId, run) : run();
572
+ return { workspaceId, read };
573
+ }
574
+ var SIGNATURE_FIELD_TYPES = [
575
+ "signature",
576
+ "initials",
577
+ "date",
578
+ "name"
579
+ ];
580
+ var FIELD_TYPE_LABELS = {
581
+ signature: "Signature",
582
+ initials: "Initials",
583
+ date: "Date signed",
584
+ name: "Name"
585
+ };
586
+ var DEFAULT_FIELD_SIZE = {
587
+ signature: { width: 0.28, height: 0.05 },
588
+ initials: { width: 0.1, height: 0.04 },
589
+ date: { width: 0.22, height: 0.028 },
590
+ name: { width: 0.28, height: 0.028 }
591
+ };
592
+ var SIGNER_PROGRESS_LABELS = {
593
+ invited: "Invited",
594
+ registered: "Not opened yet",
595
+ viewed: "Viewed",
596
+ signed: "Signed",
597
+ declined: "Declined"
598
+ };
599
+ var EMAIL = /^[^@\s<>,;]{1,64}@[^@\s<>,;]{1,255}\.[^@\s<>,;]{1,63}$/;
600
+ var isEmailSigner = (signer) => EMAIL.test(signer.trim());
601
+ var MARK_FIELD_TYPES = ["signature", "initials"];
602
+ function detail(error, fallback) {
603
+ const d = error?.detail;
604
+ if (typeof d === "string") return d;
605
+ if (Array.isArray(d) && d[0] && typeof d[0] === "object" && "msg" in d[0]) {
606
+ return String(d[0].msg);
607
+ }
608
+ return fallback;
609
+ }
610
+ function newFieldId() {
611
+ return `f${Math.random().toString(36).slice(2, 10)}`;
612
+ }
613
+ function initialsOf(name) {
614
+ return name.split(/\s+/).filter(Boolean).map((part) => part[0].toUpperCase()).join("").slice(0, 4);
615
+ }
616
+ function fieldsInSigningOrder(fields, signer) {
617
+ return fields.filter((f) => f.signer === signer).sort((a, b) => a.page - b.page || a.y - b.y || a.x - b.x);
618
+ }
619
+ function sendBlocker(fields) {
620
+ if (fields.length === 0) return "Place at least one field.";
621
+ const signers = [...new Set(fields.map((f) => f.signer))];
622
+ for (const signer of signers) {
623
+ if (!fields.some((f) => f.signer === signer && MARK_FIELD_TYPES.includes(f.type))) {
624
+ return "Every signer needs a signature or initials field.";
625
+ }
626
+ }
627
+ return null;
628
+ }
629
+ function fieldsFromSuggestions(suggestions, assign, members = /* @__PURE__ */ new Map()) {
630
+ return suggestions.flatMap((s) => {
631
+ const email = s.signer_email?.toLowerCase();
632
+ const signer = email ? members.get(email) ?? email : assign[s.group];
633
+ if (!signer) return [];
634
+ return [
635
+ {
636
+ id: newFieldId(),
637
+ type: s.type,
638
+ page: s.page,
639
+ x: s.x,
640
+ y: s.y,
641
+ width: s.width,
642
+ height: s.height,
643
+ signer,
644
+ required: s.required ?? true
645
+ }
646
+ ];
647
+ });
648
+ }
649
+ function splitSuggestions(suggestions) {
650
+ return {
651
+ placed: suggestions.filter((s) => s.source === "anchor"),
652
+ extras: suggestions.filter((s) => s.source !== "anchor")
653
+ };
654
+ }
655
+ async function fetchDocumentSignatureRequests(client, documentId) {
656
+ const { data, error } = await client.GET("/v1/document/{document_ext_id}/signature-requests", {
657
+ params: { path: { document_ext_id: documentId } }
658
+ });
659
+ if (!data) throw new Error(detail(error, "The signature requests could not be read"));
660
+ return data;
661
+ }
662
+ function useDocumentSignatureRequests(documentId, enabled = true) {
663
+ const { client } = useDocumentTools();
664
+ return useQuery({
665
+ queryKey: arbiQueryKeys.signatureRequestsForDocument(documentId ?? ""),
666
+ queryFn: () => fetchDocumentSignatureRequests(client, documentId),
667
+ enabled: enabled && !!documentId,
668
+ staleTime: 15e3
669
+ });
670
+ }
671
+ function useSignatureRequestsAwaitingMe(enabled = true) {
672
+ const { client } = useDocumentTools();
673
+ const { workspaceId, read } = useOpenWorkspaceRead();
674
+ return useQuery({
675
+ queryKey: arbiQueryKeys.signatureRequestsAwaitingMe(workspaceId),
676
+ queryFn: () => read(async () => {
677
+ const { data, error } = await client.GET("/v1/signature-requests", {
678
+ params: { query: { awaiting_me: true } }
679
+ });
680
+ if (!data) throw new Error(detail(error, "The signature requests could not be read"));
681
+ return data;
682
+ }),
683
+ enabled,
684
+ staleTime: 15e3
685
+ });
686
+ }
687
+ function useSignatureRequest(requestId) {
688
+ const { client } = useDocumentTools();
689
+ return useQuery({
690
+ queryKey: arbiQueryKeys.signatureRequest(requestId ?? ""),
691
+ queryFn: async () => {
692
+ const { data, error } = await client.GET("/v1/signature-requests/{request_ext_id}", {
693
+ params: { path: { request_ext_id: requestId } }
694
+ });
695
+ if (!data) throw new Error(detail(error, "The signature request could not be read"));
696
+ return data;
697
+ },
698
+ enabled: !!requestId
699
+ });
700
+ }
701
+ async function signatureFieldSuggestions(client, documentId, version) {
702
+ const { data, error } = await client.GET(
703
+ "/v1/document/{document_ext_id}/signature-fields/suggest",
704
+ {
705
+ params: { path: { document_ext_id: documentId }, ...version ? { query: { version } } : {} }
706
+ }
707
+ );
708
+ if (!data) throw new Error(detail(error, "Signature blocks could not be read"));
709
+ return { fields: data.fields, signers: data.signers ?? [] };
710
+ }
711
+ async function suggestSignatureFields(client, documentId, version) {
712
+ return (await signatureFieldSuggestions(client, documentId, version)).fields;
713
+ }
714
+ async function saveSignatureRequest(client, { documentId, version, fields, message, requestId, send }) {
715
+ if (!requestId) {
716
+ const { data, error } = await client.POST("/v1/document/{document_ext_id}/signature-requests", {
717
+ params: { path: { document_ext_id: documentId } },
718
+ body: { fields, message: message || null, send, ...version ? { version } : {} }
719
+ });
720
+ if (!data) throw new Error(detail(error, "The signature request could not be saved"));
721
+ return data;
722
+ }
723
+ const path = { request_ext_id: requestId };
724
+ const updated = await client.PATCH("/v1/signature-requests/{request_ext_id}", {
725
+ params: { path },
726
+ body: { fields, message: message ?? null }
727
+ });
728
+ if (!updated.data) throw new Error(detail(updated.error, "The draft could not be saved"));
729
+ if (!send) return updated.data;
730
+ const sent = await client.POST("/v1/signature-requests/{request_ext_id}/send", {
731
+ params: { path }
732
+ });
733
+ if (!sent.data) throw new Error(detail(sent.error, "The request could not be sent"));
734
+ return sent.data;
735
+ }
736
+ function useInvalidateRequests() {
737
+ const queryClient = useQueryClient();
738
+ return () => queryClient.invalidateQueries({ queryKey: arbiQueryKeys.signatureRequests() });
739
+ }
740
+ function useSaveSignatureRequest() {
741
+ const { client } = useDocumentTools();
742
+ const invalidate = useInvalidateRequests();
743
+ return useMutation({
744
+ mutationFn: (vars) => saveSignatureRequest(client, vars),
745
+ onSuccess: invalidate
746
+ });
747
+ }
748
+ function useVoidSignatureRequest() {
749
+ const { client } = useDocumentTools();
750
+ const invalidate = useInvalidateRequests();
751
+ return useMutation({
752
+ mutationFn: async (requestId) => {
753
+ const { data, error } = await client.POST("/v1/signature-requests/{request_ext_id}/void", {
754
+ params: { path: { request_ext_id: requestId } }
755
+ });
756
+ if (!data) throw new Error(detail(error, "The request could not be voided"));
757
+ return data;
758
+ },
759
+ onSuccess: invalidate
760
+ });
761
+ }
762
+ function useDeclineSignatureRequest() {
763
+ const { client } = useDocumentTools();
764
+ const invalidate = useInvalidateRequests();
765
+ return useMutation({
766
+ mutationFn: async ({ requestId, reason }) => {
767
+ const { data, error } = await client.POST("/v1/signature-requests/{request_ext_id}/decline", {
768
+ params: { path: { request_ext_id: requestId } },
769
+ body: { reason: reason || null }
770
+ });
771
+ if (!data) throw new Error(detail(error, "Could not decline"));
772
+ return data;
773
+ },
774
+ onSuccess: invalidate
775
+ });
776
+ }
777
+ async function signSignatureRequest(client, deploymentDomain, { request, pdfBlob, email, password, adopted, fields }) {
778
+ const path = { request_ext_id: request.external_id };
779
+ const body = { adopted, fields };
780
+ const prepared = await client.POST("/v1/signature-requests/{request_ext_id}/sign/prepare", {
781
+ params: { path },
782
+ body
783
+ });
784
+ if (!prepared.data) {
785
+ throw new SigningError(detail(prepared.error, "Could not prepare signing"), "failed");
786
+ }
787
+ const { statement, intent, source_sha256, timestamp } = prepared.data;
788
+ if (source_sha256 !== request.source_sha256 || await sha256Hex(pdfBlob) !== source_sha256) {
789
+ throw new SigningError("The document changed since it was sent. Ask for it again.", "stale");
790
+ }
791
+ if (intent !== intentText({ name: adopted.name })) {
792
+ throw new Error("Signing statement mismatch; not signed.");
793
+ }
794
+ const identity = await unlockIdentityWithPassword(email, password, deploymentDomain);
795
+ const signed = await client.POST("/v1/signature-requests/{request_ext_id}/sign", {
796
+ params: { path },
797
+ body: { ...body, timestamp, signature: signMessage(statement, identity) }
798
+ });
799
+ if (!signed.data) throw new SigningError(detail(signed.error, "Signing failed"), "failed");
800
+ return signed.data;
801
+ }
802
+ function useSignSignatureRequest() {
803
+ const env = useDocumentTools();
804
+ const invalidate = useInvalidateRequests();
805
+ const queryClient = useQueryClient();
806
+ return useMutation({
807
+ mutationFn: (vars) => {
808
+ if (!env.deploymentDomain) throw new Error("Signing is not set up: no deployment domain");
809
+ return signSignatureRequest(env.client, env.deploymentDomain, vars);
810
+ },
811
+ onSuccess: async (result) => {
812
+ await invalidate();
813
+ if (result.completed && result.request.doc_ext_id) {
814
+ await env.onDocumentsChanged?.([result.request.doc_ext_id], "signed");
815
+ await queryClient.invalidateQueries({ queryKey: arbiQueryKeys.documentSignatures() });
816
+ }
817
+ }
818
+ });
819
+ }
820
+ function useSignerCandidates(enabled = true) {
821
+ const { client, user } = useDocumentTools();
822
+ const { workspaceId, read } = useOpenWorkspaceRead();
823
+ return useQuery({
824
+ queryKey: arbiQueryKeys.signatureCandidates(workspaceId ?? ""),
825
+ queryFn: async () => {
826
+ const { data, error } = await read(() => client.GET("/v1/workspace/users"));
827
+ if (!data) throw new Error(detail(error, "The workspace members could not be read"));
828
+ const people = data.filter((m) => m.user.external_id.startsWith("usr-")).map((m) => ({
829
+ externalId: m.user.external_id,
830
+ name: [m.user.given_name, m.user.family_name].filter(Boolean).join(" ") || m.user.email,
831
+ email: m.user.email,
832
+ role: String(m.role)
833
+ }));
834
+ const me = user?.email?.toLowerCase();
835
+ return [...people].sort(
836
+ (a, b) => Number(b.email.toLowerCase() === me) - Number(a.email.toLowerCase() === me)
837
+ );
838
+ },
839
+ enabled,
840
+ staleTime: 6e4
841
+ });
842
+ }
843
+ async function inviteSigners(client, documentId, emails) {
844
+ if (!emails.length) return;
845
+ const { error, response } = await client.POST("/v1/workspace/users", {
846
+ body: workspaces.addWorkspaceMembersBody({
847
+ emails,
848
+ role: "guest",
849
+ confirm_invite: true,
850
+ resource_type: "document",
851
+ resource_ext_id: documentId
852
+ })
853
+ });
854
+ if (response.status === 409) {
855
+ throw new Error("Share the document with the workspace before asking others to sign it.");
856
+ }
857
+ if (error && response.status !== 404) {
858
+ throw new Error(detail(error, "The signers could not be invited"));
859
+ }
860
+ }
861
+ function useInviteSigners() {
862
+ const { client } = useDocumentTools();
863
+ const invalidate = useInvalidateRequests();
864
+ const queryClient = useQueryClient();
865
+ return useMutation({
866
+ mutationFn: ({ documentId, emails }) => inviteSigners(client, documentId, emails),
867
+ onSuccess: async () => {
868
+ await invalidate();
869
+ await queryClient.invalidateQueries({ queryKey: arbiQueryKeys.signatureCandidates() });
870
+ }
871
+ });
872
+ }
873
+ var INFORMAL_SIGNATURE_NOTE = "Your saved signature or initials are placed as an image on the document.";
874
+ async function keypair(source = getUserEncryptionKeypair) {
875
+ await initSodium();
876
+ const pair = await source();
877
+ if (!pair) throw new Error("Sign in again to use your saved signature.");
878
+ return pair;
879
+ }
880
+ function detail2(error, fallback) {
881
+ const d = error?.detail;
882
+ return typeof d === "string" ? d : fallback;
883
+ }
884
+ async function fetchSavedSignature(client, source) {
885
+ const { data, error } = await client.GET("/v1/user/signature");
886
+ if (!data) throw new Error(detail2(error, "Your saved signature could not be read"));
887
+ if (!data.sealed) return null;
888
+ const pair = await keypair(source);
889
+ const json = new TextDecoder().decode(sealedBoxDecrypt(data.sealed, pair.secretKey));
890
+ return JSON.parse(json);
891
+ }
892
+ async function storeSavedSignature(client, signature, source) {
893
+ const pair = await keypair(source);
894
+ const sealed = sealedBoxEncrypt(
895
+ Uint8Array.from(new TextEncoder().encode(JSON.stringify(signature))),
896
+ pair.publicKey
897
+ );
898
+ const { error, response } = await client.PUT("/v1/user/signature", { body: { sealed } });
899
+ if (!response.ok) throw new Error(detail2(error, "Your signature could not be saved"));
900
+ }
901
+ async function deleteSavedSignature(client) {
902
+ const { error, response } = await client.DELETE("/v1/user/signature");
903
+ if (!response.ok) throw new Error(detail2(error, "Your saved signature could not be deleted"));
904
+ }
905
+ function useSavedSignature(enabled = true) {
906
+ const { client } = useDocumentTools();
907
+ return useQuery({
908
+ queryKey: arbiQueryKeys.savedSignature(),
909
+ queryFn: () => fetchSavedSignature(client),
910
+ enabled,
911
+ staleTime: 6e4,
912
+ retry: false
913
+ });
914
+ }
915
+ function useStoreSavedSignature() {
916
+ const { client } = useDocumentTools();
917
+ const queryClient = useQueryClient();
918
+ return useMutation({
919
+ mutationFn: (signature) => storeSavedSignature(client, signature),
920
+ onSuccess: (_, signature) => queryClient.setQueryData(arbiQueryKeys.savedSignature(), signature)
921
+ });
922
+ }
923
+ function useDeleteSavedSignature() {
924
+ const { client } = useDocumentTools();
925
+ const queryClient = useQueryClient();
926
+ return useMutation({
927
+ mutationFn: () => deleteSavedSignature(client),
928
+ onSuccess: () => queryClient.setQueryData(arbiQueryKeys.savedSignature(), null)
929
+ });
930
+ }
931
+ function pngBytes(base64) {
932
+ const raw = atob(base64);
933
+ const out = new Uint8Array(raw.length);
934
+ for (let i = 0; i < raw.length; i++) out[i] = raw.charCodeAt(i);
935
+ return out;
936
+ }
937
+ function pngSize(base64) {
938
+ const bytes = pngBytes(base64.slice(0, 64));
939
+ const view = new DataView(bytes.buffer);
940
+ return { width: view.getUint32(16), height: view.getUint32(20) };
941
+ }
942
+ function failure(error) {
943
+ const detail3 = error?.detail;
944
+ return new Error(typeof detail3 === "string" ? detail3 : "The shared file request failed");
945
+ }
946
+ async function createFileShare(client, documentId, body) {
947
+ const result = await client.POST("/v1/document/{document_ext_id}/file-shares", {
948
+ params: { path: { document_ext_id: documentId } },
949
+ body
950
+ });
951
+ if (!result.data) throw failure(result.error);
952
+ return result.data;
953
+ }
954
+ async function getFileShare(client, id) {
955
+ const result = await client.GET("/v1/file-shares/{share_id}", {
956
+ params: { path: { share_id: id } }
957
+ });
958
+ if (!result.data) throw failure(result.error);
959
+ return result.data;
960
+ }
961
+ async function fileShareBytes(client, id, kind) {
962
+ const result = await client.GET("/v1/file-shares/{share_id}/file", {
963
+ params: { path: { share_id: id }, query: { kind } },
964
+ parseAs: "blob"
965
+ });
966
+ if (!result.data) throw failure(result.error);
967
+ return result.data;
968
+ }
969
+ function fileShareSigningRequest(share) {
970
+ return {
971
+ external_id: share.external_id,
972
+ doc_ext_id: share.document_ext_id,
973
+ version: share.version,
974
+ source_sha256: share.source_sha256,
975
+ fields: share.fields,
976
+ message: share.message,
977
+ status: share.status === "signing" ? "sent" : share.status === "completed" ? "completed" : "voided",
978
+ signers: share.recipients.filter((r) => r.permission === "sign").map((r) => ({
979
+ external_id: r.email,
980
+ email: r.email,
981
+ name: r.email,
982
+ status: r.status === "signed" ? "signed" : "pending",
983
+ progress: r.status === "signed" ? "signed" : "registered",
984
+ fields: share.fields.filter((f) => f.signer === r.email).length
985
+ })),
986
+ created_at: (/* @__PURE__ */ new Date(0)).toISOString(),
987
+ stale: false
988
+ };
989
+ }
990
+ async function signFileShare(client, domain, vars) {
991
+ const path = { share_id: vars.request.external_id };
992
+ const body = { adopted: vars.adopted, fields: vars.fields };
993
+ const prepared = await client.POST("/v1/file-shares/{share_id}/sign/prepare", {
994
+ params: { path },
995
+ body
996
+ });
997
+ if (!prepared.data) throw failure(prepared.error);
998
+ const { statement, timestamp, source_sha256, intent } = prepared.data;
999
+ if (source_sha256 !== vars.request.source_sha256 || await sha256Hex(vars.pdfBlob) !== source_sha256)
1000
+ throw new Error("The shared file changed. Ask the sender for a new request.");
1001
+ if (intent !== intentText({ name: vars.adopted.name }))
1002
+ throw new Error("Signing statement mismatch");
1003
+ const identity = await unlockIdentityWithPassword(vars.email, vars.password, domain);
1004
+ const result = await client.POST("/v1/file-shares/{share_id}/sign", {
1005
+ params: { path },
1006
+ body: { ...body, timestamp, signature: signMessage(statement, identity) }
1007
+ });
1008
+ if (!result.data) throw failure(result.error);
1009
+ return {
1010
+ request: fileShareSigningRequest(result.data),
1011
+ completed: result.data.status === "completed"
1012
+ };
1013
+ }
1014
+ function useFileShare(client, shareId, signing) {
1015
+ return useQuery({
1016
+ queryKey: arbiQueryKeys.fileShare(shareId),
1017
+ queryFn: () => getFileShare(client, shareId),
1018
+ refetchInterval: signing ? false : 5e3
1019
+ });
1020
+ }
1021
+ function useFileSharePdf(client, shareId, share) {
1022
+ return useQuery({
1023
+ queryKey: arbiQueryKeys.fileSharePdf(shareId, share?.status),
1024
+ queryFn: () => fileShareBytes(client, shareId, share?.status === "completed" ? "signed" : "view"),
1025
+ enabled: !!share?.can_preview,
1026
+ staleTime: Infinity
1027
+ });
1028
+ }
1029
+ function useFileShares(client) {
1030
+ return useQuery({
1031
+ queryKey: arbiQueryKeys.fileShares(),
1032
+ queryFn: async () => {
1033
+ const result = await client.GET("/v1/file-shares");
1034
+ if (!result.data) throw new Error("Could not load shared files");
1035
+ return result.data;
1036
+ }
1037
+ });
1038
+ }
1039
+
1040
+ export { DEFAULT_FIELD_SIZE, FIELD_TYPE_LABELS, INFORMAL_SIGNATURE_NOTE, LEGACY_LAYER_PREFIX, MARK_FIELD_TYPES, MAX_MERGE_ATTEMPTS, MarkupStoreError, SIGNATURE_FIELD_TYPES, SIGNED_UNCHANGED, SIGNER_PROGRESS_LABELS, SignatureVerificationError, SigningError, VERIFY_PATH, addToMarkup, canAnnotate, copyOpenThreads, createFileShare, createMarkupStore, deleteSavedSignature, fetchDocumentSignatureRequests, fetchDocumentSignatures, fetchSavedSignature, fetchSignatureVerification, fieldsFromSuggestions, fieldsInSigningOrder, fileShareBytes, fileShareSigningRequest, forgetLegacyLayer, frozenCopyName, getFileShare, initialsOf, intentText, inviteSigners, isEmailSigner, isLegibleSignatureBox, isSignatureReference, marksByVersion, matchSignedCopy, migrateLegacyLayer, newFieldId, openThreadsOf, pngBytes, pngSize, readLegacyLayer, redactionLogName, saveSignatureRequest, sendBlocker, sha256Hex, signDocument, signFileShare, signSignatureRequest, signatureFieldSuggestions, splitSuggestions, storeSavedSignature, suggestSignatureFields, toAppearance, uploadFrozenCopy, useDeclineSignatureRequest, useDeleteSavedSignature, useDocumentBytes, useDocumentMarkup, useDocumentSignatureRequests, useDocumentSignatures, useFileShare, useFileSharePdf, useFileShares, useInviteSigners, useIsOnline, useSaveSignatureRequest, useSavedSignature, useSignDocument, useSignSignatureRequest, useSignatureRequest, useSignatureRequestsAwaitingMe, useSignatureVerification, useSignerCandidates, useStoreSavedSignature, useVoidSignatureRequest, versionsWithMarkup };
1041
+ //# sourceMappingURL=chunk-4DTTEUVY.js.map
1042
+ //# sourceMappingURL=chunk-4DTTEUVY.js.map