@stll/folio-core 0.64.1 → 0.66.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.
Files changed (62) hide show
  1. package/dist/ai-edits/apply.js +31 -9
  2. package/dist/ai-edits/headless.js +3 -3
  3. package/dist/compare/reproducible-package.d.ts +5 -0
  4. package/dist/compare/reproducible-package.js +10 -23
  5. package/dist/docx/archiveInflation.d.ts +8 -103
  6. package/dist/docx/archiveInflation.js +8 -145
  7. package/dist/docx/attributeRemainder.js +1 -1
  8. package/dist/docx/canonicalStoryRepack.js +2 -2
  9. package/dist/docx/documentSectionFacts.js +1 -1
  10. package/dist/docx/ensureParaIds.js +9 -24
  11. package/dist/docx/importedIdCollisions.js +1 -1
  12. package/dist/docx/mathToMathml.js +1 -1
  13. package/dist/docx/metadataPrivacy.js +38 -48
  14. package/dist/docx/numericIdNormalization.js +2 -2
  15. package/dist/docx/packageParts.d.ts +4 -4
  16. package/dist/docx/packageParts.js +17 -24
  17. package/dist/docx/parser.js +5 -6
  18. package/dist/docx/removeHeaderFooterParts.d.ts +2 -2
  19. package/dist/docx/removeHeaderFooterParts.js +19 -36
  20. package/dist/docx/revisionIdNormalization.js +2 -2
  21. package/dist/docx/rezip.d.ts +7 -7
  22. package/dist/docx/rezip.js +144 -532
  23. package/dist/docx/sdtProperties.js +1 -2
  24. package/dist/docx/selectedXmlIdentityScan.d.ts +1 -1
  25. package/dist/docx/selectedXmlIdentityScan.js +1 -1
  26. package/dist/docx/selectiveSave.js +37 -41
  27. package/dist/docx/selectiveSaveFlags.d.ts +1 -1
  28. package/dist/docx/selectiveSaveTripwire.js +5 -11
  29. package/dist/docx/selectiveXmlPatch.js +2 -2
  30. package/dist/docx/server/applyDocxXmlPatchProposal.js +20 -41
  31. package/dist/docx/server/boundedArchive.d.ts +2 -3
  32. package/dist/docx/server/boundedArchive.js +78 -99
  33. package/dist/docx/server/inspectDocxPackage.d.ts +1 -1
  34. package/dist/docx/server/inspectDocxPackage.js +1 -1
  35. package/dist/docx/server/validateDocxConformance.js +1 -1
  36. package/dist/docx/shapeAlternateContent.js +1 -1
  37. package/dist/docx/storyBlockReplay.js +1 -1
  38. package/dist/docx/unzip.d.ts +2 -3
  39. package/dist/docx/unzip.js +127 -155
  40. package/dist/docx/verbatimCapture.js +1 -1
  41. package/dist/docx/xmlParser.d.ts +5 -36
  42. package/dist/docx/xmlParser.js +14 -78
  43. package/dist/docx/xmlSafety.js +5 -5
  44. package/dist/layout-painter/renderPage.js +4 -2
  45. package/dist/markdown/countParagraphList.d.ts +18 -0
  46. package/dist/markdown/countParagraphList.js +90 -0
  47. package/dist/markdown/renderParagraph.js +2 -62
  48. package/dist/markdown/renderRuns.js +4 -1
  49. package/dist/prosemirror/conversion/fromProseDoc.js +5 -3
  50. package/dist/prosemirror/conversion/toProseDoc.js +4 -1
  51. package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.js +4 -1
  52. package/dist/prosemirror/replacedAnnotations.d.ts +6 -0
  53. package/dist/prosemirror/replacedAnnotations.js +30 -8
  54. package/dist/prosemirror/schema/nodes.d.ts +7 -0
  55. package/dist/prosemirror/textBoxAnchorAttrs.js +9 -1
  56. package/package.json +3 -5
  57. package/dist/docx/streamingXmlParser.d.ts +0 -60
  58. package/dist/docx/streamingXmlParser.js +0 -406
  59. package/dist/docx/xmlNamespaceContext.d.ts +0 -6
  60. package/dist/docx/xmlNamespaceContext.js +0 -53
  61. package/dist/docx/xmlResourceLimits.d.ts +0 -107
  62. package/dist/docx/xmlResourceLimits.js +0 -194
@@ -38,15 +38,14 @@ import { serializeStyle, serializeStylesXml } from "./serializer/stylesSerialize
38
38
  import { serializeThemeXml } from "./serializer/themeSerializer.js";
39
39
  import { updateEvenAndOddHeaders } from "./settingsHeaderFooterUpdate.js";
40
40
  import { parseSettings } from "./settingsParser.js";
41
- import { getXmlSourceRange, parseStreamingXmlWithSourceRanges } from "./streamingXmlParser.js";
42
41
  import { captureVerbatimXml } from "./verbatimCapture.js";
43
42
  import { hasCanonicalWordprocessingPrefixes } from "./wordprocessingPrefixes.js";
44
43
  import { OFFICE_RELATIONSHIP_NAMESPACE_URIS, OOXML_NAMESPACE_SCOPE, WORDPROCESSINGML_NAMESPACE_URIS, findAttributeByNamespaceUri, findChild, findChildByNamespaceUri, findChildrenByNamespaceUri, getAttribute, getAttributeByNamespaceUri, getChildElements, getLocalName, getNamespaceUri, matchesName, parseXml, parseXmlDocument } from "./xmlParser.js";
45
44
  import { Result, TaggedError, panic } from "better-result";
46
45
  import { assertValidOoxmlNumericIds, escapeXmlAttribute, escapeXmlText, validateDocxPackage } from "@stll/docx-core";
47
46
  import { mintRelationshipId, numberingInstanceResolves } from "@stll/docx-core/model";
48
- import JSZip from "jszip";
49
- import { writeZipPart } from "@stll/docx-core/zip";
47
+ import { getXmlSourceRange, parseStreamingXmlWithSourceRanges } from "@stll/docx-core/xml";
48
+ import { ZipArchive } from "@stll/docx-core/zip";
50
49
  // src/docx/rezip.ts
51
50
  const DocxPackageFidelityErrorBase = TaggedError("DocxPackageFidelityError");
52
51
  var DocxPackageFidelityError = class extends DocxPackageFidelityErrorBase {
@@ -172,7 +171,7 @@ function assertDocumentPackageFidelity({ originalDocumentFacts, serializedDocume
172
171
  async function serializeCommentsToZip(doc, zip, compressionLevel) {
173
172
  const comments = doc.package.document.comments ?? [];
174
173
  const sourceCommentsFile = findZipEntryCaseInsensitive(zip, "word/comments.xml");
175
- const sourceCommentsXml = sourceCommentsFile ? await sourceCommentsFile.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true })) : void 0;
174
+ const sourceCommentsXml = sourceCommentsFile ? await sourceCommentsFile.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true })) : void 0;
176
175
  if (sourceCommentsXml !== void 0 && canReplayCommentsPart(doc, sourceCommentsXml)) return;
177
176
  if (comments.length === 0) {
178
177
  const ownsCommentsPart = hasOwnedCommentsPart(doc);
@@ -180,15 +179,7 @@ async function serializeCommentsToZip(doc, zip, compressionLevel) {
180
179
  }
181
180
  const plan = planCommentParts(comments);
182
181
  const commentsXml = serializeComments(plan, sourceCommentsXml === void 0 ? void 0 : readRootNamespaceBindings(sourceCommentsXml));
183
- writeZipPart({
184
- zip,
185
- path: sourceCommentsFile?.name ?? "word/comments.xml",
186
- data: encodeUtf8(commentsXml),
187
- options: {
188
- compression: "DEFLATE",
189
- compressionOptions: { level: compressionLevel }
190
- }
191
- });
182
+ zip.set(sourceCommentsFile?.path ?? "word/comments.xml", commentsXml, { level: compressionLevel });
192
183
  await ensureCommentsContentType(zip, compressionLevel);
193
184
  await ensureCommentsRelationship(zip, compressionLevel);
194
185
  await syncCommentsExtendedPart(plan, zip, compressionLevel);
@@ -212,43 +203,30 @@ async function syncCommentsExtendedPart(plan, zip, compressionLevel) {
212
203
  const existing = findZipEntryCaseInsensitive(zip, COMMENTS_EXTENDED_PART_LOWER);
213
204
  if (!xml) {
214
205
  if (!existing) return;
215
- zip.remove(existing.name);
206
+ zip.delete(existing.path);
216
207
  await transformPackagingFile(zip, "[Content_Types].xml", removeCommentsExtendedOverride, compressionLevel);
217
208
  await transformPackagingFile(zip, "word/_rels/document.xml.rels", removeCommentsExtendedRelationship, compressionLevel);
218
209
  return;
219
210
  }
220
- writeZipPart({
221
- zip,
222
- path: existing?.name ?? "word/commentsExtended.xml",
223
- data: encodeUtf8(xml),
224
- options: {
225
- compression: "DEFLATE",
226
- compressionOptions: { level: compressionLevel }
227
- }
228
- });
211
+ zip.set(existing?.path ?? "word/commentsExtended.xml", xml, { level: compressionLevel });
229
212
  await transformPackagingFile(zip, "[Content_Types].xml", addCommentsExtendedOverride, compressionLevel);
230
213
  await transformPackagingFile(zip, "word/_rels/document.xml.rels", addCommentsExtendedRelationship, compressionLevel);
231
214
  }
232
215
  /** Read a packaging file, apply a pure transform, and write it back if it changed. */
233
216
  async function transformPackagingFile(zip, path, transform, compressionLevel) {
234
- const file = zip.file(path);
217
+ const file = zip.entry(path);
235
218
  if (!file) return;
236
- const xml = await file.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true }));
219
+ const xml = await file.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true }));
237
220
  const next = transform(xml);
