@doccloak/core 0.11.0 → 0.12.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 (60) hide show
  1. package/CHANGELOG.md +148 -2
  2. package/README.md +252 -19
  3. package/dist/doc.d.ts +68 -1
  4. package/dist/doc.js +447 -62
  5. package/dist/dom/docx.d.ts +135 -7
  6. package/dist/dom/docx.js +752 -125
  7. package/dist/dom/drawingml.d.ts +59 -0
  8. package/dist/dom/drawingml.js +176 -0
  9. package/dist/dom/errors.d.ts +35 -0
  10. package/dist/dom/errors.js +24 -0
  11. package/dist/dom/index.d.ts +3 -0
  12. package/dist/dom/index.js +6 -0
  13. package/dist/dom/office.d.ts +56 -0
  14. package/dist/dom/office.js +76 -23
  15. package/dist/dom/opc.d.ts +135 -7
  16. package/dist/dom/opc.js +407 -14
  17. package/dist/dom/package-policy.d.ts +137 -0
  18. package/dist/dom/package-policy.js +355 -0
  19. package/dist/dom/xlsx.d.ts +64 -6
  20. package/dist/dom/xlsx.js +130 -20
  21. package/dist/engine.d.ts +18 -0
  22. package/dist/engine.js +37 -13
  23. package/dist/env.d.ts +21 -1
  24. package/dist/index.d.ts +8 -6
  25. package/dist/index.js +10 -5
  26. package/dist/model-loader.d.ts +61 -6
  27. package/dist/model-loader.js +122 -17
  28. package/dist/pipeline.d.ts +87 -8
  29. package/dist/pipeline.js +249 -50
  30. package/dist/providers/bardsai.d.ts +8 -1
  31. package/dist/providers/bardsai.js +22 -2
  32. package/dist/providers/gliner-base.d.ts +8 -1
  33. package/dist/providers/gliner-base.js +22 -2
  34. package/dist/providers/gliner.d.ts +8 -1
  35. package/dist/providers/gliner.js +22 -2
  36. package/dist/regex/loader.d.ts +2 -0
  37. package/dist/regex/loader.js +2 -0
  38. package/dist/regex/rules.data.js +15 -14
  39. package/dist/regex/types.d.ts +6 -0
  40. package/dist/session.d.ts +101 -3
  41. package/dist/session.js +397 -43
  42. package/dist/surrogates.js +57 -45
  43. package/dist/worker-protocol.d.ts +13 -1
  44. package/dist/worker-protocol.js +164 -12
  45. package/package.json +1 -1
  46. package/rules/at.json +1 -1
  47. package/rules/be.json +1 -1
  48. package/rules/ch.json +1 -1
  49. package/rules/de.json +1 -1
  50. package/rules/dk.json +1 -1
  51. package/rules/es.json +1 -1
  52. package/rules/fi.json +1 -1
  53. package/rules/fr.json +1 -1
  54. package/rules/it.json +1 -1
  55. package/rules/no.json +1 -1
  56. package/rules/pl.json +1 -1
  57. package/rules/pt.json +1 -1
  58. package/rules/schema.json +4 -0
  59. package/rules/se.json +1 -1
  60. package/rules/universal.json +2 -1
@@ -9,25 +9,43 @@
9
9
  import JSZip from 'jszip';
10
10
  import { normalizeReplacements } from '../docx.js';
11
11
  import type { ValueReplacement } from '../docx.js';
12
+ import type { UnredactablePart } from './errors.js';
12
13
  export { normalizeReplacements };
13
14
  export type { ValueReplacement };
14
15
  /**
15
- * Represents a text node in the docx XML with its position in the flat text.
16
+ * Represents one unit of document text with its position in the flat text.
16
17
  * Also reused by the xlsx module (T110): the same flat-text replacement
17
18
  * machinery applies to any XML part whose text lives in leaf elements.
19
+ *
20
+ * Two variants (T174):
21
+ * - element text: the unit is `element.textContent` (a w:t, w:instrText,
22
+ * m:t, ...); `attr` is unset.
23
+ * - attribute text: the unit is a slice of `attr.value` on `element`,
24
+ * starting at `attrOffset` (0 unless the attribute holds several units, as
25
+ * a field instruction with several arguments does). Values that live only
26
+ * in attributes (field arguments, alt text, bookmark names, content control
27
+ * aliases, numbering level text) reach the detector this way instead of
28
+ * depending on a known-value scrub.
18
29
  */
19
30
  export interface TextNodeMapping {
20
- /** The <w:t> element */
31
+ /** The element that carries the text (or the attribute) */
21
32
  element: Element;
22
33
  /** Start index in the flat text */
23
34
  flatStart: number;
24
35
  /** End index in the flat text */
25
36
  flatEnd: number;
37
+ /** When set, the unit is a slice of this attribute's value, not element text */
38
+ attr?: Attr;
39
+ /** Offset of the unit inside attr.value (attribute variant only) */
40
+ attrOffset?: number;
26
41
  }
