@ansonlai/docx-redline-js 0.1.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 (64) hide show
  1. package/AGENTS.md +176 -0
  2. package/ARCHITECTURE.md +121 -0
  3. package/LICENSE +21 -0
  4. package/README.md +177 -0
  5. package/adapters/config.js +43 -0
  6. package/adapters/logger.js +89 -0
  7. package/adapters/xml-adapter.js +74 -0
  8. package/core/list-targeting.js +398 -0
  9. package/core/ooxml-identifiers.js +15 -0
  10. package/core/paragraph-offset-policy.js +50 -0
  11. package/core/paragraph-targeting.js +501 -0
  12. package/core/table-targeting.js +233 -0
  13. package/core/types.js +204 -0
  14. package/core/xml-query.js +99 -0
  15. package/dist/docx-redline-js.esm.js +8801 -0
  16. package/dist/docx-redline-js.esm.js.map +7 -0
  17. package/dist/docx-redline-js.esm.min.js +195 -0
  18. package/dist/docx-redline-js.esm.min.js.map +7 -0
  19. package/engine/format-application.js +358 -0
  20. package/engine/format-extraction.js +232 -0
  21. package/engine/format-paragraph-targeting.js +208 -0
  22. package/engine/format-span-application.js +178 -0
  23. package/engine/formatting-removal.js +330 -0
  24. package/engine/oxml-engine.js +279 -0
  25. package/engine/reconstruction-mapper.js +270 -0
  26. package/engine/reconstruction-mode.js +38 -0
  27. package/engine/reconstruction-writer.js +276 -0
  28. package/engine/rpr-helpers.js +194 -0
  29. package/engine/run-builders.js +235 -0
  30. package/engine/surgical-mode.js +520 -0
  31. package/engine/table-cell-context.js +151 -0
  32. package/engine/table-mode.js +172 -0
  33. package/index.js +308 -0
  34. package/orchestration/list-markdown.js +141 -0
  35. package/orchestration/list-parsing.js +73 -0
  36. package/orchestration/list-structural-fallback.js +530 -0
  37. package/orchestration/redline-operation-converter.js +141 -0
  38. package/orchestration/route-plan.js +160 -0
  39. package/package.json +76 -0
  40. package/pipeline/content-analysis.js +107 -0
  41. package/pipeline/diff-engine.js +204 -0
  42. package/pipeline/ingestion-export.js +255 -0
  43. package/pipeline/ingestion-paragraph.js +351 -0
  44. package/pipeline/ingestion-table.js +169 -0
  45. package/pipeline/ingestion-xml.js +39 -0
  46. package/pipeline/ingestion.js +8 -0
  47. package/pipeline/list-generation.js +280 -0
  48. package/pipeline/list-markers.js +77 -0
  49. package/pipeline/markdown-processor.js +160 -0
  50. package/pipeline/patching.js +408 -0
  51. package/pipeline/pipeline.js +326 -0
  52. package/pipeline/serialization.js +395 -0
  53. package/services/browser-demo-prompt-context.js +345 -0
  54. package/services/comment-builders.js +60 -0
  55. package/services/comment-engine.js +248 -0
  56. package/services/comment-locator.js +197 -0
  57. package/services/comment-package.js +113 -0
  58. package/services/numbering-helpers.js +416 -0
  59. package/services/numbering-service.js +290 -0
  60. package/services/package-builder.js +147 -0
  61. package/services/standalone-docx-plumbing.js +443 -0
  62. package/services/standalone-operation-runner.js +1169 -0
  63. package/services/table-reconciliation.js +344 -0
  64. package/standalone.js +5 -0
