@ansonlai/docx-redline-js 0.9.0 → 0.11.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 (36) hide show
  1. package/ARCHITECTURE.md +691 -669
  2. package/CHANGELOG.md +67 -1
  3. package/README.md +39 -8
  4. package/core/word-xml.js +160 -118
  5. package/dist/docx-redline-js.esm.js +528 -312
  6. package/dist/docx-redline-js.esm.js.map +4 -4
  7. package/dist/docx-redline-js.esm.min.js +94 -94
  8. package/dist/docx-redline-js.esm.min.js.map +4 -4
  9. package/dist/docx-redline.bundle.cjs +528 -312
  10. package/dist/docx-redline.bundle.cjs.map +4 -4
  11. package/dist/docx-redline.bundle.js +528 -312
  12. package/dist/docx-redline.bundle.js.map +4 -4
  13. package/docs/AGENT_KNOWLEDGE_BASE.md +64 -5
  14. package/docs/SKILL_AUTHORING.md +5 -1
  15. package/docs/TESTING.md +10 -1
  16. package/docs/schemas/document-operations.schema.json +1 -1
  17. package/engine/oxml-engine.js +748 -736
  18. package/engine/table-mode.js +7 -4
  19. package/index.d.ts +29 -2
  20. package/index.js +3 -0
  21. package/node/cli.js +993 -992
  22. package/package.json +1 -1
  23. package/pipeline/content-analysis.js +2 -1
  24. package/pipeline/markdown-processor.js +5 -2
  25. package/pipeline/structured-content.js +2 -1
  26. package/services/comment-parts-merge.js +124 -0
  27. package/services/comment-replies.js +1 -1
  28. package/services/document-operation-applier.js +1 -0
  29. package/services/document-operation-contract.js +1 -0
  30. package/services/document-operation-mutations.js +3362 -3346
  31. package/services/inspection-comparison.js +82 -0
  32. package/services/operation-heuristics.js +1 -1
  33. package/services/operation-preflight.js +42 -33
  34. package/services/revision-fidelity-verifier.js +5 -19
  35. package/services/standalone-docx-plumbing.js +38 -22
  36. package/services/table-reconciliation.js +11 -6
