@signiphi/pdf-signer 0.1.1

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 (192) hide show
  1. package/INSTALLING_LOCALLY.md +184 -0
  2. package/LICENSE +2 -0
  3. package/README.md +1093 -0
  4. package/assets/viewer.html +314 -0
  5. package/dist/__tests__/helpers/fixtures.d.ts +43 -0
  6. package/dist/__tests__/helpers/fixtures.d.ts.map +1 -0
  7. package/dist/__tests__/helpers/mocks.d.ts +333 -0
  8. package/dist/__tests__/helpers/mocks.d.ts.map +1 -0
  9. package/dist/__tests__/setup.d.ts +6 -0
  10. package/dist/__tests__/setup.d.ts.map +1 -0
  11. package/dist/components/AttachmentUpload.d.ts +17 -0
  12. package/dist/components/AttachmentUpload.d.ts.map +1 -0
  13. package/dist/components/EditableFieldsPanel.d.ts +30 -0
  14. package/dist/components/EditableFieldsPanel.d.ts.map +1 -0
  15. package/dist/components/ErrorBoundary.d.ts +67 -0
  16. package/dist/components/ErrorBoundary.d.ts.map +1 -0
  17. package/dist/components/FormFieldsView.d.ts +42 -0
  18. package/dist/components/FormFieldsView.d.ts.map +1 -0
  19. package/dist/components/PdfViewerStyled.d.ts +16 -0
  20. package/dist/components/PdfViewerStyled.d.ts.map +1 -0
  21. package/dist/components/PoweredBySigniphi.d.ts +11 -0
  22. package/dist/components/PoweredBySigniphi.d.ts.map +1 -0
  23. package/dist/components/SignatureCanvas.d.ts +12 -0
  24. package/dist/components/SignatureCanvas.d.ts.map +1 -0
  25. package/dist/components/SignatureInitialsBox.d.ts +23 -0
  26. package/dist/components/SignatureInitialsBox.d.ts.map +1 -0
  27. package/dist/components/SignatureModal.d.ts +22 -0
  28. package/dist/components/SignatureModal.d.ts.map +1 -0
  29. package/dist/components/SigningInstructions.d.ts +12 -0
  30. package/dist/components/SigningInstructions.d.ts.map +1 -0
  31. package/dist/components/SubmissionForm.d.ts +52 -0
  32. package/dist/components/SubmissionForm.d.ts.map +1 -0
  33. package/dist/components/ViewToggleToolbar.d.ts +30 -0
  34. package/dist/components/ViewToggleToolbar.d.ts.map +1 -0
  35. package/dist/components/form-fields/CheckboxRenderer.d.ts +10 -0
  36. package/dist/components/form-fields/CheckboxRenderer.d.ts.map +1 -0
  37. package/dist/components/form-fields/DateFieldRenderer.d.ts +14 -0
  38. package/dist/components/form-fields/DateFieldRenderer.d.ts.map +1 -0
  39. package/dist/components/form-fields/DropdownRenderer.d.ts +14 -0
  40. package/dist/components/form-fields/DropdownRenderer.d.ts.map +1 -0
  41. package/dist/components/form-fields/FormFieldRenderer.d.ts +20 -0
  42. package/dist/components/form-fields/FormFieldRenderer.d.ts.map +1 -0
  43. package/dist/components/form-fields/InitialsFieldRenderer.d.ts +14 -0
  44. package/dist/components/form-fields/InitialsFieldRenderer.d.ts.map +1 -0
  45. package/dist/components/form-fields/RadioGroupRenderer.d.ts +10 -0
  46. package/dist/components/form-fields/RadioGroupRenderer.d.ts.map +1 -0
  47. package/dist/components/form-fields/SignatureFieldRenderer.d.ts +16 -0
  48. package/dist/components/form-fields/SignatureFieldRenderer.d.ts.map +1 -0
  49. package/dist/components/form-fields/TextFieldRenderer.d.ts +14 -0
  50. package/dist/components/form-fields/TextFieldRenderer.d.ts.map +1 -0
  51. package/dist/components/form-fields/TextLabelRenderer.d.ts +14 -0
  52. package/dist/components/form-fields/TextLabelRenderer.d.ts.map +1 -0
  53. package/dist/components/form-fields/index.d.ts +14 -0
  54. package/dist/components/form-fields/index.d.ts.map +1 -0
  55. package/dist/components/index.d.ts +14 -0
  56. package/dist/components/index.d.ts.map +1 -0
  57. package/dist/components/index.js +6297 -0
  58. package/dist/components/index.js.map +1 -0
  59. package/dist/components/index.mjs +6248 -0
  60. package/dist/components/index.mjs.map +1 -0
  61. package/dist/core/PdfViewerCore.d.ts +19 -0
  62. package/dist/core/PdfViewerCore.d.ts.map +1 -0
  63. package/dist/core/SignatureCaptureCore.d.ts +37 -0
  64. package/dist/core/SignatureCaptureCore.d.ts.map +1 -0
  65. package/dist/core/index.d.ts +3 -0
  66. package/dist/core/index.d.ts.map +1 -0
  67. package/dist/core/index.js +907 -0
  68. package/dist/core/index.js.map +1 -0
  69. package/dist/core/index.mjs +884 -0
  70. package/dist/core/index.mjs.map +1 -0
  71. package/dist/hooks/index.d.ts +8 -0
  72. package/dist/hooks/index.d.ts.map +1 -0
  73. package/dist/hooks/index.js +2167 -0
  74. package/dist/hooks/index.js.map +1 -0
  75. package/dist/hooks/index.mjs +2139 -0
  76. package/dist/hooks/index.mjs.map +1 -0
  77. package/dist/hooks/useAttachments.d.ts +25 -0
  78. package/dist/hooks/useAttachments.d.ts.map +1 -0
  79. package/dist/hooks/useFieldFiltering.d.ts +29 -0
  80. package/dist/hooks/useFieldFiltering.d.ts.map +1 -0
  81. package/dist/hooks/useFormFields.d.ts +23 -0
  82. package/dist/hooks/useFormFields.d.ts.map +1 -0
  83. package/dist/hooks/useMultiSignerContext.d.ts +25 -0
  84. package/dist/hooks/useMultiSignerContext.d.ts.map +1 -0
  85. package/dist/hooks/usePdfViewer.d.ts +52 -0
  86. package/dist/hooks/usePdfViewer.d.ts.map +1 -0
  87. package/dist/hooks/useSignatureCapture.d.ts +17 -0
  88. package/dist/hooks/useSignatureCapture.d.ts.map +1 -0
  89. package/dist/hooks/useSignatures.d.ts +25 -0
  90. package/dist/hooks/useSignatures.d.ts.map +1 -0
  91. package/dist/index.css +4929 -0
  92. package/dist/index.css.map +1 -0
  93. package/dist/index.d.ts +17 -0
  94. package/dist/index.d.ts.map +1 -0
  95. package/dist/index.js +7220 -0
  96. package/dist/index.js.map +1 -0
  97. package/dist/index.mjs +7093 -0
  98. package/dist/index.mjs.map +1 -0
  99. package/dist/integrations/index.d.ts +6 -0
  100. package/dist/integrations/index.d.ts.map +1 -0
  101. package/dist/integrations/index.js +242 -0
  102. package/dist/integrations/index.js.map +1 -0
  103. package/dist/integrations/index.mjs +218 -0
  104. package/dist/integrations/index.mjs.map +1 -0
  105. package/dist/integrations/next-config.d.ts +46 -0
  106. package/dist/integrations/next-config.d.ts.map +1 -0
  107. package/dist/integrations/vite-plugin.d.ts +48 -0
  108. package/dist/integrations/vite-plugin.d.ts.map +1 -0
  109. package/dist/lib/index.d.ts +3 -0
  110. package/dist/lib/index.d.ts.map +1 -0
  111. package/dist/lib/ui/alert.d.ts +9 -0
  112. package/dist/lib/ui/alert.d.ts.map +1 -0
  113. package/dist/lib/ui/button.d.ts +12 -0
  114. package/dist/lib/ui/button.d.ts.map +1 -0
  115. package/dist/lib/ui/calendar.d.ts +10 -0
  116. package/dist/lib/ui/calendar.d.ts.map +1 -0
  117. package/dist/lib/ui/card.d.ts +9 -0
  118. package/dist/lib/ui/card.d.ts.map +1 -0
  119. package/dist/lib/ui/checkbox.d.ts +5 -0
  120. package/dist/lib/ui/checkbox.d.ts.map +1 -0
  121. package/dist/lib/ui/dialog.d.ts +20 -0
  122. package/dist/lib/ui/dialog.d.ts.map +1 -0
  123. package/dist/lib/ui/index.d.ts +12 -0
  124. package/dist/lib/ui/index.d.ts.map +1 -0
  125. package/dist/lib/ui/input.d.ts +6 -0
  126. package/dist/lib/ui/input.d.ts.map +1 -0
  127. package/dist/lib/ui/label.d.ts +6 -0
  128. package/dist/lib/ui/label.d.ts.map +1 -0
  129. package/dist/lib/ui/popover.d.ts +7 -0
  130. package/dist/lib/ui/popover.d.ts.map +1 -0
  131. package/dist/lib/ui/radio-group.d.ts +6 -0
  132. package/dist/lib/ui/radio-group.d.ts.map +1 -0
  133. package/dist/lib/ui/select.d.ts +14 -0
  134. package/dist/lib/ui/select.d.ts.map +1 -0
  135. package/dist/lib/utils.d.ts +7 -0
  136. package/dist/lib/utils.d.ts.map +1 -0
  137. package/dist/styles/index.css +5004 -0
  138. package/dist/types/index.d.ts +265 -0
  139. package/dist/types/index.d.ts.map +1 -0
  140. package/dist/types/index.js +26 -0
  141. package/dist/types/index.js.map +1 -0
  142. package/dist/types/index.mjs +23 -0
  143. package/dist/types/index.mjs.map +1 -0
  144. package/dist/utils/attachment-validators.d.ts +118 -0
  145. package/dist/utils/attachment-validators.d.ts.map +1 -0
  146. package/dist/utils/audit-trail.d.ts +27 -0
  147. package/dist/utils/audit-trail.d.ts.map +1 -0
  148. package/dist/utils/date-validation.d.ts +30 -0
  149. package/dist/utils/date-validation.d.ts.map +1 -0
  150. package/dist/utils/errors.d.ts +106 -0
  151. package/dist/utils/errors.d.ts.map +1 -0
  152. package/dist/utils/field-extraction.d.ts +27 -0
  153. package/dist/utils/field-extraction.d.ts.map +1 -0
  154. package/dist/utils/field-visibility.d.ts +104 -0
  155. package/dist/utils/field-visibility.d.ts.map +1 -0
  156. package/dist/utils/index.d.ts +17 -0
  157. package/dist/utils/index.d.ts.map +1 -0
  158. package/dist/utils/index.js +2501 -0
  159. package/dist/utils/index.js.map +1 -0
  160. package/dist/utils/index.mjs +2404 -0
  161. package/dist/utils/index.mjs.map +1 -0
  162. package/dist/utils/logger.d.ts +16 -0
  163. package/dist/utils/logger.d.ts.map +1 -0
  164. package/dist/utils/pdf-field-type-helpers.d.ts +78 -0
  165. package/dist/utils/pdf-field-type-helpers.d.ts.map +1 -0
  166. package/dist/utils/pdf-helpers.d.ts +38 -0
  167. package/dist/utils/pdf-helpers.d.ts.map +1 -0
  168. package/dist/utils/pdf-lib-loader.d.ts +45 -0
  169. package/dist/utils/pdf-lib-loader.d.ts.map +1 -0
  170. package/dist/utils/pdf-manipulation.d.ts +93 -0
  171. package/dist/utils/pdf-manipulation.d.ts.map +1 -0
  172. package/dist/utils/pdf-validators.d.ts +149 -0
  173. package/dist/utils/pdf-validators.d.ts.map +1 -0
  174. package/dist/utils/pdf-viewer-filter.d.ts +35 -0
  175. package/dist/utils/pdf-viewer-filter.d.ts.map +1 -0
  176. package/dist/utils/pdf-widget-helpers.d.ts +98 -0
  177. package/dist/utils/pdf-widget-helpers.d.ts.map +1 -0
  178. package/dist/utils/pdfjs-config.d.ts +56 -0
  179. package/dist/utils/pdfjs-config.d.ts.map +1 -0
  180. package/dist/utils/pdfjs-version-check.d.ts +28 -0
  181. package/dist/utils/pdfjs-version-check.d.ts.map +1 -0
  182. package/dist/utils/performance-monitor.d.ts +172 -0
  183. package/dist/utils/performance-monitor.d.ts.map +1 -0
  184. package/dist/utils/tracking.d.ts +89 -0
  185. package/dist/utils/tracking.d.ts.map +1 -0
  186. package/package.json +180 -0
  187. package/scripts/analyze-bundle.js +271 -0
  188. package/scripts/copy-utils.js +227 -0
  189. package/scripts/copy-utils.test.js +164 -0
  190. package/scripts/postinstall.js +109 -0
  191. package/scripts/setup.js +108 -0
  192. package/src/styles/index.css +139 -0
