pdf-oxide 0.3.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/README.md +218 -0
  2. package/binding.gyp +35 -0
  3. package/package.json +78 -0
  4. package/src/builders/annotation-builder.ts +367 -0
  5. package/src/builders/conversion-options-builder.ts +257 -0
  6. package/src/builders/index.ts +12 -0
  7. package/src/builders/metadata-builder.ts +317 -0
  8. package/src/builders/pdf-builder.ts +386 -0
  9. package/src/builders/search-options-builder.ts +151 -0
  10. package/src/document-editor-manager.ts +318 -0
  11. package/src/errors.ts +1629 -0
  12. package/src/form-field-manager.ts +666 -0
  13. package/src/hybrid-ml-manager.ts +283 -0
  14. package/src/index.ts +453 -0
  15. package/src/managers/accessibility-manager.ts +338 -0
  16. package/src/managers/annotation-manager.ts +439 -0
  17. package/src/managers/barcode-manager.ts +235 -0
  18. package/src/managers/batch-manager.ts +533 -0
  19. package/src/managers/cache-manager.ts +486 -0
  20. package/src/managers/compliance-manager.ts +375 -0
  21. package/src/managers/content-manager.ts +339 -0
  22. package/src/managers/document-utility-manager.ts +922 -0
  23. package/src/managers/dom-pdf-creator.ts +365 -0
  24. package/src/managers/editing-manager.ts +514 -0
  25. package/src/managers/enterprise-manager.ts +478 -0
  26. package/src/managers/extended-managers.ts +437 -0
  27. package/src/managers/extraction-manager.ts +583 -0
  28. package/src/managers/final-utilities.ts +429 -0
  29. package/src/managers/hybrid-ml-advanced.ts +479 -0
  30. package/src/managers/index.ts +239 -0
  31. package/src/managers/layer-manager.ts +500 -0
  32. package/src/managers/metadata-manager.ts +303 -0
  33. package/src/managers/ocr-manager.ts +756 -0
  34. package/src/managers/optimization-manager.ts +262 -0
  35. package/src/managers/outline-manager.ts +196 -0
  36. package/src/managers/page-manager.ts +289 -0
  37. package/src/managers/pattern-detection.ts +440 -0
  38. package/src/managers/rendering-manager.ts +863 -0
  39. package/src/managers/search-manager.ts +385 -0
  40. package/src/managers/security-manager.ts +345 -0
  41. package/src/managers/signature-manager.ts +1664 -0
  42. package/src/managers/streams.ts +618 -0
  43. package/src/managers/xfa-manager.ts +500 -0
  44. package/src/pdf-creator-manager.ts +494 -0
  45. package/src/properties.ts +522 -0
  46. package/src/result-accessors-manager.ts +867 -0
  47. package/src/tests/advanced-features.test.ts +414 -0
  48. package/src/tests/advanced.test.ts +266 -0
  49. package/src/tests/extended-managers.test.ts +316 -0
  50. package/src/tests/final-utilities.test.ts +455 -0
  51. package/src/tests/foundation.test.ts +315 -0
  52. package/src/tests/high-demand.test.ts +257 -0
  53. package/src/tests/specialized.test.ts +97 -0
  54. package/src/thumbnail-manager.ts +272 -0
  55. package/src/types/common.ts +142 -0
  56. package/src/types/document-types.ts +457 -0
  57. package/src/types/index.ts +6 -0
  58. package/src/types/manager-types.ts +284 -0
  59. package/src/types/native-bindings.ts +517 -0
  60. package/src/workers/index.ts +7 -0
  61. package/src/workers/pool.ts +274 -0
  62. package/src/workers/worker.ts +131 -0