@@ -36,9 +36,10 @@ function noChanges(serializer, xmlDoc) {
36
36
  * @param {DOMParser} parser - Parser instance
37
37
  * @param {string} author - Author name
38
38
  * @param {boolean} [generateRedlines=true] - Track change toggle
39
+ * @param {{ plainTextContent?: boolean }} [extra] - plainTextContent keeps cell text literal
39
40
  * @returns {{ oxml: string, hasChanges: boolean }}
40
41
  */
41
- export function applyTableReconciliation(xmlDoc, modifiedText, serializer, parser, author, generateRedlines = true) {
42
+ export function applyTableReconciliation(xmlDoc, modifiedText, serializer, parser, author, generateRedlines = true, extra = {}) {
42
43
  const tableNodes = getElementsByTagNSOrTag(xmlDoc, NS_W, 'tbl');
43
44
  const newTableData = parseTable(modifiedText);
44
45
  const hasNewContent = newTableData.rows.length > 0 || newTableData.headers.length > 0;
@@ -58,7 +59,8 @@ export function applyTableReconciliation(xmlDoc, modifiedText, serializer, parse
58
59
  const options = {
59
60
  generateRedlines,
60
61
  author,
61
- revisionIdAllocator: getRevisionIdAllocatorForDocument(xmlDoc)
62
+ revisionIdAllocator: getRevisionIdAllocatorForDocument(xmlDoc),
63
+ plainTextContent: extra.plainTextContent === true
62
64
  };
63
65
  const reconciledOxml = serializeVirtualGridToOoxml(oldGrid, operations, options);
64
66
  const wrappedOxml = `<root xmlns:w="${NS_W}">${reconciledOxml}</root>`;
@@ -92,9 +94,10 @@ export function applyTableReconciliation(xmlDoc, modifiedText, serializer, parse
92
94
  * @param {DOMParser} parser - Parser instance
93
95
  * @param {string} author - Author name
94
96
  * @param {boolean} generateRedlines - Track change toggle
97
+ * @param {{ plainTextContent?: boolean }} [extra] - plainTextContent keeps cell text literal
95
98
  * @returns {{ oxml: string, hasChanges: boolean }}
96
99
  */
97
- export function applyTextToTableTransformation(xmlDoc, modifiedText, serializer, parser, author, generateRedlines) {
100
+ export function applyTextToTableTransformation(xmlDoc, modifiedText, serializer, parser, author, generateRedlines, extra = {}) {
98
101
  const revisionIdAllocator = getRevisionIdAllocatorForDocument(xmlDoc);
99
102
  const tableData = parseTable(modifiedText);
100
103
  if (!tableData || (tableData.rows.length === 0 && tableData.headers.length === 0)) {
@@ -102,7 +105,7 @@ export function applyTextToTableTransformation(xmlDoc, modifiedText, serializer,
102
105
  return noChanges(serializer, xmlDoc);
103
106
  }
104
107
 
105
- const tableOoxml = generateTableOoxml(tableData, { generateRedlines, author, revisionIdAllocator, trackAsBlock: true });
108
+ const tableOoxml = generateTableOoxml(tableData, { generateRedlines, author, revisionIdAllocator, trackAsBlock: true, plainTextContent: extra.plainTextContent === true });
106
109
  const tableDoc = parseOoxmlSafe(`<root xmlns:w="${NS_W}">${tableOoxml}</root>`, 'application/xml').doc;
107
110
  if (!tableDoc) return noChanges(serializer, xmlDoc);
108
111
 
package/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export type OoxmlSourceType = 'package' | 'document' | 'fragment';
2
2
  export type RedlineStatus = 'ok' | 'no-op' | 'partial' | 'error';
3
- export type ExistingRevisionsPolicy = 'merge-same-author' | 'slice-cross-author' | 'reject-input' | 'accept-all-first' | 'accept-all-first-keep-normalized';
3
+ export type ExistingRevisionsPolicy = 'auto' | 'merge-same-author' | 'slice-cross-author' | 'reject-input' | 'accept-all-first' | 'accept-all-first-keep-normalized';
4
4
  export type RevisionView = 'accepted' | 'rejected';
5
5
 
6
6
  export interface RevisionTextSegment {
@@ -372,6 +372,11 @@ export function inspectDocumentParts(parts: { documentXml: string; commentsXml?:
372
372
  export function buildRevisionTokenFraming(options: { scope: string; entries?: unknown }): { framing: Uint8Array; scope: string; version: number; coveredParts: string[] };
373
373
  export function computeRevisionToken(options: { scope: string; entries?: unknown; digestFn?: (bytes: Uint8Array) => Promise<string> }): Promise<RevisionToken>;
374
374
  export function computeRevisionTokenSync(options: { scope: string; entries?: unknown; digestFn: (bytes: Uint8Array) => string }): RevisionToken;
375
+ /** Compare two inspections by visible text, table position, list label, review segments and comments (ids, dates and run boundaries ignored). */
376
+ export function compareDocumentInspections(expected: { paragraphs: unknown[]; comments?: unknown[] }, actual: { paragraphs: unknown[]; comments?: unknown[] }): { matches: boolean; differences: Array<{ path: string; expected: unknown; actual: unknown }> };
377
+ export function inspectionReviewShape(inspection: { paragraphs: unknown[]; comments?: unknown[] }): { paragraphs: unknown[]; comments: string[] };
378
+ /** Resolve `existingRevisions: 'auto'` for one target node; other policies are returned unchanged. */
379
+ export function resolveExistingRevisionsPolicy(policy: ExistingRevisionsPolicy | null | undefined, node: unknown, author: string): ExistingRevisionsPolicy | null | undefined;
375
380
  export function computeDocumentPartsRevisionToken(parts: unknown, options?: { digestFn?: (bytes: Uint8Array) => Promise<string> }): Promise<RevisionToken>;
376
381
  export function validateRevisionToken(token: unknown): { valid: boolean; error?: { code: string; message: string } };
377
382
  export function areRevisionTokensEqual(a: string, b: string): boolean;
@@ -385,7 +390,29 @@ export function wrapInDocumentFragment(rawOoxml: string, options?: Record<string
385
390
 
386
391
  export function injectCommentsIntoOoxml(oxml: string, comments: unknown[], options?: Record<string, unknown>): unknown;
387
392
  export function injectCommentsIntoPackage(packageXml: string, comments: unknown[], options?: Record<string, unknown>): unknown;
388
- export function applyCommentReplyToParts(options: { commentsXml: string; commentsExtendedXml?: string | null; parentCommentId: string | number; commentId: string | number; commentContent: string; author: string; date?: string }): { status: 'ok' | 'error'; hasChanges?: boolean; commentsXml?: string; commentsExtendedXml?: string; commentsXmlMode?: 'replace'; commentsExtendedXmlMode?: 'replace'; parentCommentId?: string; paraId?: string; parentParaId?: string; error?: RedlineError };
393
+ export function applyCommentReplyToParts(options: { commentsXml: string; commentsExtendedXml?: string | null; documentXml?: string | null; parentCommentId: string | number; commentId: string | number; commentContent: string; author: string; date?: string }): { status: 'ok' | 'error'; hasChanges?: boolean; commentsXml?: string; commentsExtendedXml?: string; commentsXmlMode?: 'replace'; commentsExtendedXmlMode?: 'replace'; parentCommentId?: string; paraId?: string; parentParaId?: string; error?: RedlineError };
394
+ export interface CommentParts {
395
+ commentsXml: string | null;
396
+ commentsExtendedXml: string | null;
397
+ commentsIdsXml: string | null;
398
+ commentsExtensibleXml: string | null;
399
+ }
400
+ export type CommentPartName = 'comments' | 'commentsExtended' | 'commentsIds' | 'commentsExtensible';
401
+ export interface MergeCommentPartsResult {
402
+ status: 'ok' | 'error';
403
+ hasChanges: boolean;
404
+ /** Updated parts on success; the input parts unchanged on error. Absent parts are null. */
405
+ parts: CommentParts;
406
+ changedParts: CommentPartName[];
407
+ /** Parts the input did not have; the host adds their relationship and content type. Part presence only: also repair a missing relationship for any changed part. */
408
+ addedParts: CommentPartName[];
409
+ error?: { code: 'PARSE_ERROR' | 'INVALID_COMMENT_PARTS' | 'DUPLICATE_COMMENT_ID' | 'UNSUPPORTED_EXISTING_COMMENT_THREAD_METADATA' | string; message: string; part?: CommentPartName; commentId?: string };
410
+ }
411
+ /** Merge a batch result's comment output into comment parts held as XML strings (no ZIP); reconciles commentsIds/commentsExtensible. */
412
+ export function mergeCommentParts(
413
+ parts?: Partial<CommentParts>,
414
+ output?: { commentsXml?: string | null; commentsXmlMode?: 'merge' | 'replace'; commentsExtendedXml?: string | null; commentsExtendedXmlMode?: 'merge' | 'replace' }
415
+ ): MergeCommentPartsResult;
389
416
  export function acceptTrackedChangesInOoxml(oxml: string, options?: RevisionFilterOptions): AcceptTrackedChangesResult;
390
417
  export function rejectTrackedChangesInOoxml(oxml: string, options?: RevisionFilterOptions): RejectTrackedChangesResult;
391
418
  export function deleteCommentsByAuthorInOoxml(oxml: string, options?: RevisionFilterOptions): DeleteCommentsResult;
package/index.js CHANGED
@@ -284,6 +284,8 @@ export {
284
284
  } from './pipeline/ingestion-export.js';
285
285
  export { extractCanonicalParagraphText, readCanonicalRunText, isNodeVisibleInRevisionView, extractParagraphRevisionSegments } from './core/paragraph-text.js';
286
286
  export { inspectDocumentParts } from './services/document-inspection.js';
287
+ export { compareDocumentInspections, inspectionReviewShape } from './services/inspection-comparison.js';
288
+ export { resolveExistingRevisionsPolicy } from './core/word-xml.js';
287
289
  export {
288
290
  buildRevisionTokenFraming,
289
291
  computeRevisionToken,
@@ -305,6 +307,7 @@ export {
305
307
  buildCommentsPartXml
306
308
  } from './services/comment-engine.js';
307
309
  export { applyCommentReplyToParts } from './services/comment-replies.js';
310
+ export { mergeCommentParts } from './services/comment-parts-merge.js';
308
311
  export {
309
312
  acceptTrackedChangesInOoxml,
310
313
  rejectTrackedChangesInOoxml,