27
42
  /**
28
- * Tracks the boundary between paragraphs in the flat text (newline positions).
43
+ * Position of an unmapped newline in the flat text: a paragraph boundary, a
44
+ * text-box boundary, a w:br / w:cr line break or the separator between two
45
+ * content parts. No TextNodeMapping covers these indices, and a replacement
46
+ * never spans one (T172): an entity that contains a newline is split at it.
29
47
  */
30
- interface ParagraphBreak {
48
+ export interface ParagraphBreak {
31
49
  flatIndex: number;
32
50
  }
33
51
  /**
@@ -48,6 +66,19 @@ export interface DocxExtraction {
48
66
  documentXmlPath: string;
49
67
  /** All content XML paths (document, headers, footers) and their extractions */
50
68
  contentParts: ContentPartExtraction[];
69
+ /**
70
+ * True when no text-bearing content was found (no paragraphs, or only
71
+ * empty ones). Hosts show "nothing to redact" instead of exporting (T172).
72
+ */
73
+ empty: boolean;
74
+ /**
75
+ * Parts the writer cannot redact (embedded objects, macros, HTML chunks,
76
+ * printer settings, unknown parts), from the package policy (T177). The
77
+ * writer refuses to export them unless allowUnredactable is passed.
78
+ */
79
+ unredactable: UnredactablePart[];
80
+ /** Read-time notes: secondary parts skipped because they did not parse. */
81
+ warnings: string[];
51
82
  }
52
83
  interface ContentPartExtraction {
53
84
  path: string;
@@ -57,10 +88,38 @@ interface ContentPartExtraction {
57
88
  flatTextStart: number;
58
89
  flatTextEnd: number;
59
90
  }
91
+ /** True for a WordprocessingML element (transitional or strict namespace). */
92
+ export declare function isWordEl(el: Element, localName: string): boolean;
93
+ /** True for an Office Math (OMML) element (transitional or strict namespace). */
94
+ export declare function isMathEl(el: Element, localName: string): boolean;
95
+ /**
96
+ * The redactable arguments of a field instruction (w:fldSimple@w:instr or
97
+ * the joined w:instrText of a complex field): the content of every quoted
98
+ * string and every bare token that is neither the field keyword (the first
99
+ * token) nor a switch (a token starting with a backslash). Offsets refer to
100
+ * the instruction string. ` HYPERLINK "mailto:a@b" \o "Mail" ` yields
101
+ * "mailto:a@b" and "Mail"; ` REF Bookmark1 \h ` yields "Bookmark1". The
102
+ * keyword is never an argument, so a replacement can never turn a HYPERLINK
103
+ * into something Word cannot evaluate (T174).
104
+ */
105
+ export declare function fieldInstructionArguments(instr: string): Array<{
106
+ offset: number;
107
+ text: string;
108
+ }>;
109
+ /** Options for readDocx (T177). */
110
+ export interface DocxReadOptions {
111
+ /**
112
+ * Limit on the sum of the declared uncompressed sizes of all zip entries
113
+ * (zip-bomb guard, L4). Above it the reader throws
114
+ * UnsupportedDocumentError('too-large') before parsing any part.
115
+ * Defaults to MAX_UNPACKED_BYTES (200 MB).
116
+ */
117
+ maxUnpackedBytes?: number;
118
+ }
60
119
  /**
61
120
  * Read a .docx file and extract its text content with position mapping.
62
121
  */
63
- export declare function readDocx(file: File): Promise<DocxExtraction>;
122
+ export declare function readDocx(file: File, options?: DocxReadOptions): Promise<DocxExtraction>;
64
123
  /**
65
124
  * Options for writeAnonymizedDocx (T110). All default to off, preserving the
66
125
  * historical output byte-for-byte for existing callers.
@@ -74,31 +133,100 @@ export interface DocxWriteOptions {
74
133
  * identifying, so the file-redaction flow turns this on.
75
134
  */
76
135
  acceptTrackedChanges?: boolean;
136
+ /**
137
+ * Export even though extraction.unredactable is non-empty (T177). The
138
+ * listed parts (embedded workbooks, OLE objects, macros, HTML chunks,
139
+ * printer settings, unknown parts) are copied verbatim and named in
140
+ * result.warnings. Default false: the writer throws
141
+ * UnsupportedDocumentError('unredactable-parts') with the part names in
142
+ * `details` before touching the extraction, so a host can ask the user
143
+ * and call again with the same extraction. Hosts that never ask keep
144
+ * fail-closed behaviour.
145
+ */
146
+ allowUnredactable?: boolean;
77
147
  }