package/AGENTS.md ADDED
@@ -0,0 +1,176 @@
1
+ # AGENTS.md - AI Agent Quick Reference
2
+
3
+ > This file helps AI coding agents understand @ansonlai/docx-redline-js quickly.
4
+ > Read this instead of exploring the full source tree.
5
+
6
+ ## What This Package Does
7
+
8
+ Converts text/markdown edits into valid Office Open XML (OOXML) with Word-native tracked changes. Feed it original OOXML + desired text and it returns OOXML with `w:ins`/`w:del` revision markup.
9
+
10
+ ## Conceptual Model
11
+
12
+ ```
13
+ Input: (paragraph OOXML, original text, modified text, options)
14
+ |
15
+ v
16
+ Engine routes to: format-only | surgical | reconstruction | list | table mode
17
+ |
18
+ v
19
+ Output: { oxml: string, hasChanges: boolean, warnings?: string[] }
20
+ ```
21
+
22
+ The engine works at paragraph scope. For full-document operations, callers iterate paragraph targets or use the standalone operation runner.
23
+
24
+ ## Entry Point
25
+
26
+ ```js
27
+ import { applyRedlineToOxml, configureXmlProvider } from '@ansonlai/docx-redline-js';
28
+ ```
29
+
30
+ `index.js` is the single package entry point.
31
+
32
+ ## Required Setup (Node.js only)
33
+
34
+ ```js
35
+ import { DOMParser, XMLSerializer } from '@xmldom/xmldom';
36
+ configureXmlProvider({ DOMParser, XMLSerializer });
37
+ ```
38
+
39
+ Browsers have native DOM APIs, so no provider injection is typically needed.
40
+
41
+ ## Key APIs by Use Case
42
+
43
+ ### Apply a text edit with tracked changes
44
+
45
+ ```js
46
+ const result = await applyRedlineToOxml(oxml, originalText, modifiedText, {
47
+ generateRedlines: true,
48
+ author: 'Agent Name'
49
+ });
50
+ ```
51
+
52
+ ### Apply a text edit without tracked changes
53
+
54
+ ```js
55
+ const result = await applyRedlineToOxml(oxml, originalText, modifiedText, {
56
+ generateRedlines: false
57
+ });
58
+ ```
59
+
60
+ ### Convert OOXML to readable text or markdown
61
+
62
+ ```js
63
+ import { ingestWordOoxmlToPlainText, ingestWordOoxmlToMarkdown } from '@ansonlai/docx-redline-js';
64
+ const plainText = ingestWordOoxmlToPlainText(documentXml);
65
+ const markdown = ingestWordOoxmlToMarkdown(documentXml);
66
+ ```
67
+
68
+ ### Add a comment to OOXML
69
+
70
+ ```js
71
+ import { injectCommentsIntoOoxml } from '@ansonlai/docx-redline-js';
72
+ const result = injectCommentsIntoOoxml(paragraphOoxml, [
73
+ { text: 'Review this clause', targetText: 'force majeure', author: 'Agent' }
74
+ ]);
75
+ ```
76
+
77
+ ### Apply multiple operations to full document XML
78
+
79
+ ```js
80
+ import { applyOperationToDocumentXml } from '@ansonlai/docx-redline-js/services/standalone-operation-runner.js';
81
+ const result = await applyOperationToDocumentXml(documentXml, operation, options);
82
+ ```
83
+
84
+ ### Convert paragraph text into a Word list
85
+
86
+ ```js
87
+ const result = await applyRedlineToOxml(oxml, 'Item text', '1. Item text', {
88
+ generateRedlines: true
89
+ });
90
+ ```
91
+
92
+ ### Reconcile a table
93
+
94
+ ```js
95
+ import { reconcileMarkdownTableOoxml } from '@ansonlai/docx-redline-js';
96
+ const result = await reconcileMarkdownTableOoxml(tableOoxml, originalText, markdownTable);
97
+ ```
98
+
99
+ ## Module Map
100
+
101
+ ```
102
+ index.js
103
+ adapters/
104
+ config.js
105
+ xml-adapter.js
106
+ logger.js
107
+ core/
108
+ types.js
109
+ paragraph-targeting.js
110
+ list-targeting.js
111
+ table-targeting.js
112
+ engine/
113
+ oxml-engine.js
114
+ surgical-mode.js
115
+ reconstruction-mode.js
116
+ format-application.js
117
+ formatting-removal.js
118
+ table-mode.js
119
+ pipeline/
120
+ pipeline.js
121
+ ingestion.js
122
+ ingestion-export.js
123
+ diff-engine.js
124
+ markdown-processor.js
125
+ serialization.js
126
+ list-generation.js
127
+ services/
128
+ standalone-operation-runner.js
129
+ standalone-docx-plumbing.js
130
+ numbering-helpers.js
131
+ comment-engine.js
132
+ table-reconciliation.js
133
+ package-builder.js
134
+ orchestration/
135
+ route-plan.js
136
+ list-markdown.js
137
+ list-structural-fallback.js
138
+ ```
139
+
140
+ ## Common Patterns
141
+
142
+ ### Options shape
143
+
144
+ ```js
145
+ {
146
+ generateRedlines: true,
147
+ author: 'Name'
148
+ }
149
+ ```
150
+
151
+ ### Typical return shape
152
+
153
+ ```js
154
+ {
155
+ oxml: string,
156
+ hasChanges: boolean,
157
+ warnings?: string[],
158
+ numberingXml?: string,
159
+ useNativeApi?: boolean
160
+ }
161
+ ```
162
+
163
+ ### OOXML wrapping for Word insertOoxml scenarios
164
+
165
+ ```js
166
+ import { wrapInDocumentFragment } from '@ansonlai/docx-redline-js';
167
+ const wrapped = wrapInDocumentFragment(rawOoxml, { includeNumbering: true, numberingXml });
168
+ ```
169
+
170
+ ## Gotchas
171
+
172
+ 1. Call `configureXmlProvider` first in Node.js.
173
+ 2. `applyRedlineToOxml` is async.
174
+ 3. Paragraph APIs expect paragraph-level OOXML, not full `word/document.xml` in all cases.
175
+ 4. List operations may return `numberingXml` that must be merged into package parts.
176
+ 5. `useNativeApi: true` means standalone mode cannot fully handle that operation path.
@@ -0,0 +1,121 @@
1
+ # Reconciliation Core Architecture
2
+
3
+ This document describes the standalone, host-agnostic OOXML reconciliation package and how to work inside it safely.
4
+
5
+ ## Scope
6
+
7
+ This repository contains only the publishable package surface:
8
+
9
+ - `adapters/`
10
+ - `core/`
11
+ - `engine/`
12
+ - `pipeline/`
13
+ - `services/`
14
+ - `orchestration/`
15
+ - `index.js`
16
+ - `standalone.js`
17
+
18
+ No Word add-in entrypoints or host-specific integration layers are part of this package.
19
+
20
+ ## Goals
21
+
22
+ - Preserve Word-compatible redlines by editing OOXML directly.
23
+ - Keep core logic host-independent (no Office.js globals, no Word API calls).
24
+ - Reuse the same engine in browser, Node.js, and other JavaScript runtimes.
25
+
26
+ ## Folder Layout
27
+
28
+ ```text
29
+ .
30
+ ├── adapters/
31
+ │ ├── config.js
32
+ │ ├── logger.js
33
+ │ └── xml-adapter.js
34
+ ├── core/
35
+ ├── engine/
36
+ │ └── formatting-removal.js
37
+ ├── orchestration/
38
+ ├── pipeline/
39
+ ├── services/
40
+ │ ├── numbering-helpers.js
41
+ │ ├── standalone-docx-plumbing.js
42
+ │ └── standalone-operation-runner.js
43
+ ├── index.js
44
+ └── standalone.js
45
+ ```
46
+
47
+ ## Entry Points
48
+
49
+ - `index.js`: primary package entrypoint.
50
+ - `standalone.js`: compatibility alias that re-exports from `index.js` (deprecated for new imports).
51
+
52
+ ## Module Responsibilities
53
+
54
+ - `adapters/config.js`
55
+ - Runtime configuration for defaults (`setDefaultAuthor`, `getDefaultAuthor`, `setPlatform`, `getPlatform`).
56
+ - `adapters/xml-adapter.js`
57
+ - XML parser/serializer injection for browser or Node.js runtimes.
58
+ - `adapters/logger.js`
59
+ - Runtime logger injection and shared logging methods.
60
+ - `core/*`
61
+ - Shared types, OOXML identity helpers, target resolution, list/table targeting heuristics, and XML query helpers.
62
+ - `engine/oxml-engine.js`
63
+ - Main reconciliation router and mode selection.
64
+ - `engine/formatting-removal.js`
65
+ - Shared formatting removal and highlight helpers.
66
+ - `pipeline/*`
67
+ - Ingestion, markdown preprocessing, diffing, patching, and serialization stages.
68
+ - `services/numbering-helpers.js`
69
+ - Dynamic numbering ID allocation, numbering payload remapping, and schema-order-safe numbering merges.
70
+ - `services/standalone-docx-plumbing.js`
71
+ - Package-level extraction/wiring/validation for `word/document.xml`, `word/numbering.xml`, and `word/comments.xml`.
72
+ - `services/standalone-operation-runner.js`
73
+ - Host-agnostic operation bridge for `redline`, `highlight`, and `comment` workflows.
74
+ - `orchestration/*`
75
+ - Route planning and list fallback orchestration utilities.
76
+
77
+ ## End-to-End Flow
78
+
79
+ 1. Caller imports from `index.js` (or legacy `standalone.js` alias).
80
+ 2. Caller configures XML provider/logger/defaults when needed via `adapters/*`.
81
+ 3. Caller invokes reconciliation APIs (`applyRedlineToOxml`, operation runner, ingestion/export helpers).
82
+ 4. `engine/oxml-engine.js` routes to format, table, list, surgical, or reconstruction flows.
83
+ 5. Pipeline/services return OOXML and optional package artifacts (`numberingXml`, comments payloads).
84
+ 6. Caller writes resulting XML back to package/document boundaries.
85
+
86
+ ## Public Surfaces
87
+
88
+ - Primary: `index.js`
89
+ - Compatibility alias: `standalone.js` (deprecated)
90
+
91
+ Keep exports centralized through `index.js`; maintain `standalone.js` only for backward compatibility.
92
+
93
+ ## Build Output
94
+
95
+ `npm run build` generates CDN-ready ESM bundles under `dist/`:
96
+
97
+ - `dist/docx-redline-js.esm.js`
98
+ - `dist/docx-redline-js.esm.js.map`
99
+ - `dist/docx-redline-js.esm.min.js`
100
+ - `dist/docx-redline-js.esm.min.js.map`
101
+
102
+ The bundle inlines `diff-match-patch` and keeps `@xmldom/xmldom` external.
103
+
104
+ ## Testing
105
+
106
+ - `npm test`
107
+ - Runs the package test runner (`scripts/run-tests.mjs`) against all `tests/*.mjs` except setup helpers.
108
+ - `npm run test:isolation`
109
+ - Runs boundary checks for Word API markers and dependency-graph isolation.
110
+
111
+ Use these checks before publishing or tagging.
112
+
113
+ ## Fast Orientation For Contributors
114
+
115
+ Use this sequence to understand or modify behavior without reading everything:
116
+
117
+ 1. Start at `index.js` to locate the exported API.
118
+ 2. Follow exports into `engine/oxml-engine.js` or relevant `services/*` module.
119
+ 3. For targeting bugs, inspect `core/paragraph-targeting.js`, `core/list-targeting.js`, and `core/table-targeting.js`.
120
+ 4. For package wiring issues, inspect `services/standalone-docx-plumbing.js`.
121
+ 5. For numbering/list issues, inspect `services/numbering-helpers.js` and orchestration list-fallback modules.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Anson Lai
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,177 @@
1
+ # @ansonlai/docx-redline-js
2
+
3
+ Host-independent OOXML reconciliation engine for `.docx` manipulation with track changes (redlines).
4
+
5
+ Converts AI-generated or programmatic text/markdown edits into valid Office Open XML (OOXML) with `w:ins`/`w:del` revision markup that Microsoft Word renders as native tracked changes.
6
+
7
+ ## Features
8
+
9
+ - Text reconciliation with word-level diffing and native-looking redlines
10
+ - Formatting updates (bold, italic, underline, strikethrough) via surgical `w:rPrChange`
11
+ - Lists: generate and edit real Word lists (`w:numPr`) from markdown
12
+ - Tables: virtual-grid diffing for cell-level edits with merge safety
13
+ - Comments: inject OOXML comments anchored to text ranges
14
+ - Highlights: apply highlight colors to runs
15
+ - Markdown and OOXML conversion in both directions
16
+ - Package plumbing helpers for numbering.xml, comments.xml, content types, and relationships
17
+ - Zero host dependencies: works in Node.js, browsers, Deno, and similar JS runtimes with DOM parsing support
18
+
19
+ ## Install
20
+
21
+ ### npm / Node.js
22
+
23
+ ```bash
24
+ npm install @ansonlai/docx-redline-js
25
+ ```
26
+
27
+ ### CDN (browser `<script type="module">`)
28
+
29
+ ```html
30
+ <script type="module">
31
+ import { applyRedlineToOxml } from 'https://esm.sh/@ansonlai/docx-redline-js';
32
+ </script>
33
+ ```
34
+
35
+ Or use the pre-bundled file (no import map needed, `diff-match-patch` is inlined):
36
+
37
+ ```html
38
+ <script type="module">
39
+ import { applyRedlineToOxml } from 'https://cdn.jsdelivr.net/npm/@ansonlai/docx-redline-js/dist/docx-redline-js.esm.min.js';
40
+ </script>
41
+ ```
42
+
43
+ ### Local git clone
44
+
45
+ ```bash
46
+ git clone https://github.com/YOUR_ORG/docx-redline-js.git
47
+ ```
48
+
49
+ ```js
50
+ import { applyRedlineToOxml } from './docx-redline-js/index.js';
51
+ ```
52
+
53
+ ## Quick Start
54
+
55
+ ### Node.js
56
+
57
+ ```js
58
+ import { DOMParser, XMLSerializer } from '@xmldom/xmldom';
59
+ import {
60
+ configureXmlProvider,
61
+ setDefaultAuthor,
62
+ applyRedlineToOxml
63
+ } from '@ansonlai/docx-redline-js';
64
+
65
+ configureXmlProvider({ DOMParser, XMLSerializer });
66
+ setDefaultAuthor('My App');
67
+
68
+ const result = await applyRedlineToOxml(
69
+ paragraphOoxml,
70
+ 'Original sentence.',
71
+ 'Updated sentence.',
72
+ { generateRedlines: true, author: 'Editor' }
73
+ );
74
+
75
+ console.log(result.hasChanges);
76
+ console.log(result.oxml);
77
+ ```
78
+
79
+ ### Browser
80
+
81
+ ```js
82
+ import {
83
+ setDefaultAuthor,
84
+ applyRedlineToOxml
85
+ } from '@ansonlai/docx-redline-js';
86
+
87
+ setDefaultAuthor('Browser Editor');
88
+
89
+ const result = await applyRedlineToOxml(oxml, original, modified, {
90
+ generateRedlines: true
91
+ });
92
+ ```
93
+
94
+ ## API Reference
95
+
96
+ ### Configuration (call once at startup)
97
+
98
+ | Function | Purpose |
99
+ |----------|---------|
100
+ | `configureXmlProvider({ DOMParser, XMLSerializer })` | Inject XML parser. Required in Node.js; browsers usually provide native support. |
101
+ | `configureLogger({ log, warn, error })` | Replace default console logger. |
102
+ | `setDefaultAuthor(name)` | Set fallback track-change author (default: `'Author'`). |
103
+ | `setPlatform(label)` | Set platform label for diagnostics (default: `'Unknown'`). |
104
+
105
+ ### Engine (primary reconciliation APIs)
106
+
107
+ | Function | Purpose |
108
+ |----------|---------|
109
+ | `applyRedlineToOxml(oxml, original, modified, options)` | Core engine entry point for text/markdown reconciliation with optional redlines. |
110
+ | `applyRedlineToOxmlWithListFallback(oxml, original, modified, options)` | Core engine with automatic single-line list structural fallback. |
111
+ | `reconcileMarkdownTableOoxml(oxml, original, markdownTable, options)` | Table-specific reconciliation helper. |
112
+
113
+ ### Pipeline (lower-level access)
114
+
115
+ | Function | Purpose |
116
+ |----------|---------|
117
+ | `ReconciliationPipeline` | Direct pipeline access (ingest, diff, patch, serialize). |
118
+ | `ingestWordOoxmlToPlainText(oxml)` | Extract plain text from OOXML. |
119
+ | `ingestWordOoxmlToMarkdown(oxml)` | Convert OOXML to markdown. |
120
+ | `ingestOoxml(oxml)` | Flatten OOXML into an internal run model with offsets. |
121
+ | `preprocessMarkdown(text)` | Normalize markdown and extract format hints. |
122
+
123
+ ### Services
124
+
125
+ | Function | Purpose |
126
+ |----------|---------|
127
+ | `injectCommentsIntoOoxml(oxml, comments, options)` | Add comments anchored to text ranges. |
128
+ | `generateTableOoxml(headers, rows, options)` | Generate a `w:tbl` from tabular data. |
129
+ | `createDynamicNumberingIdState(numberingXml)` | Allocate numbering IDs without collisions. |
130
+ | `ensureNumberingArtifactsInZip(zip, numberingXml)` | Merge numbering artifacts into a `.docx` package. |
131
+ | `ensureCommentsArtifactsInZip(zip, commentsXml)` | Merge comments artifacts into a `.docx` package. |
132
+ | `validateDocxPackage(zip)` | Validate `.docx` structural consistency. |
133
+
134
+ ### Deep Imports
135
+
136
+ For advanced usage, import specific submodules:
137
+
138
+ ```js
139
+ import { applyOperationToDocumentXml } from '@ansonlai/docx-redline-js/services/standalone-operation-runner.js';
140
+ import { getParagraphText } from '@ansonlai/docx-redline-js/core/paragraph-targeting.js';
141
+ ```
142
+
143
+ ## Working With `.docx` Files
144
+
145
+ This package operates on OOXML strings (XML parts inside `.docx` zip archives), not raw `.docx` binaries.
146
+
147
+ Typical flow:
148
+
149
+ 1. Extract the `.docx` zip (for example with JSZip, fflate, or similar)
150
+ 2. Read `word/document.xml`
151
+ 3. Apply reconciliation APIs to XML strings
152
+ 4. Merge numbering/comments artifacts when needed
153
+ 5. Write the archive back to a `.docx` file
154
+
155
+ ```js
156
+ import JSZip from 'jszip';
157
+ import {
158
+ configureXmlProvider,
159
+ applyRedlineToOxml,
160
+ ensureNumberingArtifactsInZip,
161
+ validateDocxPackage
162
+ } from '@ansonlai/docx-redline-js';
163
+
164
+ const zip = await JSZip.loadAsync(docxBuffer);
165
+ const documentXml = await zip.file('word/document.xml').async('string');
166
+
167
+ // Apply edits with applyRedlineToOxml(...)
168
+ // Merge artifacts with ensureNumberingArtifactsInZip(...) as needed
169
+
170
+ const output = await zip.generateAsync({ type: 'nodebuffer' });
171
+ ```
172
+
173
+ ## Architecture
174
+
175
+ See [ARCHITECTURE.md](./ARCHITECTURE.md) for module layout, data flow, and contributor guidance.
176
+
177
+ See [AGENTS.md](./AGENTS.md) for a concise reference for AI coding agents.
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Configurable runtime defaults for the reconciliation core.
3
+ * Callers can set these during bootstrap.
4
+ */
5
+
6
+ let _defaultAuthor = 'Author';
7
+ let _platform = 'Unknown';
8
+
9
+ /**
10
+ * Set the default track-change author for revision metadata.
11
+ *
12
+ * @param {string} author
13
+ */
14
+ export function setDefaultAuthor(author) {
15
+ _defaultAuthor = typeof author === 'string' && author.trim() ? author.trim() : 'Author';
16
+ }
17
+
18
+ /**
19
+ * Get the current default track-change author.
20
+ *
21
+ * @returns {string}
22
+ */
23
+ export function getDefaultAuthor() {
24
+ return _defaultAuthor;
25
+ }
26
+
27
+ /**
28
+ * Set the platform identifier (e.g. 'Win32', 'Mac', 'OfficeOnline').
29
+ *
30
+ * @param {string} platform
31
+ */
32
+ export function setPlatform(platform) {
33
+ _platform = typeof platform === 'string' && platform.trim() ? platform.trim() : 'Unknown';
34
+ }
35
+
36
+ /**
37
+ * Get the current platform identifier.
38
+ *
39
+ * @returns {string}
40
+ */
41
+ export function getPlatform() {
42
+ return _platform;
43
+ }
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Logger adapter for reconciliation modules.
3
+ */
4
+
5
+ let _logger = console;
6
+
7
+ const LEVELS = Object.freeze({
8
+ silent: 0,
9
+ error: 1,
10
+ warn: 2,
11
+ info: 3
12
+ });
13
+
14
+ const DEFAULT_LOG_LEVEL = (() => {
15
+ const isProd = typeof process !== 'undefined' && process?.env?.NODE_ENV === 'production';
16
+ return isProd ? 'warn' : 'info';
17
+ })();
18
+
19
+ let _logLevel = DEFAULT_LOG_LEVEL;
20
+
21
+ function normalizeLogLevel(level) {
22
+ const normalized = String(level || '').toLowerCase();
23
+ return Object.prototype.hasOwnProperty.call(LEVELS, normalized) ? normalized : _logLevel;
24
+ }
25
+
26
+ function isEnabled(level) {
27
+ return LEVELS[_logLevel] >= LEVELS[level];
28
+ }
29
+
30
+ /**
31
+ * Configures logger implementation.
32
+ *
33
+ * @param {{log?: Function, warn?: Function, error?: Function}} logger - Logger object
34
+ * @param {{ level?: 'silent'|'error'|'warn'|'info' }} [options={}] - Logger options
35
+ */
36
+ export function configureLogger(logger, options = {}) {
37
+ _logger = logger || console;
38
+ if (options.level) {
39
+ _logLevel = normalizeLogLevel(options.level);
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Sets the minimum log level for reconciliation logs.
45
+ *
46
+ * @param {'silent'|'error'|'warn'|'info'} level - Desired log level
47
+ */
48
+ export function setLogLevel(level) {
49
+ _logLevel = normalizeLogLevel(level);
50
+ }
51
+
52
+ /**
53
+ * Gets current logger level.
54
+ *
55
+ * @returns {'silent'|'error'|'warn'|'info'}
56
+ */
57
+ export function getLogLevel() {
58
+ return _logLevel;
59
+ }
60
+
61
+ /**
62
+ * Log passthrough.
63
+ *
64
+ * @param {...any} args - Log args
65
+ */
66
+ export function log(...args) {
67
+ if (!isEnabled('info')) return;
68
+ (_logger.log || (() => { }))(...args);
69
+ }
70
+
71
+ /**
72
+ * Warn passthrough.
73
+ *
74
+ * @param {...any} args - Warn args
75
+ */
76
+ export function warn(...args) {
77
+ if (!isEnabled('warn')) return;
78
+ (_logger.warn || (() => { }))(...args);
79
+ }
80
+
81
+ /**
82
+ * Error passthrough.
83
+ *
84
+ * @param {...any} args - Error args
85
+ */
86
+ export function error(...args) {
87
+ if (!isEnabled('error')) return;
88
+ (_logger.error || (() => { }))(...args);
89
+ }
@@ -0,0 +1,74 @@
1
+ /**
2
+ * XML adapter for parser/serializer portability.
3
+ *
4
+ * Default behavior uses browser-provided DOMParser/XMLSerializer.
5
+ * Consumers can override these constructors for non-browser runtimes.
6
+ */
7
+
8
+ let _DOMParser = globalThis.DOMParser;
9
+ let _XMLSerializer = globalThis.XMLSerializer;
10
+
11
+ /**
12
+ * Configures XML provider constructors.
13
+ *
14
+ * @param {Object} [options={}] - Provider overrides
15
+ * @param {typeof DOMParser} [options.DOMParser] - DOMParser constructor
16
+ * @param {typeof XMLSerializer} [options.XMLSerializer] - XMLSerializer constructor
17
+ */
18
+ export function configureXmlProvider(options = {}) {
19
+ if (options.DOMParser) _DOMParser = options.DOMParser;
20
+ if (options.XMLSerializer) _XMLSerializer = options.XMLSerializer;
21
+ }
22
+
23
+ /**
24
+ * Creates a parser instance.
25
+ *
26
+ * @returns {DOMParser}
27
+ */
28
+ export function createParser() {
29
+ if (!_DOMParser && globalThis.DOMParser) {
30
+ _DOMParser = globalThis.DOMParser;
31
+ }
32
+ if (!_DOMParser) {
33
+ throw new Error('DOMParser is not configured. Call configureXmlProvider({ DOMParser, XMLSerializer }) first.');
34
+ }
35
+ return new _DOMParser();
36
+ }
37
+
38
+ /**
39
+ * Creates a serializer instance.
40
+ *
41
+ * @returns {XMLSerializer}
42
+ */
43
+ export function createSerializer() {
44
+ if (!_XMLSerializer && globalThis.XMLSerializer) {
45
+ _XMLSerializer = globalThis.XMLSerializer;
46
+ }
47
+ if (!_XMLSerializer) {
48
+ throw new Error('XMLSerializer is not configured. Call configureXmlProvider({ DOMParser, XMLSerializer }) first.');
49
+ }
50
+ return new _XMLSerializer();
51
+ }
52
+
53
+ /**
54
+ * Parses XML text into a DOM document.
55
+ *
56
+ * @param {string} xmlString - XML string
57
+ * @param {string} [contentType='text/xml'] - MIME type
58
+ * @returns {Document}
59
+ */
60
+ export function parseXml(xmlString, contentType = 'text/xml') {
61
+ const parser = createParser();
62
+ return parser.parseFromString(xmlString, contentType);
63
+ }
64
+
65
+ /**
66
+ * Serializes a node to XML text.
67
+ *
68
+ * @param {Node} node - Node to serialize
69
+ * @returns {string}
70
+ */
71
+ export function serializeXml(node) {
72
+ const serializer = createSerializer();
73
+ return serializer.serializeToString(node);
74
+ }