238
- if (next !== xml) writeZipPart({
239
- zip,
240
- path,
241
- data: encodeUtf8(next),
242
- options: {
243
- compression: "DEFLATE",
244
- compressionOptions: { level: compressionLevel }
245
- }
246
- });
221
+ if (next !== xml) zip.set(path, next, { level: compressionLevel });
247
222
  }
248
223
  function findZipEntryCaseInsensitive(zip, lowerPath) {
249
- const direct = zip.file(lowerPath);
224
+ const direct = zip.entry(lowerPath);
250
225
  if (direct) return direct;
251
- for (const [path, file] of Object.entries(zip.files)) if (!file.dir && path.toLowerCase() === lowerPath) return file;
226
+ for (const file of zip.entries()) {
227
+ const { path } = file;
228
+ if (path.toLowerCase() === lowerPath) return file;
229
+ }
252
230
  return null;
253
231
  }
254
232
  const PACKAGE_RELATIONSHIPS_NAMESPACE_URI = "http://schemas.openxmlformats.org/package/2006/relationships";
@@ -312,7 +290,7 @@ function collectDocxParts(doc, zip) {
312
290
  addHeaderFooterParts(doc.package.footers, RELATIONSHIP_TYPES.footer);
313
291
  const addNoteParts = (notes, conventionalLowerPath) => {
314
292
  if (!notes || notes.length === 0) return;
315
- const partPath = findNotePartEntry(zip, conventionalLowerPath)?.name ?? conventionalLowerPath;
293
+ const partPath = findNotePartEntry(zip, conventionalLowerPath)?.path ?? conventionalLowerPath;
316
294
  parts.push({
317
295
  relsPath: notePartRelsPath(partPath),
318
296
  blocks: notes.flatMap((note) => note.content)
@@ -323,25 +301,25 @@ function collectDocxParts(doc, zip) {
323
301
  return parts;
324
302
  }
325
303
  async function readRelsOrStub(zip, relsPath) {
326
- const file = zip.file(relsPath);
327
- return (file ? await file.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true })) : EMPTY_RELS_XML).replace(/<Relationships(?<attrs>[^>]*)\/>/u, "<Relationships$<attrs>></Relationships>");
304
+ const file = zip.entry(relsPath);
305
+ return (file ? await file.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true })) : EMPTY_RELS_XML).replace(/<Relationships(?<attrs>[^>]*)\/>/u, "<Relationships$<attrs>></Relationships>");
328
306
  }
329
307
  function findMaxImageNum(zip) {
330
308
  let maxImageNum = 0;
331
- zip.forEach((relativePath) => {
309
+ for (const relativePath of zip.paths()) {
332
310
  const m = /^word\/media\/image(?<num>\d+)\./u.exec(relativePath);
333
311
  if (m) {
334
312
  const num = Number.parseInt(m.groups["num"], 10);
335
313
  if (num > maxImageNum) maxImageNum = num;
336
314
  }
337
- });
315
+ }
338
316
  return maxImageNum;
339
317
  }
340
318
  async function registerImageExtensions(zip, extensions, compressionLevel) {
341
319
  if (extensions.size === 0) return;
342
- const ctFile = zip.file("[Content_Types].xml");
320
+ const ctFile = zip.entry("[Content_Types].xml");
343
321
  if (!ctFile) return;
344
- let ctXml = await ctFile.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true }));
322
+ let ctXml = await ctFile.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true }));
345
323
  let changed = false;
346
324
  for (const ext of extensions) {
347
325
  if (ctXml.includes(`Extension="${ext}"`)) continue;
@@ -350,15 +328,7 @@ async function registerImageExtensions(zip, extensions, compressionLevel) {
350
328
  changed = true;
351
329
  }
352
330
  if (!changed) return;
353
- writeZipPart({
354
- zip,
355
- path: "[Content_Types].xml",
356
- data: encodeUtf8(ctXml),
357
- options: {
358
- compression: "DEFLATE",
359
- compressionOptions: { level: compressionLevel }
360
- }
361
- });
331
+ zip.set("[Content_Types].xml", ctXml, { level: compressionLevel });
362
332
  }
363
333
  function collectNewImages(blocks) {
364
334
  const images = [];
@@ -425,15 +395,7 @@ async function processNewImages(parts, zip, compressionLevel) {
425
395
  maxId++;
426
396
  const mediaPath = `word/media/${`image${maxImageNum}.${extension}`}`;
427
397
  const newRId = mintRelationshipId(maxId);
428
- writeZipPart({
429
- zip,
430
- path: mediaPath,
431
- data: new Uint8Array(data),
432
- options: {
433
- compression: "DEFLATE",
434
- compressionOptions: { level: compressionLevel }
435
- }
436
- });
398
+ zip.set(mediaPath, new Uint8Array(data), { level: compressionLevel });
437
399
  relEntries.push(`<Relationship Id="${newRId}" Type="${RELATIONSHIP_TYPES.image}" Target="${escapeXmlAttribute(relativeTargetForPart(partPath, mediaPath))}"/>`);
438
400
  extensionsAdded.add(extension);
439
401
  if (drawing.rawXml) drawing.rawXml = rebindDrawingImageRelationship({
@@ -445,15 +407,7 @@ async function processNewImages(parts, zip, compressionLevel) {
445
407
  }
446
408
  if (relEntries.length > 0) {
447
409
  const updatedRelsXml = relsXml.replace("</Relationships>", `${relEntries.join("")}</Relationships>`);
448
- writeZipPart({
449
- zip,
450
- path: relsPath,
451
- data: encodeUtf8(updatedRelsXml),
452
- options: {
453
- compression: "DEFLATE",
454
- compressionOptions: { level: compressionLevel }
455
- }
456
- });
410
+ zip.set(relsPath, updatedRelsXml, { level: compressionLevel });
457
411
  }
458
412
  }
459
413
  await registerImageExtensions(zip, extensionsAdded, compressionLevel);
@@ -466,15 +420,7 @@ const materializeEmbeddedMedia = async ({ document, zip, compressionLevel }) =>
466
420
  if (isUnsafePackagePath(media.path) || !media.path.startsWith("word/media/")) panic("Detached media has an invalid package path");
467
421
  const extension = media.path.split(".").at(-1)?.toLowerCase();
468
422
  if (!extension || !/^[a-z0-9]+$/u.test(extension)) panic("Embedded media has an invalid extension");
469
- writeZipPart({
470
- zip,
471
- path: media.path,
472
- data: new Uint8Array(media.data),
473
- options: {
474
- compression: "DEFLATE",
475
- compressionOptions: { level: compressionLevel }
476
- }
477
- });
423
+ zip.set(media.path, new Uint8Array(media.data), { level: compressionLevel });
478
424
  extensions.add(extension);
479
425
  }
480
426
  await registerImageExtensions(zip, extensions, compressionLevel);
@@ -529,15 +475,7 @@ const publishCanonicalImageResources = async ({ document, zip, compressionLevel
529
475
  const media = document.package.media?.get(absolute);
530
476
  if (!media) return refuse("missingMedia", "A canonical inline picture is missing its media bytes.");
531
477
  if (isUnsafePackagePath(media.path) || media.path !== absolute) return refuse("unsafeTarget", "A canonical inline picture media path does not match its relationship.");
532
- writeZipPart({
533
- zip,
534
- path: absolute,
535
- data: new Uint8Array(media.data),
536
- options: {
537
- compression: "DEFLATE",
538
- compressionOptions: { level: compressionLevel }
539
- }
540
- });
478
+ zip.set(absolute, new Uint8Array(media.data), { level: compressionLevel });
541
479
  const extension = absolute.split(".").at(-1)?.toLowerCase();
542
480
  if (!extension || !/^[a-z0-9]+$/u.test(extension)) return refuse("unsafeTarget", "A canonical inline picture media extension is invalid.");
543
481
  await registerImageExtensions(zip, /* @__PURE__ */ new Set([extension]), compressionLevel);
@@ -559,15 +497,7 @@ const publishCanonicalImageResources = async ({ document, zip, compressionLevel
559
497
  });
560
498
  changed = true;
561
499
  }
562
- if (changed) writeZipPart({
563
- zip,
564
- path: relsPath,
565
- data: encodeUtf8(xml),
566
- options: {
567
- compression: "DEFLATE",
568
- compressionOptions: { level: compressionLevel }
569
- }
570
- });
500
+ if (changed) zip.set(relsPath, xml, { level: compressionLevel });
571
501
  }
572
502
  };
573
503
  const collectExternalHyperlinkResources = (blocks) => {
@@ -710,15 +640,7 @@ async function processNewHyperlinks(parts, zip, compressionLevel) {
710
640
  }
711
641
  if (relEntries.length > 0) {
712
642
  (root.elements ??= []).push(...relEntries);
713
- writeZipPart({
714
- zip,
715
- path: relsPath,
716
- data: encodeUtf8(captureVerbatimXml(root)),
717
- options: {
718
- compression: "DEFLATE",
719
- compressionOptions: { level: compressionLevel }
720
- }
721
- });
643
+ zip.set(relsPath, captureVerbatimXml(root), { level: compressionLevel });
722
644
  }
723
645
  }
724
646
  }
