@stll/folio-core 0.9.0 → 0.10.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 (44) hide show
  1. package/dist/ai-edits/headless.d.ts +52 -12
  2. package/dist/ai-edits/headless.js +181 -55
  3. package/dist/ai-edits/index.d.ts +2 -2
  4. package/dist/ai-edits/index.js +2 -2
  5. package/dist/controller/layoutPipeline.js +2 -1
  6. package/dist/document-operations.d.ts +11 -2
  7. package/dist/document-operations.js +7 -1
  8. package/dist/document-stories.d.ts +10 -0
  9. package/dist/document-stories.js +27 -0
  10. package/dist/docx/corePropertiesParser.d.ts +8 -0
  11. package/dist/docx/corePropertiesParser.js +53 -0
  12. package/dist/docx/index.d.ts +2 -1
  13. package/dist/docx/index.js +2 -1
  14. package/dist/docx/metadataPrivacy.d.ts +40 -0
  15. package/dist/docx/metadataPrivacy.js +131 -0
  16. package/dist/docx/parser.js +4 -1
  17. package/dist/docx/rezip.d.ts +5 -4
  18. package/dist/docx/rezip.js +6 -8
  19. package/dist/layout-bridge/convert/toFlowBlocks.js +1 -0
  20. package/dist/layout-engine/measure/cache.js +2 -7
  21. package/dist/layout-engine/measure/effectiveLineBreakPolicy.d.ts +29 -0
  22. package/dist/layout-engine/measure/effectiveLineBreakPolicy.js +60 -0
  23. package/dist/layout-engine/measure/measureParagraph.js +32 -36
  24. package/dist/layout-engine/measure/tableCellFloating.js +39 -33
  25. package/dist/layout-engine/types.d.ts +5 -3
  26. package/dist/prosemirror/attrs/index.js +1 -0
  27. package/dist/prosemirror/conversion/effectiveTableCellFormatting.d.ts +62 -0
  28. package/dist/prosemirror/conversion/effectiveTableCellFormatting.js +131 -0
  29. package/dist/prosemirror/conversion/fromProseDoc.js +1 -0
  30. package/dist/prosemirror/conversion/toProseDoc.js +40 -68
  31. package/dist/prosemirror/extensions/core/ParagraphExtension.js +1 -1
  32. package/dist/prosemirror/extensions/marks/HighlightExtension.js +1 -1
  33. package/dist/prosemirror/extensions/marks/RunShadingExtension.js +1 -1
  34. package/dist/prosemirror/extensions/marks/TextColorExtension.js +1 -1
  35. package/dist/prosemirror/extensions/nodes/ImageExtension.js +1 -0
  36. package/dist/prosemirror/extensions/nodes/TableExtension.js +1 -1
  37. package/dist/prosemirror/schema/nodes.d.ts +2 -1
  38. package/dist/redline.d.ts +26 -11
  39. package/dist/redline.js +95 -62
  40. package/dist/server.d.ts +5 -4
  41. package/dist/server.js +5 -4
  42. package/dist/version-comparison.d.ts +65 -11
  43. package/dist/version-comparison.js +187 -29
  44. package/package.json +1 -1
package/dist/redline.js CHANGED
@@ -1,75 +1,35 @@
1
- import { FolioDocxReviewer } from "./ai-edits/headless.js";
1
+ import "./document-operations.js";
2
+ import { pairFolioDocumentStories } from "./document-stories.js";
3
+ import { FolioDocxReviewer, isFolioResolvedReviewedView } from "./ai-edits/headless.js";
4
+ import { resolveFolioDocumentPrivacyTransforms, rewriteDocxMetadataPrivacy } from "./docx/metadataPrivacy.js";
2
5
  import { alignFolioBlocks } from "./version-comparison.js";
6
+ import { TaggedError, panic } from "better-result";
3
7
  //#region src/redline.ts
