@stll/folio-core 0.19.0 → 0.21.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.
@@ -61,12 +61,11 @@ type HiddenEditorManagerDeps = {
61
61
  getPrecomputedInitialState: () => EditorState | null | undefined;
62
62
  getReadOnly: () => boolean;
63
63
  /**
64
- * Caller-provided stable identity of the loaded document: the same key across
65
- * internal edits (so typing does not trigger an external re-sync) and a
66
- * distinct key per loaded file. Authoritative when present; a metadata
67
- * signature is used as a fallback when undefined.
64
+ * Identity of the loaded document as tracked by the adapter's loader: the
65
+ * same value across internal edits (so typing does not trigger an external
66
+ * re-sync) and a distinct value per load.
68
67
  */
69
- getDocumentKey: () => string | undefined;
68
+ getDocumentIdentity: () => string;
70
69
  /** Document context for the API's `getDocument` (PM state -> Document). */
71
70
  getDocumentContext: () => document_d_exports.Document | null;
72
71
  onTransaction: (transaction: Transaction, newState: EditorState) => void;
@@ -152,11 +152,6 @@ function createHiddenEditorState(options) {
152
152
  recordHiddenEditorPhase(reason, "editor-state", performance.now() - startedAt);
153
153
  return state;
154
154
  }
155
- function getDocumentIdentity(doc) {
156
- if (!doc) return "empty";
157
- const meta = doc.package.properties;
158
- return `${meta?.created ? String(meta.created) : ""}-${meta?.modified ? String(meta.modified) : ""}-${meta?.title ?? ""}`;
159
- }
160
155
  function syncHiddenEditorAccessibility(view, readOnly) {
161
156
  const { dom } = view;
162
157
  if (!dom.hasAttribute("tabindex")) dom.tabIndex = 0;
@@ -193,7 +188,6 @@ const createHiddenEditorManager = (deps) => {
193
188
  let isInitialized = false;
194
189
  let lastDocumentId = null;
195
190
  let lastCollaborationFragment = null;
196
- const currentDocumentIdentity = () => deps.getDocumentKey() ?? getDocumentIdentity(deps.getDocument());
197
191
  const tryCreate = () => {
198
192
  if (!requested || view !== null || isDestroying) return;
199
193
  const host = deps.getHost();
@@ -264,7 +258,7 @@ const createHiddenEditorManager = (deps) => {
264
258
  recordHiddenEditorPhase("mount", "editor-view", performance.now() - viewStartedAt);
265
259
  syncHiddenEditorAccessibility(view, deps.getReadOnly());
266
260
  isInitialized = true;
267
- lastDocumentId = currentDocumentIdentity();
261
+ lastDocumentId = deps.getDocumentIdentity();
268
262
  lastCollaborationFragment = collaboration?.yXmlFragment ?? null;
269
263
  deps.onEditorViewReady(view);
270
264
  };
@@ -291,7 +285,7 @@ const createHiddenEditorManager = (deps) => {
291
285
  const collaborationModules = deps.getCollaborationModules();
292
286
  if (collaboration && !collaborationModules) return;
293
287
  const document = deps.getDocument();
294
- const currentDocId = currentDocumentIdentity();
288
+ const currentDocId = deps.getDocumentIdentity();
295
289
  const currentCollaborationFragment = collaboration?.yXmlFragment ?? null;
296
290
  const collaborationSourceChanged = currentCollaborationFragment !== lastCollaborationFragment;
297
291
  if (collaboration && !collaborationSourceChanged) return;
@@ -712,14 +712,16 @@ function parseParagraphContents(paraElement, styles, theme, _numbering, rels, me
712
712
  const run = parseRun(runElement, styles, theme, rels, media, inScopeXmlns);
713
713
  const commentReferenceId = getCommentReferenceId(runElement);
714
714
  let hasFieldBegin = false;
715
+ let beginFldLock = false;
716
+ let beginDirty = false;
715
717
  let hasFieldSeparate = false;
716
718
  let hasFieldEnd = false;
717
719
  let endOriginalValue;
718
720
  let instrText = "";
719
721
  for (const content of run.content) if (content.type === "fieldChar") if (content.charType === "begin") {
720
722
  hasFieldBegin = true;
721
- if (content.fldLock) complexFieldLock = true;
722
- if (content.dirty) complexFieldDirty = true;
723
+ beginFldLock = content.fldLock === true;
724
+ beginDirty = content.dirty === true;
723
725
  } else if (content.charType === "separate") hasFieldSeparate = true;
724
726
  else {
725
727
  hasFieldEnd = true;
@@ -736,8 +738,8 @@ function parseParagraphContents(paraElement, styles, theme, _numbering, rels, me
736
738
  complexFieldInstr = "";
737
739
  complexFieldCodeRuns = [];
738
740
  complexFieldResultRuns = [];
739
- complexFieldLock = false;
740
- complexFieldDirty = false;
741
+ complexFieldLock = beginFldLock;
742
+ complexFieldDirty = beginDirty;
741
743
  complexFieldFormatting = run.formatting;
742
744
  }
743
745
  if (inComplexField) {
@@ -371,8 +371,12 @@ function parseHeadersAndFooters(raw, styles, theme, numbering, rels, media) {
371
371
  * Parse footnotes and endnotes from raw content
372
372
  */
373
373
  function parseNotesContent(raw, styles, theme, numbering, rels, media) {
374
- const footnoteMap = parseFootnotes(raw.footnotesXml, styles, theme, numbering, rels, media);
375
- const endnoteMap = parseEndnotes(raw.endnotesXml, styles, theme, numbering, rels, media);
374
+ const relsForNotePart = (partPath) => {
375
+ const xml = getMapCaseInsensitive(raw.allXml, getRelationshipsPathForPart(partPath));
376
+ return xml ? parseRelationships(xml) : rels;
377
+ };
378
+ const footnoteMap = parseFootnotes(raw.footnotesXml, styles, theme, numbering, relsForNotePart("word/footnotes.xml"), media);
379
+ const endnoteMap = parseEndnotes(raw.endnotesXml, styles, theme, numbering, relsForNotePart("word/endnotes.xml"), media);
376
380
  return {
377
381
  footnotes: footnoteMap.getNormalFootnotes(),
378
382
  endnotes: endnoteMap.getNormalEndnotes()
@@ -1,4 +1,5 @@
1
1
  import { document_d_exports } from "../types/document.js";
2
+ import { BlockContent, Hyperlink } from "../types/content.js";
2
3
  import { RawDocxContent } from "./unzip.js";
3
4
  import JSZip from "jszip";
4
5
  //#region src/docx/rezip.d.ts
@@ -9,6 +10,11 @@ declare class DocxPackageFidelityError extends Error {
9
10
  * Find the highest rId number in a relationships XML string.
10
11
  */
11
12
  declare function findMaxRId(relsXml: string): number;
13
+ /**
14
+ * Collect all hyperlinks that have an href but no rId from block content.
15
+ * These are newly created hyperlinks that need relationship entries.
16
+ */
17
+ declare function collectHyperlinksWithoutRId(blocks: BlockContent[]): Hyperlink[];
12
18
  /**
13
19
  * Options for repacking DOCX
14
20
  */
@@ -126,6 +132,8 @@ declare function hasUnmaterializedHeaderFooter(doc: document_d_exports.Document)
126
132
  */
127
133
  declare function hasModelDrivenPictureWatermark(doc: document_d_exports.Document): boolean;
128
134
  declare function collectHeaderFooterUpdates(doc: document_d_exports.Document): Map<string, string>;
135
+ /** `word/Footnotes.xml` -> `word/_rels/Footnotes.xml.rels` (casing preserved). */
136
+ declare function notePartRelsPath(partPath: string): string;
129
137
  /** Update existing core-property values without synthesizing absent metadata. */
130
138
  declare function updateCoreProperties(corePropsXml: string, { updateModifiedDate, modifiedBy }: {
131
139
  updateModifiedDate?: boolean;
@@ -163,4 +171,4 @@ declare function createEmptyDocx(): Promise<ArrayBuffer>;
163
171
  */
164
172
  declare function createDocx(doc: document_d_exports.Document): Promise<ArrayBuffer>;
165
173
  //#endregion
166
- export { COMMENTS_CONTENT_TYPE, COMMENTS_EXTENDED_CONTENT_TYPE, COMMENTS_EXTENDED_PART, COMMENTS_EXTENDED_PART_LOWER, DocxPackageFidelityError, RepackOptions, addCommentsExtendedOverride, addCommentsExtendedRelationship, addMedia, addRelationship, applyUpdatesToZip, collectHeaderFooterUpdates, createDocx, createEmptyDocx, findMaxRId, hasModelDrivenPictureWatermark, hasUnmaterializedHeaderFooter, isDocxBuffer, removeCommentsExtendedOverride, removeCommentsExtendedRelationship, repackDocx, repackDocxFromRaw, updateCoreProperties, updateDocumentXml, updateMultipleFiles, updateXmlFile, validateDocx };
174
+ export { COMMENTS_CONTENT_TYPE, COMMENTS_EXTENDED_CONTENT_TYPE, COMMENTS_EXTENDED_PART, COMMENTS_EXTENDED_PART_LOWER, DocxPackageFidelityError, RepackOptions, addCommentsExtendedOverride, addCommentsExtendedRelationship, addMedia, addRelationship, applyUpdatesToZip, collectHeaderFooterUpdates, collectHyperlinksWithoutRId, createDocx, createEmptyDocx, findMaxRId, hasModelDrivenPictureWatermark, hasUnmaterializedHeaderFooter, isDocxBuffer, notePartRelsPath, removeCommentsExtendedOverride, removeCommentsExtendedRelationship, repackDocx, repackDocxFromRaw, updateCoreProperties, updateDocumentXml, updateMultipleFiles, updateXmlFile, validateDocx };
@@ -18,7 +18,7 @@ import { serializeStylesXml } from "./serializer/stylesSerializer.js";
18
18
  import { serializeThemeXml } from "./serializer/themeSerializer.js";
19
19
  import { escapeXml } from "./serializer/xmlUtils.js";
20
20
  import { isPreservableDocxEntry } from "./unzip.js";
21
- import { findChild, getChildElements, getLocalName, getNamespaceUri, matchesName, parseXml } from "./xmlParser.js";
21
+ import { WORDPROCESSINGML_NAMESPACE_URIS, findChild, getChildElements, getLocalName, getNamespaceUri, matchesName, parseXml } from "./xmlParser.js";
22
22
  import { assertXmlResourceLimits } from "./xmlResourceLimits.js";
23
23
  import { panic } from "better-result";
24
24
  import JSZip from "jszip";
@@ -70,8 +70,7 @@ function findMaxRId(relsXml) {
70
70
  }
71
71
  return maxId;
72
72
  }
73
- const WORDPROCESSINGML_NAMESPACES = /* @__PURE__ */ new Set(["http://schemas.openxmlformats.org/wordprocessingml/2006/main", "http://purl.oclc.org/ooxml/wordprocessingml/main"]);
74
- const isWordprocessingElement = (element, localName) => getLocalName(element.name) === localName && WORDPROCESSINGML_NAMESPACES.has(getNamespaceUri(element) ?? "");
73
+ const isWordprocessingElement = (element, localName) => getLocalName(element.name) === localName && WORDPROCESSINGML_NAMESPACE_URIS.has(getNamespaceUri(element) ?? "");
75
74
  const countDocumentSections = (xml) => {
76
75
  assertXmlResourceLimits(xml);
77
76
  let count = 0;
@@ -212,15 +211,22 @@ function relativeTargetForPart(partPath, absoluteTarget) {
212
211
  while (shared < fromDir.length && shared < to.length - 1 && fromDir[shared] === to[shared]) shared += 1;
213
212
  return `${"../".repeat(fromDir.length - shared)}${to.slice(shared).join("/")}`;
214
213
  }
215
- function collectImageParts(doc) {
214
+ /**
215
+ * Every package part whose block content may mint relationships (images,
216
+ * external hyperlinks), paired with the rels part those relationships belong
217
+ * to. Header/footer parts own `word/_rels/<part>.rels`; footnotes and endnotes
218
+ * own `word/_rels/<notes part>.rels`, derived from the ZIP entry the note
219
+ * serializer resolves (case-insensitively) so a producer's casing is reused
220
+ * instead of minting a second, lowercase rels part.
221
+ */
222
+ function collectDocxParts(doc, zip) {
216
223
  const parts = [{
217
224
  relsPath: "word/_rels/document.xml.rels",
218
225
  blocks: doc.package.document.content
219
226
  }];
220
227
  const rels = doc.package.relationships;
221
- if (!rels) return parts;
222
228
  const addHeaderFooterParts = (map, type) => {
223
- if (!map) return;
229
+ if (!map || !rels) return;
224
230
  for (const [rId, headerFooter] of map.entries()) {
225
231
  const rel = rels.get(rId);
226
232
  if (!rel || rel.type !== type || !rel.target) continue;
@@ -232,6 +238,16 @@ function collectImageParts(doc) {
232
238
  };
233
239
  addHeaderFooterParts(doc.package.headers, RELATIONSHIP_TYPES.header);
234
240
  addHeaderFooterParts(doc.package.footers, RELATIONSHIP_TYPES.footer);
241
+ const addNoteParts = (notes, conventionalLowerPath) => {
242
+ if (!notes || notes.length === 0) return;
243
+ const partPath = findNotePartEntry(zip, conventionalLowerPath)?.name ?? conventionalLowerPath;
244
+ parts.push({
245
+ relsPath: notePartRelsPath(partPath),
246
+ blocks: notes.flatMap((note) => note.content)
247
+ });
248
+ };
249
+ addNoteParts(doc.package.footnotes, "word/footnotes.xml");
250
+ addNoteParts(doc.package.endnotes, "word/endnotes.xml");
235
251
  return parts;
236
252
  }
237
253
  async function readRelsOrStub(zip, relsPath) {
@@ -360,30 +376,29 @@ function collectHyperlinksWithoutRId(blocks) {
360
376
  for (const block of blocks) if (block.type === "paragraph") {
361
377
  for (const item of block.content) if (item.type === "hyperlink" && item.href && !item.rId && !item.anchor) hyperlinks.push(item);
362
378
  } else if (block.type === "table") for (const row of block.rows) for (const cell of row.cells) hyperlinks.push(...collectHyperlinksWithoutRId(cell.content));
379
+ else hyperlinks.push(...collectHyperlinksWithoutRId(block.content));
363
380
  return hyperlinks;
364
381
  }
365
382
  /**
366
- * Process newly created hyperlinks: assign rIds and add relationship entries.
367
- * Mutates the hyperlinks' rId fields in-place.
383
+ * Process newly created hyperlinks in every part: assign rIds and add
384
+ * relationship entries to the owning part's rels. Mutates the hyperlinks' rId
385
+ * fields in-place.
368
386
  */
369
- async function processNewHyperlinks(newHyperlinks, zip, compressionLevel) {
370
- if (newHyperlinks.length === 0) return;
371
- const relsPath = "word/_rels/document.xml.rels";
372
- const relsFile = zip.file(relsPath);
373
- if (!relsFile) return;
374
- let relsXml = await relsFile.async("text");
375
- let maxId = findMaxRId(relsXml);
376
- const relEntries = [];
377
- for (const hyperlink of newHyperlinks) {
378
- maxId++;
379
- const newRId = `rId${maxId}`;
380
- if (!hyperlink.href) continue;
381
- relEntries.push(`<Relationship Id="${newRId}" Type="${RELATIONSHIP_TYPES.hyperlink}" Target="${escapeXml(hyperlink.href)}" TargetMode="External"/>`);
382
- hyperlink.rId = newRId;
383
- }
384
- if (relEntries.length > 0) {
385
- relsXml = relsXml.replace("</Relationships>", `${relEntries.join("")}</Relationships>`);
386
- zip.file(relsPath, relsXml, {
387
+ async function processNewHyperlinks(parts, zip, compressionLevel) {
388
+ for (const { relsPath, blocks } of parts) {
389
+ const newHyperlinks = collectHyperlinksWithoutRId(blocks);
390
+ if (newHyperlinks.length === 0) continue;
391
+ const relsXml = await readRelsOrStub(zip, relsPath);
392
+ let maxId = findMaxRId(relsXml);
393
+ const relEntries = [];
394
+ for (const hyperlink of newHyperlinks) {
395
+ if (!hyperlink.href) continue;
396
+ maxId++;
397
+ const newRId = `rId${maxId}`;
398
+ relEntries.push(`<Relationship Id="${newRId}" Type="${RELATIONSHIP_TYPES.hyperlink}" Target="${escapeXml(hyperlink.href)}" TargetMode="External"/>`);
399
+ hyperlink.rId = newRId;
400
+ }
401
+ zip.file(relsPath, relsXml.replace("</Relationships>", `${relEntries.join("")}</Relationships>`), {
387
402
  compression: "DEFLATE",
388
403
  compressionOptions: { level: compressionLevel }
389
404
  });
@@ -420,8 +435,9 @@ const cloneDocxZip = (source) => {
420
435
  };
421
436
  const finishRepack = async ({ document, originalZip, outputZip, originalDocumentXml, originalCorePropertiesXml, compressionLevel, updateModifiedDate, modifiedBy }) => {
422
437
  await materializeNewHeaderFooterParts(document, outputZip, compressionLevel);
423
- await processNewImages(collectImageParts(document), outputZip, compressionLevel);
424
- await processNewHyperlinks(collectHyperlinksWithoutRId(document.package.document.content), outputZip, compressionLevel);
438
+ const parts = collectDocxParts(document, outputZip);
439
+ await processNewImages(parts, outputZip, compressionLevel);
440
+ await processNewHyperlinks(parts, outputZip, compressionLevel);
425
441
  assertValidFolioDocumentModel(document, "Cannot repack invalid DOCX document model");
426
442
  applyReplyThreadMarkers(document);
427
443
  const documentXml = serializeDocument(document);
@@ -499,8 +515,9 @@ async function repackDocxFromRaw(doc, rawContent, options = {}) {
499
515
  });
500
516
  }
501
517
  await materializeNewHeaderFooterParts(exportDocument, newZip, compressionLevel);
502
- await processNewImages(collectImageParts(exportDocument), newZip, compressionLevel);
503
- await processNewHyperlinks(collectHyperlinksWithoutRId(exportDocument.package.document.content), newZip, compressionLevel);
518
+ const parts = collectDocxParts(exportDocument, newZip);
519
+ await processNewImages(parts, newZip, compressionLevel);
520
+ await processNewHyperlinks(parts, newZip, compressionLevel);
504
521
  assertValidFolioDocumentModel(exportDocument, "Cannot repack invalid DOCX document model");
505
522
  applyReplyThreadMarkers(exportDocument);
506
523
  const documentXml = serializeDocument(exportDocument);
@@ -768,14 +785,17 @@ function findMaxHeaderFooterNum(zip, prefix) {
768
785
  * relationship.
769
786
  */
770
787
  async function materializeNewHeaderFooterParts(doc, zip, compressionLevel) {
771
- const rels = doc.package.relationships;
772
- if (!rels) return;
773
788
  if (!hasUnmaterializedHeaderFooter(doc)) return;
789
+ doc.package.relationships ??= /* @__PURE__ */ new Map();
790
+ const rels = doc.package.relationships;
774
791
  const relEntries = [];
775
792
  const overrides = [];
776
793
  let maxHeaderNum = findMaxHeaderFooterNum(zip, "header");
777
794
  let maxFooterNum = findMaxHeaderFooterNum(zip, "footer");
778
- let maxRId = 0;
795
+ const relsPath = "word/_rels/document.xml.rels";
796
+ const relsXml = await readRelsOrStub(zip, relsPath);
797
+ const zipRels = parseRelationships(relsXml);
798
+ let maxRId = findMaxRId(relsXml);
779
799
  const considerNumericRId = (id) => {
780
800
  const match = /^rId(?<num>\d+)$/u.exec(id);
781
801
  if (match) {
@@ -792,7 +812,7 @@ async function materializeNewHeaderFooterParts(doc, zip, compressionLevel) {
792
812
  const materialize = (map, relType, prefix, contentType, isHeader) => {
793
813
  if (!map) return;
794
814
  for (const rId of [...map.keys()]) {
795
- const existing = rels.get(rId);
815
+ const existing = rels.get(rId) ?? zipRels.get(rId);
796
816
  if (existing && existing.type === relType && existing.target) continue;
797
817
  let effectiveRId = rId;
798
818
  if (existing) {
@@ -820,8 +840,6 @@ async function materializeNewHeaderFooterParts(doc, zip, compressionLevel) {
820
840
  materialize(doc.package.footers, RELATIONSHIP_TYPES.footer, "footer", FOOTER_CONTENT_TYPE, false);
821
841
  if (relEntries.length === 0) return;
822
842
  const compressionOptions = { level: compressionLevel };
823
- const relsPath = "word/_rels/document.xml.rels";
824
- const relsXml = await readRelsOrStub(zip, relsPath);
825
843
  zip.file(relsPath, relsXml.replace("</Relationships>", `${relEntries.join("")}</Relationships>`), {
826
844
  compression: "DEFLATE",
827
845
  compressionOptions
@@ -1109,6 +1127,11 @@ function collectChangedNoteParaIds(baselineXml, currentXml) {
1109
1127
  }
1110
1128
  return changed;
1111
1129
  }
1130
+ /** `word/Footnotes.xml` -> `word/_rels/Footnotes.xml.rels` (casing preserved). */
1131
+ function notePartRelsPath(partPath) {
1132
+ const lastSlash = partPath.lastIndexOf("/");
1133
+ return `${partPath.slice(0, lastSlash + 1)}_rels/${partPath.slice(lastSlash + 1)}.rels`;
1134
+ }
1112
1135
  /**
1113
1136
  * Locate a note part in the ZIP, matching case-insensitively so a producer that
1114
1137
  * cased the entry differently still resolves to the existing part.
@@ -1362,4 +1385,4 @@ const assertStyleNumberingReferences = (doc) => {
1362
1385
  for (const numbering of doc.package.numbering?.nums ?? []) if (!availableAbstract.has(numbering.abstractNumId)) panic(`Numbering definition ${numbering.numId} references missing abstract numbering`);
1363
1386
  };
1364
1387
  //#endregion
1365
- export { COMMENTS_CONTENT_TYPE, COMMENTS_EXTENDED_CONTENT_TYPE, COMMENTS_EXTENDED_PART, COMMENTS_EXTENDED_PART_LOWER, DocxPackageFidelityError, addCommentsExtendedOverride, addCommentsExtendedRelationship, addMedia, addRelationship, applyUpdatesToZip, collectHeaderFooterUpdates, createDocx, createEmptyDocx, findMaxRId, hasModelDrivenPictureWatermark, hasUnmaterializedHeaderFooter, isDocxBuffer, removeCommentsExtendedOverride, removeCommentsExtendedRelationship, repackDocx, repackDocxFromRaw, updateCoreProperties, updateDocumentXml, updateMultipleFiles, updateXmlFile, validateDocx };
1388
+ export { COMMENTS_CONTENT_TYPE, COMMENTS_EXTENDED_CONTENT_TYPE, COMMENTS_EXTENDED_PART, COMMENTS_EXTENDED_PART_LOWER, DocxPackageFidelityError, addCommentsExtendedOverride, addCommentsExtendedRelationship, addMedia, addRelationship, applyUpdatesToZip, collectHeaderFooterUpdates, collectHyperlinksWithoutRId, createDocx, createEmptyDocx, findMaxRId, hasModelDrivenPictureWatermark, hasUnmaterializedHeaderFooter, isDocxBuffer, notePartRelsPath, removeCommentsExtendedOverride, removeCommentsExtendedRelationship, repackDocx, repackDocxFromRaw, updateCoreProperties, updateDocumentXml, updateMultipleFiles, updateXmlFile, validateDocx };
@@ -182,6 +182,12 @@ async function attemptSelectiveSave(doc, originalBuffer, options) {
182
182
  if (originalBuffer.byteLength > maxBytes) return null;
183
183
  const content = doc.package.document.content;
184
184
  if (hasNewImagesOrHyperlinks(content)) return null;
185
+ for (const part of [
186
+ ...doc.package.headers?.values() ?? [],
187
+ ...doc.package.footers?.values() ?? [],
188
+ ...doc.package.footnotes ?? [],
189
+ ...doc.package.endnotes ?? []
190
+ ]) if (hasNewImagesOrHyperlinks(part.content)) return null;
185
191
  if (hasUnmaterializedHeaderFooter(doc)) return null;
186
192
  if (hasModelDrivenPictureWatermark(doc)) return null;
187
193
  if (withoutOrphanCommentRanges(doc) !== doc) return null;
@@ -263,6 +263,7 @@ function serializeComplexField(field) {
263
263
  const rPrXml = structuralFormatting ? serializeTextFormatting(structuralFormatting) : "";
264
264
  const beginAttrs = ["w:fldCharType=\"begin\""];
265
265
  if (field.fldLock) beginAttrs.push("w:fldLock=\"true\"");
266
+ if (field.dirty) beginAttrs.push("w:dirty=\"true\"");
266
267
  parts.push(`<w:r>${rPrXml}<w:fldChar ${beginAttrs.join(" ")}/></w:r>`);
267
268
  if (field.fieldCode.length > 0) parts.push(...field.fieldCode.map((run) => serializeRun(run)));
268
269
  else if (field.instruction.length > 0) {
@@ -4,6 +4,7 @@ const W_NS = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
4
4
  const serializeSettingsXml = (settings) => {
5
5
  const parts = [`<w:defaultTabStop w:val="${intAttr(settings.defaultTabStop)}"/>`];
6
6
  if (settings.evenAndOddHeaders) parts.push("<w:evenAndOddHeaders/>");
7
+ if (settings.updateFields) parts.push("<w:updateFields w:val=\"true\"/>");
7
8
  if (settings.themeFontLang) {
8
9
  const attrs = [];
9
10
  if (settings.themeFontLang.eastAsia) attrs.push(`w:eastAsia="${escapeXml(settings.themeFontLang.eastAsia)}"`);
@@ -0,0 +1,103 @@
1
+ import { document_d_exports } from "../../types/document.js";
2
+ import { ShadingProperties } from "../../types/colors.js";
3
+ import { Hyperlink, Paragraph, ParagraphContent, Run, Table } from "../../types/content.js";
4
+ import { ParagraphFormatting, TextFormatting } from "../../types/formatting.js";
5
+ //#region src/docx/server/build.d.ts
6
+ declare const HEADING_LEVELS: readonly [1, 2, 3, 4, 5, 6];
7
+ type HeadingLevel = (typeof HEADING_LEVELS)[number];
8
+ declare const InvalidFolioReportBuilderOptionsError_base: import("better-result").TaggedErrorClass<"InvalidFolioReportBuilderOptionsError", {
9
+ message: string;
10
+ path: string;
11
+ }>;
12
+ /**
13
+ * A builder received a value outside the model's domain (a zero `gridSpan`,
14
+ * a negative column width, a reversed TOC range). Thrown at the boundary so
15
+ * the invalid value never reaches the serializer.
16
+ */
17
+ declare class InvalidFolioReportBuilderOptionsError extends InvalidFolioReportBuilderOptionsError_base {}
18
+ declare const run: (text: string, formatting?: TextFormatting) => Run;
19
+ declare const paragraph: (content: string | ParagraphContent[], formatting?: ParagraphFormatting) => Paragraph;
20
+ type HeadingOptions = {
21
+ text: string;
22
+ level: HeadingLevel;
23
+ };
24
+ /** A paragraph in the `Heading<level>` style. */
25
+ declare const heading: ({ text, level }: HeadingOptions) => Paragraph;
26
+ /** An empty paragraph carrying a hard page break. */
27
+ declare const pageBreak: () => Paragraph;
28
+ type HyperlinkTarget = {
29
+ href: string;
30
+ anchor?: never;
31
+ } | {
32
+ anchor: string;
33
+ href?: never;
34
+ };
35
+ type HyperlinkOptions = HyperlinkTarget & {
36
+ text: string;
37
+ formatting?: TextFormatting;
38
+ tooltip?: string;
39
+ };
40
+ /**
41
+ * An external (`href`) or in-document (`anchor`, a bookmark name) link. The
42
+ * relationship for an external link is minted when the document is written.
43
+ */
44
+ declare const hyperlink: ({ text, formatting, tooltip, href, anchor }: HyperlinkOptions) => Hyperlink;
45
+ type BookmarkOptions = {
46
+ name: string;
47
+ content: ParagraphContent[];
48
+ /**
49
+ * Bookmark id, unique per document. Defaults to a process-wide counter,
50
+ * which is unique for documents built from scratch; pass an explicit id
51
+ * when adding bookmarks to a parsed document.
52
+ */
53
+ id?: number;
54
+ };
55
+ /** `content` wrapped in a named bookmark, the target of `hyperlink({ anchor })`. */
56
+ declare const bookmark: ({ name, content, id }: BookmarkOptions) => ParagraphContent[];
57
+ type TableCellSpec = string | {
58
+ content: Paragraph[];
59
+ shading?: ShadingProperties;
60
+ gridSpan?: number;
61
+ vMerge?: "restart" | "continue";
62
+ };
63
+ type TableOptions = {
64
+ /** Header row labels; rendered bold, optionally shaded and repeated per page. */
65
+ header?: string[];
66
+ rows: TableCellSpec[][];
67
+ /** Grid column widths in twips. Omitted: the consumer autofits. */
68
+ columnWidths?: number[];
69
+ headerShading?: ShadingProperties;
70
+ /** Repeat the header row at the top of every page (default true). */
71
+ repeatHeader?: boolean;
72
+ };
73
+ /**
74
+ * A full-width grid table in the `TableGrid` style. A string cell becomes one
75
+ * plain paragraph; an object cell supplies its own paragraphs plus optional
76
+ * shading and horizontal (`gridSpan`) or vertical (`vMerge`) merge.
77
+ */
78
+ declare const table: ({ header, rows, columnWidths, headerShading, repeatHeader }: TableOptions) => Table;
79
+ /**
80
+ * Register an endnote on `doc` and return the reference run to place in body
81
+ * text. Allocates the next free endnote id (Word reserves 0 and -1 for the
82
+ * separator notes) and pushes the note into `doc.package.endnotes`.
83
+ */
84
+ declare const endnote: (doc: document_d_exports.Document, content: string | Paragraph[]) => Run;
85
+ type TableOfContentsOptions = {
86
+ /** Heading levels to include, `1 <= from <= to <= 9` (default 1-3). */
87
+ levels?: {
88
+ from: number;
89
+ to: number;
90
+ };
91
+ /** Make entries hyperlinks (`\h`, default true). */
92
+ hyperlinks?: boolean;
93
+ /** Result text shown until the consumer recomputes the field. */
94
+ placeholderText?: string;
95
+ };
96
+ /**
97
+ * A paragraph holding a dirty `TOC` field, so the consumer recomputes the
98
+ * table on open. Set `package.settings.updateFields` as well to have Word
99
+ * recompute without prompting for each field.
100
+ */
101
+ declare const createTableOfContentsField: ({ levels, hyperlinks, placeholderText }?: TableOfContentsOptions) => Paragraph;
102
+ //#endregion
103
+ export { HEADING_LEVELS, HeadingLevel, InvalidFolioReportBuilderOptionsError, TableCellSpec, bookmark, createTableOfContentsField, endnote, heading, hyperlink, pageBreak, paragraph, run, table };
@@ -0,0 +1,265 @@
1
+ import { TaggedError } from "better-result";
2
+ //#region src/docx/server/build.ts
3
+ /**
4
+ * Headless document builders.
5
+ *
6
+ * Thin constructors for the `@stll/docx-core` model so a server can assemble
7
+ * a report in code (`createEmptyDocument` → push blocks → `createDocx`)
8
+ * without hand-writing model literals. They build model values only; the
9
+ * serializer owns the OOXML.
10
+ */
11
+ /** Character style applied to hyperlink text; present in the bundled style sets. */
12
+ const HYPERLINK_STYLE_ID = "Hyperlink";
13
+ /** Character style applied to the endnote reference mark. */
14
+ const ENDNOTE_REFERENCE_STYLE_ID = "EndnoteReference";
15
+ /** Paragraph style applied to endnote body paragraphs. */
16
+ const ENDNOTE_TEXT_STYLE_ID = "EndnoteText";
17
+ /** Table style used by `table()`; present in the bundled style sets. */
18
+ const TABLE_STYLE_ID = "TableGrid";
19
+ /** `w:tblW w:type="pct"` is in fiftieths of a percent: 5000 = 100%. */
20
+ const FULL_WIDTH_PCT = 5e3;
21
+ const HEADING_LEVELS = [
22
+ 1,
23
+ 2,
24
+ 3,
25
+ 4,
26
+ 5,
27
+ 6
28
+ ];
29
+ /** Outline levels a `TOC \o` switch may name (ECMA-376 `w:outlineLvl` 0-8). */
30
+ const TOC_LEVEL_MIN = 1;
31
+ const TOC_LEVEL_MAX = 9;
32
+ /**
33
+ * A builder received a value outside the model's domain (a zero `gridSpan`,
34
+ * a negative column width, a reversed TOC range). Thrown at the boundary so
35
+ * the invalid value never reaches the serializer.
36
+ */
37
+ var InvalidFolioReportBuilderOptionsError = class extends TaggedError("InvalidFolioReportBuilderOptionsError")() {};
38
+ const assertPositiveInteger = (value, path) => {
39
+ if (!Number.isInteger(value) || value < 1) throw new InvalidFolioReportBuilderOptionsError({
40
+ message: `${path} must be a positive integer, got ${String(value)}`,
41
+ path
42
+ });
43
+ };
44
+ const assertHeadingLevel = (level) => {
45
+ if (!HEADING_LEVELS.some((known) => known === level)) throw new InvalidFolioReportBuilderOptionsError({
46
+ message: `level must be one of ${HEADING_LEVELS.join(", ")}, got ${String(level)}`,
47
+ path: "level"
48
+ });
49
+ };
50
+ const assertTocLevels = ({ from, to }) => {
51
+ const inRange = (value) => Number.isInteger(value) && value >= TOC_LEVEL_MIN && value <= TOC_LEVEL_MAX;
52
+ if (!inRange(from) || !inRange(to) || from > to) throw new InvalidFolioReportBuilderOptionsError({
53
+ message: `levels must satisfy ${TOC_LEVEL_MIN} <= from <= to <= ${TOC_LEVEL_MAX}, got ${String(from)}-${String(to)}`,
54
+ path: "levels"
55
+ });
56
+ };
57
+ const run = (text, formatting) => ({
58
+ type: "run",
59
+ ...formatting ? { formatting } : {},
60
+ content: [{
61
+ type: "text",
62
+ text
63
+ }]
64
+ });
65
+ const paragraph = (content, formatting) => ({
66
+ type: "paragraph",
67
+ ...formatting ? { formatting } : {},
68
+ content: typeof content === "string" ? [run(content)] : content
69
+ });
70
+ /** A paragraph in the `Heading<level>` style. */
71
+ const heading = ({ text, level }) => {
72
+ assertHeadingLevel(level);
73
+ return paragraph(text, { styleId: `Heading${level}` });
74
+ };
75
+ /** An empty paragraph carrying a hard page break. */
76
+ const pageBreak = () => ({
77
+ type: "paragraph",
78
+ content: [{
79
+ type: "run",
80
+ content: [{
81
+ type: "break",
82
+ breakType: "page"
83
+ }]
84
+ }]
85
+ });
86
+ /**
87
+ * An external (`href`) or in-document (`anchor`, a bookmark name) link. The
88
+ * relationship for an external link is minted when the document is written.
89
+ */
90
+ const hyperlink = ({ text, formatting, tooltip, href, anchor }) => ({
91
+ type: "hyperlink",
92
+ ...href !== void 0 ? { href } : { anchor },
93
+ ...tooltip !== void 0 ? { tooltip } : {},
94
+ children: [run(text, {
95
+ styleId: HYPERLINK_STYLE_ID,
96
+ ...formatting
97
+ })]
98
+ });
99
+ let nextBookmarkId = 0;
100
+ /** `content` wrapped in a named bookmark, the target of `hyperlink({ anchor })`. */
101
+ const bookmark = ({ name, content, id }) => {
102
+ const bookmarkId = id ?? nextBookmarkId++;
103
+ const start = {
104
+ type: "bookmarkStart",
105
+ id: bookmarkId,
106
+ name
107
+ };
108
+ const end = {
109
+ type: "bookmarkEnd",
110
+ id: bookmarkId
111
+ };
112
+ return [
113
+ start,
114
+ ...content,
115
+ end
116
+ ];
117
+ };
118
+ const cellWidth = (columnWidths, gridIndex, gridSpan) => {
119
+ if (!columnWidths) return;
120
+ let width = 0;
121
+ for (let column = gridIndex; column < gridIndex + gridSpan; column++) {
122
+ const columnWidth = columnWidths.at(column);
123
+ if (columnWidth === void 0) return;
124
+ width += columnWidth;
125
+ }
126
+ return width;
127
+ };
128
+ const buildCell = ({ spec, columnWidths, gridIndex, shading, textFormatting }) => {
129
+ const resolved = typeof spec === "string" ? {
130
+ content: [paragraph([run(spec, textFormatting)])],
131
+ shading
132
+ } : {
133
+ ...spec,
134
+ shading: spec.shading ?? shading
135
+ };
136
+ const gridSpan = resolved.gridSpan ?? 1;
137
+ assertPositiveInteger(gridSpan, "gridSpan");
138
+ const width = cellWidth(columnWidths, gridIndex, gridSpan);
139
+ const formatting = {
140
+ ...width !== void 0 ? { width: {
141
+ type: "dxa",
142
+ value: width
143
+ } } : {},
144
+ ...resolved.shading ? { shading: resolved.shading } : {},
145
+ ...resolved.gridSpan !== void 0 ? { gridSpan: resolved.gridSpan } : {},
146
+ ...resolved.vMerge !== void 0 ? { vMerge: resolved.vMerge } : {}
147
+ };
148
+ return {
149
+ type: "tableCell",
150
+ ...Object.keys(formatting).length > 0 ? { formatting } : {},
151
+ content: resolved.content.length > 0 ? resolved.content : [paragraph([])]
152
+ };
153
+ };
154
+ const buildRow = ({ cells, columnWidths, shading, textFormatting, header }) => {
155
+ const built = [];
156
+ let gridIndex = 0;
157
+ for (const spec of cells) {
158
+ const cell = buildCell({
159
+ spec,
160
+ columnWidths,
161
+ gridIndex,
162
+ shading,
163
+ textFormatting
164
+ });
165
+ built.push(cell);
166
+ gridIndex += cell.formatting?.gridSpan ?? 1;
167
+ }
168
+ return {
169
+ type: "tableRow",
170
+ ...header ? { formatting: {
171
+ header: true,
172
+ cantSplit: true
173
+ } } : {},
174
+ cells: built
175
+ };
176
+ };
177
+ /**
178
+ * A full-width grid table in the `TableGrid` style. A string cell becomes one
179
+ * plain paragraph; an object cell supplies its own paragraphs plus optional
180
+ * shading and horizontal (`gridSpan`) or vertical (`vMerge`) merge.
181
+ */
182
+ const table = ({ header, rows, columnWidths, headerShading, repeatHeader = true }) => {
183
+ columnWidths?.forEach((width, index) => assertPositiveInteger(width, `columnWidths[${index}]`));
184
+ const builtRows = [];
185
+ if (header) builtRows.push(buildRow({
186
+ cells: header,
187
+ columnWidths,
188
+ shading: headerShading,
189
+ textFormatting: { bold: true },
190
+ header: repeatHeader
191
+ }));
192
+ for (const cells of rows) builtRows.push(buildRow({
193
+ cells,
194
+ columnWidths,
195
+ shading: void 0,
196
+ textFormatting: void 0,
197
+ header: false
198
+ }));
199
+ return {
200
+ type: "table",
201
+ formatting: {
202
+ styleId: TABLE_STYLE_ID,
203
+ width: {
204
+ type: "pct",
205
+ value: FULL_WIDTH_PCT
206
+ },
207
+ layout: columnWidths ? "fixed" : "autofit"
208
+ },
209
+ ...columnWidths ? { columnWidths } : {},
210
+ rows: builtRows
211
+ };
212
+ };
213
+ /**
214
+ * Register an endnote on `doc` and return the reference run to place in body
215
+ * text. Allocates the next free endnote id (Word reserves 0 and -1 for the
216
+ * separator notes) and pushes the note into `doc.package.endnotes`.
217
+ */
218
+ const endnote = (doc, content) => {
219
+ const endnotes = doc.package.endnotes ?? [];
220
+ doc.package.endnotes = endnotes;
221
+ const id = Math.max(0, ...endnotes.map((note) => note.id)) + 1;
222
+ const note = {
223
+ type: "endnote",
224
+ id,
225
+ content: typeof content === "string" ? [paragraph(content, { styleId: ENDNOTE_TEXT_STYLE_ID })] : content
226
+ };
227
+ endnotes.push(note);
228
+ return {
229
+ type: "run",
230
+ formatting: { styleId: ENDNOTE_REFERENCE_STYLE_ID },
231
+ content: [{
232
+ type: "endnoteRef",
233
+ id
234
+ }]
235
+ };
236
+ };
237
+ const DEFAULT_TOC_LEVELS = {
238
+ from: 1,
239
+ to: 3
240
+ };
241
+ const DEFAULT_TOC_PLACEHOLDER = "Update the field to build the table of contents.";
242
+ /**
243
+ * A paragraph holding a dirty `TOC` field, so the consumer recomputes the
244
+ * table on open. Set `package.settings.updateFields` as well to have Word
245
+ * recompute without prompting for each field.
246
+ */
247
+ const createTableOfContentsField = ({ levels = DEFAULT_TOC_LEVELS, hyperlinks = true, placeholderText = DEFAULT_TOC_PLACEHOLDER } = {}) => {
248
+ assertTocLevels(levels);
249
+ const switches = [`\\o "${levels.from}-${levels.to}"`];
250
+ if (hyperlinks) switches.push("\\h");
251
+ switches.push("\\z", "\\u");
252
+ return {
253
+ type: "paragraph",
254
+ content: [{
255
+ type: "complexField",
256
+ instruction: `TOC ${switches.join(" ")}`,
257
+ fieldType: "TOC",
258
+ fieldCode: [],
259
+ fieldResult: [run(placeholderText)],
260
+ dirty: true
261
+ }]
262
+ };
263
+ };
264
+ //#endregion
265
+ export { HEADING_LEVELS, InvalidFolioReportBuilderOptionsError, bookmark, createTableOfContentsField, endnote, heading, hyperlink, pageBreak, paragraph, run, table };
@@ -1,4 +1,4 @@
1
- import { findChild, findChildren, getAttribute, parseBooleanElement, parseXmlDocument } from "./xmlParser.js";
1
+ import { WORDPROCESSINGML_NAMESPACE_URIS, findChild, findChildByNamespaceUri, findChildren, getAttribute, parseBooleanElement, parseXmlDocument } from "./xmlParser.js";
2
2
  //#region src/docx/settingsParser.ts
3
3
  /** OOXML default per §17.6.13 when `w:defaultTabStop` is absent. */
4
4
  const DEFAULT_TAB_STOP_TWIPS = 720;
@@ -26,10 +26,13 @@ const MAX_KINSOKU_CHARACTERS_LENGTH = 128;
26
26
  function parseSettings(xml) {
27
27
  const root = xml ? parseXmlDocument(xml) : null;
28
28
  const settings = { defaultTabStop: parseDefaultTabStop(root) };
29
- const evenAndOddHeaders = root ? findChild(root, "w", "evenAndOddHeaders") : null;
30
- if (evenAndOddHeaders && parseBooleanElement(evenAndOddHeaders)) settings.evenAndOddHeaders = true;
31
- const mirrorMargins = root ? findChild(root, "w", "mirrorMargins") : null;
32
- if (mirrorMargins && parseBooleanElement(mirrorMargins)) settings.mirrorMargins = true;
29
+ const wordprocessingFlag = (localName) => {
30
+ const element = findChildByNamespaceUri(root, WORDPROCESSINGML_NAMESPACE_URIS, localName);
31
+ return element !== null && parseBooleanElement(element);
32
+ };
33
+ if (wordprocessingFlag("evenAndOddHeaders")) settings.evenAndOddHeaders = true;
34
+ if (wordprocessingFlag("mirrorMargins")) settings.mirrorMargins = true;
35
+ if (wordprocessingFlag("updateFields")) settings.updateFields = true;
33
36
  const themeFontLangEl = root ? findChild(root, "w", "themeFontLang") : null;
34
37
  const eastAsiaLang = themeFontLangEl ? getAttribute(themeFontLangEl, "w", "eastAsia") || void 0 : void 0;
35
38
  const bidiLang = themeFontLangEl ? getAttribute(themeFontLangEl, "w", "bidi") || void 0 : void 0;
@@ -96,6 +96,14 @@ declare function getLocalName(name: string | undefined): string;
96
96
  declare function getNamespacePrefix(name: string): string | null;
97
97
  /** Namespace URI resolved from the element's in-scope XML declarations. */
98
98
  declare const getNamespaceUri: (element: XmlElement) => string | undefined;
99
+ /** WordprocessingML main namespace, Transitional and Strict (ECMA-376 Parts 1 and 4). */
100
+ declare const WORDPROCESSINGML_NAMESPACE_URIS: ReadonlySet<string>;
101
+ /**
102
+ * First child whose local name matches AND whose resolved namespace URI is
103
+ * one of `namespaceUris`. Unlike {@link findChild}, a same-named element
104
+ * from a foreign namespace is not accepted.
105
+ */
106
+ declare function findChildByNamespaceUri(parent: XmlElement | null | undefined, namespaceUris: ReadonlySet<string>, localName: string): XmlElement | null;
99
107
  /** Get an attribute whose prefix resolves to one of the accepted namespace URIs. */
100
108
  declare function getAttributeByNamespaceUri(element: XmlElement | null | undefined, namespaceUris: ReadonlySet<string>, localName: string): string | null;
101
109
  /**
@@ -326,4 +334,4 @@ declare function mergeXmlnsDeclarations(inherited: Record<string, string>, eleme
326
334
  */
327
335
  declare function cloneWithXmlnsDeclarations(element: XmlElement, xmlnsDecls: Record<string, string>): XmlElement;
328
336
  //#endregion
329
- export { NAMESPACES, XmlElement, XmlNamespaceScope, cloneWithXmlnsDeclarations, collectXmlnsDeclarations, elementToXml, findAllDeep, findByFullName, findChild, findChildByLocalName, findChildren, findChildrenByLocalName, findDeep, getAttribute, getAttributeAny, getAttributeAnyPrefix, getAttributeByNamespaceUri, getAttributes, getChildElements, getLocalName, getNamespacePrefix, getNamespaceUri, getTextContent, hasChild, hasFlag, matchesName, mergeXmlnsDeclarations, parseBooleanElement, parseColorElement, parseNumberingLevelAttribute, parseNumericAttribute, parseTableMeasurementValue, parseXml, parseXmlDocument };
337
+ export { NAMESPACES, WORDPROCESSINGML_NAMESPACE_URIS, XmlElement, XmlNamespaceScope, cloneWithXmlnsDeclarations, collectXmlnsDeclarations, elementToXml, findAllDeep, findByFullName, findChild, findChildByLocalName, findChildByNamespaceUri, findChildren, findChildrenByLocalName, findDeep, getAttribute, getAttributeAny, getAttributeAnyPrefix, getAttributeByNamespaceUri, getAttributes, getChildElements, getLocalName, getNamespacePrefix, getNamespaceUri, getTextContent, hasChild, hasFlag, matchesName, mergeXmlnsDeclarations, parseBooleanElement, parseColorElement, parseNumberingLevelAttribute, parseNumericAttribute, parseTableMeasurementValue, parseXml, parseXmlDocument };
@@ -196,6 +196,18 @@ function getNamespacePrefix(name) {
196
196
  }
197
197
  /** Namespace URI resolved from the element's in-scope XML declarations. */
198
198
  const getNamespaceUri = (element) => element.namespaceUri;
199
+ /** WordprocessingML main namespace, Transitional and Strict (ECMA-376 Parts 1 and 4). */
200
+ const WORDPROCESSINGML_NAMESPACE_URIS = /* @__PURE__ */ new Set([NAMESPACES.w, "http://purl.oclc.org/ooxml/wordprocessingml/main"]);
201
+ /**
202
+ * First child whose local name matches AND whose resolved namespace URI is
203
+ * one of `namespaceUris`. Unlike {@link findChild}, a same-named element
204
+ * from a foreign namespace is not accepted.
205
+ */
206
+ function findChildByNamespaceUri(parent, namespaceUris, localName) {
207
+ if (!parent?.elements) return null;
208
+ for (const child of parent.elements) if (child.type === "element" && hasLocalName(child.name, localName) && namespaceUris.has(child.namespaceUri ?? "")) return child;
209
+ return null;
210
+ }
199
211
  /** Get an attribute whose prefix resolves to one of the accepted namespace URIs. */
200
212
  function getAttributeByNamespaceUri(element, namespaceUris, localName) {
201
213
  if (!element?.attributes) return null;
@@ -687,4 +699,4 @@ function cloneWithXmlnsDeclarations(element, xmlnsDecls) {
687
699
  return element;
688
700
  }
689
701
  //#endregion
690
- export { NAMESPACES, cloneWithXmlnsDeclarations, collectXmlnsDeclarations, elementToXml, findAllDeep, findByFullName, findChild, findChildByLocalName, findChildren, findChildrenByLocalName, findDeep, getAttribute, getAttributeAny, getAttributeAnyPrefix, getAttributeByNamespaceUri, getAttributes, getChildElements, getLocalName, getNamespacePrefix, getNamespaceUri, getTextContent, hasChild, hasFlag, matchesName, mergeXmlnsDeclarations, parseBooleanElement, parseColorElement, parseNumberingLevelAttribute, parseNumericAttribute, parseTableMeasurementValue, parseXml, parseXmlDocument };
702
+ export { NAMESPACES, WORDPROCESSINGML_NAMESPACE_URIS, cloneWithXmlnsDeclarations, collectXmlnsDeclarations, elementToXml, findAllDeep, findByFullName, findChild, findChildByLocalName, findChildByNamespaceUri, findChildren, findChildrenByLocalName, findDeep, getAttribute, getAttributeAny, getAttributeAnyPrefix, getAttributeByNamespaceUri, getAttributes, getChildElements, getLocalName, getNamespacePrefix, getNamespaceUri, getTextContent, hasChild, hasFlag, matchesName, mergeXmlnsDeclarations, parseBooleanElement, parseColorElement, parseNumberingLevelAttribute, parseNumericAttribute, parseTableMeasurementValue, parseXml, parseXmlDocument };
package/dist/server.d.ts CHANGED
@@ -12,14 +12,16 @@ import { STELLA_STYLE_SET_NAME, createStellaStyleDocumentPreset, createStellaSty
12
12
  import { createDocx } from "./docx/rezip.js";
13
13
  import { EnsureParaIdsError, EnsureParaIdsOptions, EnsureParaIdsResult, ensureParaIds } from "./docx/ensureParaIds.js";
14
14
  import { FOLIO_DOCUMENT_METADATA_PROPERTIES, FOLIO_DOCUMENT_PRIVACY_TRANSFORMS, FolioDocumentMetadataProperty, FolioDocumentPrivacyArchiveError, FolioDocumentPrivacyOptions, FolioDocumentPrivacyReport, FolioDocumentPrivacyTransform, InvalidFolioDocumentPrivacyOptionsError, RewriteDocxMetadataPrivacyResult, isFolioDocumentPrivacyTransform, rewriteDocxMetadataPrivacy } from "./docx/metadataPrivacy.js";
15
+ import { ParseOptions, parseDocx } from "./docx/parser.js";
15
16
  import { CreateCommentReplyInput, replyToComment } from "./docx/replyToComment.js";
16
17
  import { DocxArchiveError, DocxArchiveOptions } from "./docx/server/boundedArchive.js";
17
18
  import { EvaluateDocxXmlPatchProposalArgs, FOLIO_DOCX_XML_PATCH_PROPOSAL_DEFAULTS, FOLIO_DOCX_XML_PATCH_PROPOSAL_ISSUE_CODES, FOLIO_DOCX_XML_PATCH_PROPOSAL_PROFILE, FOLIO_DOCX_XML_PATCH_PROPOSAL_VERSION, FolioDocxPreparedXmlReplacement, FolioDocxXmlPatchProposal, FolioDocxXmlPatchProposalEvaluation, FolioDocxXmlPatchProposalIssue, FolioDocxXmlPatchProposalIssueCode, FolioDocxXmlPatchProposalLimits, FolioDocxXmlReplacement, InvalidFolioDocxXmlPatchProposalError, InvalidFolioDocxXmlPatchProposalOptionsError, evaluateDocxXmlPatchProposal, parseFolioDocxXmlPatchProposal } from "./docx/server/evaluateDocxXmlPatchProposal.js";
18
19
  import { FOLIO_DOCX_CONFORMANCE_CHECKS, FOLIO_DOCX_CONFORMANCE_ISSUE_CODES, FOLIO_DOCX_CONFORMANCE_PROFILE, FOLIO_DOCX_CONFORMANCE_REPORT_VERSION, FolioDocxConformanceCheck, FolioDocxConformanceCheckId, FolioDocxConformanceCheckStatus, FolioDocxConformanceIssue, FolioDocxConformanceIssueCode, FolioDocxConformanceReport, FolioDocxConformanceStatus, ValidateDocxConformanceOptions, validateDocxConformance } from "./docx/server/validateDocxConformance.js";
19
20
  import { ApplyDocxXmlPatchProposalArgs, FOLIO_DOCX_XML_PATCH_APPLICATION_PROFILE, FOLIO_DOCX_XML_PATCH_APPLICATION_VERSION, FolioDocxXmlPatchApplication, FolioDocxXmlPatchApplicationError, FolioDocxXmlPatchApplicationReceipt, UnsupportedFolioDocxXmlPatchApplicationProfileError, applyDocxXmlPatchProposal } from "./docx/server/applyDocxXmlPatchProposal.js";
21
+ import { HEADING_LEVELS, HeadingLevel, InvalidFolioReportBuilderOptionsError, TableCellSpec, bookmark, createTableOfContentsField, endnote, heading, hyperlink, pageBreak, paragraph, run, table } from "./docx/server/build.js";
20
22
  import { docxToMarkdown } from "./docx/server/docxToMarkdown.js";
21
23
  import { DocxParagraphSource, DocxTableRowKind, DocxTableRowPosition, ExtractedDocxParagraph, ExtractedDocxTableCell, ExtractedDocxTableCellParagraph, ExtractedDocxText, extractDocxText } from "./docx/server/extractDocxText.js";
22
24
  import { FOLIO_DOCX_PACKAGE_INSPECTION_DEFAULTS, FOLIO_DOCX_PACKAGE_INSPECTION_ERROR_CODES, FOLIO_DOCX_PACKAGE_INSPECTION_VERSION, FolioDocxInspectedXmlPart, FolioDocxPackageInspection, FolioDocxPackageInspectionError, FolioDocxPackageInspectionErrorCode, FolioDocxPackageInspectionLimits, FolioDocxPackagePart, FolioDocxPackagePartKind, InspectDocxPackageOptions, inspectDocxPackage } from "./docx/server/inspectDocxPackage.js";
23
25
  import { GenerateRedlineDocxOptions, GenerateRedlineDocxResult, GenerateRedlineUnprocessedStory, InvalidGenerateRedlineDocxOptionsError, generateRedlineDocx } from "./redline.js";
24
26
  import { FOLIO_VERSION_COMPARISON_PRIVACY_TRANSFORMS, FOLIO_VERSION_COMPARISON_SCOPES, FolioBlockDiff, FolioCompareDocxVersionsOptions, FolioDocumentMetadataValue, FolioFormatProperty, FolioMetadataDiff, FolioStoryDiff, FolioVersionBlockHandle, FolioVersionComparisonPrivacyTransform, FolioVersionComparisonScope, FolioVersionDiff, FolioVersionDiffPrivacyOptions, FolioVersionDiffPrivacyReport, FolioVersionDiffSegment, FolioVersionDiffSummaryCounts, InvalidFolioVersionComparisonOptionsError, applyFolioVersionDiffPrivacy, compareDocxVersions, isFolioVersionComparisonPrivacyTransform, isFolioVersionComparisonScope } from "./version-comparison.js";
25
- export { type ApplyDocxXmlPatchProposalArgs, type ApplyFolioAIEditsToBufferOptions, type ApplyFolioAIEditsToBufferResult, type CreateCommentReplyInput, type CreateEmptyDocumentOptions, DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION, type DeriveBlockIdInput, type DocumentPreset, type DocumentStyleCatalog, type DocumentStyleCatalogEntry, type DocumentStyleSet, DocxArchiveError, type DocxArchiveOptions, type DocxParagraphSource, type DocxTableRowKind, type DocxTableRowPosition, EnsureParaIdsError, type EnsureParaIdsOptions, type EnsureParaIdsResult, type EvaluateDocxXmlPatchProposalArgs, type ExtractDocumentStyleSetOptions, type ExtractedDocxParagraph, type ExtractedDocxTableCell, type ExtractedDocxTableCellParagraph, type ExtractedDocxText, FOLIO_DOCUMENT_METADATA_PROPERTIES, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FOLIO_DOCUMENT_PRIVACY_TRANSFORMS, FOLIO_DOCX_CONFORMANCE_CHECKS, FOLIO_DOCX_CONFORMANCE_ISSUE_CODES, FOLIO_DOCX_CONFORMANCE_PROFILE, FOLIO_DOCX_CONFORMANCE_REPORT_VERSION, FOLIO_DOCX_PACKAGE_INSPECTION_DEFAULTS, FOLIO_DOCX_PACKAGE_INSPECTION_ERROR_CODES, FOLIO_DOCX_PACKAGE_INSPECTION_VERSION, FOLIO_DOCX_XML_PATCH_APPLICATION_PROFILE, FOLIO_DOCX_XML_PATCH_APPLICATION_VERSION, FOLIO_DOCX_XML_PATCH_PROPOSAL_DEFAULTS, FOLIO_DOCX_XML_PATCH_PROPOSAL_ISSUE_CODES, FOLIO_DOCX_XML_PATCH_PROPOSAL_PROFILE, FOLIO_DOCX_XML_PATCH_PROPOSAL_VERSION, FOLIO_RESOLVED_REVIEWED_VIEWS, FOLIO_REVIEWED_VIEWS, FOLIO_VERSION_COMPARISON_PRIVACY_TRANSFORMS, FOLIO_VERSION_COMPARISON_SCOPES, type FolioAIBlock, type FolioAIBlockAnchor, type FolioAIBlockKind, type FolioAIBlockPreviewRun, type FolioAIComment, type FolioAIEditApplyMode, type FolioAIEditApplyResult, type FolioAIEditOperation, type FolioAIEditPrecondition, type FolioAIEditSnapshot, type FolioAIInlineFormatting, type FolioAITextRangeHandle, type FolioApplyDocumentOperationsOptions, type FolioApplyDocumentOperationsToStoryOptions, type FolioApplyOperationsOptions, type FolioBlockDiff, type FolioBlockId, type FolioCompareDocxVersionsOptions, type FolioDocumentMetadataProperty, type FolioDocumentMetadataValue, type FolioDocumentNavigationTarget, type FolioDocumentOperation, type FolioDocumentOperationAffectedTarget, type FolioDocumentOperationBatch, type FolioDocumentOperationCapabilities, type FolioDocumentOperationIssue, type FolioDocumentOperationMode, type FolioDocumentOperationPrecondition, type FolioDocumentOperationReceipt, type FolioDocumentOperationRecovery, type FolioDocumentOperationResult, type FolioDocumentOperationStatus, type FolioDocumentOperationStory, type FolioDocumentOperationType, type FolioDocumentOperationUndoFailureReason, type FolioDocumentOperationUndoHandle, type FolioDocumentOperationUndoResult, type FolioDocumentOutline, type FolioDocumentOutlineEntry, FolioDocumentPrivacyArchiveError, type FolioDocumentPrivacyOptions, type FolioDocumentPrivacyReport, type FolioDocumentPrivacyTransform, type FolioDocumentSection, type FolioDocumentSectionHandle, type FolioDocumentSectionReadResult, type FolioDocumentStory, type FolioDocumentStoryHandle, FolioDocumentStoryNotFoundError, type FolioDocxConformanceCheck, type FolioDocxConformanceCheckId, type FolioDocxConformanceCheckStatus, type FolioDocxConformanceIssue, type FolioDocxConformanceIssueCode, type FolioDocxConformanceReport, type FolioDocxConformanceStatus, type FolioDocxInspectedXmlPart, type FolioDocxPackageInspection, FolioDocxPackageInspectionError, type FolioDocxPackageInspectionErrorCode, type FolioDocxPackageInspectionLimits, type FolioDocxPackagePart, type FolioDocxPackagePartKind, type FolioDocxPreparedXmlReplacement, FolioDocxReviewer, type FolioDocxReviewerOptions, type FolioDocxXmlPatchApplication, FolioDocxXmlPatchApplicationError, type FolioDocxXmlPatchApplicationReceipt, type FolioDocxXmlPatchProposal, type FolioDocxXmlPatchProposalEvaluation, type FolioDocxXmlPatchProposalIssue, type FolioDocxXmlPatchProposalIssueCode, type FolioDocxXmlPatchProposalLimits, type FolioDocxXmlReplacement, type FolioEditableDocumentStoryHandle, type FolioFormatProperty, type FolioMetadataDiff, type FolioReadReviewedStoryOptions, type FolioResolveReviewedStoryOptions, type FolioResolvedReviewedView, type FolioReviewChange, type FolioReviewChangeFilter, type FolioReviewChangeKind, type FolioReviewComment, type FolioReviewCommentFilter, type FolioReviewCommentReply, type FolioReviewReplyInput, type FolioReviewedStory, type FolioReviewedView, type FolioStoryDiff, type FolioVersionBlockHandle, type FolioVersionComparisonPrivacyTransform, type FolioVersionComparisonScope, type FolioVersionDiff, type FolioVersionDiffPrivacyOptions, type FolioVersionDiffPrivacyReport, type FolioVersionDiffSegment, type FolioVersionDiffSummaryCounts, type GenerateRedlineDocxOptions, type GenerateRedlineDocxResult, type GenerateRedlineUnprocessedStory, type InspectDocxPackageOptions, InvalidFolioDocumentOperationBatchError, InvalidFolioDocumentPrivacyOptionsError, InvalidFolioDocxXmlPatchProposalError, InvalidFolioDocxXmlPatchProposalOptionsError, InvalidFolioVersionComparisonOptionsError, InvalidGenerateRedlineDocxOptionsError, type RewriteDocxMetadataPrivacyResult, STELLA_STYLE_SET_NAME, UnsupportedFolioDocumentOperationVersionError, UnsupportedFolioDocxXmlPatchApplicationProfileError, UnsupportedFolioReviewedViewError, type ValidateDocxConformanceOptions, applyDocxXmlPatchProposal, applyFolioAIEditsToBuffer, applyFolioVersionDiffPrivacy, assertSupportedFolioDocumentOperationVersion, compareDocxVersions, createDocx, createEmptyDocument, createFolioAITextRangeHandle, createStellaStyleDocumentPreset, createStellaStyleSet, deriveBlockId, docxToMarkdown, ensureParaIds, evaluateDocxXmlPatchProposal, extractDocumentStyleSet, extractDocumentStyleSetFromDocx, extractDocxText, generateRedlineDocx, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioDocumentOutline, getFolioParaIdFromBlockId, hashFolioAIBlockText, inspectDocumentStyles, inspectDocumentStylesFromDocx, inspectDocxPackage, isFolioBlockId, isFolioDocumentOperationModeSupported, isFolioDocumentPrivacyTransform, isFolioResolvedReviewedView, isFolioReviewedView, isFolioVersionComparisonPrivacyTransform, isFolioVersionComparisonScope, isSequentialFolioBlockId, isSupportedFolioDocumentOperationVersion, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, parseFolioDocxXmlPatchProposal, readFolioDocumentSection, replyToComment, rewriteDocxMetadataPrivacy, validateDocxConformance };
27
+ export { type ApplyDocxXmlPatchProposalArgs, type ApplyFolioAIEditsToBufferOptions, type ApplyFolioAIEditsToBufferResult, type CreateCommentReplyInput, type CreateEmptyDocumentOptions, DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION, type DeriveBlockIdInput, type DocumentPreset, type DocumentStyleCatalog, type DocumentStyleCatalogEntry, type DocumentStyleSet, DocxArchiveError, type DocxArchiveOptions, type DocxParagraphSource, type DocxTableRowKind, type DocxTableRowPosition, EnsureParaIdsError, type EnsureParaIdsOptions, type EnsureParaIdsResult, type EvaluateDocxXmlPatchProposalArgs, type ExtractDocumentStyleSetOptions, type ExtractedDocxParagraph, type ExtractedDocxTableCell, type ExtractedDocxTableCellParagraph, type ExtractedDocxText, FOLIO_DOCUMENT_METADATA_PROPERTIES, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FOLIO_DOCUMENT_PRIVACY_TRANSFORMS, FOLIO_DOCX_CONFORMANCE_CHECKS, FOLIO_DOCX_CONFORMANCE_ISSUE_CODES, FOLIO_DOCX_CONFORMANCE_PROFILE, FOLIO_DOCX_CONFORMANCE_REPORT_VERSION, FOLIO_DOCX_PACKAGE_INSPECTION_DEFAULTS, FOLIO_DOCX_PACKAGE_INSPECTION_ERROR_CODES, FOLIO_DOCX_PACKAGE_INSPECTION_VERSION, FOLIO_DOCX_XML_PATCH_APPLICATION_PROFILE, FOLIO_DOCX_XML_PATCH_APPLICATION_VERSION, FOLIO_DOCX_XML_PATCH_PROPOSAL_DEFAULTS, FOLIO_DOCX_XML_PATCH_PROPOSAL_ISSUE_CODES, FOLIO_DOCX_XML_PATCH_PROPOSAL_PROFILE, FOLIO_DOCX_XML_PATCH_PROPOSAL_VERSION, FOLIO_RESOLVED_REVIEWED_VIEWS, FOLIO_REVIEWED_VIEWS, FOLIO_VERSION_COMPARISON_PRIVACY_TRANSFORMS, FOLIO_VERSION_COMPARISON_SCOPES, type FolioAIBlock, type FolioAIBlockAnchor, type FolioAIBlockKind, type FolioAIBlockPreviewRun, type FolioAIComment, type FolioAIEditApplyMode, type FolioAIEditApplyResult, type FolioAIEditOperation, type FolioAIEditPrecondition, type FolioAIEditSnapshot, type FolioAIInlineFormatting, type FolioAITextRangeHandle, type FolioApplyDocumentOperationsOptions, type FolioApplyDocumentOperationsToStoryOptions, type FolioApplyOperationsOptions, type FolioBlockDiff, type FolioBlockId, type FolioCompareDocxVersionsOptions, type FolioDocumentMetadataProperty, type FolioDocumentMetadataValue, type FolioDocumentNavigationTarget, type FolioDocumentOperation, type FolioDocumentOperationAffectedTarget, type FolioDocumentOperationBatch, type FolioDocumentOperationCapabilities, type FolioDocumentOperationIssue, type FolioDocumentOperationMode, type FolioDocumentOperationPrecondition, type FolioDocumentOperationReceipt, type FolioDocumentOperationRecovery, type FolioDocumentOperationResult, type FolioDocumentOperationStatus, type FolioDocumentOperationStory, type FolioDocumentOperationType, type FolioDocumentOperationUndoFailureReason, type FolioDocumentOperationUndoHandle, type FolioDocumentOperationUndoResult, type FolioDocumentOutline, type FolioDocumentOutlineEntry, FolioDocumentPrivacyArchiveError, type FolioDocumentPrivacyOptions, type FolioDocumentPrivacyReport, type FolioDocumentPrivacyTransform, type FolioDocumentSection, type FolioDocumentSectionHandle, type FolioDocumentSectionReadResult, type FolioDocumentStory, type FolioDocumentStoryHandle, FolioDocumentStoryNotFoundError, type FolioDocxConformanceCheck, type FolioDocxConformanceCheckId, type FolioDocxConformanceCheckStatus, type FolioDocxConformanceIssue, type FolioDocxConformanceIssueCode, type FolioDocxConformanceReport, type FolioDocxConformanceStatus, type FolioDocxInspectedXmlPart, type FolioDocxPackageInspection, FolioDocxPackageInspectionError, type FolioDocxPackageInspectionErrorCode, type FolioDocxPackageInspectionLimits, type FolioDocxPackagePart, type FolioDocxPackagePartKind, type FolioDocxPreparedXmlReplacement, FolioDocxReviewer, type FolioDocxReviewerOptions, type FolioDocxXmlPatchApplication, FolioDocxXmlPatchApplicationError, type FolioDocxXmlPatchApplicationReceipt, type FolioDocxXmlPatchProposal, type FolioDocxXmlPatchProposalEvaluation, type FolioDocxXmlPatchProposalIssue, type FolioDocxXmlPatchProposalIssueCode, type FolioDocxXmlPatchProposalLimits, type FolioDocxXmlReplacement, type FolioEditableDocumentStoryHandle, type FolioFormatProperty, type FolioMetadataDiff, type FolioReadReviewedStoryOptions, type FolioResolveReviewedStoryOptions, type FolioResolvedReviewedView, type FolioReviewChange, type FolioReviewChangeFilter, type FolioReviewChangeKind, type FolioReviewComment, type FolioReviewCommentFilter, type FolioReviewCommentReply, type FolioReviewReplyInput, type FolioReviewedStory, type FolioReviewedView, type FolioStoryDiff, type FolioVersionBlockHandle, type FolioVersionComparisonPrivacyTransform, type FolioVersionComparisonScope, type FolioVersionDiff, type FolioVersionDiffPrivacyOptions, type FolioVersionDiffPrivacyReport, type FolioVersionDiffSegment, type FolioVersionDiffSummaryCounts, type GenerateRedlineDocxOptions, type GenerateRedlineDocxResult, type GenerateRedlineUnprocessedStory, HEADING_LEVELS, type HeadingLevel, type InspectDocxPackageOptions, InvalidFolioDocumentOperationBatchError, InvalidFolioDocumentPrivacyOptionsError, InvalidFolioDocxXmlPatchProposalError, InvalidFolioDocxXmlPatchProposalOptionsError, InvalidFolioReportBuilderOptionsError, InvalidFolioVersionComparisonOptionsError, InvalidGenerateRedlineDocxOptionsError, type ParseOptions, type RewriteDocxMetadataPrivacyResult, STELLA_STYLE_SET_NAME, type TableCellSpec, UnsupportedFolioDocumentOperationVersionError, UnsupportedFolioDocxXmlPatchApplicationProfileError, UnsupportedFolioReviewedViewError, type ValidateDocxConformanceOptions, applyDocxXmlPatchProposal, applyFolioAIEditsToBuffer, applyFolioVersionDiffPrivacy, assertSupportedFolioDocumentOperationVersion, bookmark, compareDocxVersions, createDocx, createEmptyDocument, createFolioAITextRangeHandle, createStellaStyleDocumentPreset, createStellaStyleSet, createTableOfContentsField, deriveBlockId, docxToMarkdown, endnote, ensureParaIds, evaluateDocxXmlPatchProposal, extractDocumentStyleSet, extractDocumentStyleSetFromDocx, extractDocxText, generateRedlineDocx, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioDocumentOutline, getFolioParaIdFromBlockId, hashFolioAIBlockText, heading, hyperlink, inspectDocumentStyles, inspectDocumentStylesFromDocx, inspectDocxPackage, isFolioBlockId, isFolioDocumentOperationModeSupported, isFolioDocumentPrivacyTransform, isFolioResolvedReviewedView, isFolioReviewedView, isFolioVersionComparisonPrivacyTransform, isFolioVersionComparisonScope, isSequentialFolioBlockId, isSupportedFolioDocumentOperationVersion, normalizeFolioAIBlockText, pageBreak, paragraph, parseDocx, parseFolioDocumentOperationBatch, parseFolioDocxXmlPatchProposal, readFolioDocumentSection, replyToComment, rewriteDocxMetadataPrivacy, run, table, validateDocxConformance };
package/dist/server.js CHANGED
@@ -4,10 +4,12 @@ import { createFolioAITextRangeHandle, hashFolioAIBlockText, normalizeFolioAIBlo
4
4
  import { FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, assertSupportedFolioDocumentOperationVersion, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, isFolioDocumentOperationModeSupported, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch } from "./document-operations.js";
5
5
  import { EnsureParaIdsError, ensureParaIds } from "./docx/ensureParaIds.js";
6
6
  import { FOLIO_DOCUMENT_METADATA_PROPERTIES, FOLIO_DOCUMENT_PRIVACY_TRANSFORMS, FolioDocumentPrivacyArchiveError, InvalidFolioDocumentPrivacyOptionsError, isFolioDocumentPrivacyTransform, rewriteDocxMetadataPrivacy } from "./docx/metadataPrivacy.js";
7
+ import { parseDocx } from "./docx/parser.js";
7
8
  import { replyToComment } from "./docx/replyToComment.js";
8
9
  import { createDocx } from "./docx/rezip.js";
9
10
  import { FOLIO_DOCX_XML_PATCH_APPLICATION_PROFILE, FOLIO_DOCX_XML_PATCH_APPLICATION_VERSION, FolioDocxXmlPatchApplicationError, UnsupportedFolioDocxXmlPatchApplicationProfileError, applyDocxXmlPatchProposal } from "./docx/server/applyDocxXmlPatchProposal.js";
10
11
  import { DocxArchiveError } from "./docx/server/boundedArchive.js";
12
+ import { HEADING_LEVELS, InvalidFolioReportBuilderOptionsError, bookmark, createTableOfContentsField, endnote, heading, hyperlink, pageBreak, paragraph, run, table } from "./docx/server/build.js";
11
13
  import { docxToMarkdown } from "./docx/server/docxToMarkdown.js";
12
14
  import { FOLIO_DOCX_XML_PATCH_PROPOSAL_DEFAULTS, FOLIO_DOCX_XML_PATCH_PROPOSAL_ISSUE_CODES, FOLIO_DOCX_XML_PATCH_PROPOSAL_PROFILE, FOLIO_DOCX_XML_PATCH_PROPOSAL_VERSION, InvalidFolioDocxXmlPatchProposalError, InvalidFolioDocxXmlPatchProposalOptionsError, evaluateDocxXmlPatchProposal, parseFolioDocxXmlPatchProposal } from "./docx/server/evaluateDocxXmlPatchProposal.js";
13
15
  import { extractDocxText } from "./docx/server/extractDocxText.js";
@@ -20,4 +22,4 @@ import { DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION } from "./style-set
20
22
  import { deriveBlockId, getFolioParaIdFromBlockId, isFolioBlockId, isSequentialFolioBlockId } from "./types/block-id.js";
21
23
  import { createEmptyDocument } from "./utils/createDocument.js";
22
24
  import { FOLIO_VERSION_COMPARISON_PRIVACY_TRANSFORMS, FOLIO_VERSION_COMPARISON_SCOPES, InvalidFolioVersionComparisonOptionsError, applyFolioVersionDiffPrivacy, compareDocxVersions, isFolioVersionComparisonPrivacyTransform, isFolioVersionComparisonScope } from "./version-comparison.js";
23
- export { DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION, DocxArchiveError, EnsureParaIdsError, FOLIO_DOCUMENT_METADATA_PROPERTIES, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FOLIO_DOCUMENT_PRIVACY_TRANSFORMS, FOLIO_DOCX_CONFORMANCE_CHECKS, FOLIO_DOCX_CONFORMANCE_ISSUE_CODES, FOLIO_DOCX_CONFORMANCE_PROFILE, FOLIO_DOCX_CONFORMANCE_REPORT_VERSION, FOLIO_DOCX_PACKAGE_INSPECTION_DEFAULTS, FOLIO_DOCX_PACKAGE_INSPECTION_ERROR_CODES, FOLIO_DOCX_PACKAGE_INSPECTION_VERSION, FOLIO_DOCX_XML_PATCH_APPLICATION_PROFILE, FOLIO_DOCX_XML_PATCH_APPLICATION_VERSION, FOLIO_DOCX_XML_PATCH_PROPOSAL_DEFAULTS, FOLIO_DOCX_XML_PATCH_PROPOSAL_ISSUE_CODES, FOLIO_DOCX_XML_PATCH_PROPOSAL_PROFILE, FOLIO_DOCX_XML_PATCH_PROPOSAL_VERSION, FOLIO_RESOLVED_REVIEWED_VIEWS, FOLIO_REVIEWED_VIEWS, FOLIO_VERSION_COMPARISON_PRIVACY_TRANSFORMS, FOLIO_VERSION_COMPARISON_SCOPES, FolioDocumentPrivacyArchiveError, FolioDocumentStoryNotFoundError, FolioDocxPackageInspectionError, FolioDocxReviewer, FolioDocxXmlPatchApplicationError, InvalidFolioDocumentOperationBatchError, InvalidFolioDocumentPrivacyOptionsError, InvalidFolioDocxXmlPatchProposalError, InvalidFolioDocxXmlPatchProposalOptionsError, InvalidFolioVersionComparisonOptionsError, InvalidGenerateRedlineDocxOptionsError, STELLA_STYLE_SET_NAME, UnsupportedFolioDocumentOperationVersionError, UnsupportedFolioDocxXmlPatchApplicationProfileError, UnsupportedFolioReviewedViewError, applyDocxXmlPatchProposal, applyFolioAIEditsToBuffer, applyFolioVersionDiffPrivacy, assertSupportedFolioDocumentOperationVersion, compareDocxVersions, createDocx, createEmptyDocument, createFolioAITextRangeHandle, createStellaStyleDocumentPreset, createStellaStyleSet, deriveBlockId, docxToMarkdown, ensureParaIds, evaluateDocxXmlPatchProposal, extractDocumentStyleSet, extractDocumentStyleSetFromDocx, extractDocxText, generateRedlineDocx, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioDocumentOutline, getFolioParaIdFromBlockId, hashFolioAIBlockText, inspectDocumentStyles, inspectDocumentStylesFromDocx, inspectDocxPackage, isFolioBlockId, isFolioDocumentOperationModeSupported, isFolioDocumentPrivacyTransform, isFolioResolvedReviewedView, isFolioReviewedView, isFolioVersionComparisonPrivacyTransform, isFolioVersionComparisonScope, isSequentialFolioBlockId, isSupportedFolioDocumentOperationVersion, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, parseFolioDocxXmlPatchProposal, readFolioDocumentSection, replyToComment, rewriteDocxMetadataPrivacy, validateDocxConformance };
25
+ export { DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION, DocxArchiveError, EnsureParaIdsError, FOLIO_DOCUMENT_METADATA_PROPERTIES, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FOLIO_DOCUMENT_PRIVACY_TRANSFORMS, FOLIO_DOCX_CONFORMANCE_CHECKS, FOLIO_DOCX_CONFORMANCE_ISSUE_CODES, FOLIO_DOCX_CONFORMANCE_PROFILE, FOLIO_DOCX_CONFORMANCE_REPORT_VERSION, FOLIO_DOCX_PACKAGE_INSPECTION_DEFAULTS, FOLIO_DOCX_PACKAGE_INSPECTION_ERROR_CODES, FOLIO_DOCX_PACKAGE_INSPECTION_VERSION, FOLIO_DOCX_XML_PATCH_APPLICATION_PROFILE, FOLIO_DOCX_XML_PATCH_APPLICATION_VERSION, FOLIO_DOCX_XML_PATCH_PROPOSAL_DEFAULTS, FOLIO_DOCX_XML_PATCH_PROPOSAL_ISSUE_CODES, FOLIO_DOCX_XML_PATCH_PROPOSAL_PROFILE, FOLIO_DOCX_XML_PATCH_PROPOSAL_VERSION, FOLIO_RESOLVED_REVIEWED_VIEWS, FOLIO_REVIEWED_VIEWS, FOLIO_VERSION_COMPARISON_PRIVACY_TRANSFORMS, FOLIO_VERSION_COMPARISON_SCOPES, FolioDocumentPrivacyArchiveError, FolioDocumentStoryNotFoundError, FolioDocxPackageInspectionError, FolioDocxReviewer, FolioDocxXmlPatchApplicationError, HEADING_LEVELS, InvalidFolioDocumentOperationBatchError, InvalidFolioDocumentPrivacyOptionsError, InvalidFolioDocxXmlPatchProposalError, InvalidFolioDocxXmlPatchProposalOptionsError, InvalidFolioReportBuilderOptionsError, InvalidFolioVersionComparisonOptionsError, InvalidGenerateRedlineDocxOptionsError, STELLA_STYLE_SET_NAME, UnsupportedFolioDocumentOperationVersionError, UnsupportedFolioDocxXmlPatchApplicationProfileError, UnsupportedFolioReviewedViewError, applyDocxXmlPatchProposal, applyFolioAIEditsToBuffer, applyFolioVersionDiffPrivacy, assertSupportedFolioDocumentOperationVersion, bookmark, compareDocxVersions, createDocx, createEmptyDocument, createFolioAITextRangeHandle, createStellaStyleDocumentPreset, createStellaStyleSet, createTableOfContentsField, deriveBlockId, docxToMarkdown, endnote, ensureParaIds, evaluateDocxXmlPatchProposal, extractDocumentStyleSet, extractDocumentStyleSetFromDocx, extractDocxText, generateRedlineDocx, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioDocumentOutline, getFolioParaIdFromBlockId, hashFolioAIBlockText, heading, hyperlink, inspectDocumentStyles, inspectDocumentStylesFromDocx, inspectDocxPackage, isFolioBlockId, isFolioDocumentOperationModeSupported, isFolioDocumentPrivacyTransform, isFolioResolvedReviewedView, isFolioReviewedView, isFolioVersionComparisonPrivacyTransform, isFolioVersionComparisonScope, isSequentialFolioBlockId, isSupportedFolioDocumentOperationVersion, normalizeFolioAIBlockText, pageBreak, paragraph, parseDocx, parseFolioDocumentOperationBatch, parseFolioDocxXmlPatchProposal, readFolioDocumentSection, replyToComment, rewriteDocxMetadataPrivacy, run, table, validateDocxConformance };
@@ -160,6 +160,8 @@ const createStellaStyleSet = () => ({
160
160
  },
161
161
  rPr: { bold: true }
162
162
  },
163
+ ...createHeadingStyles(),
164
+ ...createTableOfContentsStyles(),
163
165
  ...createClauseStyles(),
164
166
  ...createDefinitionStyles(),
165
167
  {
@@ -248,6 +250,45 @@ const createStellaStyleSet = () => ({
248
250
  uiPriority: 99,
249
251
  rPr: { vertAlign: "superscript" }
250
252
  },
253
+ {
254
+ styleId: "EndnoteText",
255
+ type: "paragraph",
256
+ name: "Endnote Text",
257
+ basedOn: "Normal",
258
+ link: "EndnoteTextChar",
259
+ semiHidden: true,
260
+ unhideWhenUsed: true,
261
+ uiPriority: 99,
262
+ pPr: { spaceAfter: 0 },
263
+ rPr: {
264
+ fontSize: 18,
265
+ fontSizeCs: 18
266
+ }
267
+ },
268
+ {
269
+ styleId: "EndnoteTextChar",
270
+ type: "character",
271
+ name: "Endnote Text Char",
272
+ basedOn: "DefaultParagraphFont",
273
+ link: "EndnoteText",
274
+ semiHidden: true,
275
+ unhideWhenUsed: true,
276
+ uiPriority: 99,
277
+ rPr: {
278
+ fontSize: 18,
279
+ fontSizeCs: 18
280
+ }
281
+ },
282
+ {
283
+ styleId: "EndnoteReference",
284
+ type: "character",
285
+ name: "Endnote Reference",
286
+ basedOn: "DefaultParagraphFont",
287
+ semiHidden: true,
288
+ unhideWhenUsed: true,
289
+ uiPriority: 99,
290
+ rPr: { vertAlign: "superscript" }
291
+ },
251
292
  {
252
293
  styleId: "Footer",
253
294
  type: "paragraph",
@@ -350,6 +391,111 @@ const createStellaStyleDocumentPreset = () => ({
350
391
  verticalAlign: "top"
351
392
  }
352
393
  });
394
+ /**
395
+ * Built-in `Heading1`..`Heading6` with outline levels, so a TOC field and
396
+ * the document navigation pane pick the headings up.
397
+ */
398
+ const createHeadingStyles = () => {
399
+ return [
400
+ {
401
+ styleId: "Heading1",
402
+ name: "heading 1",
403
+ fontSize: 28,
404
+ spaceBefore: 360
405
+ },
406
+ {
407
+ styleId: "Heading2",
408
+ name: "heading 2",
409
+ fontSize: 24,
410
+ spaceBefore: 240
411
+ },
412
+ {
413
+ styleId: "Heading3",
414
+ name: "heading 3",
415
+ fontSize: 22,
416
+ spaceBefore: 240
417
+ },
418
+ {
419
+ styleId: "Heading4",
420
+ name: "heading 4",
421
+ fontSize: 20,
422
+ spaceBefore: 120
423
+ },
424
+ {
425
+ styleId: "Heading5",
426
+ name: "heading 5",
427
+ fontSize: 20,
428
+ spaceBefore: 120
429
+ },
430
+ {
431
+ styleId: "Heading6",
432
+ name: "heading 6",
433
+ fontSize: 20,
434
+ spaceBefore: 120
435
+ }
436
+ ].map(({ styleId, name, fontSize, spaceBefore }, level) => ({
437
+ styleId,
438
+ type: "paragraph",
439
+ name,
440
+ basedOn: "Normal",
441
+ next: "BodyText",
442
+ qFormat: true,
443
+ uiPriority: 9,
444
+ pPr: {
445
+ keepNext: true,
446
+ keepLines: true,
447
+ spaceBefore,
448
+ spaceAfter: 120,
449
+ outlineLevel: level
450
+ },
451
+ rPr: {
452
+ bold: true,
453
+ fontSize,
454
+ fontSizeCs: fontSize
455
+ }
456
+ }));
457
+ };
458
+ /** `TOCHeading` plus `TOC1`..`TOC3`, the styles a `TOC \o "1-3"` field fills. */
459
+ const createTableOfContentsStyles = () => {
460
+ return [{
461
+ styleId: "TOCHeading",
462
+ type: "paragraph",
463
+ name: "TOC Heading",
464
+ basedOn: "Heading1",
465
+ next: "BodyText",
466
+ unhideWhenUsed: true,
467
+ uiPriority: 39,
468
+ pPr: { outlineLevel: 9 }
469
+ }, ...[
470
+ {
471
+ styleId: "TOC1",
472
+ name: "toc 1",
473
+ indent: 0
474
+ },
475
+ {
476
+ styleId: "TOC2",
477
+ name: "toc 2",
478
+ indent: 220
479
+ },
480
+ {
481
+ styleId: "TOC3",
482
+ name: "toc 3",
483
+ indent: 440
484
+ }
485
+ ].map(({ styleId, name, indent }) => ({
486
+ styleId,
487
+ type: "paragraph",
488
+ name,
489
+ basedOn: "Normal",
490
+ next: "Normal",
491
+ unhideWhenUsed: true,
492
+ uiPriority: 39,
493
+ pPr: {
494
+ indentLeft: indent,
495
+ spaceAfter: 100
496
+ }
497
+ }))];
498
+ };
353
499
  const createClauseStyles = () => {
354
500
  return [
355
501
  {
@@ -246,6 +246,7 @@ function createEmptyDocument(options = {}) {
246
246
  const docxPackage = {
247
247
  conformanceClass: DOCX_CONFORMANCE_CLASSES.TRANSITIONAL,
248
248
  document: documentBody,
249
+ relationships: /* @__PURE__ */ new Map(),
249
250
  styles: structuredClone(styleSet?.styles ?? defaultStyleDefinitions)
250
251
  };
251
252
  if (styleSet?.numbering) docxPackage.numbering = structuredClone(styleSet.numbering);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stll/folio-core",
3
- "version": "0.19.0",
3
+ "version": "0.21.0",
4
4
  "description": "Headless, framework-neutral core of folio: the OOXML (.docx) parser, document model, ProseMirror integration, and page-layout engine. No React.",
5
5
  "keywords": [
6
6
  "document-model",
@@ -113,7 +113,7 @@
113
113
  "perf": "bun scripts/profile-editor.ts"
114
114
  },
115
115
  "dependencies": {
116
- "@stll/docx-core": "^0.14.0",
116
+ "@stll/docx-core": "^0.15.0",
117
117
  "@stll/docx-utils": "^0.1.0",
118
118
  "@stll/template-conditions": "^0.1.0",
119
119
  "better-result": "2.10.0",