@@ -736,23 +658,18 @@ async function processNewHyperlinks(parts, zip, compressionLevel) {
736
658
  */
737
659
  const normalizePackageIdsInZip = async (zip, compressionLevel) => {
738
660
  const xmlParts = /* @__PURE__ */ new Map();
739
- for (const [path, file] of Object.entries(zip.files)) if (!file.dir && path.startsWith("word/") && path.endsWith(".xml")) {
740
- const xml = await file.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true }));
741
- assertValidOoxmlNumericIds(xml, path);
742
- xmlParts.set(path, xml);
661
+ for (const file of zip.entries()) {
662
+ const { path } = file;
663
+ if (path.startsWith("word/") && path.endsWith(".xml")) {
664
+ const xml = await file.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true }));
665
+ assertValidOoxmlNumericIds(xml, path);
666
+ xmlParts.set(path, xml);
667
+ }
743
668
  }
744
669
  const normalizedParts = normalizeParaIdRangeInXmlParts(normalizeRevisionIdsInXmlParts(xmlParts));
745
670
  for (const [path, xml] of normalizedParts) if (xml !== xmlParts.get(path)) {
746
671
  assertValidOoxmlNumericIds(xml, path);
747
- writeZipPart({
748
- zip,
749
- path,
750
- data: encodeUtf8(xml),
751
- options: {
752
- compression: "DEFLATE",
753
- compressionOptions: { level: compressionLevel }
754
- }
755
- });
672
+ zip.set(path, xml, { level: compressionLevel });
756
673
  }
757
674
  };
758
675
  const EXTENDED_PROPERTIES_PATH = "docProps/app.xml";
@@ -762,19 +679,11 @@ const EXTENDED_PROPERTIES_PATH = "docProps/app.xml";
762
679
  * holding none.
763
680
  */
764
681
  const normalizeAppVersionInZip = async (zip, compressionLevel) => {
765
- const extendedProperties = zip.file(EXTENDED_PROPERTIES_PATH);
682
+ const extendedProperties = zip.entry(EXTENDED_PROPERTIES_PATH);
766
683
  if (!extendedProperties) return;
767
- const xml = await extendedProperties.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true }));
684
+ const xml = await extendedProperties.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true }));
768
685
  const normalized = normalizeAppVersionInExtendedProperties(xml);
769
- if (normalized !== xml) writeZipPart({
770
- zip,
771
- path: EXTENDED_PROPERTIES_PATH,
772
- data: encodeUtf8(normalized),
773
- options: {
774
- compression: "DEFLATE",
775
- compressionOptions: { level: compressionLevel }
776
- }
777
- });
686
+ if (normalized !== xml) zip.set(EXTENDED_PROPERTIES_PATH, normalized, { level: compressionLevel });
778
687
  };
779
688
  /**
780
689
  * Every pass a save owes the package as a whole, in one function so that the
@@ -792,11 +701,7 @@ const normalizePackageOnSave = async (zip, compressionLevel) => {
792
701
  const generateDocxZip = async (zip, compressionLevel) => {
793
702
  await reconcilePackageReferences(zip, compressionLevel);
794
703
  await normalizePackageOnSave(zip, compressionLevel);
795
- return zip.generateAsync({
796
- type: "arraybuffer",
797
- compression: "DEFLATE",
798
- compressionOptions: { level: compressionLevel }
799
- });
704
+ return zip.generate({ level: compressionLevel }).then((bytes) => bytes.buffer);
800
705
  };
801
706
  /**
802
707
  * Section facts of each source package's `word/document.xml`, keyed by the
@@ -826,9 +731,9 @@ const parsedZipSources = /* @__PURE__ */ new WeakMap();
826
731
  const loadParsedZipSource = async (document, buffer) => {
827
732
  const cached = parsedZipSources.get(document);
828
733
  if (cached?.buffer === buffer) return cached;
829
- const zip = await JSZip.loadAsync(buffer);
734
+ const zip = await ZipArchive.read(buffer);
830
735
  removeUnsafeEntries(zip);
831
- const [documentXml, corePropertiesXml] = await Promise.all([zip.file("word/document.xml")?.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true })), zip.file("docProps/core.xml")?.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true }))]);
736
+ const [documentXml, corePropertiesXml] = await Promise.all([zip.entry("word/document.xml")?.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true })), zip.entry("docProps/core.xml")?.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true }))]);
832
737
  const source = {
833
738
  buffer,
834
739
  zip,
@@ -838,11 +743,7 @@ const loadParsedZipSource = async (document, buffer) => {
838
743
  parsedZipSources.set(document, source);
839
744
  return source;
840
745
  };
841
- const cloneDocxZip = (source) => {
842
- const clone = new JSZip();
843
- clone.files = { ...source.files };
844
- return clone;
845
- };
746
+ const cloneDocxZip = (source) => source.clone();
846
747
  const normalizeExportDrawingIds = ({ package: docxPackage }) => {
847
748
  normalizeDrawingIds({
848
749
  documentBody: docxPackage.document,
@@ -896,15 +797,7 @@ const finishRepack = async ({ document: sourceDocument, originalZip, outputZip,
896
797
  removedReferences: [...sectionReferenceRemovals ?? [], ...sectionEndpointRemoval?.removedReferences ?? []],
897
798
  compressionLevel
898
799
  });
899
- writeZipPart({
900
- zip: outputZip,
901
- path: "word/document.xml",
902
- data: encodeUtf8(documentXml),
903
- options: {
904
- compression: "DEFLATE",
905
- compressionOptions: { level: compressionLevel }
906
- }
907
- });
800
+ outputZip.set("word/document.xml", documentXml, { level: compressionLevel });
908
801
  await rebindWatermarkRelIds({
909
802
  document,
910
803
  zip: outputZip,
@@ -934,15 +827,7 @@ const finishRepack = async ({ document: sourceDocument, originalZip, outputZip,
934
827
  updateModifiedDate,
935
828
  ...modifiedBy !== void 0 ? { modifiedBy } : {}
936
829
  });
937
- writeZipPart({
938
- zip: outputZip,
939
- path: "docProps/core.xml",
940
- data: encodeUtf8(updatedCoreProperties),
941
- options: {
942
- compression: "DEFLATE",
943
- compressionOptions: { level: compressionLevel }
944
- }
945
- });
830
+ outputZip.set("docProps/core.xml", updatedCoreProperties, { level: compressionLevel });
946
831
  }
947
832
  return generateDocxZip(outputZip, compressionLevel);
948
833
  };
@@ -950,8 +835,8 @@ async function repackDocxWithSectionEndpointRemoval({ document: doc, options, se
950
835
  if (!doc.originalBuffer) panic("Cannot repack document: no original buffer for round-trip. Use createDocx() for new documents.");
951
836
  const { compressionLevel = 6, updateModifiedDate = true, modifiedBy, changedNoteParaIds } = options;
952
837
  const exportDocument = withoutExhaustedComments(withoutOrphanCommentRanges(doc));
953
- const originalZip = await JSZip.loadAsync(doc.originalBuffer);
954
- const [originalDocumentXml, originalCorePropertiesXml] = await Promise.all([originalZip.file("word/document.xml")?.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true })), originalZip.file("docProps/core.xml")?.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true }))]);
838
+ const originalZip = await ZipArchive.read(doc.originalBuffer);
839
+ const [originalDocumentXml, originalCorePropertiesXml] = await Promise.all([originalZip.entry("word/document.xml")?.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true })), originalZip.entry("docProps/core.xml")?.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true }))]);
955
840
  removeUnsafeEntries(originalZip);