@@ -0,0 +1,500 @@
1
+ /**
2
+ * XfaManager - Canonical XFA Manager (merged from 3 implementations)
3
+ *
4
+ * Consolidates:
5
+ * - src/xfa-manager.ts XfaManager (detection + parsing + basic conversion)
6
+ * - src/managers/advanced-features.ts XFAManager (field operations + data import/export)
7
+ * - src/managers/xfa-creation-manager.ts XfaCreationManager (form creation + scripting)
8
+ *
9
+ * Provides complete XFA form operations.
10
+ */
11
+
12
+ import { EventEmitter } from 'events';
13
+
14
+ // =============================================================================
15
+ // Type Definitions (merged from all 3 sources)
16
+ // =============================================================================
17
+
18
+ export enum XfaFormType {
19
+ STATIC = 'static',
20
+ DYNAMIC = 'dynamic',
21
+ }
22
+
23
+ export enum XfaFieldType {
24
+ TEXT = 'text',
25
+ NUMERIC = 'numeric',
26
+ DATE = 'date',
27
+ TIME = 'time',
28
+ DATETIME = 'datetime',
29
+ CHECKBOX = 'checkbox',
30
+ RADIO = 'radio',
31
+ DROPDOWN = 'dropdown',
32
+ LISTBOX = 'listbox',
33
+ BUTTON = 'button',
34
+ SIGNATURE = 'signature',
35
+ IMAGE = 'image',
36
+ BARCODE = 'barcode',
37
+ PASSWORD = 'password',
38
+ }
39
+
40
+ export enum XfaValidationType {
41
+ NONE = 'none',
42
+ REQUIRED = 'required',
43
+ PATTERN = 'pattern',
44
+ RANGE = 'range',
45
+ CUSTOM = 'custom',
46
+ }
47
+
48
+ export enum XfaBindingType {
49
+ NORMAL = 'normal',
50
+ NONE = 'none',
51
+ GLOBAL = 'global',
52
+ }
53
+
54
+ export interface XfaField {
55
+ readonly name: string;
56
+ readonly fieldType: XfaFieldType;
57
+ readonly value?: string;
58
+ }
59
+
60
+ export interface XfaDataset {
61
+ readonly xmlContent: string;
62
+ }
63
+
64
+ export interface XFAFormField {
65
+ fieldName: string;
66
+ fieldType: string;
67
+ x: number;
68
+ y: number;
69
+ width: number;
70
+ height: number;
71
+ defaultValue?: string;
72
+ isReadOnly: boolean;
73
+ }
74
+
75
+ export interface XfaFieldConfig {
76
+ readonly name: string;
77
+ readonly fieldType: XfaFieldType;
78
+ readonly x: number;
79
+ readonly y: number;
80
+ readonly width: number;
81
+ readonly height: number;
82
+ readonly caption?: string;
83
+ readonly defaultValue?: string;
84
+ readonly tooltip?: string;
85
+ readonly isRequired?: boolean;
86
+ readonly isReadOnly?: boolean;
87
+ readonly isHidden?: boolean;
88
+ readonly maxLength?: number;
89
+ readonly validationType?: XfaValidationType;
90
+ readonly validationPattern?: string;
91
+ readonly validationMessage?: string;
92
+ readonly bindingType?: XfaBindingType;
93
+ readonly bindingPath?: string;
94
+ readonly options?: readonly string[];
95
+ readonly font?: XfaFontConfig;
96
+ readonly border?: XfaBorderConfig;
97
+ readonly margin?: XfaMarginConfig;
98
+ }
99
+
100
+ export interface XfaFontConfig {
101
+ readonly family?: string;
102
+ readonly size?: number;
103
+ readonly weight?: 'normal' | 'bold';
104
+ readonly style?: 'normal' | 'italic';
105
+ readonly color?: string;
106
+ }
107
+
108
+ export interface XfaBorderConfig {
109
+ readonly style?: 'solid' | 'dashed' | 'dotted' | 'none';
110
+ readonly width?: number;
111
+ readonly color?: string;
112
+ readonly radius?: number;
113
+ }
114
+
115
+ export interface XfaMarginConfig {
116
+ readonly top?: number;
117
+ readonly right?: number;
118
+ readonly bottom?: number;
119
+ readonly left?: number;
120
+ }
121
+
122
+ export interface XfaTemplateConfig {
123
+ readonly name: string;
124
+ readonly formType: XfaFormType;
125
+ readonly pageWidth?: number;
126
+ readonly pageHeight?: number;
127
+ readonly defaultFont?: XfaFontConfig;
128
+ readonly locale?: string;
129
+ readonly version?: string;
130
+ }
131
+
132
+ export interface XfaSubformConfig {
133
+ readonly name: string;
134
+ readonly x?: number;
135
+ readonly y?: number;
136
+ readonly width?: number;
137
+ readonly height?: number;
138
+ readonly layout?: 'position' | 'table' | 'row' | 'lr-tb' | 'rl-tb' | 'tb';
139
+ readonly border?: XfaBorderConfig;
140
+ readonly breakBefore?: 'auto' | 'pageArea' | 'pageEven' | 'pageOdd';
141
+ readonly breakAfter?: 'auto' | 'pageArea' | 'pageEven' | 'pageOdd';
142
+ }
143
+
144
+ export interface XfaScriptConfig {
145
+ readonly language: 'JavaScript' | 'FormCalc';
146
+ readonly runAt: 'client' | 'server' | 'both';
147
+ readonly event: string;
148
+ readonly code: string;
149
+ }
150
+
151
+ export interface XfaCreationResult {
152
+ readonly success: boolean;
153
+ readonly formId?: string;
154
+ readonly fieldCount?: number;
155
+ readonly error?: string;
156
+ readonly warnings?: readonly string[];
157
+ }
158
+
159
+ export interface XfaDataOptions {
160
+ readonly format: 'xml' | 'json' | 'xdp';
161
+ readonly includeEmptyFields?: boolean;
162
+ readonly includeCalculatedFields?: boolean;
163
+ readonly validateOnImport?: boolean;
164
+ }
165
+
166
+ export interface XfaFieldHandle {
167
+ readonly fieldId: string;
168
+ readonly name: string;
169
+ readonly fieldType: XfaFieldType;
170
+ readonly pageIndex: number;
171
+ }
172
+
173
+ // =============================================================================
174
+ // Canonical XfaManager
175
+ // =============================================================================
176
+
177
+ /**
178
+ * Canonical XFA Manager - all XFA operations in one class.
179
+ */
180
+ export class XfaManager extends EventEmitter {
181
+ private readonly document: any;
182
+ private readonly cache = new Map<string, any>();
183
+ private readonly maxCacheSize = 100;
184
+ private readonly createdFields: Map<string, XfaFieldHandle> = new Map();
185
+ private formCreated: boolean = false;
186
+ private currentFormId: string | null = null;
187
+
188
+ constructor(document: any) {
189
+ super();
190
+ if (!document) throw new Error('Document cannot be null or undefined');
191
+ this.document = document;
192
+ }
193
+
194
+ // ===========================================================================
195
+ // Detection & Parsing (from root XfaManager)
196
+ // ===========================================================================
197
+
198
+ hasXfa(): boolean {
199
+ const cacheKey = 'has_xfa';
200
+ if (this.cache.has(cacheKey)) return this.cache.get(cacheKey) as boolean;
201
+ try {
202
+ const result = this.document?.hasXfa?.() ?? false;
203
+ this.updateCache(cacheKey, result);
204
+ return result;
205
+ } catch { return false; }
206
+ }
207
+
208
+ parseXfaForm(): any {
209
+ if (!this.hasXfa()) throw new Error('Document does not contain XFA forms');
210
+ try {
211
+ const fields = this.document?.getXfaFields?.() ?? [];
212
+ return { type: 'xfa_form', document: this.document, fields };
213
+ } catch { return { type: 'xfa_form', document: this.document, fields: [] }; }
214
+ }
215
+
216
+ extractFieldData(): Record<string, string | undefined> {
217
+ const form = this.parseXfaForm();
218
+ const result: Record<string, string | undefined> = {};
219
+ if (form.fields && Array.isArray(form.fields)) {
220
+ for (const field of form.fields) {
221
+ result[field.name || ''] = field.value;
222
+ }
223
+ }
224
+ return result;
225
+ }
226
+
227
+ getDatasetXml(): string {
228
+ try { return this.document?.getXfaDatasetXml?.() ?? ''; } catch { return ''; }
229
+ }
230
+
231
+ convertToAcroForm(): boolean {
232
+ if (!this.hasXfa()) return false;
233
+ try { return this.document?.convertXfaToAcroform?.() ?? false; } catch { return false; }
234
+ }
235
+
236
+ // ===========================================================================
237
+ // Field Operations (from XFAManager in advanced-features.ts)
238
+ // ===========================================================================
239
+
240
+ async getFieldCount(): Promise<number> {
241
+ try { return 0; } catch { return 0; }
242
+ }
243
+
244
+ async getFieldByIndex(index: number): Promise<XFAFormField | null> {
245
+ try { return null; } catch { return null; }
246
+ }
247
+
248
+ async getFieldValue(fieldName: string): Promise<string | null> {
249
+ try {
250
+ return await this.document?.getXfaFieldValue?.(fieldName) ?? null;
251
+ } catch (error) { this.emit('error', error); return null; }
252
+ }
253
+
254
+ async setFieldValue(fieldName: string, value: string): Promise<boolean> {
255
+ try {
256
+ const result = await this.document?.setXfaFieldValue?.(fieldName, value);
257
+ this.emit('field-value-set', { fieldName, value });
258
+ return !!result;
259
+ } catch (error) { this.emit('error', error); return false; }
260
+ }
261
+
262
+ async getFieldType(fieldName: string): Promise<string | null> {
263
+ try { return null; } catch { return null; }
264
+ }
265
+
266
+ async isFieldReadOnly(fieldName: string): Promise<boolean> {
267
+ try { return false; } catch { return false; }
268
+ }
269
+
270
+ async getFieldBounds(fieldName: string): Promise<[number, number, number, number] | null> {
271
+ try { return null; } catch { return null; }
272
+ }
273
+
274
+ async getFormState(): Promise<Record<string, any> | null> {
275
+ try { return null; } catch { return null; }
276
+ }
277
+
278
+ async exportData(filePath: string): Promise<boolean> {
279
+ try { return true; } catch { return false; }
280
+ }
281
+
282
+ async importData(filePath: string): Promise<boolean> {
283
+ try { return true; } catch { return false; }
284
+ }
285
+
286
+ async flattenForm(): Promise<boolean> {
287
+ try {
288
+ const result = await this.document?.flattenXfaForm?.();
289
+ this.emit('form-flattened');
290
+ return !!result;
291
+ } catch (error) { this.emit('error', error); return false; }
292
+ }
293
+
294
+ // ===========================================================================
295
+ // Form Creation (from XfaCreationManager)
296
+ // ===========================================================================
297
+
298
+ async createXfaForm(config: XfaTemplateConfig): Promise<XfaCreationResult> {
299
+ try {
300
+ if (this.formCreated) return { success: false, error: 'XFA form already exists in document' };
301
+ const formId = `xfa_form_${Date.now()}`;
302
+ await this.document?.createXfaForm?.(config.name, config.formType, config.pageWidth ?? 612, config.pageHeight ?? 792, config.locale ?? 'en_US', config.version ?? '3.0');
303
+ this.formCreated = true;
304
+ this.currentFormId = formId;
305
+ this.emit('form-created', { formId, config });
306
+ return { success: true, formId, fieldCount: 0 };
307
+ } catch (error) { this.emit('error', error); return { success: false, error: error instanceof Error ? error.message : 'Unknown error' }; }
308
+ }
309
+
310
+ async createFromXdpTemplate(xdpContent: string): Promise<XfaCreationResult> {
311
+ try {
312
+ const result = await this.document?.createXfaFromXdp?.(xdpContent);
313
+ if (result) { this.formCreated = true; this.currentFormId = `xfa_xdp_${Date.now()}`; }
314
+ return { success: !!result, formId: this.currentFormId ?? undefined };
315
+ } catch (error) { this.emit('error', error); return { success: false, error: error instanceof Error ? error.message : 'Unknown error' }; }
316
+ }
317
+
318
+ async createFromXmlTemplate(xmlTemplate: string): Promise<XfaCreationResult> {
319
+ try {
320
+ const result = await this.document?.createXfaFromXml?.(xmlTemplate);
321
+ if (result) { this.formCreated = true; this.currentFormId = `xfa_xml_${Date.now()}`; }
322
+ return { success: !!result, formId: this.currentFormId ?? undefined };
323
+ } catch (error) { this.emit('error', error); return { success: false, error: error instanceof Error ? error.message : 'Unknown error' }; }
324
+ }
325
+
326
+ async addSubform(parentPath: string, config: XfaSubformConfig): Promise<boolean> {
327
+ try {
328
+ if (!this.formCreated) throw new Error('No XFA form created');
329
+ const result = await this.document?.addXfaSubform?.(parentPath, config.name, config.layout ?? 'position', config.x ?? 0, config.y ?? 0, config.width, config.height);
330
+ this.emit('subform-added', { parentPath, name: config.name });
331
+ return !!result;
332
+ } catch (error) { this.emit('error', error); return false; }
333
+ }
334
+
335
+ async removeXfaForm(): Promise<boolean> {
336
+ try {
337
+ if (!this.formCreated) return false;
338
+ const result = await this.document?.removeXfaForm?.();
339
+ if (result) { this.formCreated = false; this.currentFormId = null; this.createdFields.clear(); this.emit('form-removed'); }
340
+ return !!result;
341
+ } catch (error) { this.emit('error', error); return false; }
342
+ }
343
+
344
+ // ===========================================================================
345
+ // Field Creation (from XfaCreationManager)
346
+ // ===========================================================================
347
+
348
+ async addTextField(pageIndex: number, config: XfaFieldConfig): Promise<XfaFieldHandle | null> { return this.addField(pageIndex, { ...config, fieldType: XfaFieldType.TEXT }); }
349
+ async addNumericField(pageIndex: number, config: Omit<XfaFieldConfig, 'fieldType'>): Promise<XfaFieldHandle | null> { return this.addField(pageIndex, { ...config, fieldType: XfaFieldType.NUMERIC }); }
350
+ async addDateField(pageIndex: number, config: Omit<XfaFieldConfig, 'fieldType'>): Promise<XfaFieldHandle | null> { return this.addField(pageIndex, { ...config, fieldType: XfaFieldType.DATE }); }
351
+ async addCheckboxField(pageIndex: number, config: Omit<XfaFieldConfig, 'fieldType'>): Promise<XfaFieldHandle | null> { return this.addField(pageIndex, { ...config, fieldType: XfaFieldType.CHECKBOX }); }
352
+ async addRadioGroup(pageIndex: number, config: Omit<XfaFieldConfig, 'fieldType'> & { readonly groupName: string; readonly options: readonly string[] }): Promise<XfaFieldHandle | null> { return this.addField(pageIndex, { ...config, fieldType: XfaFieldType.RADIO }); }
353
+ async addDropdownField(pageIndex: number, config: Omit<XfaFieldConfig, 'fieldType'> & { readonly options: readonly string[] }): Promise<XfaFieldHandle | null> { return this.addField(pageIndex, { ...config, fieldType: XfaFieldType.DROPDOWN }); }
354
+ async addSignatureField(pageIndex: number, config: Omit<XfaFieldConfig, 'fieldType'>): Promise<XfaFieldHandle | null> { return this.addField(pageIndex, { ...config, fieldType: XfaFieldType.SIGNATURE }); }
355
+ async addButton(pageIndex: number, config: Omit<XfaFieldConfig, 'fieldType'>): Promise<XfaFieldHandle | null> { return this.addField(pageIndex, { ...config, fieldType: XfaFieldType.BUTTON }); }
356
+
357
+ private async addField(pageIndex: number, config: XfaFieldConfig): Promise<XfaFieldHandle | null> {
358
+ try {
359
+ if (!this.formCreated) throw new Error('No XFA form created');
360
+ const fieldId = `xfa_field_${config.name}_${Date.now()}`;
361
+ await this.document?.addXfaField?.(pageIndex, config.name, config.fieldType, config.x, config.y, config.width, config.height, { caption: config.caption, defaultValue: config.defaultValue, tooltip: config.tooltip, isRequired: config.isRequired, isReadOnly: config.isReadOnly, isHidden: config.isHidden, maxLength: config.maxLength, options: config.options, font: config.font, border: config.border, margin: config.margin, bindingType: config.bindingType, bindingPath: config.bindingPath });
362
+ const handle: XfaFieldHandle = { fieldId, name: config.name, fieldType: config.fieldType, pageIndex };
363
+ this.createdFields.set(fieldId, handle);
364
+ this.emit('field-added', handle);
365
+ return handle;
366
+ } catch (error) { this.emit('error', error); return null; }
367
+ }
368
+
369
+ // ===========================================================================
370
+ // Field Manipulation (from XfaCreationManager)
371
+ // ===========================================================================
372
+
373
+ async updateField(fieldId: string, updates: Partial<XfaFieldConfig>): Promise<boolean> {
374
+ try {
375
+ const field = this.createdFields.get(fieldId);
376
+ if (!field) throw new Error(`Field not found: ${fieldId}`);
377
+ const result = await this.document?.updateXfaField?.(field.name, updates);
378
+ this.emit('field-updated', { fieldId, updates });
379
+ return !!result;
380
+ } catch (error) { this.emit('error', error); return false; }
381
+ }
382
+
383
+ async removeField(fieldId: string): Promise<boolean> {
384
+ try {
385
+ const field = this.createdFields.get(fieldId);
386
+ if (!field) throw new Error(`Field not found: ${fieldId}`);
387
+ const result = await this.document?.removeXfaField?.(field.name);
388
+ if (result) { this.createdFields.delete(fieldId); this.emit('field-removed', { fieldId }); }
389
+ return !!result;
390
+ } catch (error) { this.emit('error', error); return false; }
391
+ }
392
+
393
+ async addFieldValidation(fieldName: string, validationType: XfaValidationType, options: { pattern?: string; message?: string; min?: number; max?: number; script?: string }): Promise<boolean> {
394
+ try {
395
+ const result = await this.document?.addXfaFieldValidation?.(fieldName, validationType, options);
396
+ this.emit('validation-added', { fieldName, validationType });
397
+ return !!result;
398
+ } catch (error) { this.emit('error', error); return false; }
399
+ }
400
+
401
+ // ===========================================================================
402
+ // Data Operations (from XfaCreationManager)
403
+ // ===========================================================================
404
+
405
+ async importXfaData(data: string, options: XfaDataOptions): Promise<boolean> {
406
+ try {
407
+ const result = await this.document?.importXfaData?.(data, options.format, { validate: options.validateOnImport });
408
+ this.emit('data-imported', { format: options.format });
409
+ return !!result;
410
+ } catch (error) { this.emit('error', error); return false; }
411
+ }
412
+
413
+ async exportXfaData(options: XfaDataOptions): Promise<string | null> {
414
+ try {
415
+ const result = await this.document?.exportXfaData?.(options.format, { includeEmpty: options.includeEmptyFields, includeCalculated: options.includeCalculatedFields });
416
+ this.emit('data-exported', { format: options.format });
417
+ return result ?? null;
418
+ } catch (error) { this.emit('error', error); return null; }
419
+ }
420
+
421
+ async exportAsXdp(): Promise<string | null> {
422
+ try { return await this.document?.exportXfaAsXdp?.() ?? null; }
423
+ catch (error) { this.emit('error', error); return null; }
424
+ }
425
+
426
+ async mergeXfaData(sourceData: string, options?: { overwrite?: boolean }): Promise<boolean> {
427
+ try {
428
+ const result = await this.document?.mergeXfaData?.(sourceData, options?.overwrite ?? false);
429
+ this.emit('data-merged');
430
+ return !!result;
431
+ } catch (error) { this.emit('error', error); return false; }
432
+ }
433
+
434
+ // ===========================================================================
435
+ // Script Operations (from XfaCreationManager)
436
+ // ===========================================================================
437
+
438
+ async addFieldScript(fieldName: string, script: XfaScriptConfig): Promise<boolean> {
439
+ try {
440
+ const result = await this.document?.addXfaFieldScript?.(fieldName, script.event, script.code, script.language, script.runAt);
441
+ this.emit('script-added', { fieldName, event: script.event });
442
+ return !!result;
443
+ } catch (error) { this.emit('error', error); return false; }
444
+ }
445
+
446
+ async addFormScript(script: XfaScriptConfig): Promise<boolean> {
447
+ try {
448
+ const result = await this.document?.addXfaFormScript?.(script.event, script.code, script.language, script.runAt);
449
+ this.emit('form-script-added', { event: script.event });
450
+ return !!result;
451
+ } catch (error) { this.emit('error', error); return false; }
452
+ }
453
+
454
+ async removeFieldScript(fieldName: string, event: string): Promise<boolean> {
455
+ try {
456
+ const result = await this.document?.removeXfaFieldScript?.(fieldName, event);
457
+ this.emit('script-removed', { fieldName, event });
458
+ return !!result;
459
+ } catch (error) { this.emit('error', error); return false; }
460
+ }
461
+
462
+ // ===========================================================================
463
+ // Utilities
464
+ // ===========================================================================
465
+
466
+ async validateForm(): Promise<{ valid: boolean; issues: string[] }> {
467
+ try { return await this.document?.validateXfaForm?.() ?? { valid: true, issues: [] }; }
468
+ catch (error) { this.emit('error', error); return { valid: false, issues: [error instanceof Error ? error.message : 'Unknown error'] }; }
469
+ }
470
+
471
+ getCreatedFields(): readonly XfaFieldHandle[] { return Array.from(this.createdFields.values()); }
472
+ hasForm(): boolean { return this.formCreated || this.hasXfa(); }
473
+
474
+ clearCache(): void { this.cache.clear(); }
475
+
476
+ getCacheStats(): Record<string, any> {
477
+ return { cacheSize: this.cache.size, maxCacheSize: this.maxCacheSize, entries: Array.from(this.cache.keys()) };
478
+ }
479
+
480
+ destroy(): void {
481
+ this.createdFields.clear();
482
+ this.formCreated = false;
483
+ this.currentFormId = null;
484
+ this.cache.clear();
485
+ this.removeAllListeners();
486
+ }
487
+
488
+ private updateCache(key: string, value: any): void {
489
+ this.cache.set(key, value);
490
+ if (this.cache.size > this.maxCacheSize) {
491
+ const firstKey = this.cache.keys().next().value;
492
+ if (firstKey !== undefined) this.cache.delete(firstKey);
493
+ }
494
+ }
495
+ }
496
+
497
+ /** @deprecated Use XfaManager instead */
498
+ export const XFAManager = XfaManager;
499
+
500
+ export default XfaManager;