@@ -0,0 +1,2501 @@
1
+ 'use strict';
2
+
3
+ var pdfjsLib2 = require('pdfjs-dist');
4
+ var dateFns = require('date-fns');
5
+
6
+ function _interopNamespace(e) {
7
+ if (e && e.__esModule) return e;
8
+ var n = Object.create(null);
9
+ if (e) {
10
+ Object.keys(e).forEach(function (k) {
11
+ if (k !== 'default') {
12
+ var d = Object.getOwnPropertyDescriptor(e, k);
13
+ Object.defineProperty(n, k, d.get ? d : {
14
+ enumerable: true,
15
+ get: function () { return e[k]; }
16
+ });
17
+ }
18
+ });
19
+ }
20
+ n.default = e;
21
+ return Object.freeze(n);
22
+ }
23
+
24
+ var pdfjsLib2__namespace = /*#__PURE__*/_interopNamespace(pdfjsLib2);
25
+
26
+ var __defProp = Object.defineProperty;
27
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
28
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
29
+
30
+ // src/utils/logger.ts
31
+ function isProduction() {
32
+ return typeof process !== "undefined" && process.env.NODE_ENV === "production";
33
+ }
34
+ function createPrefix(level) {
35
+ return `[@signiphi/pdf-signer] [${level.toUpperCase()}]`;
36
+ }
37
+ function createLogger() {
38
+ const isProd = isProduction();
39
+ return {
40
+ debug(message, ...args) {
41
+ if (!isProd) {
42
+ console.log(createPrefix("debug"), message, ...args);
43
+ }
44
+ },
45
+ info(message, ...args) {
46
+ if (!isProd) {
47
+ console.log(createPrefix("info"), message, ...args);
48
+ }
49
+ },
50
+ warn(message, ...args) {
51
+ if (!isProd) {
52
+ console.warn(createPrefix("warn"), message, ...args);
53
+ }
54
+ },
55
+ error(message, ...args) {
56
+ console.error(createPrefix("error"), message, ...args);
57
+ }
58
+ };
59
+ }
60
+ var logger = createLogger();
61
+
62
+ // src/utils/pdf-validators.ts
63
+ function validatePdfBytes(pdfBytes) {
64
+ if (!pdfBytes || pdfBytes.length === 0) {
65
+ return {
66
+ valid: false,
67
+ error: "PDF bytes are empty"
68
+ };
69
+ }
70
+ const header = new TextDecoder().decode(pdfBytes.slice(0, 12));
71
+ if (!header.startsWith("%PDF-")) {
72
+ return {
73
+ valid: false,
74
+ error: "Invalid PDF format: Missing PDF header"
75
+ };
76
+ }
77
+ return { valid: true };
78
+ }
79
+ function isAutoGeneratedLabel(label) {
80
+ if (!label || !label.trim()) return true;
81
+ const autoLabels = [
82
+ "Signature",
83
+ "Initials",
84
+ "Date",
85
+ "Text",
86
+ "Checkbox",
87
+ "Dropdown",
88
+ "Option",
89
+ "Radio"
90
+ ];
91
+ return autoLabels.includes(label.trim());
92
+ }
93
+ function validateFieldValues(values) {
94
+ const errors = [];
95
+ if (!values || typeof values !== "object") {
96
+ errors.push("Field values must be an object");
97
+ return { valid: false, errors };
98
+ }
99
+ const valuesObj = values;
100
+ for (const [key, value] of Object.entries(valuesObj)) {
101
+ if (typeof value !== "string") {
102
+ errors.push(`Field value for "${key}" must be a string, got ${typeof value}`);
103
+ }
104
+ }
105
+ return {
106
+ valid: errors.length === 0,
107
+ errors
108
+ };
109
+ }
110
+ function validateSignatures(signatures) {
111
+ const errors = [];
112
+ if (!signatures || typeof signatures !== "object") {
113
+ errors.push("Signatures must be an object");
114
+ return { valid: false, errors };
115
+ }
116
+ const sigsObj = signatures;
117
+ for (const [key, value] of Object.entries(sigsObj)) {
118
+ if (typeof value !== "string") {
119
+ errors.push(`Signature for "${key}" must be a string, got ${typeof value}`);
120
+ continue;
121
+ }
122
+ if (!value.startsWith("data:image/")) {
123
+ errors.push(`Signature for "${key}" must be a data URL starting with "data:image/"`);
124
+ }
125
+ const parts = value.split(",");
126
+ if (parts.length !== 2 || !parts[1]) {
127
+ errors.push(`Signature for "${key}" has invalid data URL format (missing base64 data)`);
128
+ }
129
+ }
130
+ return {
131
+ valid: errors.length === 0,
132
+ errors
133
+ };
134
+ }
135
+ function validateFormField(field) {
136
+ const errors = [];
137
+ if (!field || typeof field !== "object") {
138
+ errors.push("Form field must be an object");
139
+ return { valid: false, errors };
140
+ }
141
+ const f = field;
142
+ if (!f.id || typeof f.id !== "string") {
143
+ errors.push('Field must have a valid "id" string');
144
+ }
145
+ if (!f.type || typeof f.type !== "string") {
146
+ errors.push('Field must have a valid "type" string');
147
+ }
148
+ if (!f.name || typeof f.name !== "string") {
149
+ errors.push('Field must have a valid "name" string');
150
+ }
151
+ if (!f.label || typeof f.label !== "string") {
152
+ errors.push('Field must have a valid "label" string');
153
+ }
154
+ if (!f.position || typeof f.position !== "object") {
155
+ errors.push('Field must have a valid "position" object');
156
+ } else {
157
+ const pos = f.position;
158
+ if (typeof pos.x !== "number") {
159
+ errors.push('Field position must have a numeric "x" coordinate');
160
+ }
161
+ if (typeof pos.y !== "number") {
162
+ errors.push('Field position must have a numeric "y" coordinate');
163
+ }
164
+ if (typeof pos.width !== "number" || pos.width <= 0) {
165
+ errors.push('Field position must have a positive "width"');
166
+ }
167
+ if (typeof pos.height !== "number" || pos.height <= 0) {
168
+ errors.push('Field position must have a positive "height"');
169
+ }
170
+ if (typeof pos.page !== "number" || pos.page < 1) {
171
+ errors.push('Field position must have a valid "page" number (1-indexed)');
172
+ }
173
+ }
174
+ if (typeof f.required !== "boolean") {
175
+ errors.push('Field must have a boolean "required" property');
176
+ }
177
+ if (f.fontSize !== void 0 && (typeof f.fontSize !== "number" || f.fontSize < 8 || f.fontSize > 72)) {
178
+ errors.push("Field fontSize must be a number between 8 and 72");
179
+ }
180
+ if (f.maxLength !== void 0 && (typeof f.maxLength !== "number" || f.maxLength <= 0)) {
181
+ errors.push("Field maxLength must be a positive number");
182
+ }
183
+ if (f.options !== void 0 && !Array.isArray(f.options)) {
184
+ errors.push("Field options must be an array");
185
+ }
186
+ return {
187
+ valid: errors.length === 0,
188
+ errors
189
+ };
190
+ }
191
+ function validatePdfUrl(url) {
192
+ if (typeof url !== "string") {
193
+ return { valid: false, error: "URL must be a string" };
194
+ }
195
+ if (!url || url.trim().length === 0) {
196
+ return { valid: false, error: "URL cannot be empty" };
197
+ }
198
+ const trimmedUrl = url.trim();
199
+ const validPrefixes = ["http://", "https://", "blob:", "data:", "file://"];
200
+ const hasValidPrefix = validPrefixes.some((prefix) => trimmedUrl.startsWith(prefix));
201
+ if (hasValidPrefix) {
202
+ return { valid: true };
203
+ }
204
+ if (trimmedUrl.startsWith("/") || trimmedUrl.startsWith("./") || trimmedUrl.startsWith("../")) {
205
+ return { valid: true };
206
+ }
207
+ return {
208
+ valid: false,
209
+ error: "URL must be an absolute URL (http://, https://, blob:, data:, file://) or a relative path (/, ./, ../)"
210
+ };
211
+ }
212
+
213
+ // src/utils/pdf-helpers.ts
214
+ function createPdfBlobUrl(pdfBytes) {
215
+ const blob = new Blob([pdfBytes], { type: "application/pdf" });
216
+ return URL.createObjectURL(blob);
217
+ }
218
+ function downloadPdf(pdfBytes, filename) {
219
+ const url = createPdfBlobUrl(pdfBytes);
220
+ const a = document.createElement("a");
221
+ a.href = url;
222
+ a.download = filename;
223
+ a.click();
224
+ URL.revokeObjectURL(url);
225
+ }
226
+ async function urlToPdfBytes(url) {
227
+ try {
228
+ const response = await fetch(url);
229
+ if (!response.ok) {
230
+ throw new Error(`Failed to fetch PDF: ${response.status} ${response.statusText}`);
231
+ }
232
+ const arrayBuffer = await response.arrayBuffer();
233
+ const pdfBytes = new Uint8Array(arrayBuffer.byteLength);
234
+ pdfBytes.set(new Uint8Array(arrayBuffer));
235
+ const validation = validatePdfBytes(pdfBytes);
236
+ if (!validation.valid) {
237
+ throw new Error(validation.error || "Invalid PDF format");
238
+ }
239
+ return pdfBytes;
240
+ } catch (error) {
241
+ logger.error("Error fetching PDF from URL:", error);
242
+ throw error;
243
+ }
244
+ }
245
+ function isValidPdf(pdfBytes) {
246
+ const validation = validatePdfBytes(pdfBytes);
247
+ return validation.valid;
248
+ }
249
+ function dataUrlToBytes(dataUrl) {
250
+ const base64Data = dataUrl.split(",")[1];
251
+ if (!base64Data) {
252
+ throw new Error("Invalid data URL format: missing base64 data");
253
+ }
254
+ return Uint8Array.from(atob(base64Data), (c) => c.charCodeAt(0));
255
+ }
256
+ function formatFieldName(fieldName) {
257
+ return fieldName.replace(/[_-]/g, " ").replace(/\b\w/g, (l) => l.toUpperCase()).replace("Signature", "").replace("Initials", "").trim();
258
+ }
259
+ function isSignatureField(fieldName) {
260
+ return fieldName.toLowerCase().includes("signature");
261
+ }
262
+ function isInitialsField(fieldName) {
263
+ return fieldName.toLowerCase().includes("initials");
264
+ }
265
+
266
+ // src/utils/pdf-lib-loader.ts
267
+ var pdfLibPromise = null;
268
+ async function loadPdfLib() {
269
+ if (!pdfLibPromise) {
270
+ pdfLibPromise = import('pdf-lib');
271
+ }
272
+ return pdfLibPromise;
273
+ }
274
+
275
+ // src/utils/field-visibility.ts
276
+ function isFieldVisibleToSigner(field, multiSignerContext) {
277
+ if (!multiSignerContext.isMultiSigner) {
278
+ return true;
279
+ }
280
+ const { currentSignerEmail, isPrimarySigner, isFinalSigner } = multiSignerContext;
281
+ if (!field.assignedSignerEmail) {
282
+ return isFinalSigner;
283
+ }
284
+ if (field.assignedSignerEmail === currentSignerEmail) {
285
+ return true;
286
+ }
287
+ if (field.assignedSignerEmail.includes("recipients")) {
288
+ return isPrimarySigner;
289
+ }
290
+ if (field.assignedSignerEmail.includes("signers")) {
291
+ return isFinalSigner;
292
+ }
293
+ return false;
294
+ }
295
+ function filterFieldsBySigner(fields, multiSignerContext) {
296
+ if (!multiSignerContext.isMultiSigner) {
297
+ return fields;
298
+ }
299
+ return fields.filter((field) => isFieldVisibleToSigner(field, multiSignerContext));
300
+ }
301
+ function shouldFlattenField(field, multiSignerContext) {
302
+ if (!multiSignerContext.isMultiSigner) {
303
+ return true;
304
+ }
305
+ const { currentSignerEmail, isPrimarySigner, isFinalSigner } = multiSignerContext;
306
+ if (!field.assignedSignerEmail) {
307
+ return isFinalSigner;
308
+ }
309
+ if (field.assignedSignerEmail === currentSignerEmail) {
310
+ return true;
311
+ }
312
+ if (field.assignedSignerEmail.includes("recipients")) {
313
+ return isPrimarySigner;
314
+ }
315
+ if (field.assignedSignerEmail.includes("signers")) {
316
+ return isFinalSigner;
317
+ }
318
+ return false;
319
+ }
320
+
321
+ // src/utils/pdf-widget-helpers.ts
322
+ function findPageIndexByRef(pages, pageRef) {
323
+ if (!pageRef) return -1;
324
+ for (let i = 0; i < pages.length; i++) {
325
+ const page = pages[i];
326
+ if (page.ref === pageRef) {
327
+ return i;
328
+ }
329
+ }
330
+ return -1;
331
+ }
332
+ function getWidgetRectangleAndPage(widget, pages) {
333
+ const rect = widget.getRectangle?.();
334
+ if (!rect) return null;
335
+ const pageRef = widget.P?.();
336
+ if (!pageRef) return null;
337
+ const pageIndex = findPageIndexByRef(pages, pageRef);
338
+ if (pageIndex === -1) return null;
339
+ const page = pages[pageIndex];
340
+ if (!page) return null;
341
+ return { rect, page, pageIndex };
342
+ }
343
+ function findPageIndexWithFallback(pages, pageRef) {
344
+ if (!pageRef) {
345
+ return pages.length === 1 ? 0 : -1;
346
+ }
347
+ const pageIndex = findPageIndexByRef(pages, pageRef);
348
+ if (pageIndex === -1 && pages.length === 1) {
349
+ return 0;
350
+ }
351
+ return pageIndex;
352
+ }
353
+
354
+ // src/utils/pdf-field-type-helpers.ts
355
+ function detectFieldType(field) {
356
+ const typeName = field.constructor.name;
357
+ if (typeName === "PDFTextField") {
358
+ return "text";
359
+ } else if (typeName === "PDFCheckBox") {
360
+ return "checkbox";
361
+ } else if (typeName === "PDFDropdown") {
362
+ return "dropdown";
363
+ } else if (typeName === "PDFOptionList") {
364
+ return "optionlist";
365
+ } else if (typeName === "PDFRadioGroup") {
366
+ return "radiogroup";
367
+ } else if (typeName === "PDFSignature") {
368
+ return "signature";
369
+ }
370
+ return "unknown";
371
+ }
372
+ function extractFieldValue(field, fieldType) {
373
+ try {
374
+ switch (fieldType) {
375
+ case "text":
376
+ return field.getText?.() || "";
377
+ case "checkbox":
378
+ return field.isChecked?.() ? "true" : "false";
379
+ case "dropdown":
380
+ case "optionlist":
381
+ case "radiogroup":
382
+ return field.getSelected?.()?.[0] || "";
383
+ default:
384
+ return "";
385
+ }
386
+ } catch (error) {
387
+ return "";
388
+ }
389
+ }
390
+ function isRequiredField(field, fieldName, fieldType) {
391
+ if (fieldType === "signature") {
392
+ return true;
393
+ }
394
+ try {
395
+ if (field.isRequired?.()) {
396
+ return true;
397
+ }
398
+ } catch {
399
+ }
400
+ if (fieldName.includes("signature") || fieldName.includes("initials") || fieldName.includes("required")) {
401
+ return true;
402
+ }
403
+ return false;
404
+ }
405
+
406
+ // src/utils/pdf-manipulation.ts
407
+ async function pdfToImages(pdfBytes, options) {
408
+ try {
409
+ const validation = validatePdfBytes(pdfBytes);
410
+ if (!validation.valid) {
411
+ logger.error("Invalid PDF:", validation.error);
412
+ throw new Error(validation.error || "Invalid PDF format");
413
+ }
414
+ const loadingTask = pdfjsLib2__namespace.getDocument({
415
+ data: pdfBytes,
416
+ verbosity: 0
417
+ });
418
+ const pdfDoc = await loadingTask.promise;
419
+ const pages = [];
420
+ let pdfLibDoc = null;
421
+ try {
422
+ const { PDFDocument } = await loadPdfLib();
423
+ pdfLibDoc = await PDFDocument.load(pdfBytes);
424
+ } catch {
425
+ }
426
+ for (let pageNum = 1; pageNum <= pdfDoc.numPages; pageNum++) {
427
+ const page = await pdfDoc.getPage(pageNum);
428
+ const viewport = page.getViewport({ scale: 2 });
429
+ let pdfWidth = viewport.width / 2;
430
+ let pdfHeight = viewport.height / 2;
431
+ if (pdfLibDoc) {
432
+ try {
433
+ const pdfLibPage = pdfLibDoc.getPage(pageNum - 1);
434
+ pdfWidth = pdfLibPage.getWidth();
435
+ pdfHeight = pdfLibPage.getHeight();
436
+ } catch {
437
+ }
438
+ }
439
+ const canvas = document.createElement("canvas");
440
+ const context = canvas.getContext("2d");
441
+ canvas.height = viewport.height;
442
+ canvas.width = viewport.width;
443
+ const hideFormFields = options?.hideFormFields ?? false;
444
+ const renderContext = {
445
+ canvasContext: context,
446
+ viewport,
447
+ renderInteractiveForms: !hideFormFields,
448
+ ...hideFormFields && {
449
+ annotationMode: 0,
450
+ renderAnnotations: false,
451
+ renderForms: false
452
+ }
453
+ };
454
+ await page.render(renderContext).promise;
455
+ const imageUrl = canvas.toDataURL("image/png");
456
+ pages.push({
457
+ pageNumber: pageNum,
458
+ width: pdfWidth,
459
+ height: pdfHeight,
460
+ imageUrl,
461
+ renderWidth: viewport.width,
462
+ renderHeight: viewport.height
463
+ });
464
+ }
465
+ return pages;
466
+ } catch (error) {
467
+ logger.error("Error converting PDF to images with PDF.js:", error);
468
+ return await fallbackPdfToImages(pdfBytes);
469
+ }
470
+ }
471
+ async function fallbackPdfToImages(pdfBytes) {
472
+ try {
473
+ const { PDFDocument } = await loadPdfLib();
474
+ const pdf = await PDFDocument.load(pdfBytes);
475
+ const pages = [];
476
+ for (let i = 0; i < pdf.getPageCount(); i++) {
477
+ const page = pdf.getPage(i);
478
+ const { width, height } = page.getSize();
479
+ const canvas = document.createElement("canvas");
480
+ const context = canvas.getContext("2d");
481
+ const scale = 1.5;
482
+ canvas.width = width * scale;
483
+ canvas.height = height * scale;
484
+ context.fillStyle = "#ffffff";
485
+ context.fillRect(0, 0, canvas.width, canvas.height);
486
+ context.strokeStyle = "#e5e7eb";
487
+ context.lineWidth = 2;
488
+ context.strokeRect(0, 0, canvas.width, canvas.height);
489
+ context.fillStyle = "#6b7280";
490
+ context.font = "bold 24px system-ui";
491
+ context.textAlign = "center";
492
+ context.fillText(`Page ${i + 1}`, canvas.width / 2, canvas.height / 2 - 20);
493
+ context.font = "16px system-ui";
494
+ context.fillStyle = "#9ca3af";
495
+ context.fillText("PDF preview not available", canvas.width / 2, canvas.height / 2 + 20);
496
+ const imageUrl = canvas.toDataURL("image/png");
497
+ pages.push({
498
+ pageNumber: i + 1,
499
+ width,
500
+ height,
501
+ imageUrl,
502
+ renderWidth: canvas.width,
503
+ renderHeight: canvas.height
504
+ });
505
+ }
506
+ return pages;
507
+ } catch (error) {
508
+ logger.error("Error with pdf-lib fallback:", error);
509
+ return [createErrorPage()];
510
+ }
511
+ }
512
+ function createErrorPage() {
513
+ const canvas = document.createElement("canvas");
514
+ const context = canvas.getContext("2d");
515
+ canvas.width = 600;
516
+ canvas.height = 800;
517
+ context.fillStyle = "#ffffff";
518
+ context.fillRect(0, 0, canvas.width, canvas.height);
519
+ context.strokeStyle = "#e5e7eb";
520
+ context.lineWidth = 2;
521
+ context.strokeRect(0, 0, canvas.width, canvas.height);
522
+ context.fillStyle = "#ef4444";
523
+ context.font = "bold 20px system-ui";
524
+ context.textAlign = "center";
525
+ context.fillText("PDF Processing Error", canvas.width / 2, canvas.height / 2 - 20);
526
+ context.font = "14px system-ui";
527
+ context.fillStyle = "#6b7280";
528
+ context.fillText("Unable to process the uploaded PDF file", canvas.width / 2, canvas.height / 2 + 20);
529
+ return {
530
+ pageNumber: 1,
531
+ width: 600,
532
+ height: 800,
533
+ imageUrl: canvas.toDataURL("image/png"),
534
+ renderWidth: canvas.width,
535
+ renderHeight: canvas.height
536
+ };
537
+ }
538
+ async function removeAllFormFields(pdfBytes, stripAllAnnotations = false) {
539
+ try {
540
+ const validation = validatePdfBytes(pdfBytes);
541
+ if (!validation.valid) {
542
+ return pdfBytes;
543
+ }
544
+ const { PDFDocument } = await loadPdfLib();
545
+ const pdfDoc = await PDFDocument.load(pdfBytes);
546
+ if (stripAllAnnotations) {
547
+ const pages = pdfDoc.getPages();
548
+ for (let i = 0; i < pages.length; i++) {
549
+ const page = pages[i];
550
+ try {
551
+ const pageRef = page.ref;
552
+ const pageDictObj = pdfDoc.context.lookup(pageRef);
553
+ if (pageDictObj) {
554
+ const pageDict = pageDictObj;
555
+ if (pageDict && pageDict.get && pageDict.delete) {
556
+ const annotsRef = pageDict.get(pdfDoc.context.obj("Annots"));
557
+ if (annotsRef) {
558
+ pageDict.delete(pdfDoc.context.obj("Annots"));
559
+ }
560
+ } else {
561
+ }
562
+ } else {
563
+ }
564
+ } catch (pageError) {
565
+ }
566
+ }
567
+ } else {
568
+ const form = pdfDoc.getForm();
569
+ const fields = form.getFields();
570
+ if (fields.length === 0) {
571
+ return pdfBytes;
572
+ }
573
+ for (const field of fields) {
574
+ try {
575
+ const acroField = field.acroField;
576
+ if (acroField && typeof acroField.getWidgets === "function") {
577
+ let widgets = acroField.getWidgets();
578
+ let safetyCounter = 0;
579
+ const maxIterations = widgets.length + 5;
580
+ while (widgets.length > 0 && safetyCounter < maxIterations) {
581
+ try {
582
+ const widgetIndex = widgets.length - 1;
583
+ acroField.removeWidget(widgetIndex);
584
+ widgets = acroField.getWidgets();
585
+ } catch (widgetError) {
586
+ break;
587
+ }
588
+ safetyCounter++;
589
+ }
590
+ if (safetyCounter >= maxIterations) {
591
+ }
592
+ }
593
+ form.removeField(field);
594
+ } catch (error) {
595
+ try {
596
+ form.removeField(field);
597
+ } catch (fallbackError) {
598
+ }
599
+ }
600
+ }
601
+ const remainingFields = form.getFields();
602
+ if (remainingFields.length > 0) {
603
+ }
604
+ }
605
+ const updatedPdfBytes = await pdfDoc.save();
606
+ return updatedPdfBytes;
607
+ } catch (error) {
608
+ logger.error("Error removing form fields from PDF:", error);
609
+ return pdfBytes;
610
+ }
611
+ }
612
+ async function readPdfFormFields(pdfBytes) {
613
+ try {
614
+ const validation = validatePdfBytes(pdfBytes);
615
+ if (!validation.valid) {
616
+ return [];
617
+ }
618
+ const { PDFDocument } = await loadPdfLib();
619
+ const pdfDoc = await PDFDocument.load(pdfBytes);
620
+ const form = pdfDoc.getForm();
621
+ const fields = form.getFields();
622
+ if (fields.length === 0) {
623
+ return [];
624
+ }
625
+ const formFields = fields.map((field) => {
626
+ const fieldName = field.getName();
627
+ const pdfField = field;
628
+ try {
629
+ const fieldType = detectFieldType(pdfField);
630
+ const value = extractFieldValue(pdfField, fieldType);
631
+ const required = isRequiredField(pdfField, fieldName, fieldType);
632
+ return {
633
+ name: fieldName,
634
+ type: fieldType,
635
+ required,
636
+ value
637
+ };
638
+ } catch (fieldError) {
639
+ return {
640
+ name: fieldName,
641
+ type: "text",
642
+ required: false,
643
+ value: ""
644
+ };
645
+ }
646
+ });
647
+ return formFields;
648
+ } catch (error) {
649
+ logger.error("Error reading PDF form fields:", error);
650
+ return [];
651
+ }
652
+ }
653
+ async function readCurrentPdfFormValues(pdfBytes) {
654
+ try {
655
+ if (!pdfBytes || pdfBytes.length === 0) {
656
+ return {};
657
+ }
658
+ const { PDFDocument } = await loadPdfLib();
659
+ const pdfDoc = await PDFDocument.load(pdfBytes);
660
+ const form = pdfDoc.getForm();
661
+ const fields = form.getFields();
662
+ const currentValues = {};
663
+ for (const field of fields) {
664
+ const fieldName = field.getName();
665
+ const pdfField = field;
666
+ try {
667
+ const fieldType = detectFieldType(pdfField);
668
+ const value = extractFieldValue(pdfField, fieldType);
669
+ currentValues[fieldName] = value;
670
+ } catch (fieldError) {
671
+ currentValues[fieldName] = "";
672
+ }
673
+ }
674
+ return currentValues;
675
+ } catch (error) {
676
+ logger.error("Error reading current PDF form values:", error);
677
+ return {};
678
+ }
679
+ }
680
+ async function validatePdfFormFields(pdfBytes, fieldValues, signatures, extractedFields, multiSignerContext) {
681
+ try {
682
+ const pdfFormFields = await readPdfFormFields(pdfBytes);
683
+ const errors = [];
684
+ for (const field of pdfFormFields) {
685
+ if (field.required) {
686
+ if (multiSignerContext?.isMultiSigner && extractedFields) {
687
+ const extractedField = extractedFields.find((f) => f.name === field.name);
688
+ if (!extractedField) {
689
+ continue;
690
+ }
691
+ }
692
+ let hasValue = false;
693
+ const fieldValue = fieldValues[field.name];
694
+ const signatureValue = signatures[field.name];
695
+ const mainSignature = signatures["signature_field_main"];
696
+ const mainInitials = signatures["initials_field_main"] || fieldValues["initials_field_main"];
697
+ if (field.name.includes("signature") || field.type === "signature") {
698
+ hasValue = !!(signatureValue || fieldValue || mainSignature);
699
+ } else if (field.name.includes("initials")) {
700
+ hasValue = !!(signatureValue || fieldValue || mainInitials);
701
+ } else {
702
+ hasValue = !!(fieldValue || field.value);
703
+ }
704
+ if (!hasValue) {
705
+ let friendlyName = field.name.replace(/[_-]/g, " ").replace(/\b\w/g, (l) => l.toUpperCase());
706
+ if (field.name.includes("signature")) {
707
+ friendlyName = "Signature";
708
+ } else if (field.name.includes("initials")) {
709
+ friendlyName = "Initials";
710
+ }
711
+ errors.push(`${friendlyName} is required`);
712
+ }
713
+ }
714
+ }
715
+ return errors;
716
+ } catch (error) {
717
+ logger.error("Error validating PDF form fields:", error);
718
+ return ["Unable to validate form fields. Please ensure all required fields are completed."];
719
+ }
720
+ }
721
+ async function validateCurrentPdfState(pdfBytes, signatures, formFieldValues = {}) {
722
+ try {
723
+ const pdfFormFields = await readPdfFormFields(pdfBytes);
724
+ const currentValues = await readCurrentPdfFormValues(pdfBytes);
725
+ const errors = [];
726
+ for (const field of pdfFormFields) {
727
+ const isSignatureField2 = field.name.includes("signature") && field.type === "text";
728
+ const isInitialsField2 = field.name.includes("initials") && field.type === "text";
729
+ if (field.required || isSignatureField2 || isInitialsField2) {
730
+ let hasValue = false;
731
+ const currentValue = currentValues[field.name] || "";
732
+ if (isSignatureField2) {
733
+ hasValue = !!signatures[field.name];
734
+ } else if (isInitialsField2) {
735
+ hasValue = !!signatures[field.name] || !!formFieldValues[field.name]?.trim() || currentValue.trim() !== "";
736
+ } else {
737
+ hasValue = currentValue.trim() !== "";
738
+ }
739
+ if (!hasValue) {
740
+ let friendlyName = field.name.replace(/[_-]/g, " ").replace(/\b\w/g, (l) => l.toUpperCase());
741
+ if (field.name.includes("signature")) {
742
+ friendlyName = "Signature";
743
+ } else if (field.name.includes("initials")) {
744
+ friendlyName = "Initials";
745
+ }
746
+ errors.push(`${friendlyName} is required`);
747
+ }
748
+ }
749
+ }
750
+ return errors;
751
+ } catch (error) {
752
+ logger.error("Error validating current PDF state:", error);
753
+ throw error;
754
+ }
755
+ }
756
+ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {}, currentSignerEmail, extractedFormFields, metadata, auditTrail, multiSignerContext) {
757
+ try {
758
+ const { PDFDocument, rgb, StandardFonts } = await loadPdfLib();
759
+ const pdfDoc = await PDFDocument.load(pdfBytes);
760
+ const form = pdfDoc.getForm();
761
+ const pages = pdfDoc.getPages();
762
+ const { pageMap: fieldPageMap, positionMap: fieldPositionMap } = await getFieldPageNumbers(pdfBytes);
763
+ const fields = form.getFields();
764
+ for (const field of fields) {
765
+ const fieldName = field.getName();
766
+ const fieldValue = formFieldValues[fieldName];
767
+ if (!fieldValue) {
768
+ if (fieldName.includes("radio")) {
769
+ }
770
+ continue;
771
+ }
772
+ try {
773
+ const fieldTypeName = field.constructor.name;
774
+ if (fieldTypeName === "PDFTextField" || fieldTypeName === "PDFTextField2") {
775
+ const textField = field;
776
+ textField.setText?.(fieldValue);
777
+ } else if (fieldTypeName === "PDFCheckBox" || fieldTypeName === "PDFCheckBox2") {
778
+ const checkBox = field;
779
+ if (fieldValue === "true" || fieldValue === "Yes" || fieldValue === "checked" || fieldValue === "On") {
780
+ checkBox.check?.();
781
+ } else {
782
+ checkBox.uncheck?.();
783
+ }
784
+ } else if (fieldTypeName === "PDFRadioGroup" || fieldTypeName === "PDFRadioGroup2") {
785
+ const radioGroup = field;
786
+ if (fieldValue === "true" || fieldValue === "false") {
787
+ continue;
788
+ }
789
+ const idxMatch = fieldValue.match(/__RADIO_OPTION_INDEX_(\d+)__/);
790
+ if (idxMatch && idxMatch[1]) {
791
+ const selectedIndex = parseInt(idxMatch[1], 10);
792
+ const options = radioGroup.getOptions?.() || [];
793
+ if (selectedIndex >= 0 && selectedIndex < options.length) {
794
+ radioGroup.select?.(options[selectedIndex]);
795
+ }
796
+ } else {
797
+ const options = radioGroup.getOptions?.() || [];
798
+ if (options.includes(fieldValue)) {
799
+ radioGroup.select?.(fieldValue);
800
+ } else {
801
+ }
802
+ }
803
+ } else if (fieldTypeName === "PDFDropdown" || fieldTypeName === "PDFDropdown2") {
804
+ const dropdown = field;
805
+ dropdown.select?.(fieldValue);
806
+ }
807
+ } catch (fieldError) {
808
+ logger.error(`Error setting field "${fieldName}":`, fieldError);
809
+ }
810
+ }
811
+ try {
812
+ form.updateFieldAppearances();
813
+ } catch (appearanceError) {
814
+ }
815
+ const mainSignatureData = signatures["signature_field_main"];
816
+ if (mainSignatureData && mainSignatureData.trim()) {
817
+ const signatureFieldNames = [
818
+ "signature",
819
+ "Signature",
820
+ "SIGNATURE",
821
+ "sign",
822
+ "Sign",
823
+ "SIGN",
824
+ "_es_:signer:signature"
825
+ ];
826
+ let foundSignatureFields = Object.keys(fieldPageMap).filter((fieldName) => {
827
+ const hasSignaturePattern = signatureFieldNames.some(
828
+ (pattern) => fieldName.toLowerCase().includes(pattern.toLowerCase())
829
+ );
830
+ const isNotTextField = !fieldName.toLowerCase().startsWith("text_");
831
+ return hasSignaturePattern && isNotTextField;
832
+ });
833
+ if (extractedFormFields && extractedFormFields.length > 0) {
834
+ const currentSignerSignatureFields = foundSignatureFields.filter((fieldName) => {
835
+ const fieldInfo = extractedFormFields.find((f) => f.name === fieldName);
836
+ if (!fieldInfo) {
837
+ return false;
838
+ }
839
+ const isActualSignatureField = (fieldInfo.type === "signature" || fieldName.toLowerCase().includes("signature")) && !fieldName.toLowerCase().includes("initials");
840
+ if (!isActualSignatureField) {
841
+ return false;
842
+ }
843
+ const isVisible = multiSignerContext ? isFieldVisibleToSigner(fieldInfo, multiSignerContext) : true;
844
+ return isVisible;
845
+ });
846
+ foundSignatureFields = currentSignerSignatureFields;
847
+ }
848
+ if (foundSignatureFields.length > 0) {
849
+ try {
850
+ let signatureImage;
851
+ if (mainSignatureData.includes("data:image/png")) {
852
+ signatureImage = await pdfDoc.embedPng(mainSignatureData);
853
+ } else if (mainSignatureData.includes("data:image/jpeg") || mainSignatureData.includes("data:image/jpg")) {
854
+ signatureImage = await pdfDoc.embedJpg(mainSignatureData);
855
+ } else {
856
+ const base64Data = mainSignatureData.split(",")[1];
857
+ if (!base64Data) {
858
+ throw new Error("Invalid signature data format: missing base64 data");
859
+ }
860
+ const binaryData = atob(base64Data);
861
+ if (binaryData.charCodeAt(0) === 137 && binaryData.charCodeAt(1) === 80 && binaryData.charCodeAt(2) === 78 && binaryData.charCodeAt(3) === 71) {
862
+ signatureImage = await pdfDoc.embedPng(mainSignatureData);
863
+ } else if (binaryData.charCodeAt(0) === 255 && binaryData.charCodeAt(1) === 216 && binaryData.charCodeAt(2) === 255) {
864
+ signatureImage = await pdfDoc.embedJpg(mainSignatureData);
865
+ } else {
866
+ throw new Error("Unsupported image format. Please use PNG or JPEG.");
867
+ }
868
+ }
869
+ for (const fieldName of foundSignatureFields) {
870
+ const pageNumber = fieldPageMap[fieldName];
871
+ if (pageNumber && pageNumber <= pages.length) {
872
+ const page = pages[pageNumber - 1];
873
+ if (!page) continue;
874
+ const fieldPosition = fieldPositionMap[fieldName];
875
+ if (fieldPosition) {
876
+ const x = fieldPosition.x;
877
+ const y = fieldPosition.y;
878
+ const width = Math.max(fieldPosition.width, 80);
879
+ const height = Math.max(fieldPosition.height, 30);
880
+ const signatureDims = signatureImage.scaleToFit(width - 4, height - 4);
881
+ const finalX = x;
882
+ const finalY = y;
883
+ page.drawImage(signatureImage, {
884
+ x: finalX,
885
+ y: finalY,
886
+ width: signatureDims.width,
887
+ height: signatureDims.height
888
+ });
889
+ } else {
890
+ }
891
+ } else {
892
+ }
893
+ }
894
+ } catch (error) {
895
+ logger.error("Error applying signatures:", error);
896
+ }
897
+ }
898
+ }
899
+ const mainInitialsData = formFieldValues["initials_field_main"];
900
+ if (mainInitialsData && mainInitialsData.trim()) {
901
+ const initialsFieldNames = ["initials", "Initials", "INITIALS"];
902
+ let foundInitialsFields = Object.keys(fieldPageMap).filter(
903
+ (fieldName) => initialsFieldNames.some((pattern) => fieldName.toLowerCase().includes(pattern.toLowerCase()))
904
+ );
905
+ if (extractedFormFields && extractedFormFields.length > 0) {
906
+ foundInitialsFields = foundInitialsFields.filter((fieldName) => {
907
+ const fieldInfo = extractedFormFields.find((f) => f.name === fieldName);
908
+ if (!fieldInfo) return false;
909
+ const isActualInitialsField = fieldInfo.type === "initials" || fieldName.toLowerCase().includes("initials");
910
+ if (!isActualInitialsField) return false;
911
+ return !fieldInfo.assignedSignerEmail || fieldInfo.assignedSignerEmail === currentSignerEmail;
912
+ });
913
+ }
914
+ if (foundInitialsFields.length > 0) {
915
+ try {
916
+ const font = await pdfDoc.embedFont(StandardFonts.HelveticaBold);
917
+ for (const fieldName of foundInitialsFields) {
918
+ const pageNumber = fieldPageMap[fieldName];
919
+ if (pageNumber && pageNumber <= pages.length) {
920
+ const page = pages[pageNumber - 1];
921
+ if (!page) continue;
922
+ const fieldPosition = fieldPositionMap[fieldName];
923
+ if (fieldPosition) {
924
+ const x = fieldPosition.x;
925
+ const y = fieldPosition.y;
926
+ const height = Math.max(fieldPosition.height, 20);
927
+ const fontSize = Math.min(height * 0.7, 14);
928
+ const finalX = x + 2;
929
+ const finalY = y + (height - fontSize) / 2;
930
+ page.drawText(mainInitialsData, {
931
+ x: finalX,
932
+ y: finalY,
933
+ size: fontSize,
934
+ font,
935
+ color: rgb(0, 0, 0)
936
+ });
937
+ }
938
+ }
939
+ }
940
+ } catch (error) {
941
+ logger.error("Error applying initials:", error);
942
+ }
943
+ }
944
+ }
945
+ const allFormFields = form.getFields();
946
+ let fieldsToFlatten;
947
+ if (multiSignerContext?.isMultiSigner) {
948
+ fieldsToFlatten = allFormFields.filter((f) => {
949
+ const fieldName = f.getName();
950
+ if (!extractedFormFields || extractedFormFields.length === 0) {
951
+ return true;
952
+ }
953
+ const fieldInfo = extractedFormFields.find((fi) => fi.name === fieldName);
954
+ if (!fieldInfo) {
955
+ return true;
956
+ }
957
+ return shouldFlattenField(fieldInfo, multiSignerContext);
958
+ });
959
+ } else {
960
+ fieldsToFlatten = allFormFields;
961
+ }
962
+ let flattenedCount = 0;
963
+ for (const field of fieldsToFlatten) {
964
+ const fieldName = field.getName();
965
+ const fieldType = field.constructor.name;
966
+ try {
967
+ const userEnteredValue = formFieldValues[fieldName];
968
+ const isSignatureField2 = fieldName.toLowerCase().includes("signature") || fieldType.includes("Signature");
969
+ const isInitialsField2 = fieldName.toLowerCase().includes("initials") || fieldType.includes("Initials");
970
+ if (isSignatureField2 || isInitialsField2) {
971
+ form.removeField(field);
972
+ flattenedCount++;
973
+ continue;
974
+ }
975
+ const fieldWithWidgets = field;
976
+ const widgets = fieldWithWidgets.acroField?.getWidgets?.() || [];
977
+ if (fieldType === "PDFCheckBox" || fieldType === "PDFCheckBox2") {
978
+ const stringValue = String(userEnteredValue || "");
979
+ const isChecked = stringValue === "true" || stringValue === "Yes" || stringValue === "On" || stringValue === "checked";
980
+ if (isChecked) {
981
+ for (const widget of widgets) {
982
+ const result = getWidgetRectangleAndPage(widget, pages);
983
+ if (!result) continue;
984
+ const { rect, page } = result;
985
+ const checkboxSize = Math.min(rect.width, rect.height) * 0.6;
986
+ const checkboxX = rect.x + (rect.width - checkboxSize) / 2;
987
+ const checkboxY = rect.y + (rect.height - checkboxSize) / 2;
988
+ page.drawRectangle({
989
+ x: checkboxX,
990
+ y: checkboxY,
991
+ width: checkboxSize,
992
+ height: checkboxSize,
993
+ borderColor: rgb(0, 0, 0),
994
+ borderWidth: 1
995
+ });
996
+ const checkSize = checkboxSize * 0.6;
997
+ const textWidth = checkSize * 0.6;
998
+ const textHeight = checkSize * 0.8;
999
+ page.drawText("X", {
1000
+ x: checkboxX + (checkboxSize - textWidth) / 2,
1001
+ y: checkboxY + (checkboxSize - textHeight) / 2 + textHeight * 0.2,
1002
+ size: checkSize,
1003
+ font: await pdfDoc.embedFont(StandardFonts.HelveticaBold),
1004
+ color: rgb(0, 0, 0)
1005
+ });
1006
+ }
1007
+ }
1008
+ } else if (fieldType === "PDFRadioGroup" || fieldType === "PDFRadioGroup2") {
1009
+ let selectedValue = userEnteredValue ? String(userEnteredValue) : "";
1010
+ if (selectedValue.startsWith("__RADIO_OPTION_INDEX_")) {
1011
+ const indexMatch = selectedValue.match(/__RADIO_OPTION_INDEX_(\d+)__/);
1012
+ if (indexMatch && indexMatch[1]) {
1013
+ selectedValue = indexMatch[1];
1014
+ }
1015
+ }
1016
+ let selectedIndex = -1;
1017
+ if (selectedValue === "Option A") selectedIndex = 0;
1018
+ else if (selectedValue === "Option B") selectedIndex = 1;
1019
+ else if (selectedValue === "Option C") selectedIndex = 2;
1020
+ else if (/^\d+$/.test(selectedValue)) {
1021
+ selectedIndex = parseInt(selectedValue, 10);
1022
+ }
1023
+ const fieldInfo = extractedFormFields?.find((f) => f.name === fieldName);
1024
+ const labelFont = await pdfDoc.embedFont("Helvetica-Bold");
1025
+ const drawnGroupLabels = /* @__PURE__ */ new Set();
1026
+ const drawnOptionLabels = /* @__PURE__ */ new Set();
1027
+ for (let i = 0; i < widgets.length; i++) {
1028
+ const widget = widgets[i];
1029
+ const result = getWidgetRectangleAndPage(widget, pages);
1030
+ if (!result) continue;
1031
+ const { rect, page, pageIndex } = result;
1032
+ if (i === 0 && fieldInfo?.label && fieldInfo.label.trim() && !isAutoGeneratedLabel(fieldInfo.label)) {
1033
+ const groupLabelKey = `${pageIndex}-${fieldName}-grouplabel`;
1034
+ if (!drawnGroupLabels.has(groupLabelKey)) {
1035
+ const labelY = rect.y + rect.height + 3;
1036
+ page.drawText(fieldInfo.label, {
1037
+ x: rect.x,
1038
+ y: labelY,
1039
+ size: 8,
1040
+ font: labelFont,
1041
+ color: rgb(0, 0, 0)
1042
+ });
1043
+ drawnGroupLabels.add(groupLabelKey);
1044
+ }
1045
+ } else if (i === 0 && isAutoGeneratedLabel(fieldInfo?.label || "")) {
1046
+ }
1047
+ const radioSize = Math.min(rect.width, rect.height) * 0.5;
1048
+ const radioX = rect.x + (rect.width - radioSize) / 2;
1049
+ const radioY = rect.y + (rect.height - radioSize) / 2;
1050
+ page.drawCircle({
1051
+ x: radioX + radioSize / 2,
1052
+ y: radioY + radioSize / 2,
1053
+ size: radioSize,
1054
+ borderColor: rgb(0, 0, 0),
1055
+ borderWidth: 1
1056
+ });
1057
+ if (i === selectedIndex) {
1058
+ const circleSize = radioSize * 0.5;
1059
+ page.drawCircle({
1060
+ x: radioX + radioSize / 2,
1061
+ y: radioY + radioSize / 2,
1062
+ size: circleSize,
1063
+ color: rgb(0, 0, 0)
1064
+ });
1065
+ }
1066
+ if (fieldInfo?.options && fieldInfo.options.length > i) {
1067
+ const optionText = fieldInfo.options[i];
1068
+ const optionKey = `${pageIndex}-${fieldName}-opt-${i}`;
1069
+ if (optionText && !drawnOptionLabels.has(optionKey)) {
1070
+ page.drawText(optionText, {
1071
+ x: rect.x + rect.width + 4,
1072
+ y: rect.y + (rect.height - 8) / 2,
1073
+ size: 8,
1074
+ font: labelFont,
1075
+ color: rgb(0, 0, 0)
1076
+ });
1077
+ drawnOptionLabels.add(optionKey);
1078
+ }
1079
+ }
1080
+ }
1081
+ if (fieldInfo?.options) {
1082
+ }
1083
+ } else if (fieldType === "PDFDropdown" || fieldType === "PDFDropdown2") {
1084
+ const selectedValue = userEnteredValue ? String(userEnteredValue) : "";
1085
+ if (selectedValue && selectedValue.trim()) {
1086
+ for (const widget of widgets) {
1087
+ const result = getWidgetRectangleAndPage(widget, pages);
1088
+ if (!result) continue;
1089
+ const { rect, page } = result;
1090
+ page.drawText(selectedValue, {
1091
+ x: rect.x + 2,
1092
+ y: rect.y + 2,
1093
+ size: 10,
1094
+ font: await pdfDoc.embedFont(StandardFonts.Helvetica),
1095
+ color: rgb(0, 0, 0)
1096
+ });
1097
+ }
1098
+ }
1099
+ } else {
1100
+ const fieldValue = userEnteredValue ? String(userEnteredValue) : "";
1101
+ if (fieldValue && fieldValue.trim()) {
1102
+ for (const widget of widgets) {
1103
+ const result = getWidgetRectangleAndPage(widget, pages);
1104
+ if (!result) continue;
1105
+ const { rect, page } = result;
1106
+ page.drawText(fieldValue, {
1107
+ x: rect.x + 2,
1108
+ y: rect.y + 2,
1109
+ size: 10,
1110
+ font: await pdfDoc.embedFont(StandardFonts.Helvetica),
1111
+ color: rgb(0, 0, 0)
1112
+ });
1113
+ }
1114
+ }
1115
+ }
1116
+ form.removeField(field);
1117
+ flattenedCount++;
1118
+ } catch (error) {
1119
+ }
1120
+ }
1121
+ const remainingFields = form.getFields();
1122
+ if (multiSignerContext?.isMultiSigner) {
1123
+ const expectedRemainingCount = allFormFields.length - fieldsToFlatten.length;
1124
+ if (remainingFields.length === expectedRemainingCount) {
1125
+ } else {
1126
+ }
1127
+ } else {
1128
+ if (remainingFields.length > 0) {
1129
+ logger.error(`MANUAL FLATTENING INCOMPLETE: ${remainingFields.length} fields still exist!`);
1130
+ logger.error(`Remaining fields:`, remainingFields.map((f) => f.getName()));
1131
+ try {
1132
+ form.flatten();
1133
+ const finalRemainingFields = form.getFields();
1134
+ if (finalRemainingFields.length > 0) {
1135
+ logger.error(`ALL FLATTENING METHODS FAILED: ${finalRemainingFields.length} fields still exist!`);
1136
+ logger.error(`Final remaining fields:`, finalRemainingFields.map((f) => f.getName()));
1137
+ }
1138
+ } catch (fallbackError) {
1139
+ logger.error("Fallback flattening failed:", fallbackError);
1140
+ }
1141
+ } else {
1142
+ }
1143
+ }
1144
+ try {
1145
+ const documentTitle = metadata?.documentId ? `Document ID: ${metadata.documentId}` : "Signed Document";
1146
+ pdfDoc.setTitle(documentTitle);
1147
+ const documentSubject = metadata?.submissionId ? `Submission ID: ${metadata.submissionId}` : metadata?.signerEmail ? `Signed by ${metadata.signerEmail}` : "Digitally Signed Document";
1148
+ pdfDoc.setSubject(documentSubject);
1149
+ const documentAuthor = metadata?.author || metadata?.signerEmail || "Unknown";
1150
+ pdfDoc.setAuthor(documentAuthor);
1151
+ pdfDoc.setProducer("Created by signiphi (https://signiphi.ai/)");
1152
+ if (metadata?.signerEmail) {
1153
+ pdfDoc.setCreator(`Signer: ${metadata.signerEmail}`);
1154
+ }
1155
+ if (metadata?.createdAt) {
1156
+ pdfDoc.setCreationDate(metadata.createdAt);
1157
+ }
1158
+ pdfDoc.setModificationDate(/* @__PURE__ */ new Date());
1159
+ const keywords = [];
1160
+ if (metadata?.signerEmail) {
1161
+ keywords.push(metadata.signerEmail);
1162
+ }
1163
+ if (metadata?.signerInitials && metadata.signerInitials.trim()) {
1164
+ keywords.push(`Initials:${metadata.signerInitials.trim()}`);
1165
+ }
1166
+ if (auditTrail) {
1167
+ try {
1168
+ if (auditTrail.userAgent) {
1169
+ keywords.push(`UserAgent:${auditTrail.userAgent}`);
1170
+ }
1171
+ if (auditTrail.screenResolution) {
1172
+ keywords.push(`Screen:${auditTrail.screenResolution}`);
1173
+ }
1174
+ if (auditTrail.timezone) {
1175
+ keywords.push(`Timezone:${auditTrail.timezone}`);
1176
+ }
1177
+ if (auditTrail.language) {
1178
+ keywords.push(`Language:${auditTrail.language}`);
1179
+ }
1180
+ if (auditTrail.platform) {
1181
+ keywords.push(`Platform:${auditTrail.platform}`);
1182
+ }
1183
+ if (auditTrail.ipAddress) {
1184
+ keywords.push(`IP:${auditTrail.ipAddress}`);
1185
+ }
1186
+ if (auditTrail.geolocation) {
1187
+ const { latitude, longitude, accuracy } = auditTrail.geolocation;
1188
+ const lat = latitude.toFixed(4);
1189
+ const lon = longitude.toFixed(4);
1190
+ keywords.push(`Location:${lat},${lon}`);
1191
+ if (accuracy !== void 0) {
1192
+ keywords.push(`LocationAccuracy:${accuracy}m`);
1193
+ }
1194
+ }
1195
+ } catch (auditError) {
1196
+ logger.warn("\u26A0\uFE0F Error adding audit trail to keywords:", auditError);
1197
+ }
1198
+ }
1199
+ if (keywords.length > 0) {
1200
+ pdfDoc.setKeywords(keywords);
1201
+ }
1202
+ } catch (error) {
1203
+ logger.error("Failed to set document metadata:", error);
1204
+ }
1205
+ const finalPdfBytes = await pdfDoc.save();
1206
+ return finalPdfBytes;
1207
+ } catch (error) {
1208
+ logger.error("Error filling PDF with signatures:", error);
1209
+ throw error;
1210
+ }
1211
+ }
1212
+ async function fillFormFieldsWithSignatures(pdfBytes, formFields, fieldValues, signatures) {
1213
+ try {
1214
+ const { PDFDocument, rgb, StandardFonts } = await loadPdfLib();
1215
+ const pdfDoc = await PDFDocument.load(pdfBytes);
1216
+ const pages = pdfDoc.getPages();
1217
+ const font = await pdfDoc.embedFont(StandardFonts.Helvetica);
1218
+ for (const field of formFields) {
1219
+ const pageIndex = field.position.page - 1;
1220
+ if (pageIndex < 0 || pageIndex >= pages.length) {
1221
+ continue;
1222
+ }
1223
+ const page = pages[pageIndex];
1224
+ if (!page) continue;
1225
+ const { height: pageHeight } = page.getSize();
1226
+ const pdfX = field.position.x;
1227
+ const pdfY = pageHeight - field.position.y - field.position.height;
1228
+ try {
1229
+ switch (field.type) {
1230
+ case "text" /* TEXT */: {
1231
+ const value = fieldValues[field.id] || "";
1232
+ if (value) {
1233
+ const fontSize = field.fontSize && field.fontSize >= 8 && field.fontSize <= 72 ? field.fontSize : Math.min(12, field.position.height * 0.6);
1234
+ page.drawText(value, {
1235
+ x: pdfX + 2,
1236
+ // Small padding
1237
+ y: pdfY + (field.position.height - fontSize) / 2,
1238
+ // Center vertically
1239
+ size: fontSize,
1240
+ font,
1241
+ color: rgb(0, 0, 0),
1242
+ maxWidth: field.position.width - 4
1243
+ // Leave padding
1244
+ });
1245
+ }
1246
+ break;
1247
+ }
1248
+ case "date" /* DATE */: {
1249
+ const value = fieldValues[field.id] || "";
1250
+ if (value) {
1251
+ const fontSize = field.fontSize && field.fontSize >= 8 && field.fontSize <= 72 ? field.fontSize : Math.min(12, field.position.height * 0.6);
1252
+ page.drawText(value, {
1253
+ x: pdfX + 2,
1254
+ y: pdfY + (field.position.height - fontSize) / 2,
1255
+ size: fontSize,
1256
+ font,
1257
+ color: rgb(0, 0, 0),
1258
+ maxWidth: field.position.width - 4
1259
+ });
1260
+ }
1261
+ break;
1262
+ }
1263
+ case "checkbox" /* CHECKBOX */: {
1264
+ const value = fieldValues[field.id] || "";
1265
+ if (value === "true") {
1266
+ const checkSize = Math.min(field.position.width, field.position.height) * 0.6;
1267
+ const checkX = pdfX + (field.position.width - checkSize) / 2;
1268
+ const checkY = pdfY + (field.position.height - checkSize) / 2;
1269
+ page.drawText("\u2713", {
1270
+ x: checkX,
1271
+ y: checkY,
1272
+ size: checkSize,
1273
+ font,
1274
+ color: rgb(0, 0, 0)
1275
+ });
1276
+ }
1277
+ break;
1278
+ }
1279
+ case "signature" /* SIGNATURE */: {
1280
+ const signatureDataUrl = signatures[field.id];
1281
+ if (signatureDataUrl) {
1282
+ const base64Data = signatureDataUrl.split(",")[1];
1283
+ if (!base64Data) {
1284
+ logger.error("Invalid signature data URL format for field:", field.id);
1285
+ break;
1286
+ }
1287
+ const imageBytes = Uint8Array.from(atob(base64Data), (c) => c.charCodeAt(0));
1288
+ const image = await pdfDoc.embedPng(imageBytes);
1289
+ const imageSize = image.size();
1290
+ const fieldAspectRatio = field.position.width / field.position.height;
1291
+ const imageAspectRatio = imageSize.width / imageSize.height;
1292
+ let drawWidth = field.position.width;
1293
+ let drawHeight = field.position.height;
1294
+ if (imageAspectRatio > fieldAspectRatio) {
1295
+ drawHeight = drawWidth / imageAspectRatio;
1296
+ } else {
1297
+ drawWidth = drawHeight * imageAspectRatio;
1298
+ }
1299
+ const imageX = pdfX + (field.position.width - drawWidth) / 2;
1300
+ const imageY = pdfY + (field.position.height - drawHeight) / 2;
1301
+ page.drawImage(image, {
1302
+ x: imageX,
1303
+ y: imageY,
1304
+ width: drawWidth,
1305
+ height: drawHeight
1306
+ });
1307
+ }
1308
+ break;
1309
+ }
1310
+ case "initials" /* INITIALS */: {
1311
+ const value = fieldValues[field.id] || "";
1312
+ if (value) {
1313
+ const fontSize = Math.min(18, field.position.height * 0.8);
1314
+ page.drawText(value, {
1315
+ x: pdfX + 2,
1316
+ y: pdfY + (field.position.height - fontSize) / 2,
1317
+ size: fontSize,
1318
+ font,
1319
+ color: rgb(0, 0, 0),
1320
+ maxWidth: field.position.width - 4
1321
+ });
1322
+ }
1323
+ break;
1324
+ }
1325
+ case "dropdown" /* DROPDOWN */: {
1326
+ const value = fieldValues[field.id] || "";
1327
+ if (value) {
1328
+ const fontSize = Math.min(12, field.position.height * 0.6);
1329
+ page.drawText(value, {
1330
+ x: pdfX + 2,
1331
+ y: pdfY + (field.position.height - fontSize) / 2,
1332
+ size: fontSize,
1333
+ font,
1334
+ color: rgb(0, 0, 0),
1335
+ maxWidth: field.position.width - 4
1336
+ });
1337
+ }
1338
+ break;
1339
+ }
1340
+ default:
1341
+ break;
1342
+ }
1343
+ } catch (fieldError) {
1344
+ logger.error(`Error processing field ${field.name}:`, fieldError);
1345
+ }
1346
+ }
1347
+ return await pdfDoc.save();
1348
+ } catch (error) {
1349
+ logger.error("Error filling form fields:", error);
1350
+ throw error;
1351
+ }
1352
+ }
1353
+ async function getFieldPageNumbers(pdfBytes) {
1354
+ try {
1355
+ const { PDFDocument } = await loadPdfLib();
1356
+ const pdfDoc = await PDFDocument.load(pdfBytes);
1357
+ const form = pdfDoc.getForm();
1358
+ const fields = form.getFields();
1359
+ const pages = pdfDoc.getPages();
1360
+ const pageMap = {};
1361
+ const positionMap = {};
1362
+ for (const field of fields) {
1363
+ const fieldName = field.getName();
1364
+ const fieldWithAcro = field;
1365
+ const widgets = fieldWithAcro.acroField?.getWidgets?.() ?? [];
1366
+ if (widgets.length > 0) {
1367
+ const widget = widgets[0];
1368
+ if (!widget) continue;
1369
+ const rect = widget.getRectangle?.();
1370
+ const pageRef = widget.P?.();
1371
+ if (rect && pageRef) {
1372
+ const pageIndex = findPageIndexByRef(pages, pageRef);
1373
+ if (pageIndex !== -1) {
1374
+ const pageNumber = pageIndex + 1;
1375
+ pageMap[fieldName] = pageNumber;
1376
+ positionMap[fieldName] = {
1377
+ x: rect.x,
1378
+ y: rect.y,
1379
+ width: rect.width,
1380
+ height: rect.height,
1381
+ page: pageNumber
1382
+ };
1383
+ }
1384
+ }
1385
+ }
1386
+ }
1387
+ return { pageMap, positionMap };
1388
+ } catch (error) {
1389
+ logger.error("Error extracting field page numbers:", error);
1390
+ return { pageMap: {}, positionMap: {} };
1391
+ }
1392
+ }
1393
+
1394
+ // src/utils/field-extraction.ts
1395
+ var PDFName;
1396
+ async function extractVisibleFormFields(pdfBytes, currentSignerEmail) {
1397
+ try {
1398
+ const pdfLibModule = await loadPdfLib();
1399
+ const { PDFDocument, AnnotationFlags, PDFName: PDFNameClass } = pdfLibModule;
1400
+ PDFName = PDFNameClass;
1401
+ const pdfDoc = await PDFDocument.load(pdfBytes);
1402
+ const form = pdfDoc.getForm();
1403
+ const fields = form.getFields();
1404
+ const visibleFields = [];
1405
+ let hasAnyInitialsFields = false;
1406
+ let hasInitialsFieldsForCurrentSigner = false;
1407
+ for (const field of fields) {
1408
+ const fieldName = field.getName();
1409
+ const fieldWithExtensions = field;
1410
+ const widgets = fieldWithExtensions.acroField?.getWidgets?.() ?? [];
1411
+ let isVisible = true;
1412
+ for (const widget of widgets) {
1413
+ const hasHiddenFlag = widget.hasFlag?.(AnnotationFlags.Hidden) ?? false;
1414
+ const hasNoViewFlag = widget.hasFlag?.(AnnotationFlags.NoView) ?? false;
1415
+ if (hasHiddenFlag || hasNoViewFlag) {
1416
+ isVisible = false;
1417
+ break;
1418
+ }
1419
+ }
1420
+ if (isVisible) {
1421
+ const decodedInfo = decodeFieldName(fieldName);
1422
+ let fieldType = "text" /* TEXT */;
1423
+ const fieldTypeName = field.constructor.name;
1424
+ const isActualSignatureField = fieldTypeName.includes("Signature") || decodedInfo.decodedFieldName.toLowerCase().includes("signature") && !fieldTypeName.includes("CheckBox") && !fieldTypeName.includes("Radio");
1425
+ const isActualInitialsField = decodedInfo.decodedFieldName.toLowerCase().includes("initials") && !fieldTypeName.includes("CheckBox") && !fieldTypeName.includes("Radio");
1426
+ if (isActualSignatureField) {
1427
+ fieldType = "signature" /* SIGNATURE */;
1428
+ } else if (isActualInitialsField) {
1429
+ fieldType = "initials" /* INITIALS */;
1430
+ hasAnyInitialsFields = true;
1431
+ if (currentSignerEmail && decodedInfo.assignedSignerEmail === currentSignerEmail) {
1432
+ hasInitialsFieldsForCurrentSigner = true;
1433
+ }
1434
+ } else if (decodedInfo.decodedFieldName.toLowerCase().includes("date")) {
1435
+ fieldType = "date" /* DATE */;
1436
+ } else if (fieldTypeName.includes("CheckBox")) {
1437
+ fieldType = "checkbox" /* CHECKBOX */;
1438
+ } else if (fieldTypeName.toLowerCase().includes("dropdown")) {
1439
+ fieldType = "dropdown" /* DROPDOWN */;
1440
+ } else if (fieldTypeName.includes("Radio")) {
1441
+ fieldType = "radio" /* RADIO */;
1442
+ }
1443
+ let displayLabel = decodedInfo.displayLabel;
1444
+ if (!displayLabel || !displayLabel.trim()) {
1445
+ try {
1446
+ const tuLabel = extractTULabel(fieldWithExtensions);
1447
+ if (tuLabel && tuLabel.trim()) {
1448
+ displayLabel = tuLabel;
1449
+ }
1450
+ } catch (tuError) {
1451
+ }
1452
+ }
1453
+ if (!displayLabel || !displayLabel.trim()) {
1454
+ displayLabel = generateFallbackLabel(decodedInfo.decodedFieldName, fieldType);
1455
+ }
1456
+ const esignField = {
1457
+ id: fieldName,
1458
+ // Keep original encoded name as ID
1459
+ name: fieldName,
1460
+ // Keep original for removal later
1461
+ type: fieldType,
1462
+ label: displayLabel,
1463
+ // Use friendly label for display
1464
+ position: { x: 0, y: 0, width: 100, height: 30, page: 1 },
1465
+ // Default position
1466
+ required: fieldWithExtensions.isRequired?.() ?? false,
1467
+ placeholder: decodedInfo.fieldPlaceholder || "",
1468
+ // Use extracted placeholder
1469
+ assignedSignerEmail: decodedInfo.assignedSignerEmail
1470
+ // Add assigned signer
1471
+ };
1472
+ if (fieldType === "dropdown" /* DROPDOWN */ || fieldType === "radio" /* RADIO */) {
1473
+ try {
1474
+ const options = fieldWithExtensions.getOptions?.() ?? [];
1475
+ esignField.options = options;
1476
+ if (options.length > 0) {
1477
+ }
1478
+ } catch (error) {
1479
+ logger.warn("Error extracting options for field:", error);
1480
+ }
1481
+ }
1482
+ visibleFields.push(esignField);
1483
+ }
1484
+ }
1485
+ const mainFields = [];
1486
+ mainFields.push({
1487
+ id: "signature_field_main",
1488
+ name: "signature_field_main",
1489
+ type: "signature" /* SIGNATURE */,
1490
+ label: "Your Signature (will be applied to all signature fields)",
1491
+ position: { x: 0, y: 0, width: 200, height: 60, page: 1 },
1492
+ required: true,
1493
+ placeholder: "Draw or upload your signature",
1494
+ assignedSignerEmail: currentSignerEmail
1495
+ });
1496
+ const shouldCreateInitialsField = hasInitialsFieldsForCurrentSigner || hasAnyInitialsFields && !currentSignerEmail;
1497
+ if (shouldCreateInitialsField) {
1498
+ if (hasInitialsFieldsForCurrentSigner) {
1499
+ } else {
1500
+ }
1501
+ mainFields.push({
1502
+ id: "initials_field_main",
1503
+ name: "initials_field_main",
1504
+ type: "initials" /* INITIALS */,
1505
+ label: "Your Initials (will be applied to all initials fields)",
1506
+ position: { x: 0, y: 0, width: 100, height: 40, page: 1 },
1507
+ required: true,
1508
+ placeholder: "Enter your initials",
1509
+ assignedSignerEmail: currentSignerEmail
1510
+ });
1511
+ }
1512
+ return [...mainFields, ...visibleFields];
1513
+ } catch (error) {
1514
+ logger.error("Error extracting visible form fields from PDF:", error);
1515
+ return [];
1516
+ }
1517
+ }
1518
+ function decodeFieldName(fieldName) {
1519
+ let decodedFieldName = fieldName;
1520
+ let displayLabel = "";
1521
+ let assignedSignerEmail = void 0;
1522
+ let fieldPlaceholder = "";
1523
+ if (fieldName.includes("__LABEL__")) {
1524
+ const labelMarkerIndex = fieldName.indexOf("__LABEL__");
1525
+ decodedFieldName = fieldName.substring(0, labelMarkerIndex);
1526
+ const afterLabel = fieldName.substring(labelMarkerIndex + 9);
1527
+ if (afterLabel.includes("__SIGNER__")) {
1528
+ const signerMarkerIndex = afterLabel.indexOf("__SIGNER__");
1529
+ displayLabel = afterLabel.substring(0, signerMarkerIndex);
1530
+ const afterSigner = afterLabel.substring(signerMarkerIndex + 10);
1531
+ if (afterSigner.includes("__PLACEHOLDER__")) {
1532
+ const placeholderMarkerIndex = afterSigner.indexOf("__PLACEHOLDER__");
1533
+ assignedSignerEmail = afterSigner.substring(0, placeholderMarkerIndex);
1534
+ fieldPlaceholder = afterSigner.substring(placeholderMarkerIndex + 15);
1535
+ } else {
1536
+ assignedSignerEmail = afterSigner;
1537
+ }
1538
+ } else if (afterLabel.includes("__PLACEHOLDER__")) {
1539
+ const placeholderMarkerIndex = afterLabel.indexOf("__PLACEHOLDER__");
1540
+ displayLabel = afterLabel.substring(0, placeholderMarkerIndex);
1541
+ fieldPlaceholder = afterLabel.substring(placeholderMarkerIndex + 15);
1542
+ } else {
1543
+ displayLabel = afterLabel;
1544
+ }
1545
+ } else if (fieldName.includes("__SIGNER__")) {
1546
+ const signerMarkerIndex = fieldName.indexOf("__SIGNER__");
1547
+ decodedFieldName = fieldName.substring(0, signerMarkerIndex);
1548
+ const afterSigner = fieldName.substring(signerMarkerIndex + 10);
1549
+ if (afterSigner.includes("__PLACEHOLDER__")) {
1550
+ const placeholderMarkerIndex = afterSigner.indexOf("__PLACEHOLDER__");
1551
+ assignedSignerEmail = afterSigner.substring(0, placeholderMarkerIndex);
1552
+ fieldPlaceholder = afterSigner.substring(placeholderMarkerIndex + 15);
1553
+ } else {
1554
+ assignedSignerEmail = afterSigner;
1555
+ }
1556
+ } else if (fieldName.includes("__PLACEHOLDER__")) {
1557
+ const placeholderMarkerIndex = fieldName.indexOf("__PLACEHOLDER__");
1558
+ decodedFieldName = fieldName.substring(0, placeholderMarkerIndex);
1559
+ fieldPlaceholder = fieldName.substring(placeholderMarkerIndex + 15);
1560
+ }
1561
+ return {
1562
+ decodedFieldName,
1563
+ displayLabel,
1564
+ assignedSignerEmail,
1565
+ fieldPlaceholder
1566
+ };
1567
+ }
1568
+ function generateFallbackLabel(decodedFieldName, fieldType) {
1569
+ let displayLabel = decodedFieldName;
1570
+ displayLabel = displayLabel.replace(/_signature$/i, "").replace(/_initials$/i, "").replace(/_date$/i, "");
1571
+ if (/^(text|signature|initials|date)_\d+$/i.test(displayLabel)) {
1572
+ if (fieldType === "signature" /* SIGNATURE */) return "Signature";
1573
+ if (fieldType === "initials" /* INITIALS */) return "Initials";
1574
+ if (fieldType === "date" /* DATE */) return "Date";
1575
+ if (fieldType === "text" /* TEXT */) return "Text";
1576
+ if (fieldType === "checkbox" /* CHECKBOX */) return "Checkbox";
1577
+ if (fieldType === "dropdown" /* DROPDOWN */) return "Dropdown";
1578
+ if (fieldType === "radio" /* RADIO */) return "Option";
1579
+ } else {
1580
+ displayLabel = displayLabel.replace(/_/g, " ").replace(/\s+\d+$/g, "").trim().split(" ").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
1581
+ }
1582
+ return displayLabel;
1583
+ }
1584
+ function extractTULabel(field) {
1585
+ try {
1586
+ if (!field.acroField?.dict) {
1587
+ return "";
1588
+ }
1589
+ const tuLabel = field.acroField.dict.get(PDFName.of("TU"));
1590
+ if (!tuLabel) {
1591
+ return "";
1592
+ }
1593
+ const tuValue = tuLabel.toString();
1594
+ if (!tuValue || !tuValue.trim()) {
1595
+ return "";
1596
+ }
1597
+ if (tuValue.startsWith("(") || tuValue.startsWith("<")) {
1598
+ const match = tuValue.match(/^\((.*)\)$/) || tuValue.match(/^<(.*)>$/);
1599
+ if (match && match[1]) {
1600
+ return match[1];
1601
+ }
1602
+ }
1603
+ return tuValue;
1604
+ } catch (error) {
1605
+ return "";
1606
+ }
1607
+ }
1608
+ var CDN_WORKER = "https://cdn.jsdelivr.net/npm/pdfjs-dist@5.3.93/build/pdf.worker.mjs";
1609
+ var CDN_VIEWER = "https://mozilla.github.io/pdf.js";
1610
+ var _workerSrcComputed = false;
1611
+ var _cachedWorkerSrc = null;
1612
+ function getDefaultWorkerSrc() {
1613
+ if (_workerSrcComputed && _cachedWorkerSrc !== null) {
1614
+ return _cachedWorkerSrc;
1615
+ }
1616
+ let workerSrc = "/pdfjs/build/pdf.worker.mjs";
1617
+ if (typeof window !== "undefined" && typeof URL !== "undefined") {
1618
+ try {
1619
+ const meta = (function() {
1620
+ try {
1621
+ return new Function("return import.meta")();
1622
+ } catch {
1623
+ return null;
1624
+ }
1625
+ })();
1626
+ if (meta && meta.url) {
1627
+ workerSrc = new URL("pdfjs-dist/build/pdf.worker.mjs", meta.url).href;
1628
+ }
1629
+ } catch {
1630
+ }
1631
+ }
1632
+ _workerSrcComputed = true;
1633
+ _cachedWorkerSrc = workerSrc;
1634
+ return workerSrc;
1635
+ }
1636
+ var globalConfig = {
1637
+ viewerBasePath: "/pdfjs",
1638
+ workerSrc: ""
1639
+ // Lazy-initialized below
1640
+ };
1641
+ var _configInitialized = false;
1642
+ function ensureConfigInitialized() {
1643
+ if (!_configInitialized) {
1644
+ globalConfig.workerSrc = getDefaultWorkerSrc();
1645
+ _configInitialized = true;
1646
+ if (typeof window !== "undefined") {
1647
+ pdfjsLib2__namespace.GlobalWorkerOptions.workerSrc = globalConfig.workerSrc;
1648
+ }
1649
+ }
1650
+ }
1651
+ function setPdfJsConfig(config) {
1652
+ ensureConfigInitialized();
1653
+ if (config.viewerBasePath !== void 0) {
1654
+ globalConfig.viewerBasePath = config.viewerBasePath;
1655
+ }
1656
+ if (config.workerSrc !== void 0) {
1657
+ globalConfig.workerSrc = config.workerSrc;
1658
+ } else if (config.viewerBasePath !== void 0) {
1659
+ globalConfig.workerSrc = `${config.viewerBasePath}/build/pdf.worker.mjs`;
1660
+ }
1661
+ if (typeof window !== "undefined") {
1662
+ pdfjsLib2__namespace.GlobalWorkerOptions.workerSrc = globalConfig.workerSrc;
1663
+ }
1664
+ }
1665
+ function getPdfJsConfig() {
1666
+ ensureConfigInitialized();
1667
+ return { ...globalConfig };
1668
+ }
1669
+ function resetPdfJsConfig() {
1670
+ _workerSrcComputed = false;
1671
+ _cachedWorkerSrc = null;
1672
+ _configInitialized = false;
1673
+ ensureConfigInitialized();
1674
+ }
1675
+ function initializePdfJs() {
1676
+ ensureConfigInitialized();
1677
+ }
1678
+ async function checkPdfJsVersion(viewerBasePath = "/pdfjs") {
1679
+ const workerVersion = pdfjsLib2__namespace.version;
1680
+ try {
1681
+ const versionUrl = `${viewerBasePath}/.version`;
1682
+ const response = await fetch(versionUrl);
1683
+ if (!response.ok) {
1684
+ return {
1685
+ viewerExists: false,
1686
+ versionMatch: false,
1687
+ workerVersion,
1688
+ errorMessage: "PDF.js viewer files not found. Run: npx signiphi-setup"
1689
+ };
1690
+ }
1691
+ const viewerVersion = (await response.text()).trim();
1692
+ const versionMatch = viewerVersion === workerVersion;
1693
+ if (!versionMatch) {
1694
+ return {
1695
+ viewerExists: true,
1696
+ versionMatch: false,
1697
+ viewerVersion,
1698
+ workerVersion,
1699
+ errorMessage: `Version mismatch: viewer=${viewerVersion}, worker=${workerVersion}. Run: npx signiphi-setup`
1700
+ };
1701
+ }
1702
+ return {
1703
+ viewerExists: true,
1704
+ versionMatch: true,
1705
+ viewerVersion,
1706
+ workerVersion
1707
+ };
1708
+ } catch (error) {
1709
+ return {
1710
+ viewerExists: false,
1711
+ versionMatch: false,
1712
+ workerVersion,
1713
+ errorMessage: "Unable to check PDF.js version. Viewer files may not be installed."
1714
+ };
1715
+ }
1716
+ }
1717
+ function logVersionCheckWarning(result) {
1718
+ if (!result.viewerExists) {
1719
+ console.warn(
1720
+ "\u26A0\uFE0F PDF.js viewer files not found.\nThe PDF viewer requires viewer files to be installed.\nRun: npx signiphi-setup\nOr reinstall the package: npm install @signiphi/pdf-signer"
1721
+ );
1722
+ } else if (!result.versionMatch) {
1723
+ console.warn(
1724
+ `\u26A0\uFE0F PDF.js version mismatch detected:
1725
+ Viewer: ${result.viewerVersion}
1726
+ Worker: ${result.workerVersion}
1727
+ Run: npx signiphi-setup to sync versions`
1728
+ );
1729
+ }
1730
+ }
1731
+ async function checkAndLogPdfJsVersion(viewerBasePath = "/pdfjs") {
1732
+ const result = await checkPdfJsVersion(viewerBasePath);
1733
+ if (!result.viewerExists || !result.versionMatch) {
1734
+ logVersionCheckWarning(result);
1735
+ }
1736
+ return result;
1737
+ }
1738
+
1739
+ // src/utils/audit-trail.ts
1740
+ function captureDeviceMetadata() {
1741
+ const metadata = {};
1742
+ try {
1743
+ if (navigator.userAgent) {
1744
+ metadata.userAgent = navigator.userAgent.substring(0, 100);
1745
+ }
1746
+ if (screen.width && screen.height) {
1747
+ metadata.screenResolution = `${screen.width}x${screen.height}`;
1748
+ }
1749
+ try {
1750
+ const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
1751
+ if (timezone) {
1752
+ metadata.timezone = timezone;
1753
+ }
1754
+ } catch {
1755
+ }
1756
+ if (navigator.language) {
1757
+ metadata.language = navigator.language;
1758
+ }
1759
+ if (navigator.platform) {
1760
+ metadata.platform = navigator.platform;
1761
+ }
1762
+ } catch (error) {
1763
+ logger.warn("Error capturing device metadata:", error);
1764
+ }
1765
+ return metadata;
1766
+ }
1767
+ async function captureIpAddress() {
1768
+ try {
1769
+ const controller = new AbortController();
1770
+ const timeoutId = setTimeout(() => controller.abort(), 5e3);
1771
+ const ipResponse = await fetch("https://api.ipify.org?format=json", {
1772
+ method: "GET",
1773
+ signal: controller.signal
1774
+ });
1775
+ clearTimeout(timeoutId);
1776
+ if (ipResponse.ok) {
1777
+ const ipData = await ipResponse.json();
1778
+ if (ipData.ip) {
1779
+ return ipData.ip;
1780
+ }
1781
+ }
1782
+ return "Unknown";
1783
+ } catch (error) {
1784
+ return "Unknown";
1785
+ }
1786
+ }
1787
+ async function captureGeolocation() {
1788
+ if (!navigator.geolocation) {
1789
+ return null;
1790
+ }
1791
+ try {
1792
+ const position = await new Promise((resolve, reject) => {
1793
+ navigator.geolocation.getCurrentPosition(resolve, reject, {
1794
+ timeout: 1e4,
1795
+ // 10 second timeout
1796
+ enableHighAccuracy: false,
1797
+ maximumAge: 3e5
1798
+ // 5 minutes cache
1799
+ });
1800
+ });
1801
+ if (position.coords) {
1802
+ const latitude = parseFloat(position.coords.latitude.toFixed(4));
1803
+ const longitude = parseFloat(position.coords.longitude.toFixed(4));
1804
+ const accuracy = position.coords.accuracy ? Math.round(position.coords.accuracy) : void 0;
1805
+ return {
1806
+ latitude,
1807
+ longitude,
1808
+ accuracy
1809
+ };
1810
+ }
1811
+ return null;
1812
+ } catch (error) {
1813
+ return null;
1814
+ }
1815
+ }
1816
+ async function captureAuditTrail() {
1817
+ const auditTrail = {};
1818
+ try {
1819
+ const deviceMetadata = captureDeviceMetadata();
1820
+ Object.assign(auditTrail, deviceMetadata);
1821
+ try {
1822
+ const ipAddress = await captureIpAddress();
1823
+ auditTrail.ipAddress = ipAddress;
1824
+ } catch (error) {
1825
+ logger.warn("Error capturing IP address:", error);
1826
+ auditTrail.ipAddress = "Unknown";
1827
+ }
1828
+ try {
1829
+ const geolocation = await captureGeolocation();
1830
+ if (geolocation) {
1831
+ auditTrail.geolocation = geolocation;
1832
+ }
1833
+ } catch (error) {
1834
+ logger.warn("Error capturing geolocation:", error);
1835
+ }
1836
+ } catch (error) {
1837
+ logger.warn("Error capturing audit trail:", error);
1838
+ }
1839
+ return auditTrail;
1840
+ }
1841
+
1842
+ // src/utils/errors.ts
1843
+ var PdfValidationError = class _PdfValidationError extends Error {
1844
+ constructor(message, details) {
1845
+ super(message);
1846
+ this.details = details;
1847
+ this.name = "PdfValidationError";
1848
+ if (Error.captureStackTrace) {
1849
+ Error.captureStackTrace(this, _PdfValidationError);
1850
+ }
1851
+ }
1852
+ };
1853
+ var PdfProcessingError = class _PdfProcessingError extends Error {
1854
+ constructor(message, details) {
1855
+ super(message);
1856
+ this.details = details;
1857
+ this.name = "PdfProcessingError";
1858
+ if (Error.captureStackTrace) {
1859
+ Error.captureStackTrace(this, _PdfProcessingError);
1860
+ }
1861
+ }
1862
+ };
1863
+ var FormFieldError = class _FormFieldError extends Error {
1864
+ constructor(message, fieldName, details) {
1865
+ super(message);
1866
+ this.fieldName = fieldName;
1867
+ this.details = details;
1868
+ this.name = "FormFieldError";
1869
+ if (Error.captureStackTrace) {
1870
+ Error.captureStackTrace(this, _FormFieldError);
1871
+ }
1872
+ }
1873
+ };
1874
+ var AttachmentValidationError = class _AttachmentValidationError extends Error {
1875
+ constructor(message, fileName, details) {
1876
+ super(message);
1877
+ this.fileName = fileName;
1878
+ this.details = details;
1879
+ this.name = "AttachmentValidationError";
1880
+ if (Error.captureStackTrace) {
1881
+ Error.captureStackTrace(this, _AttachmentValidationError);
1882
+ }
1883
+ }
1884
+ };
1885
+ function isPdfValidationError(error) {
1886
+ return error instanceof PdfValidationError;
1887
+ }
1888
+ function isPdfProcessingError(error) {
1889
+ return error instanceof PdfProcessingError;
1890
+ }
1891
+ function isFormFieldError(error) {
1892
+ return error instanceof FormFieldError;
1893
+ }
1894
+ function isAttachmentValidationError(error) {
1895
+ return error instanceof AttachmentValidationError;
1896
+ }
1897
+ function getErrorMessage(error, defaultMessage = "Unknown error") {
1898
+ if (error instanceof Error) {
1899
+ return error.message;
1900
+ }
1901
+ if (typeof error === "string") {
1902
+ return error;
1903
+ }
1904
+ return defaultMessage;
1905
+ }
1906
+
1907
+ // src/utils/attachment-validators.ts
1908
+ var DEFAULT_ATTACHMENT_CONSTRAINTS = {
1909
+ maxFileSize: 10 * 1024 * 1024,
1910
+ // 10MB
1911
+ maxTotalSize: 50 * 1024 * 1024,
1912
+ // 50MB
1913
+ maxFiles: 10,
1914
+ allowedTypes: ["image/*", "application/pdf", "application/msword", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"],
1915
+ allowedExtensions: [".pdf", ".jpg", ".jpeg", ".png", ".gif", ".doc", ".docx"]
1916
+ };
1917
+ function validateFile(file, constraints = DEFAULT_ATTACHMENT_CONSTRAINTS) {
1918
+ const errors = [];
1919
+ if (file.size > constraints.maxFileSize) {
1920
+ const maxMB = (constraints.maxFileSize / 1024 / 1024).toFixed(1);
1921
+ const fileMB = (file.size / 1024 / 1024).toFixed(1);
1922
+ errors.push(`File "${file.name}" is too large (${fileMB}MB). Maximum size is ${maxMB}MB.`);
1923
+ }
1924
+ const isTypeAllowed = constraints.allowedTypes.some((allowedType) => {
1925
+ if (allowedType.endsWith("/*")) {
1926
+ const prefix = allowedType.slice(0, -2);
1927
+ return file.type.startsWith(prefix);
1928
+ }
1929
+ return file.type === allowedType;
1930
+ });
1931
+ if (!isTypeAllowed && constraints.allowedTypes.length > 0) {
1932
+ errors.push(
1933
+ `File type "${file.type}" is not allowed for "${file.name}". Allowed types: ${constraints.allowedTypes.join(", ")}`
1934
+ );
1935
+ }
1936
+ if (constraints.allowedExtensions && constraints.allowedExtensions.length > 0) {
1937
+ const fileExt = "." + file.name.split(".").pop()?.toLowerCase();
1938
+ const isExtAllowed = constraints.allowedExtensions.some(
1939
+ (ext) => ext.toLowerCase() === fileExt
1940
+ );
1941
+ if (!isExtAllowed) {
1942
+ errors.push(
1943
+ `File extension "${fileExt}" is not allowed for "${file.name}". Allowed extensions: ${constraints.allowedExtensions.join(", ")}`
1944
+ );
1945
+ }
1946
+ }
1947
+ return {
1948
+ valid: errors.length === 0,
1949
+ errors
1950
+ };
1951
+ }
1952
+ function validateFiles(files, existingAttachments = [], constraints = DEFAULT_ATTACHMENT_CONSTRAINTS) {
1953
+ const errors = [];
1954
+ const totalFiles = files.length + existingAttachments.length;
1955
+ if (totalFiles > constraints.maxFiles) {
1956
+ errors.push(
1957
+ `Too many files. Maximum allowed is ${constraints.maxFiles}, you have ${totalFiles} files total.`
1958
+ );
1959
+ }
1960
+ const existingSize = existingAttachments.reduce((sum, att) => sum + att.size, 0);
1961
+ const newFilesSize = files.reduce((sum, file) => sum + file.size, 0);
1962
+ const totalSize = existingSize + newFilesSize;
1963
+ if (totalSize > constraints.maxTotalSize) {
1964
+ const maxMB = (constraints.maxTotalSize / 1024 / 1024).toFixed(1);
1965
+ const totalMB = (totalSize / 1024 / 1024).toFixed(1);
1966
+ errors.push(
1967
+ `Total size of all files is too large (${totalMB}MB). Maximum total size is ${maxMB}MB.`
1968
+ );
1969
+ }
1970
+ for (const file of files) {
1971
+ const fileResult = validateFile(file, constraints);
1972
+ errors.push(...fileResult.errors);
1973
+ }
1974
+ return {
1975
+ valid: errors.length === 0,
1976
+ errors
1977
+ };
1978
+ }
1979
+ function validateFileOrThrow(file, constraints = DEFAULT_ATTACHMENT_CONSTRAINTS) {
1980
+ const result = validateFile(file, constraints);
1981
+ if (!result.valid) {
1982
+ throw new AttachmentValidationError(
1983
+ `File validation failed: ${result.errors.join("; ")}`,
1984
+ file.name,
1985
+ { file, errors: result.errors }
1986
+ );
1987
+ }
1988
+ }
1989
+ function validateFilesOrThrow(files, existingAttachments = [], constraints = DEFAULT_ATTACHMENT_CONSTRAINTS) {
1990
+ const result = validateFiles(files, existingAttachments, constraints);
1991
+ if (!result.valid) {
1992
+ throw new AttachmentValidationError(
1993
+ `File validation failed: ${result.errors.join("; ")}`,
1994
+ void 0,
1995
+ { files, errors: result.errors }
1996
+ );
1997
+ }
1998
+ }
1999
+ function isImageType(fileType) {
2000
+ return fileType.startsWith("image/");
2001
+ }
2002
+ function formatFileSize(bytes) {
2003
+ if (bytes === 0) return "0 Bytes";
2004
+ const k = 1024;
2005
+ const sizes = ["Bytes", "KB", "MB", "GB"];
2006
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
2007
+ return Math.round(bytes / Math.pow(k, i) * 100) / 100 + " " + sizes[i];
2008
+ }
2009
+
2010
+ // src/utils/performance-monitor.ts
2011
+ var PerformanceMonitor = class {
2012
+ constructor(enabled = true) {
2013
+ __publicField(this, "metrics", /* @__PURE__ */ new Map());
2014
+ __publicField(this, "enabled");
2015
+ this.enabled = enabled;
2016
+ }
2017
+ /**
2018
+ * Start a timer for a labeled operation
2019
+ *
2020
+ * @param label - Unique label for the operation
2021
+ * @returns Function to call when operation completes
2022
+ *
2023
+ * @example
2024
+ * ```typescript
2025
+ * const endTimer = monitor.startTimer('pdf-load');
2026
+ * await loadPdf(url);
2027
+ * endTimer(); // Records duration
2028
+ * ```
2029
+ */
2030
+ startTimer(label) {
2031
+ if (!this.enabled) {
2032
+ return () => {
2033
+ };
2034
+ }
2035
+ const startTime = performance.now();
2036
+ return () => {
2037
+ const duration = performance.now() - startTime;
2038
+ this.measure(label, duration);
2039
+ };
2040
+ }
2041
+ /**
2042
+ * Manually record a performance measurement
2043
+ *
2044
+ * @param label - Unique label for the operation
2045
+ * @param duration - Duration in milliseconds
2046
+ *
2047
+ * @example
2048
+ * ```typescript
2049
+ * monitor.measure('pdf-load', 1234.56);
2050
+ * ```
2051
+ */
2052
+ measure(label, duration) {
2053
+ if (!this.enabled) {
2054
+ return;
2055
+ }
2056
+ const measurements = this.metrics.get(label) || [];
2057
+ measurements.push(duration);
2058
+ this.metrics.set(label, measurements);
2059
+ logger.info(`[Performance] ${label}: ${duration.toFixed(2)}ms`);
2060
+ }
2061
+ /**
2062
+ * Get all recorded metrics with statistics
2063
+ *
2064
+ * @returns Object containing metrics for all labels
2065
+ *
2066
+ * @example
2067
+ * ```typescript
2068
+ * const metrics = monitor.getMetrics();
2069
+ * console.log(`PDF load avg: ${metrics['pdf-load'].avg}ms`);
2070
+ * ```
2071
+ */
2072
+ getMetrics() {
2073
+ const result = {};
2074
+ for (const [label, measurements] of this.metrics.entries()) {
2075
+ if (measurements.length === 0) continue;
2076
+ const total = measurements.reduce((sum, val) => sum + val, 0);
2077
+ const avg = total / measurements.length;
2078
+ const min = Math.min(...measurements);
2079
+ const max = Math.max(...measurements);
2080
+ const last = measurements[measurements.length - 1] ?? 0;
2081
+ result[label] = {
2082
+ count: measurements.length,
2083
+ total,
2084
+ avg,
2085
+ min,
2086
+ max,
2087
+ last
2088
+ };
2089
+ }
2090
+ return result;
2091
+ }
2092
+ /**
2093
+ * Get metrics for a specific label
2094
+ *
2095
+ * @param label - Label to get metrics for
2096
+ * @returns Metrics for the label, or null if not found
2097
+ */
2098
+ getMetric(label) {
2099
+ const measurements = this.metrics.get(label);
2100
+ if (!measurements || measurements.length === 0) {
2101
+ return null;
2102
+ }
2103
+ const total = measurements.reduce((sum, val) => sum + val, 0);
2104
+ const avg = total / measurements.length;
2105
+ const min = Math.min(...measurements);
2106
+ const max = Math.max(...measurements);
2107
+ const last = measurements[measurements.length - 1] ?? 0;
2108
+ return {
2109
+ count: measurements.length,
2110
+ total,
2111
+ avg,
2112
+ min,
2113
+ max,
2114
+ last
2115
+ };
2116
+ }
2117
+ /**
2118
+ * Get raw measurements for a label
2119
+ *
2120
+ * @param label - Label to get measurements for
2121
+ * @returns Array of duration measurements in milliseconds
2122
+ */
2123
+ getRawMeasurements(label) {
2124
+ return this.metrics.get(label) || [];
2125
+ }
2126
+ /**
2127
+ * Clear all metrics
2128
+ */
2129
+ clear() {
2130
+ this.metrics.clear();
2131
+ }
2132
+ /**
2133
+ * Clear metrics for a specific label
2134
+ *
2135
+ * @param label - Label to clear metrics for
2136
+ */
2137
+ clearLabel(label) {
2138
+ this.metrics.delete(label);
2139
+ }
2140
+ /**
2141
+ * Enable or disable performance monitoring
2142
+ *
2143
+ * @param enabled - Whether to enable monitoring
2144
+ */
2145
+ setEnabled(enabled) {
2146
+ this.enabled = enabled;
2147
+ }
2148
+ /**
2149
+ * Check if monitoring is enabled
2150
+ */
2151
+ isEnabled() {
2152
+ return this.enabled;
2153
+ }
2154
+ /**
2155
+ * Generate a performance report as a string
2156
+ *
2157
+ * @returns Formatted performance report
2158
+ */
2159
+ generateReport() {
2160
+ const metrics = this.getMetrics();
2161
+ const lines = [];
2162
+ lines.push("Performance Report");
2163
+ lines.push("=".repeat(50));
2164
+ lines.push("");
2165
+ for (const [label, stats] of Object.entries(metrics)) {
2166
+ lines.push(`${label}:`);
2167
+ lines.push(` Count: ${stats.count}`);
2168
+ lines.push(` Avg: ${stats.avg.toFixed(2)}ms`);
2169
+ lines.push(` Min: ${stats.min.toFixed(2)}ms`);
2170
+ lines.push(` Max: ${stats.max.toFixed(2)}ms`);
2171
+ lines.push(` Last: ${stats.last.toFixed(2)}ms`);
2172
+ lines.push("");
2173
+ }
2174
+ return lines.join("\n");
2175
+ }
2176
+ };
2177
+ function createPerformanceMonitor(enabled) {
2178
+ const isEnabled = enabled ?? process.env.NODE_ENV === "development";
2179
+ return new PerformanceMonitor(isEnabled);
2180
+ }
2181
+ var globalPerformanceMonitor = createPerformanceMonitor();
2182
+ function measurePerformance(label, monitor = globalPerformanceMonitor) {
2183
+ return function(_target, _propertyKey, descriptor) {
2184
+ const originalMethod = descriptor.value;
2185
+ descriptor.value = async function(...args) {
2186
+ const endTimer = monitor.startTimer(label);
2187
+ try {
2188
+ const result = await originalMethod.apply(this, args);
2189
+ return result;
2190
+ } finally {
2191
+ endTimer();
2192
+ }
2193
+ };
2194
+ return descriptor;
2195
+ };
2196
+ }
2197
+ function withPerformanceMonitoring(label, fn, monitor = globalPerformanceMonitor) {
2198
+ return (async (...args) => {
2199
+ const endTimer = monitor.startTimer(label);
2200
+ try {
2201
+ return await fn(...args);
2202
+ } finally {
2203
+ endTimer();
2204
+ }
2205
+ });
2206
+ }
2207
+ function toIsoDateString(date) {
2208
+ const year = date.getFullYear();
2209
+ const month = String(date.getMonth() + 1).padStart(2, "0");
2210
+ const day = String(date.getDate()).padStart(2, "0");
2211
+ return `${year}-${month}-${day}`;
2212
+ }
2213
+ function parseAndValidateDate(value) {
2214
+ const originalValue = String(value || "");
2215
+ if (!value || value.trim() === "") {
2216
+ return {
2217
+ isValid: false,
2218
+ date: null,
2219
+ isoString: null,
2220
+ originalValue,
2221
+ error: "Date is required"
2222
+ };
2223
+ }
2224
+ const trimmedValue = value.trim();
2225
+ const sanitizedValue = trimmedValue.replace(/(\d+)(st|nd|rd|th)/gi, "$1").replace(/,/g, " ").replace(/\s+/g, " ").trim();
2226
+ try {
2227
+ const isoDate = dateFns.parseISO(trimmedValue);
2228
+ if (dateFns.isValid(isoDate) && !isNaN(isoDate.getTime())) {
2229
+ return {
2230
+ isValid: true,
2231
+ date: isoDate,
2232
+ isoString: trimmedValue,
2233
+ originalValue
2234
+ };
2235
+ }
2236
+ } catch {
2237
+ }
2238
+ const shortYearMatch = sanitizedValue.match(/^(\d{1,2})[\/.\-](\d{1,2})[\/.\-](\d{2})$/);
2239
+ if (shortYearMatch) {
2240
+ const [, monthStr, dayStr, yearStr] = shortYearMatch;
2241
+ const month = parseInt(monthStr ?? "", 10);
2242
+ const day = parseInt(dayStr ?? "", 10);
2243
+ const year = 2e3 + parseInt(yearStr ?? "", 10);
2244
+ const candidate = new Date(year, month - 1, day);
2245
+ if (dateFns.isValid(candidate) && !isNaN(candidate.getTime())) {
2246
+ return {
2247
+ isValid: true,
2248
+ date: candidate,
2249
+ isoString: toIsoDateString(candidate),
2250
+ originalValue
2251
+ };
2252
+ }
2253
+ }
2254
+ const formats = [
2255
+ // US formats (MM/DD/YYYY - assume US for ambiguous dates)
2256
+ { pattern: "MM/dd/yyyy", name: "US format (MM/DD/YYYY)" },
2257
+ { pattern: "M/d/yyyy", name: "US format (M/D/YYYY)" },
2258
+ { pattern: "MM-dd-yyyy", name: "US format (MM-DD-YYYY)" },
2259
+ { pattern: "M-d-yyyy", name: "US format (M-D-YYYY)" },
2260
+ { pattern: "MM/dd/yy", name: "US format (MM/DD/YY)" },
2261
+ { pattern: "M/d/yy", name: "US format (M/D/YY)" },
2262
+ { pattern: "MM-dd-yy", name: "US format (MM-DD-YY)" },
2263
+ { pattern: "M-d-yy", name: "US format (M-D-YY)" },
2264
+ // EU formats (DD/MM/YYYY)
2265
+ { pattern: "dd/MM/yyyy", name: "EU format (DD/MM/YYYY)" },
2266
+ { pattern: "d/M/yyyy", name: "EU format (D/M/YYYY)" },
2267
+ { pattern: "dd.MM.yyyy", name: "EU format (DD.MM.YYYY)" },
2268
+ { pattern: "d.M.yyyy", name: "EU format (D.M.YYYY)" },
2269
+ // Full month names
2270
+ { pattern: "MMMM d, yyyy", name: "Month name format (January 1, 2024)" },
2271
+ { pattern: "MMM d, yyyy", name: "Short month format (Jan 1, 2024)" },
2272
+ { pattern: "MMMM d yyyy", name: "Month name format without comma (January 1 2024)" },
2273
+ { pattern: "MMM d yyyy", name: "Short month without comma (Jan 1 2024)" },
2274
+ { pattern: "d MMMM yyyy", name: "EU month format (1 January 2024)" },
2275
+ { pattern: "d MMM yyyy", name: "EU month short format (1 Jan 2024)" },
2276
+ { pattern: "d MMM, yyyy", name: "EU month short with comma (1 Jan, 2024)" },
2277
+ // Year first formats
2278
+ { pattern: "yyyy/MM/dd", name: "ISO-like format (YYYY/MM/DD)" },
2279
+ { pattern: "yyyy-MM-dd", name: "ISO format with dashes" }
2280
+ ];
2281
+ const referenceDate = /* @__PURE__ */ new Date();
2282
+ for (const { pattern } of formats) {
2283
+ try {
2284
+ const parsedDate = dateFns.parse(sanitizedValue, pattern, referenceDate);
2285
+ if (dateFns.isValid(parsedDate) && !isNaN(parsedDate.getTime())) {
2286
+ const isoString = toIsoDateString(parsedDate);
2287
+ return {
2288
+ isValid: true,
2289
+ date: parsedDate,
2290
+ isoString,
2291
+ originalValue
2292
+ };
2293
+ }
2294
+ } catch {
2295
+ }
2296
+ }
2297
+ if (/^\d+$/.test(trimmedValue)) {
2298
+ try {
2299
+ const timestamp = parseInt(trimmedValue, 10);
2300
+ const date = new Date(timestamp);
2301
+ if (dateFns.isValid(date) && !isNaN(date.getTime())) {
2302
+ const isoString = toIsoDateString(date);
2303
+ return {
2304
+ isValid: true,
2305
+ date,
2306
+ isoString,
2307
+ originalValue
2308
+ };
2309
+ }
2310
+ } catch {
2311
+ }
2312
+ }
2313
+ const nativeParsed = new Date(sanitizedValue);
2314
+ if (dateFns.isValid(nativeParsed) && !isNaN(nativeParsed.getTime())) {
2315
+ return {
2316
+ isValid: true,
2317
+ date: nativeParsed,
2318
+ isoString: toIsoDateString(nativeParsed),
2319
+ originalValue
2320
+ };
2321
+ }
2322
+ return {
2323
+ isValid: false,
2324
+ date: null,
2325
+ isoString: null,
2326
+ originalValue,
2327
+ error: `Invalid date format: "${trimmedValue}". Please use a date picker or format like MM/DD/YYYY.`
2328
+ };
2329
+ }
2330
+ function isValidISODate(value) {
2331
+ if (!value || typeof value !== "string") {
2332
+ return false;
2333
+ }
2334
+ const isoPattern = /^\d{4}-\d{2}-\d{2}$/;
2335
+ if (!isoPattern.test(value)) {
2336
+ return false;
2337
+ }
2338
+ try {
2339
+ const date = dateFns.parseISO(value);
2340
+ return dateFns.isValid(date) && !isNaN(date.getTime());
2341
+ } catch {
2342
+ return false;
2343
+ }
2344
+ }
2345
+
2346
+ // src/utils/tracking.ts
2347
+ var DEFAULT_API_ENDPOINT = "https://api-dev.signiphi.ai";
2348
+ var API_PREFIX = "/api/v1";
2349
+ async function trackDocumentSent(config, data) {
2350
+ const endpoint = config.endpoint || DEFAULT_API_ENDPOINT;
2351
+ const url = `${endpoint}${API_PREFIX}/usage/track-send`;
2352
+ try {
2353
+ const response = await fetch(url, {
2354
+ method: "POST",
2355
+ headers: {
2356
+ "Content-Type": "application/json",
2357
+ "x-api-key": config.apiKey
2358
+ },
2359
+ body: JSON.stringify({
2360
+ recipientEmail: data.recipientEmail,
2361
+ documentId: data.documentId,
2362
+ idempotencyKey: data.idempotencyKey,
2363
+ metadata: data.metadata
2364
+ })
2365
+ });
2366
+ if (!response.ok) {
2367
+ const errorText = await response.text();
2368
+ logger.error(`Track send failed: ${response.status} - ${errorText}`);
2369
+ throw new Error(`Tracking failed: ${response.status}`);
2370
+ }
2371
+ const result = await response.json();
2372
+ logger.debug("Document send tracked successfully", { eventId: result.eventId });
2373
+ return result;
2374
+ } catch (error) {
2375
+ logger.error("Failed to track document send:", error);
2376
+ throw error;
2377
+ }
2378
+ }
2379
+ async function trackDocumentSigned(config, data) {
2380
+ const endpoint = config.endpoint || DEFAULT_API_ENDPOINT;
2381
+ const url = `${endpoint}${API_PREFIX}/usage/track-signing`;
2382
+ try {
2383
+ const response = await fetch(url, {
2384
+ method: "POST",
2385
+ headers: {
2386
+ "Content-Type": "application/json",
2387
+ "x-api-key": config.apiKey
2388
+ },
2389
+ body: JSON.stringify({
2390
+ signerEmail: data.signerEmail,
2391
+ documentId: data.documentId,
2392
+ idempotencyKey: data.idempotencyKey,
2393
+ metadata: data.metadata
2394
+ })
2395
+ });
2396
+ if (!response.ok) {
2397
+ const errorText = await response.text();
2398
+ logger.error(`Track signing failed: ${response.status} - ${errorText}`);
2399
+ throw new Error(`Tracking failed: ${response.status}`);
2400
+ }
2401
+ const result = await response.json();
2402
+ logger.debug("Document signing tracked successfully", { eventId: result.eventId });
2403
+ return result;
2404
+ } catch (error) {
2405
+ logger.error("Failed to track document signing:", error);
2406
+ throw error;
2407
+ }
2408
+ }
2409
+ async function trackDocumentSignedSilent(config, data) {
2410
+ try {
2411
+ await trackDocumentSigned(config, data);
2412
+ } catch (error) {
2413
+ logger.warn("Silent tracking failed (non-critical):", error);
2414
+ }
2415
+ }
2416
+ async function trackDocumentSentSilent(config, data) {
2417
+ try {
2418
+ await trackDocumentSent(config, data);
2419
+ } catch (error) {
2420
+ logger.warn("Silent tracking failed (non-critical):", error);
2421
+ }
2422
+ }
2423
+
2424
+ exports.AttachmentValidationError = AttachmentValidationError;
2425
+ exports.CDN_VIEWER = CDN_VIEWER;
2426
+ exports.CDN_WORKER = CDN_WORKER;
2427
+ exports.DEFAULT_ATTACHMENT_CONSTRAINTS = DEFAULT_ATTACHMENT_CONSTRAINTS;
2428
+ exports.FormFieldError = FormFieldError;
2429
+ exports.PdfProcessingError = PdfProcessingError;
2430
+ exports.PdfValidationError = PdfValidationError;
2431
+ exports.PerformanceMonitor = PerformanceMonitor;
2432
+ exports.captureAuditTrail = captureAuditTrail;
2433
+ exports.captureDeviceMetadata = captureDeviceMetadata;
2434
+ exports.captureGeolocation = captureGeolocation;
2435
+ exports.captureIpAddress = captureIpAddress;
2436
+ exports.checkAndLogPdfJsVersion = checkAndLogPdfJsVersion;
2437
+ exports.checkPdfJsVersion = checkPdfJsVersion;
2438
+ exports.createPdfBlobUrl = createPdfBlobUrl;
2439
+ exports.createPerformanceMonitor = createPerformanceMonitor;
2440
+ exports.dataUrlToBytes = dataUrlToBytes;
2441
+ exports.decodeFieldName = decodeFieldName;
2442
+ exports.detectFieldType = detectFieldType;
2443
+ exports.downloadPdf = downloadPdf;
2444
+ exports.extractFieldValue = extractFieldValue;
2445
+ exports.extractVisibleFormFields = extractVisibleFormFields;
2446
+ exports.fillFormFieldsWithSignatures = fillFormFieldsWithSignatures;
2447
+ exports.fillPdfWithSignatures = fillPdfWithSignatures;
2448
+ exports.filterFieldsBySigner = filterFieldsBySigner;
2449
+ exports.findPageIndexByRef = findPageIndexByRef;
2450
+ exports.findPageIndexWithFallback = findPageIndexWithFallback;
2451
+ exports.formatFieldName = formatFieldName;
2452
+ exports.formatFileSize = formatFileSize;
2453
+ exports.generateFallbackLabel = generateFallbackLabel;
2454
+ exports.getErrorMessage = getErrorMessage;
2455
+ exports.getFieldPageNumbers = getFieldPageNumbers;
2456
+ exports.getPdfJsConfig = getPdfJsConfig;
2457
+ exports.getWidgetRectangleAndPage = getWidgetRectangleAndPage;
2458
+ exports.globalPerformanceMonitor = globalPerformanceMonitor;
2459
+ exports.initializePdfJs = initializePdfJs;
2460
+ exports.isAttachmentValidationError = isAttachmentValidationError;
2461
+ exports.isAutoGeneratedLabel = isAutoGeneratedLabel;
2462
+ exports.isFieldVisibleToSigner = isFieldVisibleToSigner;
2463
+ exports.isFormFieldError = isFormFieldError;
2464
+ exports.isImageType = isImageType;
2465
+ exports.isInitialsField = isInitialsField;
2466
+ exports.isPdfProcessingError = isPdfProcessingError;
2467
+ exports.isPdfValidationError = isPdfValidationError;
2468
+ exports.isRequiredField = isRequiredField;
2469
+ exports.isSignatureField = isSignatureField;
2470
+ exports.isValidISODate = isValidISODate;
2471
+ exports.isValidPdf = isValidPdf;
2472
+ exports.logVersionCheckWarning = logVersionCheckWarning;
2473
+ exports.logger = logger;
2474
+ exports.measurePerformance = measurePerformance;
2475
+ exports.parseAndValidateDate = parseAndValidateDate;
2476
+ exports.pdfToImages = pdfToImages;
2477
+ exports.readCurrentPdfFormValues = readCurrentPdfFormValues;
2478
+ exports.readPdfFormFields = readPdfFormFields;
2479
+ exports.removeAllFormFields = removeAllFormFields;
2480
+ exports.resetPdfJsConfig = resetPdfJsConfig;
2481
+ exports.setPdfJsConfig = setPdfJsConfig;
2482
+ exports.shouldFlattenField = shouldFlattenField;
2483
+ exports.trackDocumentSent = trackDocumentSent;
2484
+ exports.trackDocumentSentSilent = trackDocumentSentSilent;
2485
+ exports.trackDocumentSigned = trackDocumentSigned;
2486
+ exports.trackDocumentSignedSilent = trackDocumentSignedSilent;
2487
+ exports.urlToPdfBytes = urlToPdfBytes;
2488
+ exports.validateCurrentPdfState = validateCurrentPdfState;
2489
+ exports.validateFieldValues = validateFieldValues;
2490
+ exports.validateFile = validateFile;
2491
+ exports.validateFileOrThrow = validateFileOrThrow;
2492
+ exports.validateFiles = validateFiles;
2493
+ exports.validateFilesOrThrow = validateFilesOrThrow;
2494
+ exports.validateFormField = validateFormField;
2495
+ exports.validatePdfBytes = validatePdfBytes;
2496
+ exports.validatePdfFormFields = validatePdfFormFields;
2497
+ exports.validatePdfUrl = validatePdfUrl;
2498
+ exports.validateSignatures = validateSignatures;
2499
+ exports.withPerformanceMonitoring = withPerformanceMonitoring;
2500
+ //# sourceMappingURL=index.js.map
2501
+ //# sourceMappingURL=index.js.map