956
841
  const newZip = cloneDocxZip(originalZip);
957
842
  return finishRepack({
@@ -991,32 +876,8 @@ function repackDocx(doc, options = {}) {
991
876
  async function repackDocxFromRaw(doc, rawContent, options = {}) {
992
877
  const { compressionLevel = 6, updateModifiedDate = true, modifiedBy, changedNoteParaIds } = options;
993
878
  let exportDocument = withoutExhaustedComments(withoutOrphanCommentRanges(cloneExportDocument(doc)));
994
- const newZip = new JSZip();
995
- for (const [path, file] of Object.entries(rawContent.originalZip.files)) {
996
- if (file.dir) {
997
- writeZipPart({
998
- zip: newZip,
999
- path,
1000
- data: /* @__PURE__ */ new Uint8Array(),
1001
- options: {
1002
- dir: true,
1003
- date: file.date
1004
- }
1005
- });
1006
- continue;
1007
- }
1008
- if (isUnsafePackagePath(path)) continue;
1009
- const content = await file.async("arraybuffer");
1010
- writeZipPart({
1011
- zip: newZip,
1012
- path,
1013
- data: new Uint8Array(content),
1014
- options: {
1015
- compression: "DEFLATE",
1016
- compressionOptions: { level: compressionLevel }
1017
- }
1018
- });
1019
- }
879
+ const newZip = rawContent.originalZip.clone();
880
+ for (const path of newZip.paths()) if (isUnsafePackagePath(path)) newZip.delete(path);
1020
881
  exportDocument = await materializeNewHeaderFooterParts(exportDocument, newZip, compressionLevel);
1021
882
  await materializeEmbeddedMedia({
1022
883
  document: exportDocument,
@@ -1044,15 +905,7 @@ async function repackDocxFromRaw(doc, rawContent, options = {}) {
1044
905
  serializedDocumentXml: documentXml,
1045
906
  doc: exportDocument
1046
907
  });
1047
- writeZipPart({
1048
- zip: newZip,
1049
- path: "word/document.xml",
1050
- data: encodeUtf8(documentXml),
1051
- options: {
1052
- compression: "DEFLATE",
1053
- compressionOptions: { level: compressionLevel }
1054
- }
1055
- });
908
+ newZip.set("word/document.xml", documentXml, { level: compressionLevel });
1056
909
  await rebindWatermarkRelIds({
1057
910
  document: exportDocument,
1058
911
  zip: newZip,
@@ -1082,15 +935,7 @@ async function repackDocxFromRaw(doc, rawContent, options = {}) {
1082
935
  updateModifiedDate,
1083
936
  ...modifiedBy !== void 0 ? { modifiedBy } : {}
1084
937
  });
1085
- writeZipPart({
1086
- zip: newZip,
1087
- path: "docProps/core.xml",
1088
- data: encodeUtf8(updatedCoreProps),
1089
- options: {
1090
- compression: "DEFLATE",
1091
- compressionOptions: { level: compressionLevel }
1092
- }
1093
- });
938
+ newZip.set("docProps/core.xml", updatedCoreProps, { level: compressionLevel });
1094
939
  }
1095
940
  return generateDocxZip(newZip, compressionLevel);
1096
941
  }
@@ -1176,49 +1021,25 @@ function withoutRelationships(relsXml, ids) {
1176
1021
  * can neither read nor rewrite. Both sides are filtered on save.
1177
1022
  */
1178
1023
  async function dropAttachedTemplateReference(zip, compressionLevel) {
1179
- const settingsFile = zip.file(SETTINGS_PART);
1024
+ const settingsFile = zip.entry(SETTINGS_PART);
1180
1025
  if (!settingsFile) return;
1181
- const relsFile = zip.file(SETTINGS_RELS_PART);
1182
- const filtered = withoutAttachedTemplate(await settingsFile.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true })), await relsFile?.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true })));
1183
- if (filtered.settingsXml !== void 0) writeZipPart({
1184
- zip,
1185
- path: SETTINGS_PART,
1186
- data: encodeUtf8(filtered.settingsXml),
1187
- options: {
1188
- compression: "DEFLATE",
1189
- compressionOptions: { level: compressionLevel }
1190
- }
1191
- });
1192
- if (filtered.relsXml !== void 0) writeZipPart({
1193
- zip,
1194
- path: SETTINGS_RELS_PART,
1195
- data: encodeUtf8(filtered.relsXml),
1196
- options: {
1197
- compression: "DEFLATE",
1198
- compressionOptions: { level: compressionLevel }
1199
- }
1200
- });
1026
+ const relsFile = zip.entry(SETTINGS_RELS_PART);
1027
+ const filtered = withoutAttachedTemplate(await settingsFile.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true })), await relsFile?.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true })));
1028
+ if (filtered.settingsXml !== void 0) zip.set(SETTINGS_PART, filtered.settingsXml, { level: compressionLevel });
1029
+ if (filtered.relsXml !== void 0) zip.set(SETTINGS_RELS_PART, filtered.relsXml, { level: compressionLevel });
1201
1030
  }
1202
1031
  /**
1203
1032
  * Ensure [Content_Types].xml contains an Override for word/comments.xml.
1204
1033
  * If the document already had comments, this is a no-op.
1205
1034
  */
1206
1035
  async function ensureCommentsContentType(zip, compressionLevel) {
1207
- const ctFile = zip.file("[Content_Types].xml");
1036
+ const ctFile = zip.entry("[Content_Types].xml");
1208
1037
  if (!ctFile) return;
1209
- let ctXml = await ctFile.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true }));
1038
+ let ctXml = await ctFile.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true }));
1210
1039
  if (ctXml.toLowerCase().includes("/word/comments.xml")) return;
1211
- const commentsPartPath = findZipEntryCaseInsensitive(zip, "word/comments.xml")?.name ?? "word/comments.xml";
1040
+ const commentsPartPath = findZipEntryCaseInsensitive(zip, "word/comments.xml")?.path ?? "word/comments.xml";
1212
1041
  ctXml = ctXml.replace("</Types>", `<Override PartName="/${commentsPartPath}" ContentType="${COMMENTS_CONTENT_TYPE}"/></Types>`);
1213
- writeZipPart({
1214
- zip,
1215
- path: "[Content_Types].xml",
1216
- data: encodeUtf8(ctXml),
1217
- options: {
1218
- compression: "DEFLATE",
1219
- compressionOptions: { level: compressionLevel }
1220
- }
1221
- });
1042
+ zip.set("[Content_Types].xml", ctXml, { level: compressionLevel });
1222
1043
  }
1223
1044
  /**
1224
1045
  * Ensure word/_rels/document.xml.rels contains a Relationship for comments.xml.
@@ -1226,22 +1047,14 @@ async function ensureCommentsContentType(zip, compressionLevel) {
1226
1047
  */
1227
1048
  async function ensureCommentsRelationship(zip, compressionLevel) {
1228
1049
  const relsPath = "word/_rels/document.xml.rels";
1229
- const relsFile = zip.file(relsPath);
1050
+ const relsFile = zip.entry(relsPath);
1230
1051
  if (!relsFile) return;
1231
- let relsXml = await relsFile.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true }));
1052
+ let relsXml = await relsFile.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true }));
1232
1053
  if (relsXml.toLowerCase().includes("comments.xml")) return;
1233
- const commentsPartPath = findZipEntryCaseInsensitive(zip, "word/comments.xml")?.name ?? "word/comments.xml";
1054
+ const commentsPartPath = findZipEntryCaseInsensitive(zip, "word/comments.xml")?.path ?? "word/comments.xml";
1234
1055
  const newRId = `rId${findMaxRId(relsXml) + 1}`;
1235
1056
  relsXml = relsXml.replace("</Relationships>", `<Relationship Id="${newRId}" Type="${RELATIONSHIP_TYPES.comments}" Target="${commentsPartPath.slice(5)}"/></Relationships>`);
1236
- writeZipPart({
1237
- zip,
1238
- path: relsPath,
1239
- data: encodeUtf8(relsXml),
1240
- options: {
1241
- compression: "DEFLATE",
1242
- compressionOptions: { level: compressionLevel }
1243
- }
1244
- });
1057
+ zip.set(relsPath, relsXml, { level: compressionLevel });
1245
1058
  }
1246
1059
  /**
1247
1060
  * Update only document.xml in a DOCX buffer (minimal changes)
@@ -1253,21 +1066,9 @@ async function ensureCommentsRelationship(zip, compressionLevel) {
1253
1066
  */
