@stll/folio-core 0.7.0 → 0.8.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 (92) hide show
  1. package/dist/ai-edits/blockRange.d.ts +13 -2
  2. package/dist/ai-edits/blockRange.js +23 -2
  3. package/dist/ai-edits/index.d.ts +4 -2
  4. package/dist/ai-edits/index.js +3 -1
  5. package/dist/ai-edits/scoped-reading.d.ts +9 -0
  6. package/dist/ai-edits/scoped-reading.js +60 -0
  7. package/dist/ai-edits/snapshot.js +13 -4
  8. package/dist/ai-edits/types.d.ts +44 -2
  9. package/dist/compat/eigenpal.d.ts +5 -2
  10. package/dist/compat/eigenpal.js +4 -1
  11. package/dist/controller/fontReadiness.d.ts +29 -0
  12. package/dist/controller/fontReadiness.js +139 -0
  13. package/dist/controller/layoutPipeline.js +76 -9
  14. package/dist/docx/blockContentParser.js +51 -8
  15. package/dist/docx/capabilities.d.ts +41 -0
  16. package/dist/docx/capabilities.js +322 -0
  17. package/dist/docx/compatibility.d.ts +34 -2
  18. package/dist/docx/compatibility.js +128 -38
  19. package/dist/docx/conformance.d.ts +6 -0
  20. package/dist/docx/conformance.js +18 -0
  21. package/dist/docx/ensureParaIds.d.ts +29 -0
  22. package/dist/docx/ensureParaIds.js +423 -0
  23. package/dist/docx/numberingParser.js +1 -0
  24. package/dist/docx/paragraphParser.js +9 -0
  25. package/dist/docx/paragraphTextBoxEnrichment.js +141 -2
  26. package/dist/docx/parser.js +2 -0
  27. package/dist/docx/selectiveXmlPatch.d.ts +10 -1
  28. package/dist/docx/selectiveXmlPatch.js +1 -1
  29. package/dist/docx/serializer/paragraphSerializer.js +4 -0
  30. package/dist/docx/serializer/runSerializer.js +7 -2
  31. package/dist/docx/serializer/tableSerializer.js +4 -0
  32. package/dist/docx/tableParser.js +8 -0
  33. package/dist/docx/textBoxParser.js +6 -1
  34. package/dist/docx/vmlImageParser.js +145 -1
  35. package/dist/index.d.ts +5 -2
  36. package/dist/index.js +4 -1
  37. package/dist/layout-bridge/convert/headerFooterLayout.js +16 -2
  38. package/dist/layout-bridge/convert/paragraphFrames.d.ts +7 -0
  39. package/dist/layout-bridge/convert/paragraphFrames.js +136 -0
  40. package/dist/layout-bridge/convert/toFlowBlocks.js +42 -3
  41. package/dist/layout-bridge/engine/hitTest.js +2 -1
  42. package/dist/layout-bridge/engine/selectionRects.js +2 -1
  43. package/dist/layout-engine/headerFooterRefs.d.ts +7 -0
  44. package/dist/layout-engine/headerFooterRefs.js +40 -0
  45. package/dist/layout-engine/index.d.ts +3 -2
  46. package/dist/layout-engine/index.js +36 -82
  47. package/dist/layout-engine/measure/listMarkerWidth.d.ts +3 -1
  48. package/dist/layout-engine/measure/listMarkerWidth.js +23 -4
  49. package/dist/layout-engine/measure/measureBlocks.d.ts +3 -0
  50. package/dist/layout-engine/measure/measureBlocks.js +133 -51
  51. package/dist/layout-engine/measure/measureParagraph.js +13 -4
  52. package/dist/layout-engine/measure/tableCellGrid.d.ts +15 -0
  53. package/dist/layout-engine/measure/tableCellGrid.js +62 -0
  54. package/dist/layout-engine/measure/textBoxParagraphLayout.d.ts +18 -0
  55. package/dist/layout-engine/measure/textBoxParagraphLayout.js +32 -0
  56. package/dist/layout-engine/paginator.d.ts +2 -1
  57. package/dist/layout-engine/paginator.js +7 -6
  58. package/dist/layout-engine/paragraphFrame.d.ts +22 -0
  59. package/dist/layout-engine/paragraphFrame.js +17 -0
  60. package/dist/layout-engine/paragraphSequence.d.ts +7 -0
  61. package/dist/layout-engine/paragraphSequence.js +25 -0
  62. package/dist/layout-engine/paragraphSpacing.d.ts +17 -0
  63. package/dist/layout-engine/paragraphSpacing.js +30 -0
  64. package/dist/layout-engine/renderedBreakReconciliation.d.ts +58 -0
  65. package/dist/layout-engine/renderedBreakReconciliation.js +63 -0
  66. package/dist/layout-engine/types.d.ts +17 -8
  67. package/dist/layout-engine/types.js +15 -2
  68. package/dist/layout-painter/renderPage.js +2 -2
  69. package/dist/layout-painter/renderParagraph.js +6 -5
  70. package/dist/layout-painter/renderTable.js +42 -12
  71. package/dist/layout-painter/renderTextBox.js +17 -7
  72. package/dist/managers/DocumentLoaderManager.js +1 -1
  73. package/dist/paged-layout/sectionBlockWidths.d.ts +3 -0
  74. package/dist/paged-layout/sectionBlockWidths.js +9 -0
  75. package/dist/paged-layout/sectionGeometry.js +1 -1
  76. package/dist/prosemirror/attrs/index.js +11 -0
  77. package/dist/prosemirror/conversion/fromProseDoc.js +2 -0
  78. package/dist/prosemirror/conversion/toProseDoc.js +27 -23
  79. package/dist/prosemirror/extensions/core/ParagraphExtension.js +1 -0
  80. package/dist/prosemirror/extensions/features/ListExtension.js +1 -0
  81. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +68 -16
  82. package/dist/prosemirror/extensions/nodes/TextBoxExtension.d.ts +2 -1
  83. package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +7 -0
  84. package/dist/prosemirror/schema/nodes.d.ts +4 -2
  85. package/dist/prosemirror/styles/resolvedStyleAttrs.js +1 -0
  86. package/dist/server.d.ts +4 -2
  87. package/dist/server.js +3 -1
  88. package/dist/types/index.d.ts +2 -1
  89. package/dist/utils/createDocument.js +26 -18
  90. package/dist/utils/hexId.d.ts +8 -1
  91. package/dist/utils/hexId.js +11 -3
  92. package/package.json +1 -1