4
8
  /**
5
- * Redline generator: compare two `.docx` buffers and produce a THIRD `.docx`
6
- * whose differences are recorded as real Word tracked changes (`w:ins` /
7
- * `w:del`), ready for review in Word or the folio editor — the
8
- * document-producing counterpart to {@link compareDocxVersions}'s structured
9
- * diff.
9
+ * Compare two `.docx` buffers and produce a third buffer whose text
10
+ * differences are represented as tracked changes.
10
11
  *
11
- * The pipeline is a composition of existing machinery, not a new engine:
12
- * the base buffer is opened in a headless {@link FolioDocxReviewer}, the two
13
- * snapshots are aligned with {@link alignFolioBlocks} (the same three-pass
14
- * alignment the comparer uses), each alignment event maps onto a
15
- * tracked-changes {@link FolioAIEditOperation}, and the reviewer's shared
16
- * apply path records them as redlines. Word-level minimality comes free:
17
- * the apply engine word-diffs a `replaceBlock` and only marks the divergent
18
- * tokens.
19
- *
20
- * ## Semantics and limitations
21
- *
22
- * - **As-accepted inputs.** Pending tracked changes in either input count as
23
- * applied before comparison (mirroring {@link compareDocxVersions}): the
24
- * base's own pending redlines are accepted in the output, so the tracked
25
- * changes it carries are exactly base → revised. This is intentionally
26
- * lossy about the inputs' own revision history and authorship.
27
- * - **Text redlines only.** Format-only changes and relocated blocks are
28
- * redlined as plain edits (a move becomes a delete + insert, like Word
29
- * compare with move detection off); block-level formatting is carried via
30
- * the revised block's `styleId` on inserted blocks.
31
- * - **Anchoring.** Insertions anchor `before` the next surviving base block
32
- * so consecutive additions keep their order; additions past the last base
33
- * block anchor `after` it. A base document with no content blocks offers
34
- * no anchor at all — such additions surface in `skipped` rather than
35
- * silently vanishing.
36
- */
37
- /**
38
- * For each event index, the base block id the next `revisedOnly` event
39
- * should anchor before: the base side of the nearest later `pair` or
40
- * `baseOnly` event, or `null` when only additions remain until the end of
41
- * the document. One backward pass, so a long run of trailing additions
42
- * (huge revised document vs. a small base) stays linear instead of
43
- * re-scanning the tail per added block.
12
+ * Each matched editable story is processed independently through the shared
13
+ * block alignment and document-operation paths. Package parts that exist on
14
+ * only one side are reported because creating or removing those parts is a
15
+ * distinct package-level operation.
44
16
  */