1254
1067
  async function updateDocumentXml(originalBuffer, newDocumentXml, options = {}) {
1255
1068
  const { compressionLevel = 6 } = options;
1256
- const zip = await JSZip.loadAsync(originalBuffer);
1257
- writeZipPart({
1258
- zip,
1259
- path: "word/document.xml",
1260
- data: encodeUtf8(newDocumentXml),
1261
- options: {
1262
- compression: "DEFLATE",
1263
- compressionOptions: { level: compressionLevel }
1264
- }
1265
- });
1266
- return zip.generateAsync({
1267
- type: "arraybuffer",
1268
- compression: "DEFLATE",
1269
- compressionOptions: { level: compressionLevel }
1270
- });
1069
+ const zip = await ZipArchive.read(originalBuffer);
1070
+ zip.set("word/document.xml", newDocumentXml, { level: compressionLevel });
1071
+ return zip.generate({ level: compressionLevel }).then((bytes) => bytes.buffer);
1271
1072
  }
1272
1073
  /**
1273
1074
  * Update a specific XML file in a DOCX buffer
@@ -1280,21 +1081,9 @@ async function updateDocumentXml(originalBuffer, newDocumentXml, options = {}) {
1280
1081
  */
1281
1082
  async function updateXmlFile(originalBuffer, path, content, options = {}) {
1282
1083
  const { compressionLevel = 6 } = options;
1283
- const zip = await JSZip.loadAsync(originalBuffer);
1284
- writeZipPart({
1285
- zip,
1286
- path,
1287
- data: encodeUtf8(content),
1288
- options: {
1289
- compression: "DEFLATE",
1290
- compressionOptions: { level: compressionLevel }
1291
- }
1292
- });
1293
- return zip.generateAsync({
1294
- type: "arraybuffer",
1295
- compression: "DEFLATE",
1296
- compressionOptions: { level: compressionLevel }
1297
- });
1084
+ const zip = await ZipArchive.read(originalBuffer);
1085
+ zip.set(path, content, { level: compressionLevel });
1086
+ return zip.generate({ level: compressionLevel }).then((bytes) => bytes.buffer);
1298
1087
  }
1299
1088
  /**
1300
1089
  * Update multiple files in a DOCX buffer
@@ -1305,10 +1094,10 @@ async function updateXmlFile(originalBuffer, path, content, options = {}) {
1305
1094
  * @returns Promise resolving to DOCX as ArrayBuffer
1306
1095
  */
1307
1096
  async function updateMultipleFiles(originalBuffer, updates, options = {}) {
1308
- return applyUpdatesToZip(await JSZip.loadAsync(originalBuffer), updates, options);
1097
+ return applyUpdatesToZip(await ZipArchive.read(originalBuffer), updates, options);
1309
1098
  }
1310
1099
  /**
1311
- * Apply file updates to an already-loaded JSZip instance and generate the output.
1100
+ * Apply file updates to an already-loaded ZipArchive instance and generate the output.
1312
1101
  * Use this when the zip is already loaded to avoid a redundant decompression pass.
1313
1102
  *
1314
1103
  * This is the selective save's exit, so it owes the package the same passes
@@ -1319,21 +1108,9 @@ async function updateMultipleFiles(originalBuffer, updates, options = {}) {
1319
1108
  */
1320
1109
  async function applyUpdatesToZip(zip, updates, options = {}) {
1321
1110
  const { compressionLevel = 6 } = options;
1322
- for (const [path, content] of updates) writeZipPart({
1323
- zip,
1324
- path,
1325
- data: typeof content === "string" ? encodeUtf8(content) : new Uint8Array(content),
1326
- options: {
1327
- compression: "DEFLATE",
1328
- compressionOptions: { level: compressionLevel }
1329
- }
1330
- });
1111
+ for (const [path, content] of updates) zip.set(path, typeof content === "string" ? encodeUtf8(content) : new Uint8Array(content), { level: compressionLevel });
1331
1112
  await normalizePackageOnSave(zip, compressionLevel);
1332
- return await zip.generateAsync({
1333
- type: "arraybuffer",
1334
- compression: "DEFLATE",
1335
- compressionOptions: { level: compressionLevel }
1336
- });
1113
+ return (await zip.generate({ level: compressionLevel })).buffer;
1337
1114
  }
1338
1115
  /**
1339
1116
  * Add a new relationship to document.xml.rels
@@ -1343,26 +1120,18 @@ async function applyUpdatesToZip(zip, updates, options = {}) {
1343
1120
  * @returns Promise resolving to { buffer: ArrayBuffer, rId: string }
1344
1121
  */
1345
1122
  async function addRelationship(originalBuffer, relationship) {
1346
- const zip = await JSZip.loadAsync(originalBuffer);
1123
+ const zip = await ZipArchive.read(originalBuffer);
1347
1124
  const relsPath = "word/_rels/document.xml.rels";
1348
- const relsFile = zip.file(relsPath);
1125
+ const relsFile = zip.entry(relsPath);
1349
1126
  if (!relsFile) panic("document.xml.rels not found in DOCX");
1350
- const relsXml = await relsFile.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true }));
1127
+ const relsXml = await relsFile.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true }));
1351
1128
  const newRId = `rId${findMaxRId(relsXml) + 1}`;
1352
1129
  const targetModeAttr = relationship.targetMode === "External" ? " TargetMode=\"External\"" : "";
1353
1130
  const newRelElement = `<Relationship Id="${newRId}" Type="${relationship.type}" Target="${escapeXmlAttribute(relationship.target)}"${targetModeAttr}/>`;
1354
1131
  const updatedRelsXml = relsXml.replace("</Relationships>", `${newRelElement}</Relationships>`);
1355
- writeZipPart({
1356
- zip,
1357
- path: relsPath,
1358
- data: encodeUtf8(updatedRelsXml)
1359
- });
1132
+ zip.set(relsPath, updatedRelsXml);
1360
1133
  return {
1361
- buffer: await zip.generateAsync({
1362
- type: "arraybuffer",
1363
- compression: "DEFLATE",
1364
- compressionOptions: { level: 6 }
1365
- }),
1134
+ buffer: (await zip.generate({ level: 6 })).buffer,
1366
1135
  rId: newRId
1367
1136
  };
1368
1137
  }
@@ -1376,36 +1145,24 @@ async function addRelationship(originalBuffer, relationship) {
1376
1145
  * @returns Promise resolving to { buffer: ArrayBuffer, rId: string, path: string }
1377
1146
  */
1378
1147
  async function addMedia(originalBuffer, filename, data, mimeType) {
1379
- const zip = await JSZip.loadAsync(originalBuffer);
1148
+ const zip = await ZipArchive.read(originalBuffer);
1380
1149
  const mediaPath = `word/media/${filename}`;
1381
- writeZipPart({
1382
- zip,
1383
- path: mediaPath,
1384
- data: new Uint8Array(data)
1385
- });
1386
- const relResult = await addRelationship(await zip.generateAsync({ type: "arraybuffer" }), {
1150
+ zip.set(mediaPath, new Uint8Array(data));
1151
+ const relResult = await addRelationship((await zip.generate({ compression: "store" })).buffer, {
1387
1152
  type: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
1388
1153
  target: `media/${filename}`
1389
1154
  });
1390
- const contentTypesFile = zip.file("[Content_Types].xml");
1155
+ const contentTypesFile = zip.entry("[Content_Types].xml");
1391
1156
  if (contentTypesFile) {
1392
- const contentTypesXml = await contentTypesFile.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true }));
1157
+ const contentTypesXml = await contentTypesFile.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true }));
1393
1158
  const extension = filename.split(".").pop()?.toLowerCase() || "";