148
+ /**
149
+ * Refuse to export while unredactable parts are present and the host did
150
+ * not opt in. Shared by the docx and xlsx writers; runs before any mutation.
151
+ */
152
+ export declare function assertUnredactableAllowed(unredactable: ReadonlyArray<UnredactablePart>, allowUnredactable: boolean | undefined): void;
153
+ /** Warning line per unredactable part copied verbatim (allowUnredactable). */
154
+ export declare function unredactableWarnings(unredactable: ReadonlyArray<UnredactablePart>): string[];
78
155
  /**
79
156
  * Apply a list of flat-text replacements to mapped text nodes, handling
80
157
  * replacements that span multiple elements. Ranges are normalized (sorted,
81
158
  * overlaps clamped) and applied end-to-start so earlier positions stay valid.
82
159
  * Shared by the docx and xlsx writers (T110).
160
+ *
161
+ * `breaks` (T172) lists the flat indices of unmapped newlines (paragraph and
162
+ * text-box boundaries, line breaks, part separators). A replacement never
163
+ * crosses one: a range that contains a break is split at it, the first piece
164
+ * receives the replacement and the following pieces are emptied, so text is
165
+ * never moved from one paragraph (or text box) into another. Restoring the
166
+ * placeholder then yields the original value once, not once per piece.
83
167
  */
84
168
  export declare function applyTextReplacements(textNodes: TextNodeMapping[], replacements: Array<{
85
169
  start: number;
86
170
  end: number;
87
171
  replacement: string;
88
- }>): void;
172
+ }>, breaks?: ReadonlyArray<number>): void;
173
+ /** Result of writeAnonymizedDocxWithReport (T172). */
174
+ export interface DocxWriteResult {
175
+ /** The redacted .docx */
176
+ blob: Blob;
177
+ /**
178
+ * Human-readable notes about parts the layer-zero scrub could not parse and
179
+ * therefore left untouched. Empty on a clean run.
180
+ */
181
+ warnings: string[];
182
+ }
89
183
  /**
90
184
  * Apply text replacements to the docx XML, preserving all formatting.
91
185
  * Takes the original extraction and a list of replacements (sorted by position),
92
186
  * and modifies the XML in-place. Also scrubs document metadata and relationship
93
187
  * targets that can leak identities (authors, revision names, hyperlink emails).
94
188
  *
95
- * Returns a new .docx file as a Blob.
189
+ * Returns a new .docx file as a Blob. writeAnonymizedDocxWithReport returns
190
+ * the same Blob together with the layer-zero warnings.
96
191
  */
97
192
  export declare function writeAnonymizedDocx(extraction: DocxExtraction, replacements: Array<{
98
193
  start: number;
99
194
  end: number;
100
195
  replacement: string;
101
196
  }>, valueReplacements?: ValueReplacement[], options?: DocxWriteOptions): Promise<Blob>;
197
+ /**
198
+ * Same as writeAnonymizedDocx, with the write report. Pipeline (T172, T174):
199
+ * 1. offset replacements in the mapped text and attribute units (never
200
+ * across a break);
201
+ * 2. bookmark rename: a bookmark whose name changed gets a valid identifier,
202
+ * and every reference to it (w:hyperlink@w:anchor, REF / PAGEREF /
203
+ * NOTEREF / HYPERLINK \l instructions) is updated across all parts;
204
+ * 3. per content part: attribute scrub (authors, field arguments, alt
205
+ * text), reference update, always-scrub group (tooltips, content control
206
+ * tags without a data binding), optional tracked-change acceptance,
207
+ * serialize;
208
+ * 4. metadata scrub (docProps, people, settings incl. docVars, rels incl.
209
+ * the mailto rule, thumbnail);
210
+ * 5. layer zero: every known value scrubbed from every other XML/rels/vml
211
+ * part in the package (charts, diagrams, customXml, docVars, glossary,
212
+ * docProps), so a value the extractors never saw cannot leave in a part
213
+ * they do not read;
214
+ * 6. container normalisation (T179): every zip entry dated 1980-01-01 with
215
+ * an empty comment, empty archive comment; then zip generation.
216
+ */
217
+ export declare function writeAnonymizedDocxWithReport(extraction: DocxExtraction, replacements: Array<{
218
+ start: number;
219
+ end: number;
220
+ replacement: string;
221
+ }>, valueReplacements?: ValueReplacement[], options?: DocxWriteOptions): Promise<DocxWriteResult>;
222
+ /**
223
+ * Turn a replacement into a valid Word bookmark identifier: ASCII letters,
224
+ * digits and underscores, starting with a letter, at most 40 characters,
225
+ * unique among the names in `used` (which it extends). '[PERSON_1]' becomes
226
+ * 'PERSON_1'; a replacement that starts with a digit or underscore gets the
227
+ * 'bm_' prefix.
228
+ */
229
+ export declare function bookmarkIdentifier(replacement: string, used?: Set<string>): string;
102
230
  /**
103
231
  * Get the file extension from a filename.
104
232
  */