17
+ var InvalidGenerateRedlineDocxOptionsError = class extends TaggedError("InvalidGenerateRedlineDocxOptionsError")() {};
45
18
  const nextBaseBlockIdByIndex = (events) => {
46
19
  const nextIds = Array.from({ length: events.length });
47
20
  let nextId = null;
48
- for (let i = events.length - 1; i >= 0; i--) {
49
- nextIds[i] = nextId;
50
- const event = events[i];
21
+ for (let index = events.length - 1; index >= 0; index--) {
22
+ nextIds[index] = nextId;
23
+ const event = events[index];
51
24
  if (event?.type === "pair") nextId = event.baseBlock.id;
52
25
  else if (event?.type === "baseOnly") nextId = event.block.id;
53
26
  }
54
27
  return nextIds;
55
28
  };
56
- /**
57
- * Compare `base` and `revised` and return the base document with every
58
- * difference recorded as a tracked change. See the module doc comment for
59
- * semantics and limitations; `skipped` reports any block the generator could
60
- * not redline (e.g. additions with no anchor in an empty base document).
61
- */
62
- const generateRedlineDocx = async (base, revised, options = {}) => {
63
- const [baseReviewer, revisedReviewer] = await Promise.all([FolioDocxReviewer.fromBuffer(base, { author: options.author ?? "folio compare" }), FolioDocxReviewer.fromBuffer(revised)]);
64
- baseReviewer.acceptAll();
65
- const baseSnapshot = baseReviewer.snapshot();
66
- const revisedBlocks = revisedReviewer.snapshot().blocks;
29
+ const buildRedlineOperations = ({ baseSnapshot, revisedBlocks, nextOperationId }) => {
67
30
  const events = alignFolioBlocks(baseSnapshot.blocks, revisedBlocks);
68
31
  const anchorIds = nextBaseBlockIdByIndex(events);
69
32
  const operations = [];
70
- let operationSeq = 0;
71
- const nextOperationId = () => `redline-${++operationSeq}`;
72
- /** Additions past the last base block, kept in revised-document order. */
73
33
  const trailingAdditions = [];
74
34
  const lastBaseBlockId = baseSnapshot.blocks.at(-1)?.id ?? null;
75
35
  events.forEach((event, eventIndex) => {
@@ -123,15 +83,88 @@ const generateRedlineDocx = async (base, revised, options = {}) => {
123
83
  text: addition.text,
124
84
  ...addition.styleId !== void 0 && { styleId: addition.styleId }
125
85
  });
126
- const { applied, skipped } = baseReviewer.applyOperations(operations, {
127
- mode: "tracked-changes",
128
- snapshot: baseSnapshot
86
+ return operations;
87
+ };
88
+ const resolveInputView = (value, option) => {
89
+ if (value === void 0) return "final";
90
+ if (!isFolioResolvedReviewedView(value)) throw new InvalidGenerateRedlineDocxOptionsError({
91
+ message: `${option} must be original or final.`,
92
+ option,
93
+ receivedValue: value
129
94
  });
95
+ return value;
96
+ };
97
+ /** Compare two buffers and return tracked changes for every matched editable story. */
98
+ const generateRedlineDocx = async (base, revised, options = {}) => {
99
+ const baseView = resolveInputView(options.baseView, "baseView");
100
+ const revisedView = resolveInputView(options.revisedView, "revisedView");
101
+ const privacyTransforms = resolveFolioDocumentPrivacyTransforms(options.privacy?.transforms ?? []);
102
+ const [baseReviewer, revisedReviewer] = await Promise.all([FolioDocxReviewer.fromBuffer(base, { author: options.author ?? "folio compare" }), FolioDocxReviewer.fromBuffer(revised)]);
103
+ const baseStories = baseReviewer.listStories().map(({ handle }) => handle);
104
+ const revisedStories = revisedReviewer.listStories().map(({ handle }) => handle);
105
+ for (const story of baseStories) if (!baseReviewer.resolveReviewedStory({
106
+ story,
107
+ view: baseView
108
+ })) panic("A listed base story could not be resolved");
109
+ const applied = [];
110
+ const skipped = [];
111
+ const unprocessedStories = [];
112
+ let operationSequence = 0;
113
+ const nextOperationId = () => `redline-${++operationSequence}`;
114
+ for (const pair of pairFolioDocumentStories(baseStories, revisedStories)) {
115
+ if (!pair.baseStory) {
116
+ unprocessedStories.push({
117
+ ...pair,
118
+ reason: "missing-base-story"
119
+ });
120
+ continue;
121
+ }
122
+ if (!pair.revisedStory) {
123
+ unprocessedStories.push({
124
+ ...pair,
125
+ reason: "missing-revised-story"
126
+ });
127
+ continue;
128
+ }
129
+ const baseSnapshot = baseReviewer.snapshotStory(pair.baseStory);
130
+ const revisedSnapshot = revisedReviewer.readReviewedStory({
131
+ story: pair.revisedStory,
132
+ view: revisedView
133
+ })?.snapshot;
134
+ if (!baseSnapshot || !revisedSnapshot) panic("A matched document story could not be read");
135
+ const operations = buildRedlineOperations({
136
+ baseSnapshot,
137
+ revisedBlocks: revisedSnapshot.blocks,
138
+ nextOperationId
139
+ });
140
+ if (operations.length === 0) continue;
141
+ const result = baseReviewer.applyDocumentOperationsToStory({
142
+ story: pair.baseStory,
143
+ snapshot: baseSnapshot,
144
+ batch: {
145
+ version: 1,
146
+ mode: "tracked-changes",
147
+ operations
148
+ }
149
+ });
150
+ applied.push(...result.applied);
151
+ skipped.push(...result.skipped);
152
+ }
153
+ const redlineBuffer = await baseReviewer.toBuffer();
154
+ const privacyResult = privacyTransforms.length === 0 ? {
155
+ buffer: redlineBuffer,
156
+ privacyReport: {
157
+ appliedTransforms: [],
158
+ removedMetadataProperties: []
159
+ }
160
+ } : await rewriteDocxMetadataPrivacy(redlineBuffer, { transforms: privacyTransforms });
130
161
  return {
131
- buffer: await baseReviewer.toBuffer(),
162
+ buffer: privacyResult.buffer,
132
163
  applied,
133
- skipped
164
+ skipped,
165
+ unprocessedStories,
166
+ privacyReport: privacyResult.privacyReport
134
167
  };
135
168
  };
136
169
  //#endregion
137
- export { generateRedlineDocx };
170
+ export { InvalidGenerateRedlineDocxOptionsError, generateRedlineDocx };
package/dist/server.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { FolioAIBlock, FolioAIBlockAnchor, FolioAIBlockKind, FolioAIBlockPreviewRun, FolioAIComment, FolioAIEditApplyMode, FolioAIEditApplyResult, FolioAIEditOperation, FolioAIEditPrecondition, FolioAIEditSnapshot, FolioAIInlineFormatting, FolioAITextRangeHandle, FolioDocumentNavigationTarget, FolioDocumentOutline, FolioDocumentOutlineEntry, FolioDocumentSection, FolioDocumentSectionHandle, FolioDocumentSectionReadResult } from "./ai-edits/types.js";
2
2
  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, FolioDocumentOperation, FolioDocumentOperationAffectedTarget, FolioDocumentOperationBatch, FolioDocumentOperationCapabilities, FolioDocumentOperationIssue, FolioDocumentOperationMode, FolioDocumentOperationPrecondition, FolioDocumentOperationReceipt, FolioDocumentOperationRecovery, FolioDocumentOperationResult, FolioDocumentOperationStatus, FolioDocumentOperationStory, FolioDocumentOperationType, FolioDocumentOperationUndoFailureReason, FolioDocumentOperationUndoHandle, FolioDocumentOperationUndoResult, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, assertSupportedFolioDocumentOperationVersion, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, isFolioDocumentOperationModeSupported, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch } from "./document-operations.js";
3
3
  import { FolioReviewChange, FolioReviewChangeKind } from "./ai-edits/read.js";
4
- import { ApplyFolioAIEditsToBufferOptions, ApplyFolioAIEditsToBufferResult, FolioApplyDocumentOperationsOptions, FolioApplyDocumentOperationsToStoryOptions, FolioApplyOperationsOptions, FolioDocumentStory, FolioDocumentStoryHandle, FolioDocumentStoryNotFoundError, FolioDocxReviewer, FolioDocxReviewerOptions, FolioEditableDocumentStoryHandle, FolioReviewChangeFilter, FolioReviewComment, FolioReviewCommentFilter, FolioReviewCommentReply, FolioReviewReplyInput, applyFolioAIEditsToBuffer } from "./ai-edits/headless.js";
4
+ import { ApplyFolioAIEditsToBufferOptions, ApplyFolioAIEditsToBufferResult, FOLIO_RESOLVED_REVIEWED_VIEWS, FOLIO_REVIEWED_VIEWS, FolioApplyDocumentOperationsOptions, FolioApplyDocumentOperationsToStoryOptions, FolioApplyOperationsOptions, FolioDocumentStory, FolioDocumentStoryHandle, FolioDocumentStoryNotFoundError, FolioDocxReviewer, FolioDocxReviewerOptions, FolioEditableDocumentStoryHandle, FolioReadReviewedStoryOptions, FolioResolveReviewedStoryOptions, FolioResolvedReviewedView, FolioReviewChangeFilter, FolioReviewComment, FolioReviewCommentFilter, FolioReviewCommentReply, FolioReviewReplyInput, FolioReviewedStory, FolioReviewedView, UnsupportedFolioReviewedViewError, applyFolioAIEditsToBuffer, isFolioResolvedReviewedView, isFolioReviewedView } from "./ai-edits/headless.js";
5
5
  import { createFolioAITextRangeHandle } from "./ai-edits/snapshot.js";
6
6
  import { getFolioDocumentOutline, readFolioDocumentSection } from "./ai-edits/scoped-reading.js";
7
7
  import { DeriveBlockIdInput, FolioBlockId, deriveBlockId, getFolioParaIdFromBlockId, isFolioBlockId, isSequentialFolioBlockId } from "./types/block-id.js";
@@ -11,9 +11,10 @@ import { DocumentStyleCatalog, DocumentStyleCatalogEntry, ExtractDocumentStyleSe
11
11
  import { STELLA_STYLE_SET_NAME, createStellaStyleDocumentPreset, createStellaStyleSet } from "./style-sets/stellaStyle.js";
12
12
  import { createDocx } from "./docx/rezip.js";
13
13
  import { EnsureParaIdsError, EnsureParaIdsOptions, EnsureParaIdsResult, ensureParaIds } from "./docx/ensureParaIds.js";
14
+ import { FOLIO_DOCUMENT_METADATA_PROPERTIES, FOLIO_DOCUMENT_PRIVACY_TRANSFORMS, FolioDocumentMetadataProperty, FolioDocumentPrivacyArchiveError, FolioDocumentPrivacyOptions, FolioDocumentPrivacyReport, FolioDocumentPrivacyTransform, InvalidFolioDocumentPrivacyOptionsError, RewriteDocxMetadataPrivacyResult, isFolioDocumentPrivacyTransform, rewriteDocxMetadataPrivacy } from "./docx/metadataPrivacy.js";
14
15
  import { CreateCommentReplyInput, replyToComment } from "./docx/replyToComment.js";
15
16
  import { DocxArchiveError } from "./docx/server/boundedArchive.js";
16
17
  import { DocxParagraphSource, ExtractedDocxParagraph, ExtractedDocxText, extractDocxText } from "./docx/server/extractDocxText.js";
17
- import { GenerateRedlineDocxOptions, GenerateRedlineDocxResult, generateRedlineDocx } from "./redline.js";
18
- import { FolioBlockDiff, FolioFormatProperty, FolioVersionDiff, FolioVersionDiffSegment, compareDocxVersions } from "./version-comparison.js";
19
- export { 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 DocxParagraphSource, EnsureParaIdsError, type EnsureParaIdsOptions, type EnsureParaIdsResult, type ExtractDocumentStyleSetOptions, type ExtractedDocxParagraph, type ExtractedDocxText, 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, 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 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, type FolioDocumentSection, type FolioDocumentSectionHandle, type FolioDocumentSectionReadResult, type FolioDocumentStory, type FolioDocumentStoryHandle, FolioDocumentStoryNotFoundError, FolioDocxReviewer, type FolioDocxReviewerOptions, type FolioEditableDocumentStoryHandle, type FolioFormatProperty, type FolioReviewChange, type FolioReviewChangeFilter, type FolioReviewChangeKind, type FolioReviewComment, type FolioReviewCommentFilter, type FolioReviewCommentReply, type FolioReviewReplyInput, type FolioVersionDiff, type FolioVersionDiffSegment, type GenerateRedlineDocxOptions, type GenerateRedlineDocxResult, InvalidFolioDocumentOperationBatchError, STELLA_STYLE_SET_NAME, UnsupportedFolioDocumentOperationVersionError, applyFolioAIEditsToBuffer, assertSupportedFolioDocumentOperationVersion, compareDocxVersions, createDocx, createEmptyDocument, createFolioAITextRangeHandle, createStellaStyleDocumentPreset, createStellaStyleSet, deriveBlockId, ensureParaIds, extractDocumentStyleSet, extractDocumentStyleSetFromDocx, extractDocxText, generateRedlineDocx, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioDocumentOutline, getFolioParaIdFromBlockId, inspectDocumentStyles, inspectDocumentStylesFromDocx, isFolioBlockId, isFolioDocumentOperationModeSupported, isSequentialFolioBlockId, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch, readFolioDocumentSection, replyToComment };
18
+ import { GenerateRedlineDocxOptions, GenerateRedlineDocxResult, GenerateRedlineUnprocessedStory, InvalidGenerateRedlineDocxOptionsError, generateRedlineDocx } from "./redline.js";
19
+ 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";
20
+ export { 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 DocxParagraphSource, EnsureParaIdsError, type EnsureParaIdsOptions, type EnsureParaIdsResult, type ExtractDocumentStyleSetOptions, type ExtractedDocxParagraph, 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_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, FolioDocxReviewer, type FolioDocxReviewerOptions, 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, InvalidFolioDocumentOperationBatchError, InvalidFolioDocumentPrivacyOptionsError, InvalidFolioVersionComparisonOptionsError, InvalidGenerateRedlineDocxOptionsError, type RewriteDocxMetadataPrivacyResult, STELLA_STYLE_SET_NAME, UnsupportedFolioDocumentOperationVersionError, UnsupportedFolioReviewedViewError, applyFolioAIEditsToBuffer, applyFolioVersionDiffPrivacy, assertSupportedFolioDocumentOperationVersion, compareDocxVersions, createDocx, createEmptyDocument, createFolioAITextRangeHandle, createStellaStyleDocumentPreset, createStellaStyleSet, deriveBlockId, ensureParaIds, extractDocumentStyleSet, extractDocumentStyleSetFromDocx, extractDocxText, generateRedlineDocx, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioDocumentOutline, getFolioParaIdFromBlockId, inspectDocumentStyles, inspectDocumentStylesFromDocx, isFolioBlockId, isFolioDocumentOperationModeSupported, isFolioDocumentPrivacyTransform, isFolioResolvedReviewedView, isFolioReviewedView, isFolioVersionComparisonPrivacyTransform, isFolioVersionComparisonScope, isSequentialFolioBlockId, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch, readFolioDocumentSection, replyToComment, rewriteDocxMetadataPrivacy };
package/dist/server.js CHANGED
@@ -8,10 +8,11 @@ import { STELLA_STYLE_SET_NAME, createStellaStyleDocumentPreset, createStellaSty
8
8
  import { createDocx } from "./docx/rezip.js";
9
9
  import { getFolioDocumentOutline, readFolioDocumentSection } from "./ai-edits/scoped-reading.js";
10
10
  import { replyToComment } from "./docx/replyToComment.js";
11
- import { FolioDocumentStoryNotFoundError, FolioDocxReviewer, applyFolioAIEditsToBuffer } from "./ai-edits/headless.js";
12
- import { compareDocxVersions } from "./version-comparison.js";
13
- import { generateRedlineDocx } from "./redline.js";
11
+ import { FOLIO_RESOLVED_REVIEWED_VIEWS, FOLIO_REVIEWED_VIEWS, FolioDocumentStoryNotFoundError, FolioDocxReviewer, UnsupportedFolioReviewedViewError, applyFolioAIEditsToBuffer, isFolioResolvedReviewedView, isFolioReviewedView } from "./ai-edits/headless.js";
12
+ import { FOLIO_DOCUMENT_METADATA_PROPERTIES, FOLIO_DOCUMENT_PRIVACY_TRANSFORMS, FolioDocumentPrivacyArchiveError, InvalidFolioDocumentPrivacyOptionsError, isFolioDocumentPrivacyTransform, rewriteDocxMetadataPrivacy } from "./docx/metadataPrivacy.js";
13
+ import { FOLIO_VERSION_COMPARISON_PRIVACY_TRANSFORMS, FOLIO_VERSION_COMPARISON_SCOPES, InvalidFolioVersionComparisonOptionsError, applyFolioVersionDiffPrivacy, compareDocxVersions, isFolioVersionComparisonPrivacyTransform, isFolioVersionComparisonScope } from "./version-comparison.js";
14
+ import { InvalidGenerateRedlineDocxOptionsError, generateRedlineDocx } from "./redline.js";
14
15
  import { EnsureParaIdsError, ensureParaIds } from "./docx/ensureParaIds.js";
15
16
  import { DocxArchiveError } from "./docx/server/boundedArchive.js";
16
17
  import { extractDocxText } from "./docx/server/extractDocxText.js";
17
- export { DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION, DocxArchiveError, EnsureParaIdsError, 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, FolioDocumentStoryNotFoundError, FolioDocxReviewer, InvalidFolioDocumentOperationBatchError, STELLA_STYLE_SET_NAME, UnsupportedFolioDocumentOperationVersionError, applyFolioAIEditsToBuffer, assertSupportedFolioDocumentOperationVersion, compareDocxVersions, createDocx, createEmptyDocument, createFolioAITextRangeHandle, createStellaStyleDocumentPreset, createStellaStyleSet, deriveBlockId, ensureParaIds, extractDocumentStyleSet, extractDocumentStyleSetFromDocx, extractDocxText, generateRedlineDocx, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioDocumentOutline, getFolioParaIdFromBlockId, inspectDocumentStyles, inspectDocumentStylesFromDocx, isFolioBlockId, isFolioDocumentOperationModeSupported, isSequentialFolioBlockId, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch, readFolioDocumentSection, replyToComment };
18
+ 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_RESOLVED_REVIEWED_VIEWS, FOLIO_REVIEWED_VIEWS, FOLIO_VERSION_COMPARISON_PRIVACY_TRANSFORMS, FOLIO_VERSION_COMPARISON_SCOPES, FolioDocumentPrivacyArchiveError, FolioDocumentStoryNotFoundError, FolioDocxReviewer, InvalidFolioDocumentOperationBatchError, InvalidFolioDocumentPrivacyOptionsError, InvalidFolioVersionComparisonOptionsError, InvalidGenerateRedlineDocxOptionsError, STELLA_STYLE_SET_NAME, UnsupportedFolioDocumentOperationVersionError, UnsupportedFolioReviewedViewError, applyFolioAIEditsToBuffer, applyFolioVersionDiffPrivacy, assertSupportedFolioDocumentOperationVersion, compareDocxVersions, createDocx, createEmptyDocument, createFolioAITextRangeHandle, createStellaStyleDocumentPreset, createStellaStyleSet, deriveBlockId, ensureParaIds, extractDocumentStyleSet, extractDocumentStyleSetFromDocx, extractDocxText, generateRedlineDocx, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioDocumentOutline, getFolioParaIdFromBlockId, inspectDocumentStyles, inspectDocumentStylesFromDocx, isFolioBlockId, isFolioDocumentOperationModeSupported, isFolioDocumentPrivacyTransform, isFolioResolvedReviewedView, isFolioReviewedView, isFolioVersionComparisonPrivacyTransform, isFolioVersionComparisonScope, isSequentialFolioBlockId, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch, readFolioDocumentSection, replyToComment, rewriteDocxMetadataPrivacy };
@@ -1,59 +1,111 @@
1
1
  import { FolioAIBlock } from "./ai-edits/types.js";
2
+ import { FolioDocumentStoryHandle } from "./ai-edits/headless.js";
2
3
  import { WordDiffSegment } from "./ai-edits/word-diff.js";
4
+ import { FOLIO_DOCUMENT_METADATA_PROPERTIES, FolioDocumentMetadataProperty, FolioDocumentPrivacyOptions, FolioDocumentPrivacyReport, FolioDocumentPrivacyTransform } from "./docx/metadataPrivacy.js";
3
5
 
4
6
  //#region src/version-comparison.d.ts
5
7
  /** One word-level diff segment within a `modified` block. Mirrors {@link WordDiffSegment}. */
6
8
  type FolioVersionDiffSegment = WordDiffSegment;
9
+ /** Independently selectable comparison scopes. */
10
+ declare const FOLIO_VERSION_COMPARISON_SCOPES: readonly ["text", "formatting", "metadata"];
11
+ type FolioVersionComparisonScope = (typeof FOLIO_VERSION_COMPARISON_SCOPES)[number];
12
+ declare const isFolioVersionComparisonScope: (value: unknown) => value is FolioVersionComparisonScope;
13
+ type FolioCompareDocxVersionsOptions = {
14
+ /** Selected scopes; defaults to text and formatting. */include?: readonly FolioVersionComparisonScope[]; /** Optional output-only privacy transforms. Source buffers are never mutated. */
15
+ privacy?: FolioVersionDiffPrivacyOptions;
16
+ };
17
+ declare const InvalidFolioVersionComparisonOptionsError_base: import("better-result").TaggedErrorClass<"InvalidFolioVersionComparisonOptionsError", {
18
+ message: string;
19
+ option: "include" | "privacy.transforms";
20
+ receivedValue: unknown;
21
+ }>;
22
+ declare class InvalidFolioVersionComparisonOptionsError extends InvalidFolioVersionComparisonOptionsError_base {}
23
+ type FolioDocumentMetadataValue = string | number | null;
24
+ type FolioMetadataDiff = {
25
+ property: FolioDocumentMetadataProperty;
26
+ baseValue: FolioDocumentMetadataValue;
27
+ revisedValue: FolioDocumentMetadataValue;
28
+ };
29
+ declare const FOLIO_VERSION_COMPARISON_PRIVACY_TRANSFORMS: readonly ["remove-attribution", "remove-timestamps", "remove-descriptive-metadata"];
30
+ type FolioVersionComparisonPrivacyTransform = FolioDocumentPrivacyTransform;
31
+ declare const isFolioVersionComparisonPrivacyTransform: (value: unknown) => value is FolioVersionComparisonPrivacyTransform;
32
+ type FolioVersionDiffPrivacyOptions = FolioDocumentPrivacyOptions;
33
+ type FolioVersionDiffPrivacyReport = FolioDocumentPrivacyReport;
7
34
  /** Run-level formatting properties compared for `formatChanged` detection. */
8
35
  declare const FORMAT_PROPERTIES: readonly ["bold", "italic", "underline", "strike", "fontFamily", "fontSizePt", "color"];
9
36
  /** A run-level formatting property that can differ in a `formatChanged` block. */
10
37
  type FolioFormatProperty = (typeof FORMAT_PROPERTIES)[number];
38
+ /** Stable location of one compared block within its source document. */
39
+ type FolioVersionBlockHandle = {
40
+ story: FolioDocumentStoryHandle;
41
+ blockId: string;
42
+ };
11
43
  /** One block-level change between two document versions, in revised-side document order. */
12
44
  type FolioBlockDiff = {
13
45
  type: "added";
14
46
  blockId: string;
15
47
  kind: string;
16
48
  text: string;
49
+ revisedHandle: FolioVersionBlockHandle;
17
50
  } | {
18
51
  type: "deleted";
19
52
  blockId: string;
20
53
  kind: string;
21
54
  text: string;
55
+ baseHandle: FolioVersionBlockHandle;
22
56
  } | {
23
57
  type: "modified";
24
58
  blockId: string;
25
59
  kind: string;
26
60
  segments: FolioVersionDiffSegment[];
61
+ baseHandle: FolioVersionBlockHandle;
62
+ revisedHandle: FolioVersionBlockHandle;
27
63
  } | {
28
64
  type: "formatChanged";
29
65
  blockId: string;
30
66
  kind: string;
31
67
  text: string;
32
68
  changedProperties: FolioFormatProperty[];
69
+ baseHandle: FolioVersionBlockHandle;
70
+ revisedHandle: FolioVersionBlockHandle;
33
71
  } | {
34
72
  type: "movedFrom";
35
73
  blockId: string;
36
74
  kind: string;
37
75
  text: string;
38
76
  moveGroupId: number;
77
+ baseHandle: FolioVersionBlockHandle;
39
78
  } | {
40
79
  type: "movedTo";
41
80
  blockId: string;
42
81
  kind: string;
43
82
  text: string;
44
83
  moveGroupId: number;
84
+ revisedHandle: FolioVersionBlockHandle;
85
+ };
86
+ type FolioVersionDiffSummaryCounts = {
87
+ added: number;
88
+ deleted: number;
89
+ modified: number;
90
+ formatChanged: number;
91
+ moved: number;
92
+ metadataChanged: number;
93
+ unchanged: number;
94
+ };
95
+ /** Changes within one matched, added, or deleted document story. */
96
+ type FolioStoryDiff = {
97
+ baseStory: FolioDocumentStoryHandle | null;
98
+ revisedStory: FolioDocumentStoryHandle | null;
99
+ changes: FolioBlockDiff[];
100
+ summaryCounts: FolioVersionDiffSummaryCounts;
45
101
  };
46
102
  /** Result of {@link compareDocxVersions}. */
47
103
  type FolioVersionDiff = {
48
- /** Every changed block, in revised-side document order (deletions and move sources slotted where they sat). */changes: FolioBlockDiff[]; /** Counts across every paired/unpaired block, including the unchanged blocks `changes` omits. `moved` counts pairs, not entries. */
49
- summaryCounts: {
50
- added: number;
51
- deleted: number;
52
- modified: number;
53
- formatChanged: number;
54
- moved: number;
55
- unchanged: number;
56
- };
104
+ /** Every changed block, in revised-side document order (deletions and move sources slotted where they sat). */changes: FolioBlockDiff[]; /** Per-story results in base order followed by stories added in the revised document. */
105
+ stories: FolioStoryDiff[]; /** Changed package metadata fields in stable property order. */
106
+ metadataChanges: FolioMetadataDiff[]; /** Applied privacy policy and the fields it removed from this result. */
107
+ privacyReport: FolioVersionDiffPrivacyReport; /** Counts across every paired/unpaired block, including the unchanged blocks `changes` omits. `moved` counts pairs, not entries. */
108
+ summaryCounts: FolioVersionDiffSummaryCounts;
57
109
  };
58
110
  /** True when an `unpairedBaseCount * unpairedRevisedCount` LCS table would exceed {@link MAX_LCS_CELLS}. */
59
111
  declare const exceedsLcsBudget: (unpairedBaseCount: number, unpairedRevisedCount: number) => boolean;
@@ -81,12 +133,14 @@ type FolioAlignedBlockEvent = {
81
133
  * one document walk instead of re-deriving it.
82
134
  */
83
135
  declare const alignFolioBlocks: (baseBlocks: readonly FolioAIBlock[], revisedBlocks: readonly FolioAIBlock[]) => FolioAlignedBlockEvent[];
136
+ /** Apply auditable, output-only privacy transforms to a structured version diff. */
137
+ declare const applyFolioVersionDiffPrivacy: (diff: FolioVersionDiff, options: FolioVersionDiffPrivacyOptions) => FolioVersionDiff;
84
138
  /**
85
139
  * Compare two `.docx` buffers and return a structured, block-level diff.
86
140
  * See the module doc comment for the as-accepted comparison semantics, the
87
141
  * three-pass alignment algorithm, move detection, and format-only change
88
142
  * detection.
89
143
  */
90
- declare const compareDocxVersions: (base: ArrayBuffer, revised: ArrayBuffer) => Promise<FolioVersionDiff>;
144
+ declare const compareDocxVersions: (base: ArrayBuffer, revised: ArrayBuffer, options?: FolioCompareDocxVersionsOptions) => Promise<FolioVersionDiff>;
91
145
  //#endregion
92
- export { FolioAlignedBlockEvent, FolioBlockDiff, FolioFormatProperty, FolioVersionDiff, FolioVersionDiffSegment, alignFolioBlocks, compareDocxVersions, exceedsLcsBudget };
146
+ export { FOLIO_DOCUMENT_METADATA_PROPERTIES, FOLIO_VERSION_COMPARISON_PRIVACY_TRANSFORMS, FOLIO_VERSION_COMPARISON_SCOPES, FolioAlignedBlockEvent, FolioBlockDiff, FolioCompareDocxVersionsOptions, type FolioDocumentMetadataProperty, FolioDocumentMetadataValue, FolioFormatProperty, FolioMetadataDiff, FolioStoryDiff, FolioVersionBlockHandle, FolioVersionComparisonPrivacyTransform, FolioVersionComparisonScope, FolioVersionDiff, FolioVersionDiffPrivacyOptions, FolioVersionDiffPrivacyReport, FolioVersionDiffSegment, FolioVersionDiffSummaryCounts, InvalidFolioVersionComparisonOptionsError, alignFolioBlocks, applyFolioVersionDiffPrivacy, compareDocxVersions, exceedsLcsBudget, isFolioVersionComparisonPrivacyTransform, isFolioVersionComparisonScope };