1394
1159
  if (!contentTypesXml.includes(`Extension="${extension}"`) && extension) {
1395
1160
  const extensionElement = `<Default Extension="${extension}" ContentType="${getContentTypeForExtension(extension, mimeType)}"/>`;
1396
1161
  const updatedContentTypes = contentTypesXml.replace("</Types>", `${extensionElement}</Types>`);
1397
- const finalZip = await JSZip.loadAsync(relResult.buffer);
1398
- writeZipPart({
1399
- zip: finalZip,
1400
- path: "[Content_Types].xml",
1401
- data: encodeUtf8(updatedContentTypes)
1402
- });
1162
+ const finalZip = await ZipArchive.read(relResult.buffer);
1163
+ finalZip.set("[Content_Types].xml", updatedContentTypes);
1403
1164
  return {
1404
- buffer: await finalZip.generateAsync({
1405
- type: "arraybuffer",
1406
- compression: "DEFLATE",
1407
- compressionOptions: { level: 6 }
1408
- }),
1165
+ buffer: (await finalZip.generate({ level: 6 })).buffer,
1409
1166
  rId: relResult.rId,
1410
1167
  path: mediaPath
1411
1168
  };
@@ -1445,13 +1202,13 @@ function hasUnmaterializedHeaderFooter(doc) {
1445
1202
  function findMaxHeaderFooterNum(zip, prefix) {
1446
1203
  let max = 0;
1447
1204
  const pattern = new RegExp(`^word/${prefix}(\\d+)\\.xml$`, "u");
1448
- zip.forEach((relativePath) => {
1205
+ for (const relativePath of zip.paths()) {
1449
1206
  const m = pattern.exec(relativePath);
1450
1207
  if (m) {
1451
1208
  const num = Number.parseInt(m[1], 10);
1452
1209
  if (num > max) max = num;
1453
1210
  }
1454
- });
1211
+ }
1455
1212
  return max;
1456
1213
  }
1457
1214
  /**
@@ -1545,28 +1302,12 @@ async function materializeNewHeaderFooterParts(doc, zip, compressionLevel) {
1545
1302
  materialize(doc.package.headers, RELATIONSHIP_TYPES.header, "header", HEADER_CONTENT_TYPE, true);
1546
1303
  materialize(doc.package.footers, RELATIONSHIP_TYPES.footer, "footer", FOOTER_CONTENT_TYPE, false);
1547
1304
  const compressionOptions = { level: compressionLevel };
1548
- if (relEntries.length > 0) writeZipPart({
1549
- zip,
1550
- path: relsPath,
1551
- data: encodeUtf8(relsXml.replace("</Relationships>", `${relEntries.join("")}</Relationships>`)),
1552
- options: {
1553
- compression: "DEFLATE",
1554
- compressionOptions
1555
- }
1556
- });
1557
- const ctFile = zip.file("[Content_Types].xml");
1305
+ if (relEntries.length > 0) zip.set(relsPath, relsXml.replace("</Relationships>", `${relEntries.join("")}</Relationships>`), compressionOptions);
1306
+ const ctFile = zip.entry("[Content_Types].xml");
1558
1307
  if (ctFile) {
1559
- const original = await ctFile.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true }));
1308
+ const original = await ctFile.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true }));
1560
1309
  const updated = registerContentTypeParts(original, contentTypeParts);
1561
- if (updated !== original) writeZipPart({
1562
- zip,
1563
- path: "[Content_Types].xml",
1564
- data: encodeUtf8(updated),
1565
- options: {
1566
- compression: "DEFLATE",
1567
- compressionOptions
1568
- }
1569
- });
1310
+ if (updated !== original) zip.set("[Content_Types].xml", updated, compressionOptions);
1570
1311
  }
1571
1312
  return doc;
1572
1313
  }
@@ -1672,7 +1413,7 @@ async function rebindWatermarkRelIds({ document: doc, zip, compressionLevel, bod
1672
1413
  if (bodyAuthority === "canonical") throw new DocxPackageFidelityError("A canonical picture watermark image cannot be resolved");
1673
1414
  continue;
1674
1415
  }
1675
- if (bodyAuthority === "canonical" && canonical.mode === "internal" && !zip.file(canonical.absolute)) throw new DocxPackageFidelityError("A canonical picture watermark image part is missing");
1416
+ if (bodyAuthority === "canonical" && canonical.mode === "internal" && !zip.entry(canonical.absolute)) throw new DocxPackageFidelityError("A canonical picture watermark image part is missing");
1676
1417
  const relsXml = relsXmlByPath.get(relsPath) ?? EMPTY_RELS_XML;
1677
1418
  const localRels = parseRelationships(relsXml);
1678
1419
  const local = relImage(localRels.get(watermark.imageRId), relsPath);
@@ -1712,15 +1453,7 @@ async function rebindWatermarkRelIds({ document: doc, zip, compressionLevel, bod
1712
1453
  const compressionOptions = { level: compressionLevel };
1713
1454
  for (const path of changedPaths) {
1714
1455
  const xml = relsXmlByPath.get(path);
1715
- if (xml) writeZipPart({
1716
- zip,
1717
- path,
1718
- data: encodeUtf8(xml),
1719
- options: {
1720
- compression: "DEFLATE",
1721
- compressionOptions
1722
- }
1723
- });
1456
+ if (xml) zip.set(path, xml, compressionOptions);
1724
1457
  }
1725
1458
  }
1726
1459
  async function collectHeaderFooterUpdates(doc, { sourceZip, onDiagnostic }) {
@@ -1742,7 +1475,7 @@ async function collectHeaderFooterUpdates(doc, { sourceZip, onDiagnostic }) {
1742
1475
  if (rel && rel.type === type && rel.target) {
1743
1476
  const path = resolveRelativePath(documentRelsPath, rel.target);
1744
1477
  const sourceFile = findZipEntryCaseInsensitive(sourceZip, path.toLowerCase());
1745
- const bindings = sourceFile ? readRootNamespaceBindings(await sourceFile.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true }))) : /* @__PURE__ */ new Map();
1478
+ const bindings = sourceFile ? readRootNamespaceBindings(await sourceFile.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true }))) : /* @__PURE__ */ new Map();
1746
1479
  updates.set(path, serializeHeaderFooter(headerFooter, {
1747
1480
  path,
1748
1481
  bindings,
@@ -1759,15 +1492,7 @@ async function serializeHeadersFootersToZip({ doc, zip, compressionLevel, onDiag
1759
1492
  for (const [filename, xml] of await collectHeaderFooterUpdates(doc, {
1760
1493
  sourceZip: zip,
1761
1494
  onDiagnostic
1762
- })) writeZipPart({
1763
- zip,
1764
- path: filename,
1765
- data: encodeUtf8(xml),
1766
- options: {
1767
- compression: "DEFLATE",
1768
- compressionOptions
1769
- }
1770
- });
1495
+ })) zip.set(filename, xml, compressionOptions);
1771
1496
  }
1772
1497
  async function serializeNotesToZip({ doc, originalZip, newZip, compressionLevel, changedNoteParaIds }) {
1773
1498
  const footnotes = doc.package.footnotes ?? [];
@@ -1822,46 +1547,22 @@ async function materializeNewNotePart({ contentType, newZip, partPath, relations
1822
1547
  const relationshipsFile = findNotePartEntry(newZip, "word/_rels/document.xml.rels");
1823
1548
  if (!contentTypesFile || !relationshipsFile) throw new DocxPackageFidelityError(`Cannot create ${partPath}: the DOCX package is missing content types or document relationships`);
1824
1549
  const compressionOptions = { level: compressionLevel };
1825
- const contentTypesXml = await contentTypesFile.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true }));
1550
+ const contentTypesXml = await contentTypesFile.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true }));
1826
1551
  const partName = `/${partPath}`;
1827
- if (!contentTypesXml.toLowerCase().includes(partName.toLowerCase())) writeZipPart({
1828
- zip: newZip,
1829
- path: contentTypesFile.name,
1830
- data: encodeUtf8(contentTypesXml.replace("</Types>", () => `<Override PartName="${partName}" ContentType="${contentType}"/></Types>`)),
1831
- options: {
1832
- compression: "DEFLATE",
1833
- compressionOptions
1834
- }
1835
- });
1836
- const relationshipsXml = await relationshipsFile.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true }));
1552
+ if (!contentTypesXml.toLowerCase().includes(partName.toLowerCase())) newZip.set(contentTypesFile.path, contentTypesXml.replace("</Types>", () => `<Override PartName="${partName}" ContentType="${contentType}"/></Types>`), compressionOptions);
1553
+ const relationshipsXml = await relationshipsFile.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true }));
1837
1554
  if (!relationshipsXml.includes(relationshipType)) {
1838
1555
  const relationshipId = `rId${findMaxRId(relationshipsXml) + 1}`;
1839
1556
  const target = partPath.slice(5);
1840
- writeZipPart({
1841
- zip: newZip,
1842
- path: relationshipsFile.name,
1843
- data: encodeUtf8(relationshipsXml.replace("</Relationships>", () => `<Relationship Id="${relationshipId}" Type="${relationshipType}" Target="${target}"/></Relationships>`)),
1844
- options: {
1845
- compression: "DEFLATE",
1846
- compressionOptions
1847
- }
1848
- });
1557
+ newZip.set(relationshipsFile.path, relationshipsXml.replace("</Relationships>", () => `<Relationship Id="${relationshipId}" Type="${relationshipType}" Target="${target}"/></Relationships>`), compressionOptions);
1849
1558
  }
1850
- writeZipPart({
1851
- zip: newZip,
1852
- path: partPath,
1853
- data: encodeUtf8(serializedPart),
1854
- options: {
1855
- compression: "DEFLATE",
1856
- compressionOptions
1857
- }
1858
- });
1559
+ newZip.set(partPath, serializedPart, compressionOptions);
1859
1560
  }
1860
1561
  /** Only this modeled switch changes on story lifecycle edits; preserve all other settings. */
