@createiq/htmldiff 1.1.0 → 1.2.0-beta.1

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.
@@ -0,0 +1,15 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(diff -u test/input1.html test/input2.html)",
5
+ "Bash(npm test -- test/Bug.spec.tsx)",
6
+ "Bash(timeout 30s npm run test:ci -- test/Bug.spec.tsx)",
7
+ "Bash(npm run build)",
8
+ "Bash(timeout 10s npm run test:ci -- test/Bug.spec.tsx)",
9
+ "Bash(npm run lint)",
10
+ "Bash(npm run test:ci)",
11
+ "Bash(npm run bench:ci)"
12
+ ],
13
+ "deny": []
14
+ }
15
+ }
package/README.md CHANGED
@@ -127,6 +127,73 @@ This property defines relative size of the match to be considered as orphan, fro
127
127
 
128
128
  Returns the diff from an HtmlDiff instance.
129
129
 
130
+ ### HtmlDiff.executeThreeWay(genesis, cpLatest, meCurrent, options?)
131
+
132
+ Three-way HTML diff for back-and-forth negotiation scenarios. Given:
133
+
134
+ - `genesis` — the shared common ancestor (the version both parties started from)
135
+ - `cpLatest` — the counterparty's current published position
136
+ - `meCurrent` — your current draft
137
+
138
+ `executeThreeWay` compares both `cpLatest` and `meCurrent` against `genesis` and attributes each side's accumulated
139
+ changes independently:
140
+
141
+ - Counterparty insertions / deletions: `<ins class='diffins cp' data-author='cp'>` / `<del class='diffdel cp' ...>`
142
+ - Your insertions / deletions: `<ins class='diffins me' data-author='me'>` / `<del class='diffdel me' ...>`
143
+ - When both authors made the same change, the result is treated as **settled** and emitted plain (no markup).
144
+ - When both authors deleted the same content, the deletion is settled and silenced.
145
+
146
+ ```ts
147
+ import HtmlDiff from '@createiq/htmldiff'
148
+
149
+ const genesis = '<p>The quick brown fox jumps over the lazy dog</p>'
150
+ const cpLatest = '<p>The fast brown fox leaps</p>'
151
+ const meCurrent = '<p>The quick brown antelope leaps over the lazy pig</p>'
152
+
153
+ const merged = HtmlDiff.executeThreeWay(genesis, cpLatest, meCurrent)
154
+ // CP changed quick→fast and removed "over the lazy dog" — attributed to cp.
155
+ // You changed fox→antelope and added "pig" — attributed to me.
156
+ // "jumps→leaps" was made by CP and kept by you — settled, emitted plain.
157
+ ```
158
+
159
+ Tables (including multi-table documents) and row/column structural changes are supported with the same author
160
+ attribution at row, cell, and content granularity. Settled changes (both sides agree) propagate through every
161
+ granularity — a table both sides added unchanged emits plain; a row only one side added carries that author's
162
+ attribution.
163
+
164
+ #### Picking a genesis
165
+
166
+ The right `genesis` depends on the negotiation:
167
+
168
+ - **Single shared starting point** (e.g. `prefillReceiverAnswers=true`): use the V1.0 version that both parties
169
+ started from.
170
+ - **Per-user starting points** (e.g. `prefillReceiverAnswers=false`): each party has their own genesis (their
171
+ `initialAnswers` — preset state or schema defaults). The diff from each user's perspective uses their own genesis,
172
+ and the two views may differ.
173
+
174
+ The library doesn't pick the genesis itself — it consumes whatever the caller supplies. CreateiQ's frontend does the
175
+ per-user selection.
176
+
177
+ #### Options
178
+
179
+ `executeThreeWay` accepts the same options as `HtmlDiff` (`repeatingWordsAccuracy`, `ignoreWhitespaceDifferences`,
180
+ `orphanMatchThreshold`, `blockExpressions`). They flow into both internal pair-wise analyses identically.
181
+
182
+ `useProjections` controls structural-tag normalisation. When undefined (default), the decision is the conjunction of
183
+ both pair-wise heuristics — projection only activates when both `genesis↔cpLatest` and `genesis↔meCurrent` would
184
+ benefit from it. Pass an explicit boolean to override.
185
+
186
+ #### Known limitations
187
+
188
+ - **Partial-adoption visibility.** When one author deletes a phrase but the other keeps part of it (e.g. CP deletes
189
+ "over the lazy dog" but Me keeps "over the lazy" and only changes "dog"→"pig"), the algorithm reports the deletion
190
+ at the token level — each kept token appears as a pending `<del cp>` while the one changed token appears separately.
191
+ The reader sees an honest token-level picture rather than a misleading phrase-level summary.
192
+ - **Boundary-based settled-insertion detection.** Two authors inserting the same content but at slightly different
193
+ boundaries (because their respective diffs decomposed the surrounding change differently) may be reported as two
194
+ separate one-sided insertions rather than one settled insertion. Identical content at identical boundaries does
195
+ detect as settled.
196
+
130
197
  ## Contributing
131
198
 
132
199
  The library uses [Biome](https://biomejs.dev/) for linting and formatting, and [Vitest](https://vitest.dev/) for unit