@adeu/core 1.23.0 → 1.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +185 -53
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +185 -53
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/diff.ts +23 -8
- package/src/engine.ts +132 -39
- package/src/repro_qa_report_v6.test.ts +486 -0
- package/src/sanitize/core.ts +57 -1
- package/src/sanitize/report.ts +5 -3
- package/src/sanitize/sanitize.test.ts +5 -4
- package/src/sanitize/transforms.ts +94 -16
package/dist/index.js
CHANGED
|
@@ -2787,7 +2787,8 @@ function _row_ops_for_table(table_o, table_m, opcodes, warnings) {
|
|
|
2787
2787
|
type: "modify",
|
|
2788
2788
|
target_text: o_txt,
|
|
2789
2789
|
new_text: m_txt,
|
|
2790
|
-
comment: "Diff: Table row modified"
|
|
2790
|
+
comment: "Diff: Table row modified",
|
|
2791
|
+
_is_table_edit: true
|
|
2791
2792
|
});
|
|
2792
2793
|
}
|
|
2793
2794
|
}
|
|
@@ -2889,14 +2890,18 @@ function generate_structured_edits(text_orig, struct_orig, text_mod, struct_mod)
|
|
|
2889
2890
|
if (row_opcodes !== null) {
|
|
2890
2891
|
edits.push(..._row_ops_for_table(t_o, t_m, row_opcodes, warnings));
|
|
2891
2892
|
} else {
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2893
|
+
for (let k = 0; k < t_o.rows.length; k++) {
|
|
2894
|
+
const o_txt = t_o.rows[k].cells.join(" | ");
|
|
2895
|
+
const m_txt = t_m.rows[k].cells.join(" | ");
|
|
2896
|
+
if (o_txt === m_txt) continue;
|
|
2897
|
+
edits.push({
|
|
2898
|
+
type: "modify",
|
|
2899
|
+
target_text: o_txt,
|
|
2900
|
+
new_text: m_txt,
|
|
2901
|
+
comment: "Diff: Table row modified",
|
|
2902
|
+
_is_table_edit: true
|
|
2903
|
+
});
|
|
2898
2904
|
}
|
|
2899
|
-
edits.push(...tbl_edits);
|
|
2900
2905
|
}
|
|
2901
2906
|
}
|
|
2902
2907
|
}
|
|
@@ -2914,7 +2919,7 @@ function generate_structured_edits(text_orig, struct_orig, text_mod, struct_mod)
|
|
|
2914
2919
|
};
|
|
2915
2920
|
let ambiguous_anchor_warned = false;
|
|
2916
2921
|
for (const e of edits) {
|
|
2917
|
-
if ((e.type === "insert_row" || e.type === "delete_row") && !ambiguous_anchor_warned) {
|
|
2922
|
+
if ((e.type === "insert_row" || e.type === "delete_row" || e._is_table_edit) && !ambiguous_anchor_warned) {
|
|
2918
2923
|
if (e.target_text && countOccurrences(text_orig, e.target_text) > 1) {
|
|
2919
2924
|
warnings.push(
|
|
2920
2925
|
`The row anchor "${e.target_text.substring(0, 60)}" appears more than once in the document. Applying this diff from its JSON output may be rejected as ambiguous \u2014 make the anchor rows unique, or apply the row changes with explicit insert_row/delete_row edits.`
|
|
@@ -4666,7 +4671,7 @@ var RedlineEngine = class _RedlineEngine {
|
|
|
4666
4671
|
*
|
|
4667
4672
|
* Does NOT attach comments; callers handle that.
|
|
4668
4673
|
*/
|
|
4669
|
-
_track_insert_multiline(text, anchor_run, anchor_paragraph, reuse_id) {
|
|
4674
|
+
_track_insert_multiline(text, anchor_run, anchor_paragraph, reuse_id, positional_anchor_el = null) {
|
|
4670
4675
|
if (!text) {
|
|
4671
4676
|
return {
|
|
4672
4677
|
first_node: null,
|
|
@@ -4687,7 +4692,29 @@ var RedlineEngine = class _RedlineEngine {
|
|
|
4687
4692
|
}
|
|
4688
4693
|
current_p = walker;
|
|
4689
4694
|
}
|
|
4690
|
-
|
|
4695
|
+
const suffix_nodes = [];
|
|
4696
|
+
const pos_source = positional_anchor_el && positional_anchor_el.parentNode ? positional_anchor_el : anchor_run !== null && anchor_run._element.parentNode ? anchor_run._element : null;
|
|
4697
|
+
if (current_p !== null && pos_source !== null) {
|
|
4698
|
+
let pos_anchor = pos_source;
|
|
4699
|
+
while (pos_anchor && pos_anchor.parentNode !== current_p) {
|
|
4700
|
+
pos_anchor = pos_anchor.parentNode;
|
|
4701
|
+
if (pos_anchor === current_p) {
|
|
4702
|
+
pos_anchor = null;
|
|
4703
|
+
break;
|
|
4704
|
+
}
|
|
4705
|
+
}
|
|
4706
|
+
if (pos_anchor) {
|
|
4707
|
+
const relocatable = /* @__PURE__ */ new Set(["w:r", "w:ins", "w:del"]);
|
|
4708
|
+
let nxt = pos_anchor.nextSibling;
|
|
4709
|
+
while (nxt) {
|
|
4710
|
+
if (nxt.nodeType === 1 && relocatable.has(nxt.tagName)) {
|
|
4711
|
+
suffix_nodes.push(nxt);
|
|
4712
|
+
}
|
|
4713
|
+
nxt = nxt.nextSibling;
|
|
4714
|
+
}
|
|
4715
|
+
}
|
|
4716
|
+
}
|
|
4717
|
+
while (lines.length > 1 && lines[lines.length - 1] === "" && suffix_nodes.length === 0) {
|
|
4691
4718
|
lines.pop();
|
|
4692
4719
|
}
|
|
4693
4720
|
if (lines.length === 0) {
|
|
@@ -4785,6 +4812,12 @@ var RedlineEngine = class _RedlineEngine {
|
|
|
4785
4812
|
first_node = new_p;
|
|
4786
4813
|
}
|
|
4787
4814
|
}
|
|
4815
|
+
if (!block_mode && last_p && suffix_nodes.length > 0) {
|
|
4816
|
+
for (const node of suffix_nodes) {
|
|
4817
|
+
node.parentNode?.removeChild(node);
|
|
4818
|
+
last_p.appendChild(node);
|
|
4819
|
+
}
|
|
4820
|
+
}
|
|
4788
4821
|
return { first_node, last_p, last_ins, used_block_mode: block_mode };
|
|
4789
4822
|
}
|
|
4790
4823
|
/**
|
|
@@ -4842,7 +4875,7 @@ var RedlineEngine = class _RedlineEngine {
|
|
|
4842
4875
|
if (stripped_text.startsWith("* ") || stripped_text.startsWith("- ")) {
|
|
4843
4876
|
return [stripped_text.substring(2).trim(), "List Paragraph"];
|
|
4844
4877
|
}
|
|
4845
|
-
const match = stripped_text.match(
|
|
4878
|
+
const match = stripped_text.match(/^1\.\s+/);
|
|
4846
4879
|
if (match) {
|
|
4847
4880
|
return [stripped_text.substring(match[0].length).trim(), "List Number"];
|
|
4848
4881
|
}
|
|
@@ -5724,14 +5757,17 @@ var RedlineEngine = class _RedlineEngine {
|
|
|
5724
5757
|
resolved_edits.push([edit, edit.new_text || null]);
|
|
5725
5758
|
} else if (edit.type === "insert_row" || edit.type === "delete_row") {
|
|
5726
5759
|
let matches = this.mapper.find_all_match_indices(edit.target_text);
|
|
5760
|
+
let resolved_mapper = this.mapper;
|
|
5727
5761
|
if (matches.length === 0) {
|
|
5728
5762
|
if (!this.clean_mapper) {
|
|
5729
5763
|
this.clean_mapper = new DocumentMapper(this.doc, true);
|
|
5730
5764
|
}
|
|
5731
5765
|
matches = this.clean_mapper.find_all_match_indices(edit.target_text);
|
|
5766
|
+
resolved_mapper = this.clean_mapper;
|
|
5732
5767
|
}
|
|
5733
5768
|
if (matches.length > 0) {
|
|
5734
5769
|
edit._resolved_start_idx = matches[0][0];
|
|
5770
|
+
edit._active_mapper_ref = resolved_mapper;
|
|
5735
5771
|
resolved_edits.push([edit, null]);
|
|
5736
5772
|
} else {
|
|
5737
5773
|
skipped++;
|
|
@@ -5980,7 +6016,8 @@ var RedlineEngine = class _RedlineEngine {
|
|
|
5980
6016
|
}
|
|
5981
6017
|
_apply_table_edit(edit, rebuild_map) {
|
|
5982
6018
|
const start_idx = edit._resolved_start_idx !== void 0 && edit._resolved_start_idx !== null ? edit._resolved_start_idx : edit._match_start_index || 0;
|
|
5983
|
-
const
|
|
6019
|
+
const active_mapper = edit._active_mapper_ref || this.mapper;
|
|
6020
|
+
const [anchor_run, anchor_para] = active_mapper.get_insertion_anchor(
|
|
5984
6021
|
start_idx,
|
|
5985
6022
|
rebuild_map
|
|
5986
6023
|
);
|
|
@@ -6161,9 +6198,14 @@ var RedlineEngine = class _RedlineEngine {
|
|
|
6161
6198
|
if (overlaps_virtual_pipe) {
|
|
6162
6199
|
const actual_cells = actual_doc_text.split("|");
|
|
6163
6200
|
const new_cells = current_effective_new_text.split("|");
|
|
6164
|
-
if (actual_cells.length
|
|
6201
|
+
if (actual_cells.length !== new_cells.length) {
|
|
6202
|
+
throw new BatchValidationError([
|
|
6203
|
+
`Target text spans ${actual_cells.length} table cells, but replacement provides ${new_cells.length}. To modify text without altering table structure (rows or columns), ensure the replacement contains the exact same number of '|' separators (e.g., replace with 'CellC | ' to empty the second cell).`
|
|
6204
|
+
]);
|
|
6205
|
+
}
|
|
6206
|
+
if (actual_cells.length > 1) {
|
|
6165
6207
|
const sub_edits2 = [];
|
|
6166
|
-
let
|
|
6208
|
+
let cell_start_in_target = 0;
|
|
6167
6209
|
let target_comment_idx = 0;
|
|
6168
6210
|
for (let idx = 0; idx < actual_cells.length; idx++) {
|
|
6169
6211
|
if (actual_cells[idx].trim() !== new_cells[idx].trim()) {
|
|
@@ -6176,13 +6218,7 @@ var RedlineEngine = class _RedlineEngine {
|
|
|
6176
6218
|
const n_cell = new_cells[cell_idx];
|
|
6177
6219
|
const a_clean = a_cell.trim();
|
|
6178
6220
|
const n_clean = n_cell.trim();
|
|
6179
|
-
|
|
6180
|
-
if (a_clean) {
|
|
6181
|
-
actual_start = this.mapper.full_text.indexOf(a_clean, search_offset);
|
|
6182
|
-
if (actual_start === -1 || actual_start > search_offset + 10) {
|
|
6183
|
-
actual_start = search_offset;
|
|
6184
|
-
}
|
|
6185
|
-
}
|
|
6221
|
+
const actual_start = start_idx + cell_start_in_target + (a_clean ? a_cell.indexOf(a_clean) : 0);
|
|
6186
6222
|
const should_attach_comment = edit.comment !== null && edit.comment !== void 0 && cell_idx === target_comment_idx;
|
|
6187
6223
|
if (a_clean !== n_clean || should_attach_comment) {
|
|
6188
6224
|
const cell_sub_edits = this._word_diff_sub_edits(
|
|
@@ -6199,24 +6235,12 @@ var RedlineEngine = class _RedlineEngine {
|
|
|
6199
6235
|
sub_edits2.push(se);
|
|
6200
6236
|
}
|
|
6201
6237
|
}
|
|
6202
|
-
|
|
6203
|
-
search_offset = actual_start + a_clean.length;
|
|
6204
|
-
}
|
|
6205
|
-
const next_pipe = this.mapper.full_text.indexOf(" | ", search_offset);
|
|
6206
|
-
if (next_pipe !== -1 && next_pipe <= search_offset + 10) {
|
|
6207
|
-
search_offset = next_pipe + 3;
|
|
6208
|
-
} else {
|
|
6209
|
-
search_offset += a_cell.length + 1;
|
|
6210
|
-
}
|
|
6238
|
+
cell_start_in_target += a_cell.length + 1;
|
|
6211
6239
|
}
|
|
6212
6240
|
for (const sub of sub_edits2) {
|
|
6213
6241
|
all_sub_edits.push(sub);
|
|
6214
6242
|
}
|
|
6215
6243
|
continue;
|
|
6216
|
-
} else {
|
|
6217
|
-
throw new BatchValidationError([
|
|
6218
|
-
`Target text spans ${actual_cells.length} table cells, but replacement provides ${new_cells.length}. To modify text without altering table structure (rows or columns), ensure the replacement contains the exact same number of '|' separators (e.g., replace with 'CellC | ' to empty the second cell).`
|
|
6219
|
-
]);
|
|
6220
6244
|
}
|
|
6221
6245
|
}
|
|
6222
6246
|
let has_markdown = false;
|
|
@@ -6310,6 +6334,19 @@ var RedlineEngine = class _RedlineEngine {
|
|
|
6310
6334
|
continue;
|
|
6311
6335
|
}
|
|
6312
6336
|
}
|
|
6337
|
+
if (!final_target && final_new) {
|
|
6338
|
+
all_sub_edits.push({
|
|
6339
|
+
type: "modify",
|
|
6340
|
+
target_text: "",
|
|
6341
|
+
new_text: final_new,
|
|
6342
|
+
comment: edit.comment,
|
|
6343
|
+
_resolved_start_idx: effective_start_idx,
|
|
6344
|
+
_match_start_index: effective_start_idx,
|
|
6345
|
+
_internal_op: "INSERTION",
|
|
6346
|
+
_active_mapper_ref: active_mapper
|
|
6347
|
+
});
|
|
6348
|
+
continue;
|
|
6349
|
+
}
|
|
6313
6350
|
const sub_edits = this._word_diff_sub_edits(
|
|
6314
6351
|
actual_doc_text,
|
|
6315
6352
|
current_effective_new_text,
|
|
@@ -6751,7 +6788,10 @@ var RedlineEngine = class _RedlineEngine {
|
|
|
6751
6788
|
edit.new_text,
|
|
6752
6789
|
style_source_run,
|
|
6753
6790
|
mod_anchor_para,
|
|
6754
|
-
ins_id
|
|
6791
|
+
ins_id,
|
|
6792
|
+
// The insertion physically follows the deletion block; the style
|
|
6793
|
+
// run was detached when the deletion cloned it into <w:del>.
|
|
6794
|
+
last_del
|
|
6755
6795
|
);
|
|
6756
6796
|
if (result.first_node) {
|
|
6757
6797
|
const is_inline_first = result.first_node.tagName === "w:ins";
|
|
@@ -7298,7 +7338,10 @@ function scrub_doc_properties(doc) {
|
|
|
7298
7338
|
["keywords", "Keywords"],
|
|
7299
7339
|
["subject", "Subject"],
|
|
7300
7340
|
["contentStatus", "Content status"],
|
|
7301
|
-
["description", "Description/comments"]
|
|
7341
|
+
["description", "Description/comments"],
|
|
7342
|
+
["identifier", "Identifier"],
|
|
7343
|
+
["language", "Language"],
|
|
7344
|
+
["version", "Version"]
|
|
7302
7345
|
];
|
|
7303
7346
|
for (const [local, label] of leakFields) {
|
|
7304
7347
|
findDescendantsByLocalName(corePart._element, local).forEach((c) => {
|
|
@@ -7356,27 +7399,73 @@ function scrub_timestamps(doc) {
|
|
|
7356
7399
|
}
|
|
7357
7400
|
return modified ? ["Timestamps normalized to epoch"] : [];
|
|
7358
7401
|
}
|
|
7359
|
-
function
|
|
7360
|
-
const
|
|
7361
|
-
|
|
7362
|
-
const
|
|
7363
|
-
|
|
7364
|
-
const removeRelationsTo = (relsPart) => {
|
|
7402
|
+
function ejectPackageMembers(doc, matcher, relTargetMatcher) {
|
|
7403
|
+
const pkg = doc.pkg;
|
|
7404
|
+
const normalized = (p) => p.startsWith("/") ? p.substring(1) : p;
|
|
7405
|
+
for (const part of pkg.parts) {
|
|
7406
|
+
if (!part.partname.endsWith(".rels")) continue;
|
|
7365
7407
|
const toRemove = [];
|
|
7366
|
-
for (const rel of findAllDescendants(
|
|
7367
|
-
const target = rel.getAttribute("Target");
|
|
7368
|
-
if (target
|
|
7408
|
+
for (const rel of findAllDescendants(part._element, "Relationship")) {
|
|
7409
|
+
const target = rel.getAttribute("Target") || "";
|
|
7410
|
+
if (relTargetMatcher(target)) {
|
|
7411
|
+
toRemove.push(rel);
|
|
7412
|
+
const sourcePath = part.partname.replace("/_rels/", "/").replace(".rels", "");
|
|
7413
|
+
const sourcePart = pkg.getPartByPath(sourcePath);
|
|
7414
|
+
if (sourcePart) {
|
|
7415
|
+
const relId = rel.getAttribute("Id");
|
|
7416
|
+
if (relId) sourcePart.rels.delete(relId);
|
|
7417
|
+
}
|
|
7418
|
+
}
|
|
7369
7419
|
}
|
|
7370
7420
|
toRemove.forEach((r) => r.parentNode?.removeChild(r));
|
|
7371
|
-
}
|
|
7372
|
-
const
|
|
7373
|
-
if (
|
|
7374
|
-
|
|
7375
|
-
|
|
7421
|
+
}
|
|
7422
|
+
const ctPart = pkg.getPartByPath("[Content_Types].xml");
|
|
7423
|
+
if (ctPart) {
|
|
7424
|
+
const toRemove = [];
|
|
7425
|
+
for (const override of findAllDescendants(ctPart._element, "Override")) {
|
|
7426
|
+
const partName = override.getAttribute("PartName") || "";
|
|
7427
|
+
if (matcher(normalized(partName))) toRemove.push(override);
|
|
7428
|
+
}
|
|
7429
|
+
toRemove.forEach((o) => o.parentNode?.removeChild(o));
|
|
7430
|
+
}
|
|
7431
|
+
pkg.parts = pkg.parts.filter((p) => !matcher(normalized(p.partname)));
|
|
7432
|
+
for (const key of Object.keys(pkg.unzipped)) {
|
|
7433
|
+
if (matcher(normalized(key))) delete pkg.unzipped[key];
|
|
7434
|
+
}
|
|
7435
|
+
}
|
|
7436
|
+
function strip_custom_xml(doc) {
|
|
7437
|
+
const isCustomXml = (path) => path.startsWith("customXml/");
|
|
7438
|
+
const customParts = doc.pkg.parts.filter(
|
|
7439
|
+
(p) => isCustomXml(p.partname.startsWith("/") ? p.partname.substring(1) : p.partname)
|
|
7440
|
+
);
|
|
7441
|
+
const leakedMembers = Object.keys(doc.pkg.unzipped).filter(isCustomXml);
|
|
7442
|
+
if (customParts.length === 0 && leakedMembers.length === 0) return [];
|
|
7443
|
+
ejectPackageMembers(doc, isCustomXml, (target) => target.includes("customXml"));
|
|
7376
7444
|
for (const sdtPr of findAllDescendants(doc.element, "w:sdtPr")) {
|
|
7377
7445
|
findChildren(sdtPr, "w:dataBinding").forEach((b) => sdtPr.removeChild(b));
|
|
7378
7446
|
}
|
|
7379
|
-
return [`Custom XML parts: ${customParts.length} removed`];
|
|
7447
|
+
return [`Custom XML parts: ${Math.max(customParts.length, leakedMembers.length)} removed`];
|
|
7448
|
+
}
|
|
7449
|
+
var CUSTOM_PROPS_PATH = "docProps/custom.xml";
|
|
7450
|
+
function strip_custom_properties(doc) {
|
|
7451
|
+
const part = doc.pkg.getPartByPath(CUSTOM_PROPS_PATH);
|
|
7452
|
+
const leaked = Object.keys(doc.pkg.unzipped).includes(CUSTOM_PROPS_PATH);
|
|
7453
|
+
if (!part && !leaked) return [];
|
|
7454
|
+
const lines = [];
|
|
7455
|
+
if (part) {
|
|
7456
|
+
for (const prop of findDescendantsByLocalName(part._element, "property")) {
|
|
7457
|
+
const name = prop.getAttribute("name") || "(unnamed)";
|
|
7458
|
+
const value = (prop.textContent || "").trim();
|
|
7459
|
+
lines.push(` Custom property removed: ${name} = "${_truncate(value, 60)}"`);
|
|
7460
|
+
}
|
|
7461
|
+
}
|
|
7462
|
+
ejectPackageMembers(
|
|
7463
|
+
doc,
|
|
7464
|
+
(path) => path === CUSTOM_PROPS_PATH,
|
|
7465
|
+
(target) => target.includes("docProps/custom.xml")
|
|
7466
|
+
);
|
|
7467
|
+
const count = Math.max(lines.length, 1);
|
|
7468
|
+
return [`Custom document properties: ${count} removed (docProps/custom.xml)`].concat(lines);
|
|
7380
7469
|
}
|
|
7381
7470
|
function strip_image_alt_text(doc) {
|
|
7382
7471
|
let count = 0;
|
|
@@ -8730,6 +8819,9 @@ function _collect_footnote_ids_fast(owned_items) {
|
|
|
8730
8819
|
return ordered;
|
|
8731
8820
|
}
|
|
8732
8821
|
|
|
8822
|
+
// src/sanitize/core.ts
|
|
8823
|
+
import { strFromU8 as strFromU82, unzipSync as unzipSync2 } from "fflate";
|
|
8824
|
+
|
|
8733
8825
|
// src/sanitize/report.ts
|
|
8734
8826
|
var SanitizeReport = class {
|
|
8735
8827
|
filename;
|
|
@@ -8764,7 +8856,7 @@ var SanitizeReport = class {
|
|
|
8764
8856
|
} else {
|
|
8765
8857
|
this.removed_comment_lines.push(line);
|
|
8766
8858
|
}
|
|
8767
|
-
} else if (lower.includes("author") || lower.includes("template") || lower.includes("company") || lower.includes("manager") || lower.includes("metadata") || lower.includes("timestamp") || lower.includes("custom xml") || lower.includes("last modified by") || lower.includes("revision count") || lower.includes("last printed")) {
|
|
8859
|
+
} else if (lower.includes("author") || lower.includes("template") || lower.includes("company") || lower.includes("manager") || lower.includes("metadata") || lower.includes("timestamp") || lower.includes("custom xml") || lower.includes("custom propert") || lower.includes("identifier") || lower.includes("language") || lower.includes("version") || lower.includes("last modified by") || lower.includes("revision count") || lower.includes("last printed")) {
|
|
8768
8860
|
this.metadata_lines.push(line);
|
|
8769
8861
|
} else if (lower.includes("hyperlink") || lower.includes("warning")) {
|
|
8770
8862
|
this.warnings.push(line);
|
|
@@ -8877,6 +8969,7 @@ async function finalize_document(doc, options) {
|
|
|
8877
8969
|
report.add_transform_lines(scrub_doc_properties(doc));
|
|
8878
8970
|
report.add_transform_lines(scrub_timestamps(doc));
|
|
8879
8971
|
report.add_transform_lines(strip_custom_xml(doc));
|
|
8972
|
+
report.add_transform_lines(strip_custom_properties(doc));
|
|
8880
8973
|
report.add_transform_lines(strip_image_alt_text(doc));
|
|
8881
8974
|
const warnings = audit_hyperlinks(doc);
|
|
8882
8975
|
for (const w of warnings) report.warnings.push(w);
|
|
@@ -8924,8 +9017,47 @@ async function finalize_document(doc, options) {
|
|
|
8924
9017
|
}
|
|
8925
9018
|
if (report.warnings.length > 0) report.status = "clean_with_warnings";
|
|
8926
9019
|
const outBuffer = await doc.save();
|
|
9020
|
+
verifySanitizedPackage(outBuffer);
|
|
8927
9021
|
return { reportText: report.render(), outBuffer };
|
|
8928
9022
|
}
|
|
9023
|
+
var VERIFIED_CORE_FIELDS = [
|
|
9024
|
+
["creator", "author (dc:creator)"],
|
|
9025
|
+
["lastModifiedBy", "last modified by (cp:lastModifiedBy)"],
|
|
9026
|
+
["identifier", "identifier (dc:identifier)"],
|
|
9027
|
+
["description", "description (dc:description)"],
|
|
9028
|
+
["keywords", "keywords (cp:keywords)"],
|
|
9029
|
+
["category", "category (cp:category)"],
|
|
9030
|
+
["subject", "subject (dc:subject)"],
|
|
9031
|
+
["contentStatus", "content status (cp:contentStatus)"],
|
|
9032
|
+
["language", "language (dc:language)"],
|
|
9033
|
+
["version", "version (cp:version)"]
|
|
9034
|
+
];
|
|
9035
|
+
function verifySanitizedPackage(outBuffer) {
|
|
9036
|
+
const unzipped = unzipSync2(new Uint8Array(outBuffer));
|
|
9037
|
+
const names = Object.keys(unzipped);
|
|
9038
|
+
const problems = [];
|
|
9039
|
+
if (names.includes("docProps/custom.xml")) {
|
|
9040
|
+
problems.push("docProps/custom.xml (custom document properties) is still in the package");
|
|
9041
|
+
}
|
|
9042
|
+
if (names.some((n) => n.startsWith("customXml/"))) {
|
|
9043
|
+
problems.push("customXml/* parts are still in the package");
|
|
9044
|
+
}
|
|
9045
|
+
if (names.includes("docProps/core.xml")) {
|
|
9046
|
+
const core = parseXml(strFromU82(unzipped["docProps/core.xml"]));
|
|
9047
|
+
for (const [local, label] of VERIFIED_CORE_FIELDS) {
|
|
9048
|
+
for (const el of findDescendantsByLocalName(core.documentElement, local)) {
|
|
9049
|
+
if ((el.textContent || "").trim()) {
|
|
9050
|
+
problems.push(`core property ${label} still contains a value`);
|
|
9051
|
+
}
|
|
9052
|
+
}
|
|
9053
|
+
}
|
|
9054
|
+
}
|
|
9055
|
+
if (problems.length > 0) {
|
|
9056
|
+
throw new Error(
|
|
9057
|
+
"Sanitize integrity check failed \u2014 the saved package still contains metadata this run claims to remove:\n - " + problems.join("\n - ") + "\nRefusing to report a clean document."
|
|
9058
|
+
);
|
|
9059
|
+
}
|
|
9060
|
+
}
|
|
8929
9061
|
|
|
8930
9062
|
// src/index.ts
|
|
8931
9063
|
function identifyEngine() {
|