1861
1562
  async function serializeHeaderFooterSettingsIntoZip(doc, zip, compressionLevel) {
1862
1563
  if (!doc.package.settings) return;
1863
1564
  const enabled = doc.package.settings.evenAndOddHeaders;
1864
- const file = zip.file(SETTINGS_PART);
1565
+ const file = zip.entry(SETTINGS_PART);
1865
1566
  if (!file) {
1866
1567
  if (enabled === void 0) return;
1867
1568
  await materializeNewNotePart({
@@ -1874,19 +1575,11 @@ async function serializeHeaderFooterSettingsIntoZip(doc, zip, compressionLevel)
1874
1575
  });
1875
1576
  return;
1876
1577
  }
1877
- const xml = await file.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true }));
1578
+ const xml = await file.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true }));
1878
1579
  if (parseSettings(xml).evenAndOddHeaders === enabled) return;
1879
1580
  const patched = updateEvenAndOddHeaders(xml, enabled);
1880
1581
  if (patched === null) throw new DocxPackageFidelityError("Cannot update malformed header/footer settings");
1881
- writeZipPart({
1882
- zip,
1883
- path: file.name,
1884
- data: encodeUtf8(patched),
1885
- options: {
1886
- compression: "DEFLATE",
1887
- compressionOptions: { level: compressionLevel }
1888
- }
1889
- });
1582
+ zip.set(file.path, patched, { level: compressionLevel });
1890
1583
  }
1891
1584
  const numberingBaselines = /* @__PURE__ */ new WeakMap();
1892
1585
  async function serializeNumberingIntoZip(doc, originalZip, newZip, compressionLevel) {
@@ -1906,7 +1599,7 @@ async function serializeNumberingIntoZip(doc, originalZip, newZip, compressionLe
1906
1599
  }
1907
1600
  let baseline = doc.originalBuffer ? numberingBaselines.get(doc) : void 0;
1908
1601
  if (!baseline || baseline.originalBuffer !== doc.originalBuffer) {
1909
- const originalXml = await file.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true }));
1602
+ const originalXml = await file.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true }));
1910
1603
  baseline = {
1911
1604
  originalBuffer: doc.originalBuffer,
1912
1605
  originalXml,
@@ -1921,15 +1614,7 @@ async function serializeNumberingIntoZip(doc, originalZip, newZip, compressionLe
1921
1614
  currentXml
1922
1615
  });
1923
1616
  if (patched === null || patched === baseline.originalXml) return;
1924
- writeZipPart({
1925
- zip: newZip,
1926
- path: file.name,
1927
- data: encodeUtf8(patched),
1928
- options: {
1929
- compression: "DEFLATE",
1930
- compressionOptions: { level: compressionLevel }
1931
- }
1932
- });
1617
+ newZip.set(file.path, patched, { level: compressionLevel });
1933
1618
  }
1934
1619
  const STYLES_PART_PATH = "word/styles.xml";
1935
1620
  const STYLES_CLOSE_ROOT = "</w:styles>";
@@ -2041,7 +1726,7 @@ async function planAddedStyles(doc, originalZip) {
2041
1726
  const styles = doc.package.styles;
2042
1727
  if (!styles || styles.styles.length === 0) return { type: "unchanged" };
2043
1728
  const file = findNotePartEntry(originalZip, STYLES_PART_PATH);
2044
- const originalXml = file ? await file.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true })) : null;
1729
+ const originalXml = file ? await file.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true })) : null;
2045
1730
  const rootName = originalXml === null ? void 0 : /<(?![?!])(?<name>[^\s/>]+)/u.exec(originalXml)?.groups?.["name"];
2046
1731
  const rootClose = originalXml !== null && rootName !== void 0 && getLocalName(rootName) === "styles" ? originalXml.lastIndexOf(`</${rootName}>`) : -1;
2047
1732
  if (file === null || originalXml === null || rootClose < 0) return {
@@ -2057,10 +1742,10 @@ async function planAddedStyles(doc, originalZip) {
2057
1742
  }
2058
1743
  if (added.length === 0) return { type: "unchanged" };
2059
1744
  const canonical = hasCanonicalWordprocessingPrefixes(originalXml);
2060
- const patched = originalXml.slice(0, rootClose) + added.map((style) => canonical ? serializeStyle(style) : selfBoundFragment(serializeStyle(style), file.name)).join("") + originalXml.slice(rootClose);
1745
+ const patched = originalXml.slice(0, rootClose) + added.map((style) => canonical ? serializeStyle(style) : selfBoundFragment(serializeStyle(style), file.path)).join("") + originalXml.slice(rootClose);
2061
1746
  return {
2062
1747
  type: "patch",
2063
- path: file.name,
1748
+ path: file.path,
2064
1749
  xml: patched
2065
1750
  };
2066
1751
  }
@@ -2079,15 +1764,7 @@ async function serializeAddedStylesIntoZip(doc, originalZip, newZip, compression
2079
1764
  });
2080
1765
  return;
2081
1766
  case "patch":
2082
- writeZipPart({
2083
- zip: newZip,
2084
- path: plan.path,
2085
- data: encodeUtf8(plan.xml),
2086
- options: {
2087
- compression: "DEFLATE",
2088
- compressionOptions: { level: compressionLevel }
2089
- }
2090
- });
1767
+ newZip.set(plan.path, plan.xml, { level: compressionLevel });
2091
1768
  return;
2092
1769
  default: return plan;
2093
1770
  }
@@ -2125,7 +1802,7 @@ const notePartXmlFor = ({ patch, originalXml, replacementXml, hasDirtyParagraph,
2125
1802
  async function patchNotePartIntoZip({ conventionalLowerPath, currentXml, replacementXml, baselineFrom, elementName, changedNoteParaIds, originalZip, newZip, compressionLevel }) {
2126
1803
  const file = findNotePartEntry(originalZip, conventionalLowerPath);
2127
1804
  if (!file) return;
2128
- const originalXml = await file.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true }));
1805
+ const originalXml = await file.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true }));
2129
1806
  const baselineXml = baselineFrom(originalXml);
2130
1807
  const effectiveChangedNoteParaIds = changedNoteParaIds ?? collectChangedNoteParaIds(baselineXml, currentXml);
2131
1808
  const currentParaIds = collectParaIds(currentXml);
@@ -2142,18 +1819,10 @@ async function patchNotePartIntoZip({ conventionalLowerPath, currentXml, replace
2142
1819
  replacementXml,
2143
1820
  hasDirtyParagraph: [...effectiveChangedNoteParaIds].some((paraId) => currentParaIds.has(paraId)),
2144
1821
  elementName,
2145
- partName: file.name
1822
+ partName: file.path
2146
1823
  });
2147
1824
  if (patchedXml === null || patchedXml === originalXml) return;
2148
- writeZipPart({
2149
- zip: newZip,
2150
- path: file.name,
2151
- data: encodeUtf8(patchedXml),
2152
- options: {
2153
- compression: "DEFLATE",
2154
- compressionOptions: { level: compressionLevel }
2155
- }
2156
- });
1825
+ newZip.set(file.path, patchedXml, { level: compressionLevel });
2157
1826
  }
2158
1827
  /** `word/Footnotes.xml` -> `word/_rels/Footnotes.xml.rels` (casing preserved). */