@@ -19,15 +19,42 @@ import { tryBuildIncrementalMeasures } from "../paged-layout/incrementalMeasure.
19
19
  import { computePerBlockMeasureInputs } from "../paged-layout/sectionBlockWidths.js";
20
20
  import { getDocumentWatermark } from "../watermark/index.js";
21
21
  //#region src/controller/layoutPipeline.ts
22
- function bodyMarginsBelowHeader(authoredMargins, preparedHeader) {
23
- if (!preparedHeader) return authoredMargins;
24
- const headerBottom = (authoredMargins.header ?? 0) + (preparedHeader.marginPushBottom ?? preparedHeader.height);
25
- if (headerBottom <= authoredMargins.top) return authoredMargins;
22
+ function bodyMarginsClearHeaderFooter({ authoredMargins, preparedHeader, preparedFooter }) {
23
+ const headerBottom = preparedHeader ? (authoredMargins.header ?? 0) + (preparedHeader.marginPushBottom ?? preparedHeader.height) : authoredMargins.top;
24
+ const footerClearance = preparedFooter ? (authoredMargins.footer ?? 0) + (preparedFooter.marginPushBottom ?? preparedFooter.height) : authoredMargins.bottom;
25
+ const top = Math.max(authoredMargins.top, headerBottom);
26
+ const bottom = Math.max(authoredMargins.bottom, footerClearance);
27
+ if (top === authoredMargins.top && bottom === authoredMargins.bottom) return authoredMargins;
26
28
  return {
27
29
  ...authoredMargins,
28
- top: headerBottom
30
+ top,
31
+ bottom
29
32
  };
30
33
  }
34
+ function bodyBlocksClearSectionHeaderFooter(blocks, { authoredMargins, sectionHeaderFooterRefs, headerContentByRId, footerContentByRId }) {
35
+ if (!sectionHeaderFooterRefs || !headerContentByRId && !footerContentByRId) return blocks;
36
+ let sectionIndex = 0;
37
+ let currentAuthoredMargins = authoredMargins;
38
+ let changed = false;
39
+ const nextBlocks = blocks.map((block) => {
40
+ if (block.kind !== "sectionBreak") return block;
41
+ currentAuthoredMargins = block.margins ?? currentAuthoredMargins;
42
+ const refs = sectionHeaderFooterRefs[sectionIndex];
43
+ sectionIndex += 1;
44
+ const effectiveMargins = bodyMarginsClearHeaderFooter({
45
+ authoredMargins: currentAuthoredMargins,
46
+ preparedHeader: refs?.headerDefault ? headerContentByRId?.get(refs.headerDefault) : void 0,
47
+ preparedFooter: refs?.footerDefault ? footerContentByRId?.get(refs.footerDefault) : void 0
48
+ });
49
+ if (effectiveMargins === currentAuthoredMargins && block.margins === currentAuthoredMargins) return block;
50
+ changed = true;
51
+ return {
52
+ ...block,
53
+ margins: effectiveMargins
54
+ };
55
+ });
56
+ return changed ? nextBlocks : blocks;
57
+ }
31
58
  function runLayoutPipeline(deps, state, options = {}) {
32
59
  const { contentWidth, columns, pageSize, margins, pageGap, syncCoordinator, headerContent, footerContent, firstPageHeaderContent, firstPageFooterContent, headerContentRId, footerContentRId, firstPageHeaderContentRId, firstPageFooterContentRId, sectionHeaderFooterRefs, theme: _theme, sectionProperties, document, defaultTabStop, mirrorMargins, styles, layout, hfPMs, painter, pagesContainer, session, renderHfFromContentOrPm, renderHeaderFooterContentByRId, documentFontsAreLoaded, buildFootnoteRenderItems, describeInvalidHighlightMarks, emptyTemplatePreviewEntries: EMPTY_TEMPLATE_PREVIEW_ENTRIES } = deps;
33
60
  let outcome = {};
@@ -101,7 +128,28 @@ function runLayoutPipeline(deps, state, options = {}) {
101
128
  let firstPageFooterForRender = hasTitlePg ? renderHfFromContentOrPm(firstPageFooterContent, firstPageFooterContentRId, hfPMs, contentWidth, hfMetricsFooter, hfOptions) : void 0;
102
129
  let headerContentByRId = renderHeaderFooterContentByRId(document?.package.headers, hfPMs, contentWidth, hfMetricsHeader, hfOptions);
103
130
  let footerContentByRId = renderHeaderFooterContentByRId(document?.package.footers, hfPMs, contentWidth, hfMetricsFooter, hfOptions);
104
- const initialBodyMargins = bodyMarginsBelowHeader(margins, headerContentForRender);
131
+ const sectionPropertiesForMargins = document?.package.document.sections?.map((section) => section.properties) ?? (sectionProperties ? [sectionProperties] : []);
132
+ const sectionEvenPageMargins = sectionHeaderFooterRefs?.map((refs, index) => {
133
+ if (refs.evenAndOddHeaders !== true) return;
134
+ const properties = sectionPropertiesForMargins[index];
135
+ return bodyMarginsClearHeaderFooter({
136
+ authoredMargins: properties ? getMargins(properties) : margins,
137
+ preparedHeader: refs.headerEven ? headerContentByRId?.get(refs.headerEven) : void 0,
138
+ preparedFooter: refs.footerEven ? footerContentByRId?.get(refs.footerEven) : void 0
139
+ });
140
+ });
141
+ newBlocks = bodyBlocksClearSectionHeaderFooter(newBlocks, {
142
+ authoredMargins: margins,
143
+ sectionHeaderFooterRefs,
144
+ headerContentByRId,
145
+ footerContentByRId
146
+ });
147
+ outcome.blocks = newBlocks;
148
+ const initialBodyMargins = bodyMarginsClearHeaderFooter({
149
+ authoredMargins: margins,
150
+ preparedHeader: headerContentForRender,
151
+ preparedFooter: footerContentForRender
152
+ });
105
153
  recordPhaseDuration("header-footer", phaseStartedAt);
106
154
  phaseStartedAt = performance.now();
107
155
  const bodyLayoutConfig = {
@@ -114,9 +162,17 @@ function runLayoutPipeline(deps, state, options = {}) {
114
162
  let finalHeaderForLayout = headerContentForRender;
115
163
  if (finalHeaderRId) finalHeaderForLayout = headerContentByRId?.get(finalHeaderRId);
116
164
  else if (sectionHeaderFooterRefs !== void 0) finalHeaderForLayout = void 0;
165
+ const finalFooterRId = sectionHeaderFooterRefs?.at(-1)?.footerDefault;
166
+ let finalFooterForLayout = footerContentForRender;
167
+ if (finalFooterRId) finalFooterForLayout = footerContentByRId?.get(finalFooterRId);
168
+ else if (sectionHeaderFooterRefs !== void 0) finalFooterForLayout = void 0;
117
169
  const finalLayoutConfig = finalSectionProperties ? {
118
170
  pageSize: getPageSize(finalSectionProperties),
119
- margins: bodyMarginsBelowHeader(getMargins(finalSectionProperties), finalHeaderForLayout)
171
+ margins: bodyMarginsClearHeaderFooter({
172
+ authoredMargins: getMargins(finalSectionProperties),
173
+ preparedHeader: finalHeaderForLayout,
174
+ preparedFooter: finalFooterForLayout
175
+ })
120
176
  } : bodyLayoutConfig;
121
177
  const finalColumns = getColumns(finalSectionProperties);
122
178
  if (finalColumns !== void 0) finalLayoutConfig.columns = finalColumns;
@@ -137,7 +193,10 @@ function runLayoutPipeline(deps, state, options = {}) {
137
193
  measureBlock: measureSingleBlockWithoutFloatingZones
138
194
  }) : null;
139
195
  let newMeasures = incrementalResult?.measures ?? measureBlocks(newBlocks, blockWidths, blockMeasureInputs.marginTops, {
196
+ pageWidth: blockMeasureInputs.pageWidths,
140
197
  pageHeight: blockMeasureInputs.pageHeights,
198
+ marginLeft: blockMeasureInputs.marginLefts,
199
+ marginRight: blockMeasureInputs.marginRights,
141
200
  marginBottom: blockMeasureInputs.marginBottoms
142
201
  });
143
202
  pendingArtifacts = {
@@ -151,7 +210,7 @@ function runLayoutPipeline(deps, state, options = {}) {
151
210
  let newLayout;
152
211
  let pageFootnoteMap = /* @__PURE__ */ new Map();
153
212
  let footnoteContentMap = /* @__PURE__ */ new Map();
154
- const bodyBreakType = sectionProperties?.sectionStart;
213
+ const bodyBreakType = finalSectionProperties?.sectionStart;
155
214
  const buildLayoutOpts = () => {
156
215
  const nextLayoutOpts = {
157
216
  pageSize,
@@ -159,7 +218,11 @@ function runLayoutPipeline(deps, state, options = {}) {
159
218
  pageGap,
160
219
  mirrorMargins
161
220
  };
162
- if (hasTitlePg) nextLayoutOpts.firstPageMargins = bodyMarginsBelowHeader(margins, firstPageHeaderForRender);
221
+ if (hasTitlePg) nextLayoutOpts.firstPageMargins = bodyMarginsClearHeaderFooter({
222
+ authoredMargins: margins,
223
+ preparedHeader: firstPageHeaderForRender,
224
+ preparedFooter: firstPageFooterForRender
225
+ });
163
226
  if (finalSectionProperties) {
164
227
  nextLayoutOpts.finalPageSize = finalLayoutConfig.pageSize;
165
228
  nextLayoutOpts.finalMargins = finalLayoutConfig.margins;
@@ -171,6 +234,7 @@ function runLayoutPipeline(deps, state, options = {}) {
171
234
  if (columns !== void 0) nextLayoutOpts.columns = columns;
172
235
  if (bodyBreakType !== void 0) nextLayoutOpts.bodyBreakType = bodyBreakType;
173
236
  if (sectionHeaderFooterRefs !== void 0) nextLayoutOpts.sectionHeaderFooterRefs = sectionHeaderFooterRefs;
237
+ if (sectionEvenPageMargins !== void 0) nextLayoutOpts.sectionEvenPageMargins = sectionEvenPageMargins;
174
238
  return nextLayoutOpts;
175
239
  };
176
240
  const layoutOpts = buildLayoutOpts();
@@ -237,7 +301,10 @@ function runLayoutPipeline(deps, state, options = {}) {
237
301
  previousFieldValues = values;
238
302
  stabilizedFieldValues = values;
239
303
  newMeasures = measureBlocks(newBlocks, blockWidths, blockMeasureInputs.marginTops, {
304
+ pageWidth: blockMeasureInputs.pageWidths,
240
305
  pageHeight: blockMeasureInputs.pageHeights,
306
+ marginLeft: blockMeasureInputs.marginLefts,
307
+ marginRight: blockMeasureInputs.marginRights,
241
308
  marginBottom: blockMeasureInputs.marginBottoms
242
309
  }, values);
243
310
  relayoutWithCurrentMeasures(layoutOptsUsed);
@@ -49,23 +49,42 @@ const formatNumber = (value, numFmt) => {
49
49
  case "lowerRoman": return toRoman(value).toLowerCase();
50
50
  case "upperRoman": return toRoman(value);
51
51
  case "bullet": return "•";
52
+ case "none": return "";
52
53
  default: return String(value);
53
54
  }
54
55
  };
55
- const computeListMarker = (paragraph, numbering, listCounters, abstractCounters) => {
56
+ const computeListMarker = (paragraph, { numbering, listCounters, abstractCounters, restartedNumIds, previousList }) => {
56
57
  const listRendering = paragraph.listRendering;
57
- if (!listRendering || !numbering) return;
58
+ if (!listRendering || !numbering) {
59
+ previousList.abstractNumId = null;
60
+ previousList.fromStyle = false;
61
+ previousList.numId = null;
62
+ return;
63
+ }
58
64
  const { numId, level } = listRendering;
59
- if (numId === 0) return;
60
- if (!listCounters.has(numId)) listCounters.set(numId, Array.from({ length: 9 }).fill(NaN));
65
+ if (numId === 0) {
66
+ previousList.abstractNumId = null;
67
+ previousList.fromStyle = false;
68
+ previousList.numId = null;
69
+ return;
70
+ }
71
+ const firstEncounter = !listCounters.has(numId);
72
+ if (firstEncounter) listCounters.set(numId, Array.from({ length: 9 }).fill(NaN));
61
73
  const counters = listCounters.get(numId);
62
74
  if (!counters) return;
63
75
  const abstractNumId = numbering.getAbstractNumId(numId);
64
76
  const styleNumbering = paragraph.formatting?.numPrFromStyle;
65
- if (abstractNumId !== null && styleNumbering) {
77
+ let resumedAbstractCounters;
78
+ const resumesRestartedInstance = firstEncounter && listRendering.startOverride === void 0 && abstractNumId !== null && previousList.abstractNumId === abstractNumId && previousList.numId !== null && previousList.numId !== numId && restartedNumIds.has(previousList.numId);
79
+ const resumesStyleInstance = firstEncounter && !styleNumbering && listRendering.startOverride === void 0 && abstractNumId !== null && previousList.abstractNumId === abstractNumId && previousList.fromStyle === true;
80
+ if (abstractNumId !== null && (styleNumbering || resumesRestartedInstance || resumesStyleInstance)) {
66
81
  const latestAbstractCounters = abstractCounters.get(abstractNumId);
67
- if (latestAbstractCounters) for (let i = 0; i < counters.length; i += 1) counters[i] = latestAbstractCounters[i] ?? NaN;
82
+ if (latestAbstractCounters) {
83
+ for (let i = 0; i < counters.length; i += 1) counters[i] = latestAbstractCounters[i] ?? NaN;
84
+ resumedAbstractCounters = latestAbstractCounters;
85
+ }
68
86
  }
87
+ if (listRendering.startOverride !== void 0 || resumesRestartedInstance || previousList.numId === numId && restartedNumIds.has(numId)) restartedNumIds.add(numId);
69
88
  if (abstractNumId !== null && level > 0) {
70
89
  const latestAbstractCounters = abstractCounters.get(abstractNumId);
71
90
  if (counters.slice(0, level).every(Number.isNaN)) for (let i = 0; i < level; i += 1) {
@@ -81,10 +100,22 @@ const computeListMarker = (paragraph, numbering, listCounters, abstractCounters)
81
100
  const childCounter = counters[level + 1];
82
101
  counters[level + 1] = (childCounter === void 0 || Number.isNaN(childCounter) ? 0 : childCounter) + childAdvances;
83
102
  }
84
- if (abstractNumId !== null) abstractCounters.set(abstractNumId, [...counters]);
103
+ if (abstractNumId !== null) {
104
+ if (resumedAbstractCounters) for (const [otherNumId, otherCounters] of listCounters) {
105
+ if (otherNumId === numId || numbering.getAbstractNumId(otherNumId) !== abstractNumId || !otherCounters.every((value, index) => Object.is(value, resumedAbstractCounters[index]))) continue;
106
+ for (let i = 0; i < otherCounters.length; i += 1) otherCounters[i] = counters[i] ?? NaN;
107
+ }
108
+ abstractCounters.set(abstractNumId, [...counters]);
109
+ }
110
+ previousList.abstractNumId = abstractNumId;
111
+ previousList.fromStyle = Boolean(styleNumbering);
112
+ previousList.numId = numId;
85
113
  const pattern = listRendering.marker;
86
114
  if (listRendering.isBullet) {
87
115
  listRendering.marker = convertBulletToUnicode(pattern || "");
116
+ previousList.abstractNumId = null;
117
+ previousList.fromStyle = false;
118
+ previousList.numId = null;
88
119
  return;
89
120
  }
90
121
  let computedMarker = pattern;
@@ -103,6 +134,12 @@ const computeListMarker = (paragraph, numbering, listCounters, abstractCounters)
103
134
  const parseBlockContent = (parent, styles, theme, numbering, rels, media, options) => parseBlockContentWithState(parent, styles, theme, numbering, rels, media, {
104
135
  listCounters: /* @__PURE__ */ new Map(),
105
136
  abstractCounters: /* @__PURE__ */ new Map(),
137
+ restartedNumIds: /* @__PURE__ */ new Set(),
138
+ previousList: {
139
+ abstractNumId: null,
140
+ fromStyle: false,
141
+ numId: null
142
+ },
106
143
  options: {
107
144
  ...options,
108
145
  rootXmlns: mergeXmlnsDeclarations(options?.rootXmlns ?? {}, parent)
@@ -118,7 +155,13 @@ const parseBlockContentWithState = (parent, styles, theme, numbering, rels, medi
118
155
  const paragraph = parseParagraph(child, styles, theme, numbering, rels, media, state.options);
119
156
  prependPendingBookmarkMarkers(paragraph, pendingBookmarkMarkers);
120
157
  enrichParagraphTextBoxes(paragraph, child, styles, theme, numbering, rels, media);
121
- computeListMarker(paragraph, numbering, state.listCounters, state.abstractCounters);
158
+ computeListMarker(paragraph, {
159
+ numbering,
160
+ listCounters: state.listCounters,
161
+ abstractCounters: state.abstractCounters,
162
+ restartedNumIds: state.restartedNumIds,
163
+ previousList: state.previousList
164
+ });
122
165
  content.push(paragraph);
123
166
  continue;
124
167
  }
@@ -0,0 +1,41 @@
1
+ //#region src/docx/capabilities.d.ts
2
+ declare const FOLIO_DOCX_CAPABILITY_MANIFEST_VERSION: 1;
3
+ declare const FOLIO_DOCX_CAPABILITY_HOSTS: readonly ["browser", "server"];
4
+ declare const FOLIO_DOCX_COMPATIBILITY_HOSTS: readonly ["browser", "server", "unknown"];
5
+ declare const FOLIO_DOCX_PROFILES: readonly ["strict", "transitional"];
6
+ declare const FOLIO_DOCX_COMPATIBILITY_PROFILES: readonly ["strict", "transitional", "unknown"];
7
+ declare const FOLIO_DOCX_CAPABILITY_OPERATIONS: readonly ["create", "edit", "preserve", "read", "render"];
8
+ declare const FOLIO_DOCX_SUPPORT_STATES: readonly ["supported", "partial", "unsupported"];
9
+ declare const FOLIO_DOCX_CAPABILITY_IDS: readonly ["comments", "contentControls", "fields", "headersFooters", "hyperlinks", "images", "math", "notes", "numbering", "opaqueDrawing", "paragraphs", "sections", "styles", "tables", "trackedChanges"];
10
+ type FolioDocxCapabilityHost = (typeof FOLIO_DOCX_CAPABILITY_HOSTS)[number];
11
+ type FolioDocxCompatibilityHost = (typeof FOLIO_DOCX_COMPATIBILITY_HOSTS)[number];
12
+ type FolioDocxProfile = (typeof FOLIO_DOCX_PROFILES)[number];
13
+ type FolioDocxCompatibilityProfile = (typeof FOLIO_DOCX_COMPATIBILITY_PROFILES)[number];
14
+ type FolioDocxCapabilityOperation = (typeof FOLIO_DOCX_CAPABILITY_OPERATIONS)[number];
15
+ type FolioDocxSupportState = (typeof FOLIO_DOCX_SUPPORT_STATES)[number];
16
+ type FolioDocxCapabilityId = (typeof FOLIO_DOCX_CAPABILITY_IDS)[number];
17
+ type FolioDocxFeatureCapability = {
18
+ readonly id: FolioDocxCapabilityId;
19
+ readonly feature: "comments" | "contentControls" | "drawings" | "fields" | "headersFooters" | "hyperlinks" | "images" | "math" | "notes" | "numbering" | "paragraphs" | "revisions" | "sections" | "styles" | "tables";
20
+ readonly hosts: readonly FolioDocxCapabilityHost[];
21
+ readonly profiles: readonly FolioDocxProfile[];
22
+ readonly support: Readonly<Record<FolioDocxCapabilityOperation, FolioDocxSupportState>>;
23
+ readonly evidence: readonly {
24
+ readonly type: "test";
25
+ readonly path: string;
26
+ }[];
27
+ };
28
+ type FolioDocxCapabilityManifest = {
29
+ readonly version: typeof FOLIO_DOCX_CAPABILITY_MANIFEST_VERSION;
30
+ readonly capabilities: Readonly<Record<FolioDocxCapabilityId, FolioDocxFeatureCapability>>;
31
+ };
32
+ declare const FOLIO_DOCX_CAPABILITY_MANIFEST: FolioDocxCapabilityManifest;
33
+ declare const InvalidFolioDocxCapabilityIdError_base: import("better-result").TaggedErrorClass<"InvalidFolioDocxCapabilityIdError", {
34
+ message: string;
35
+ receivedId: unknown;
36
+ }>;
37
+ declare class InvalidFolioDocxCapabilityIdError extends InvalidFolioDocxCapabilityIdError_base {}
38
+ declare const isFolioDocxCapabilityId: (value: unknown) => value is FolioDocxCapabilityId;
39
+ declare const getFolioDocxCapability: (id: unknown) => FolioDocxFeatureCapability;
40
+ //#endregion
41
+ export { FOLIO_DOCX_CAPABILITY_HOSTS, FOLIO_DOCX_CAPABILITY_IDS, FOLIO_DOCX_CAPABILITY_MANIFEST, FOLIO_DOCX_CAPABILITY_MANIFEST_VERSION, FOLIO_DOCX_CAPABILITY_OPERATIONS, FOLIO_DOCX_COMPATIBILITY_HOSTS, FOLIO_DOCX_COMPATIBILITY_PROFILES, FOLIO_DOCX_PROFILES, FOLIO_DOCX_SUPPORT_STATES, FolioDocxCapabilityHost, FolioDocxCapabilityId, FolioDocxCapabilityManifest, FolioDocxCapabilityOperation, FolioDocxCompatibilityHost, FolioDocxCompatibilityProfile, FolioDocxFeatureCapability, FolioDocxProfile, FolioDocxSupportState, InvalidFolioDocxCapabilityIdError, getFolioDocxCapability, isFolioDocxCapabilityId };
@@ -0,0 +1,322 @@
1
+ import { TaggedError } from "better-result";
2
+ //#region src/docx/capabilities.ts
3
+ const FOLIO_DOCX_CAPABILITY_MANIFEST_VERSION = 1;
4
+ const FOLIO_DOCX_CAPABILITY_HOSTS = Object.freeze(["browser", "server"]);
5
+ const FOLIO_DOCX_COMPATIBILITY_HOSTS = Object.freeze([...FOLIO_DOCX_CAPABILITY_HOSTS, "unknown"]);
6
+ const FOLIO_DOCX_PROFILES = Object.freeze(["strict", "transitional"]);
7
+ const FOLIO_DOCX_COMPATIBILITY_PROFILES = Object.freeze([...FOLIO_DOCX_PROFILES, "unknown"]);
8
+ const FOLIO_DOCX_CAPABILITY_OPERATIONS = Object.freeze([
9
+ "create",
10
+ "edit",
11
+ "preserve",
12
+ "read",
13
+ "render"
14
+ ]);
15
+ const FOLIO_DOCX_SUPPORT_STATES = Object.freeze([
16
+ "supported",
17
+ "partial",
18
+ "unsupported"
19
+ ]);
20
+ const FOLIO_DOCX_CAPABILITY_IDS = Object.freeze([
21
+ "comments",
22
+ "contentControls",
23
+ "fields",
24
+ "headersFooters",
25
+ "hyperlinks",
26
+ "images",
27
+ "math",
28
+ "notes",
29
+ "numbering",
30
+ "opaqueDrawing",
31
+ "paragraphs",
32
+ "sections",
33
+ "styles",
34
+ "tables",
35
+ "trackedChanges"
36
+ ]);
37
+ const TRANSITIONAL_PROFILE_COVERAGE = Object.freeze(["transitional"]);
38
+ const STRUCTURED_FEATURE_SUPPORT = Object.freeze({
39
+ create: "supported",
40
+ edit: "supported",
41
+ preserve: "supported",
42
+ read: "supported",
43
+ render: "partial"
44
+ });
45
+ const PARTIAL_AUTHORING_FEATURE_SUPPORT = Object.freeze({
46
+ create: "partial",
47
+ edit: "partial",
48
+ preserve: "supported",
49
+ read: "supported",
50
+ render: "partial"
51
+ });
52
+ const COMMENTS_CAPABILITY = Object.freeze({
53
+ id: "comments",
54
+ feature: "comments",
55
+ hosts: FOLIO_DOCX_CAPABILITY_HOSTS,
56
+ profiles: TRANSITIONAL_PROFILE_COVERAGE,
57
+ support: STRUCTURED_FEATURE_SUPPORT,
58
+ evidence: Object.freeze([{
59
+ type: "test",
60
+ path: "packages/core/src/docx/commentReplyThreads.test.ts"
61
+ }, {
62
+ type: "test",
63
+ path: "packages/core/src/prosemirror/commands/comments.test.ts"
64
+ }])
65
+ });
66
+ const CONTENT_CONTROLS_CAPABILITY = Object.freeze({
67
+ id: "contentControls",
68
+ feature: "contentControls",
69
+ hosts: FOLIO_DOCX_CAPABILITY_HOSTS,
70
+ profiles: TRANSITIONAL_PROFILE_COVERAGE,
71
+ support: Object.freeze({
72
+ ...PARTIAL_AUTHORING_FEATURE_SUPPORT,
73
+ edit: "supported"
74
+ }),
75
+ evidence: Object.freeze([{
76
+ type: "test",
77
+ path: "packages/core/src/content-controls/contentControls.test.ts"
78
+ }, {
79
+ type: "test",
80
+ path: "packages/core/src/docx/sdtRoundtrip.property.test.ts"
81
+ }])
82
+ });
83
+ const FIELDS_CAPABILITY = Object.freeze({
84
+ id: "fields",
85
+ feature: "fields",
86
+ hosts: FOLIO_DOCX_CAPABILITY_HOSTS,
87
+ profiles: TRANSITIONAL_PROFILE_COVERAGE,
88
+ support: PARTIAL_AUTHORING_FEATURE_SUPPORT,
89
+ evidence: Object.freeze([{
90
+ type: "test",
91
+ path: "packages/core/src/fields/evaluateField.test.ts"
92
+ }, {
93
+ type: "test",
94
+ path: "packages/core/src/prosemirror/extensions/nodes/FieldExtension.test.ts"
95
+ }])
96
+ });
97
+ const HEADERS_FOOTERS_CAPABILITY = Object.freeze({
98
+ id: "headersFooters",
99
+ feature: "headersFooters",
100
+ hosts: FOLIO_DOCX_CAPABILITY_HOSTS,
101
+ profiles: TRANSITIONAL_PROFILE_COVERAGE,
102
+ support: STRUCTURED_FEATURE_SUPPORT,
103
+ evidence: Object.freeze([{
104
+ type: "test",
105
+ path: "packages/core/src/docx/headerFooterParser.test.ts"
106
+ }, {
107
+ type: "test",
108
+ path: "packages/core/src/docx/headerFooterMaterialize.test.ts"
109
+ }])
110
+ });
111
+ const HYPERLINKS_CAPABILITY = Object.freeze({
112
+ id: "hyperlinks",
113
+ feature: "hyperlinks",
114
+ hosts: FOLIO_DOCX_CAPABILITY_HOSTS,
115
+ profiles: TRANSITIONAL_PROFILE_COVERAGE,
116
+ support: PARTIAL_AUTHORING_FEATURE_SUPPORT,
117
+ evidence: Object.freeze([{
118
+ type: "test",
119
+ path: "packages/core/src/docx/hyperlinkParser.test.ts"
120
+ }, {
121
+ type: "test",
122
+ path: "packages/core/src/prosemirror/conversion/toProseDoc-hyperlink-content.test.ts"
123
+ }])
124
+ });
125
+ const IMAGES_CAPABILITY = Object.freeze({
126
+ id: "images",
127
+ feature: "images",
128
+ hosts: FOLIO_DOCX_CAPABILITY_HOSTS,
129
+ profiles: TRANSITIONAL_PROFILE_COVERAGE,
130
+ support: Object.freeze({
131
+ ...PARTIAL_AUTHORING_FEATURE_SUPPORT,
132
+ edit: "supported"
133
+ }),
134
+ evidence: Object.freeze([{
135
+ type: "test",
136
+ path: "packages/core/src/docx/__tests__/image-crop-roundtrip.test.ts"
137
+ }, {
138
+ type: "test",
139
+ path: "packages/core/src/prosemirror/commands/image.test.ts"
140
+ }])
141
+ });
142
+ const MATH_CAPABILITY = Object.freeze({
143
+ id: "math",
144
+ feature: "math",
145
+ hosts: FOLIO_DOCX_CAPABILITY_HOSTS,
146
+ profiles: TRANSITIONAL_PROFILE_COVERAGE,
147
+ support: PARTIAL_AUTHORING_FEATURE_SUPPORT,
148
+ evidence: Object.freeze([{
149
+ type: "test",
150
+ path: "packages/core/src/docx/mathToMathml.test.ts"
151
+ }, {
152
+ type: "test",
153
+ path: "packages/core/src/docx/serializer/math-roundtrip.test.ts"
154
+ }])
155
+ });
156
+ const NOTES_CAPABILITY = Object.freeze({
157
+ id: "notes",
158
+ feature: "notes",
159
+ hosts: FOLIO_DOCX_CAPABILITY_HOSTS,
160
+ profiles: TRANSITIONAL_PROFILE_COVERAGE,
161
+ support: Object.freeze({
162
+ create: "partial",
163
+ edit: "supported",
164
+ preserve: "supported",
165
+ read: "supported",
166
+ render: "partial"
167
+ }),
168
+ evidence: Object.freeze([{
169
+ type: "test",
170
+ path: "packages/core/src/docx/noteSave.test.ts"
171
+ }, {
172
+ type: "test",
173
+ path: "packages/core/src/layout-bridge/convert/footnoteLayout-collect.test.ts"
174
+ }])
175
+ });
176
+ const NUMBERING_CAPABILITY = Object.freeze({
177
+ id: "numbering",
178
+ feature: "numbering",
179
+ hosts: FOLIO_DOCX_CAPABILITY_HOSTS,
180
+ profiles: TRANSITIONAL_PROFILE_COVERAGE,
181
+ support: Object.freeze({
182
+ create: "partial",
183
+ edit: "supported",
184
+ preserve: "supported",
185
+ read: "supported",
186
+ render: "partial"
187
+ }),
188
+ evidence: Object.freeze([{
189
+ type: "test",
190
+ path: "packages/core/src/docx/numberingParser-custom-format.test.ts"
191
+ }, {
192
+ type: "test",
193
+ path: "packages/core/src/docx/numberingSave.test.ts"
194
+ }])
195
+ });
196
+ const OPAQUE_DRAWING_CAPABILITY = Object.freeze({
197
+ id: "opaqueDrawing",
198
+ feature: "drawings",
199
+ hosts: FOLIO_DOCX_CAPABILITY_HOSTS,
200
+ profiles: TRANSITIONAL_PROFILE_COVERAGE,
201
+ support: Object.freeze({
202
+ create: "unsupported",
203
+ edit: "unsupported",
204
+ preserve: "supported",
205
+ read: "supported",
206
+ render: "partial"
207
+ }),
208
+ evidence: Object.freeze([{
209
+ type: "test",
210
+ path: "packages/core/src/docx/compatibility.test.ts"
211
+ }, {
212
+ type: "test",
213
+ path: "packages/core/src/docx/rezip.test.ts"
214
+ }])
215
+ });
216
+ const PARAGRAPHS_CAPABILITY = Object.freeze({
217
+ id: "paragraphs",
218
+ feature: "paragraphs",
219
+ hosts: FOLIO_DOCX_CAPABILITY_HOSTS,
220
+ profiles: TRANSITIONAL_PROFILE_COVERAGE,
221
+ support: STRUCTURED_FEATURE_SUPPORT,
222
+ evidence: Object.freeze([{
223
+ type: "test",
224
+ path: "packages/core/src/docx/paragraphParser.test.ts"
225
+ }, {
226
+ type: "test",
227
+ path: "packages/core/src/docx/serializer/paragraphSerializer.test.ts"
228
+ }])
229
+ });
230
+ const SECTIONS_CAPABILITY = Object.freeze({
231
+ id: "sections",
232
+ feature: "sections",
233
+ hosts: FOLIO_DOCX_CAPABILITY_HOSTS,
234
+ profiles: TRANSITIONAL_PROFILE_COVERAGE,
235
+ support: STRUCTURED_FEATURE_SUPPORT,
236
+ evidence: Object.freeze([{
237
+ type: "test",
238
+ path: "packages/core/src/docx/serializer/sectionPropertiesSerializer.test.ts"
239
+ }, {
240
+ type: "test",
241
+ path: "packages/core/src/prosemirror/commands/sectionBreak.test.ts"
242
+ }])
243
+ });
244
+ const STYLES_CAPABILITY = Object.freeze({
245
+ id: "styles",
246
+ feature: "styles",
247
+ hosts: FOLIO_DOCX_CAPABILITY_HOSTS,
248
+ profiles: TRANSITIONAL_PROFILE_COVERAGE,
249
+ support: Object.freeze({
250
+ create: "partial",
251
+ edit: "partial",
252
+ preserve: "supported",
253
+ read: "supported",
254
+ render: "partial"
255
+ }),
256
+ evidence: Object.freeze([{
257
+ type: "test",
258
+ path: "packages/core/src/docx/styleParser.test.ts"
259
+ }, {
260
+ type: "test",
261
+ path: "packages/core/src/prosemirror/conversion/characterStyleRoundtrip.test.ts"
262
+ }])
263
+ });
264
+ const TABLES_CAPABILITY = Object.freeze({
265
+ id: "tables",
266
+ feature: "tables",
267
+ hosts: FOLIO_DOCX_CAPABILITY_HOSTS,
268
+ profiles: TRANSITIONAL_PROFILE_COVERAGE,
269
+ support: STRUCTURED_FEATURE_SUPPORT,
270
+ evidence: Object.freeze([{
271
+ type: "test",
272
+ path: "packages/core/src/docx/tableParser.test.ts"
273
+ }, {
274
+ type: "test",
275
+ path: "packages/core/src/prosemirror/conversion/tableBordersRoundtrip.test.ts"
276
+ }])
277
+ });
278
+ const TRACKED_CHANGES_CAPABILITY = Object.freeze({
279
+ id: "trackedChanges",
280
+ feature: "revisions",
281
+ hosts: FOLIO_DOCX_CAPABILITY_HOSTS,
282
+ profiles: TRANSITIONAL_PROFILE_COVERAGE,
283
+ support: STRUCTURED_FEATURE_SUPPORT,
284
+ evidence: Object.freeze([{
285
+ type: "test",
286
+ path: "packages/core/src/redline.test.ts"
287
+ }, {
288
+ type: "test",
289
+ path: "packages/core/src/prosemirror/plugins/suggestionMode.test.ts"
290
+ }])
291
+ });
292
+ const FOLIO_DOCX_CAPABILITY_MANIFEST = Object.freeze({
293
+ version: 1,
294
+ capabilities: Object.freeze({
295
+ comments: COMMENTS_CAPABILITY,
296
+ contentControls: CONTENT_CONTROLS_CAPABILITY,
297
+ fields: FIELDS_CAPABILITY,
298
+ headersFooters: HEADERS_FOOTERS_CAPABILITY,
299
+ hyperlinks: HYPERLINKS_CAPABILITY,
300
+ images: IMAGES_CAPABILITY,
301
+ math: MATH_CAPABILITY,
302
+ notes: NOTES_CAPABILITY,
303
+ numbering: NUMBERING_CAPABILITY,
304
+ opaqueDrawing: OPAQUE_DRAWING_CAPABILITY,
305
+ paragraphs: PARAGRAPHS_CAPABILITY,
306
+ sections: SECTIONS_CAPABILITY,
307
+ styles: STYLES_CAPABILITY,
308
+ tables: TABLES_CAPABILITY,
309
+ trackedChanges: TRACKED_CHANGES_CAPABILITY
310
+ })
311
+ });
312
+ var InvalidFolioDocxCapabilityIdError = class extends TaggedError("InvalidFolioDocxCapabilityIdError")() {};
313
+ const isFolioDocxCapabilityId = (value) => FOLIO_DOCX_CAPABILITY_IDS.some((id) => id === value);
314
+ const getFolioDocxCapability = (id) => {
315
+ if (!isFolioDocxCapabilityId(id)) throw new InvalidFolioDocxCapabilityIdError({
316
+ message: "Invalid DOCX capability id.",
317
+ receivedId: id
318
+ });
319
+ return FOLIO_DOCX_CAPABILITY_MANIFEST.capabilities[id];
320
+ };
321
+ //#endregion
322
+ export { FOLIO_DOCX_CAPABILITY_HOSTS, FOLIO_DOCX_CAPABILITY_IDS, FOLIO_DOCX_CAPABILITY_MANIFEST, FOLIO_DOCX_CAPABILITY_MANIFEST_VERSION, FOLIO_DOCX_CAPABILITY_OPERATIONS, FOLIO_DOCX_COMPATIBILITY_HOSTS, FOLIO_DOCX_COMPATIBILITY_PROFILES, FOLIO_DOCX_PROFILES, FOLIO_DOCX_SUPPORT_STATES, InvalidFolioDocxCapabilityIdError, getFolioDocxCapability, isFolioDocxCapabilityId };
@@ -1,12 +1,44 @@
1
1
  import { document_d_exports } from "../types/document.js";
2
+ import { FolioDocxCompatibilityHost, FolioDocxCompatibilityProfile, FolioDocxFeatureCapability } from "./capabilities.js";
2
3
 
3
4
  //#region src/docx/compatibility.d.ts
4
5
  type DocxCompatibilityReason = "opaqueDrawing";
6
+ type DocxCompatibilityContext = {
7
+ host: FolioDocxCompatibilityHost;
8
+ profile: FolioDocxCompatibilityProfile;
9
+ };
10
+ type InspectDocxCompatibilityOptions = Partial<DocxCompatibilityContext>;
11
+ type DocxCompatibilityPart = {
12
+ type: "document";
13
+ } | {
14
+ type: "header" | "footer";
15
+ relationshipId: string;
16
+ } | {
17
+ type: "footnote" | "endnote";
18
+ id: number;
19
+ };
20
+ type DocxCompatibilityLocation = {
21
+ part: DocxCompatibilityPart;
22
+ path: string;
23
+ blockId?: string;
24
+ };
25
+ type DocxCompatibilityIssue = {
26
+ code: DocxCompatibilityReason;
27
+ capability: FolioDocxFeatureCapability;
28
+ coverage: {
29
+ host: "covered" | "unknown" | "unverified";
30
+ profile: "covered" | "unknown" | "unverified";
31
+ };
32
+ location: DocxCompatibilityLocation;
33
+ };
5
34
  type DocxCompatibility = {
35
+ schemaVersion: 1;
36
+ context: DocxCompatibilityContext;
6
37
  canSafelyEdit: boolean;
38
+ issues: DocxCompatibilityIssue[];
7
39
  reasons: DocxCompatibilityReason[];
8
40
  unsupportedContentCount: number;
9
41
  };
10
- declare function inspectDocxCompatibility(doc: document_d_exports.Document): DocxCompatibility;
42
+ declare const inspectDocxCompatibility: (doc: document_d_exports.Document, options?: InspectDocxCompatibilityOptions) => DocxCompatibility;
11
43
  //#endregion
12
- export { DocxCompatibility, DocxCompatibilityReason, inspectDocxCompatibility };
44
+ export { DocxCompatibility, DocxCompatibilityContext, DocxCompatibilityIssue, DocxCompatibilityLocation, DocxCompatibilityPart, DocxCompatibilityReason, InspectDocxCompatibilityOptions, inspectDocxCompatibility };