2159
1828
  function notePartRelsPath(partPath) {
@@ -2165,9 +1834,12 @@ function notePartRelsPath(partPath) {
2165
1834
  * cased the entry differently still resolves to the existing part.
2166
1835
  */
2167
1836
  function findNotePartEntry(zip, conventionalLowerPath) {
2168
- const direct = zip.file(conventionalLowerPath);
1837
+ const direct = zip.entry(conventionalLowerPath);
2169
1838
  if (direct) return direct;
2170
- for (const [path, file] of Object.entries(zip.files)) if (!file.dir && path.toLowerCase() === conventionalLowerPath) return file;
1839
+ for (const file of zip.entries()) {
1840
+ const { path } = file;
1841
+ if (path.toLowerCase() === conventionalLowerPath) return file;
1842
+ }
2171
1843
  return null;
2172
1844
  }
2173
1845
  /** Update existing core-property values without synthesizing absent metadata. */
@@ -2249,11 +1921,8 @@ function createEmptyDocx(properties = {}) {
2249
1921
  return generateDocxZip(createEmptyDocxZip(properties), 6);
2250
1922
  }
2251
1923
  const createEmptyDocxZip = ({ creator, application }) => {
2252
- const zip = new JSZip();
2253
- writeZipPart({
2254
- zip,
2255
- path: "[Content_Types].xml",
2256
- data: encodeUtf8(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
1924
+ const zip = new ZipArchive();
1925
+ zip.set("[Content_Types].xml", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2257
1926
  <Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
2258
1927
  <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
2259
1928
  <Default Extension="xml" ContentType="application/xml"/>
@@ -2261,30 +1930,18 @@ const createEmptyDocxZip = ({ creator, application }) => {
2261
1930
  <Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/>
2262
1931
  <Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>
2263
1932
  <Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/>
2264
- </Types>`)
2265
- });
2266
- writeZipPart({
2267
- zip,
2268
- path: "_rels/.rels",
2269
- data: encodeUtf8(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
1933
+ </Types>`);
1934
+ zip.set("_rels/.rels", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2270
1935
  <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
2271
1936
  <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>
2272
1937
  <Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>
2273
1938
  <Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/>
2274
- </Relationships>`)
2275
- });
2276
- writeZipPart({
2277
- zip,
2278
- path: "word/_rels/document.xml.rels",
2279
- data: encodeUtf8(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
1939
+ </Relationships>`);
1940
+ zip.set("word/_rels/document.xml.rels", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2280
1941
  <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
2281
1942
  <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>
2282
- </Relationships>`)
2283
- });
2284
- writeZipPart({
2285
- zip,
2286
- path: "word/document.xml",
2287
- data: encodeUtf8(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
1943
+ </Relationships>`);
1944
+ zip.set("word/document.xml", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2288
1945
  <w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
2289
1946
  <w:body>
2290
1947
  <w:p>
@@ -2297,32 +1954,19 @@ const createEmptyDocxZip = ({ creator, application }) => {
2297
1954
  <w:pgMar w:top="1440" w:right="1440" w:bottom="1440" w:left="1440" w:header="720" w:footer="720" w:gutter="0"/>
2298
1955
  </w:sectPr>
2299
1956
  </w:body>
2300
- </w:document>`)
2301
- });
2302
- writeZipPart({
2303
- zip,
2304
- path: STYLES_PART_PATH,
2305
- data: encodeUtf8(SEED_STYLES_XML)
2306
- });
1957
+ </w:document>`);
1958
+ zip.set(STYLES_PART_PATH, SEED_STYLES_XML);
2307
1959
  const now = (/* @__PURE__ */ new Date()).toISOString();
2308
1960
  const creatorElement = creator === void 0 ? "" : `\n <dc:creator>${escapeXmlText(creator)}</dc:creator>`;
2309
- writeZipPart({
2310
- zip,
2311
- path: "docProps/core.xml",
2312
- data: encodeUtf8(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
1961
+ zip.set("docProps/core.xml", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2313
1962
  <cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">${creatorElement}
2314
1963
  <dcterms:created xsi:type="dcterms:W3CDTF">${now}</dcterms:created>
2315
1964
  <dcterms:modified xsi:type="dcterms:W3CDTF">${now}</dcterms:modified>
2316
- </cp:coreProperties>`)
2317
- });
1965
+ </cp:coreProperties>`);
2318
1966
  const applicationElements = application === void 0 ? "" : `\n <Application>${escapeXmlText(application)}</Application>\n <AppVersion>${CREATED_APP_VERSION}</AppVersion>`;
2319
- writeZipPart({
2320
- zip,
2321
- path: "docProps/app.xml",
2322
- data: encodeUtf8(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
1967
+ zip.set("docProps/app.xml", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2323
1968
  <Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties">${applicationElements}
2324
- </Properties>`)
2325
- });
1969
+ </Properties>`);
2326
1970
  return zip;
2327
1971
  };
2328
1972
  /**
@@ -2354,7 +1998,7 @@ async function createDocx(doc, properties = {}) {
2354
1998
  originalZip: zip,
2355
1999
  outputZip: zip,
2356
2000
  originalDocument: void 0,
2357
- originalCorePropertiesXml: await zip.file("docProps/core.xml")?.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true })),
2001
+ originalCorePropertiesXml: await zip.entry("docProps/core.xml")?.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true })),
2358
2002
  compressionLevel: 6,
2359
2003
  updateModifiedDate: true,
2360
2004
  onDiagnostic: properties.onDiagnostic,
@@ -2369,70 +2013,38 @@ const createDocumentSeedZip = async (doc, properties) => {
2369
2013
  const styleDefinitions = styleDefinitionsToSerialize(doc);
2370
2014
  if (styleDefinitions) {
2371
2015
  assertStyleNumberingReferences(doc);
2372
- writeZipPart({
2373
- zip,
2374
- path: STYLES_PART_PATH,
2375
- data: encodeUtf8(serializeStylesXml(styleDefinitions))
2376
- });
2377
- } else writeZipPart({
2378
- zip,
2379
- path: STYLES_PART_PATH,
2380
- data: encodeUtf8(seedStylesXmlWith(missingNoteReferenceStyles(void 0, noteReferenceNeeds(doc.package))))
2381
- });
2016
+ zip.set(STYLES_PART_PATH, serializeStylesXml(styleDefinitions));
2017
+ } else zip.set(STYLES_PART_PATH, seedStylesXmlWith(missingNoteReferenceStyles(void 0, noteReferenceNeeds(doc.package))));
2382
2018
  const numbering = doc.package.numbering;
2383
2019
  if (numbering && (numbering.abstractNums.length > 0 || numbering.nums.length > 0)) {
2384
- writeZipPart({
2385
- zip,
2386
- path: "word/numbering.xml",
2387
- data: encodeUtf8(serializeNumberingXml(numbering))
2388
- });
2020
+ zip.set("word/numbering.xml", serializeNumberingXml(numbering));
2389
2021
  relationships.push(relationshipXml(nextRelationshipId, "numbering", "numbering.xml"));
2390
2022
  overrides.push(overrideXml("/word/numbering.xml", "application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml"));
2391
2023
  nextRelationshipId += 1;
2392
2024
  }
2393
2025
  if (doc.package.theme) {
2394
- writeZipPart({
2395
- zip,
2396
- path: "word/theme/theme1.xml",
2397
- data: encodeUtf8(serializeThemeXml(doc.package.theme))
2398
- });
2026
+ zip.set("word/theme/theme1.xml", serializeThemeXml(doc.package.theme));
2399
2027
  relationships.push(relationshipXml(nextRelationshipId, "theme", "theme/theme1.xml"));
2400
2028
  overrides.push(overrideXml("/word/theme/theme1.xml", "application/vnd.openxmlformats-officedocument.theme+xml"));
2401
2029
  nextRelationshipId += 1;
2402
2030
  }
2403
2031
  if (doc.package.fontTable && doc.package.fontTable.fonts.length > 0) {
2404
- writeZipPart({
2405
- zip,
2406
- path: "word/fontTable.xml",
2407
- data: encodeUtf8(serializeFontTableXml(doc.package.fontTable))
2408
- });
2032
+ zip.set("word/fontTable.xml", serializeFontTableXml(doc.package.fontTable));
2409
2033
  relationships.push(relationshipXml(nextRelationshipId, "fontTable", "fontTable.xml"));
2410
2034
  overrides.push(overrideXml("/word/fontTable.xml", "application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml"));
2411
2035
  nextRelationshipId += 1;
2412
2036
  }
2413
2037
  if (doc.package.settings) {
2414
- writeZipPart({
2415
- zip,
2416
- path: "word/settings.xml",
2417
- data: encodeUtf8(serializeSettingsXml(doc.package.settings))
2418
- });
2038
+ zip.set("word/settings.xml", serializeSettingsXml(doc.package.settings));
2419
2039
  relationships.push(relationshipXml(nextRelationshipId, "settings", "settings.xml"));
2420
2040
  overrides.push(overrideXml("/word/settings.xml", "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml"));
2421
2041
  }
2422
- writeZipPart({
2423
- zip,
2424
- path: "word/_rels/document.xml.rels",
2425
- data: encodeUtf8(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">${relationships.join("")}</Relationships>`)
2426
- });
2042
+ zip.set("word/_rels/document.xml.rels", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">${relationships.join("")}</Relationships>`);
2427
2043
  if (overrides.length > 0) {
2428
- const contentTypesFile = zip.file("[Content_Types].xml");
2044
+ const contentTypesFile = zip.entry("[Content_Types].xml");
2429
2045
  if (!contentTypesFile) panic("Fresh DOCX seed is missing [Content_Types].xml");
2430
- const contentTypes = await contentTypesFile.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true }));
2431
- writeZipPart({
2432
- zip,
2433
- path: "[Content_Types].xml",
2434
- data: encodeUtf8(contentTypes.replace("</Types>", `${overrides.join("")}\n</Types>`))
2435
- });
2046
+ const contentTypes = await contentTypesFile.bytes().then((bytes) => decodeText(bytes, { ignoreBOM: true }));
2047
+ zip.set("[Content_Types].xml", contentTypes.replace("</Types>", `${overrides.join("")}\n</Types>`));
2436
2048
  }
2437
2049
  return zip;
2438
2050
  };