@ifc-lite/merge 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.
- package/LICENSE +373 -0
- package/README.md +48 -0
- package/dist/component-state.d.ts +75 -0
- package/dist/component-state.d.ts.map +1 -0
- package/dist/component-state.js +305 -0
- package/dist/component-state.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/inverse.d.ts +30 -0
- package/dist/inverse.d.ts.map +1 -0
- package/dist/inverse.js +87 -0
- package/dist/inverse.js.map +1 -0
- package/dist/merge-layer.d.ts +58 -0
- package/dist/merge-layer.d.ts.map +1 -0
- package/dist/merge-layer.js +179 -0
- package/dist/merge-layer.js.map +1 -0
- package/dist/rebase.d.ts +25 -0
- package/dist/rebase.d.ts.map +1 -0
- package/dist/rebase.js +15 -0
- package/dist/rebase.js.map +1 -0
- package/dist/ref-flow.d.ts +97 -0
- package/dist/ref-flow.d.ts.map +1 -0
- package/dist/ref-flow.js +180 -0
- package/dist/ref-flow.js.map +1 -0
- package/dist/state-diff.d.ts +25 -0
- package/dist/state-diff.d.ts.map +1 -0
- package/dist/state-diff.js +55 -0
- package/dist/state-diff.js.map +1 -0
- package/dist/three-way.d.ts +46 -0
- package/dist/three-way.d.ts.map +1 -0
- package/dist/three-way.js +416 -0
- package/dist/three-way.js.map +1 -0
- package/dist/types.d.ts +114 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/package.json +53 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Three-way merge: two runs of the two-way comparison joined on identity,
|
|
3
|
+
* iterated over the union of touched (entity, componentKey) pairs.
|
|
4
|
+
*
|
|
5
|
+
* Inputs: ancestor A = the candidate layer's base, ours O = the target
|
|
6
|
+
* ref's state, theirs T = the candidate applied to A. The plan's autoOps
|
|
7
|
+
* apply on top of O; "keep ours" therefore emits nothing.
|
|
8
|
+
*
|
|
9
|
+
* Decision matrix (05 §5.3):
|
|
10
|
+
*
|
|
11
|
+
* A→O unchanged, A→T changed → take theirs (auto)
|
|
12
|
+
* A→O changed, A→T unchanged → keep ours (auto)
|
|
13
|
+
* both changed, equal sub-hash → fold (auto)
|
|
14
|
+
* both changed, different → conflict: concurrent-edit
|
|
15
|
+
* tombstoned vs changed → conflict: delete-vs-modify
|
|
16
|
+
* changed vs tombstoned → conflict: modify-vs-delete
|
|
17
|
+
* tombstoned vs tombstoned → fold (auto)
|
|
18
|
+
*
|
|
19
|
+
* Relations use the same matrix over `child:<name>` slots; divergent
|
|
20
|
+
* reparenting surfaces as a `hierarchy` conflict.
|
|
21
|
+
*/
|
|
22
|
+
import type { IfcxFile } from '@ifc-lite/ifcx';
|
|
23
|
+
import type { ComponentAttributes, ComponentKey, MergeOp, MergePlan } from './types.js';
|
|
24
|
+
import type { StackState } from './component-state.js';
|
|
25
|
+
export interface ThreeWayInputs {
|
|
26
|
+
/** The candidate layer's base, ordered weakest first. */
|
|
27
|
+
ancestor: readonly IfcxFile[];
|
|
28
|
+
/** The target ref's state, ordered weakest first. */
|
|
29
|
+
ours: readonly IfcxFile[];
|
|
30
|
+
/** The candidate applied to its base, ordered weakest first. */
|
|
31
|
+
theirs: readonly IfcxFile[];
|
|
32
|
+
}
|
|
33
|
+
export declare function planThreeWayMerge(inputs: ThreeWayInputs): MergePlan;
|
|
34
|
+
export declare function planFromStates(a: StackState, o: StackState, t: StackState): MergePlan;
|
|
35
|
+
/**
|
|
36
|
+
* Ops that move one component from its ancestor value to the new value.
|
|
37
|
+
*
|
|
38
|
+
* The emitted node applies on top of the TARGET stack, where composition
|
|
39
|
+
* is per-attribute LWW — setting the surviving attributes alone would
|
|
40
|
+
* leave stale opinions (an attribute the candidate removed, or one ours
|
|
41
|
+
* added inside a component being resolved to theirs) shining through.
|
|
42
|
+
* Every key visible on the ancestor or ours side that `next` no longer
|
|
43
|
+
* carries is therefore explicitly nulled.
|
|
44
|
+
*/
|
|
45
|
+
export declare function opsForComponentChange(path: string, componentKey: ComponentKey, ancestor: ComponentAttributes | undefined, next: ComponentAttributes | undefined, oursVisible?: ComponentAttributes): MergeOp[];
|
|
46
|
+
//# sourceMappingURL=three-way.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"three-way.d.ts","sourceRoot":"","sources":["../src/three-way.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EACV,mBAAmB,EACnB,YAAY,EAEZ,OAAO,EACP,SAAS,EACV,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAe,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAQpE,MAAM,WAAW,cAAc;IAC7B,yDAAyD;IACzD,QAAQ,EAAE,SAAS,QAAQ,EAAE,CAAC;IAC9B,qDAAqD;IACrD,IAAI,EAAE,SAAS,QAAQ,EAAE,CAAC;IAC1B,gEAAgE;IAChE,MAAM,EAAE,SAAS,QAAQ,EAAE,CAAC;CAC7B;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,cAAc,GAAG,SAAS,CAoBnE;AAkBD,wBAAgB,cAAc,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,GAAG,SAAS,CAsCrF;AAuVD;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,YAAY,EAC1B,QAAQ,EAAE,mBAAmB,GAAG,SAAS,EACzC,IAAI,EAAE,mBAAmB,GAAG,SAAS,EACrC,WAAW,CAAC,EAAE,mBAAmB,GAChC,OAAO,EAAE,CA0BX"}
|
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
import { componentEntries, extractStackState, projectStackStates, snapshotOf, } from './component-state.js';
|
|
5
|
+
export function planThreeWayMerge(inputs) {
|
|
6
|
+
// Fast path (05 §5.7): when both sides extend the ancestor stack, only
|
|
7
|
+
// suffix-touched paths can differ — project those instead of folding
|
|
8
|
+
// and hashing the full model three times. Falls back to the reference
|
|
9
|
+
// extraction for tombstone-bearing stacks (subtree shadowing is global)
|
|
10
|
+
// and for unrelated stacks. Equivalence is enforced by the differential
|
|
11
|
+
// fuzz in fast-path-differential.test.ts.
|
|
12
|
+
if (sharesAncestorPrefix(inputs.ours, inputs.ancestor) && sharesAncestorPrefix(inputs.theirs, inputs.ancestor)) {
|
|
13
|
+
const projected = projectStackStates(inputs.ancestor, inputs.ours.slice(inputs.ancestor.length), inputs.theirs.slice(inputs.ancestor.length));
|
|
14
|
+
if (projected)
|
|
15
|
+
return planFromStates(projected.a, projected.o, projected.t);
|
|
16
|
+
}
|
|
17
|
+
return planFromStates(extractStackState(inputs.ancestor), extractStackState(inputs.ours), extractStackState(inputs.theirs));
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* True when `stack` starts with the ancestor's layers: same documents by
|
|
21
|
+
* reference, or by content address (equal blake3 ids imply identical
|
|
22
|
+
* canonical bytes; non-blake3 test ids only count when reference-equal).
|
|
23
|
+
*/
|
|
24
|
+
function sharesAncestorPrefix(stack, ancestor) {
|
|
25
|
+
if (stack.length < ancestor.length)
|
|
26
|
+
return false;
|
|
27
|
+
for (let i = 0; i < ancestor.length; i++) {
|
|
28
|
+
if (stack[i] === ancestor[i])
|
|
29
|
+
continue;
|
|
30
|
+
const id = ancestor[i].header.id;
|
|
31
|
+
if (id.startsWith('blake3:') && stack[i].header.id === id)
|
|
32
|
+
continue;
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
export function planFromStates(a, o, t) {
|
|
38
|
+
const autoOps = [];
|
|
39
|
+
const conflicts = [];
|
|
40
|
+
let touched = 0;
|
|
41
|
+
// Memoized per plan: with the projection sharing untouched component
|
|
42
|
+
// objects across sides, reference equality + this cache keep hashing
|
|
43
|
+
// proportional to the number of actually-edited components.
|
|
44
|
+
const hashes = new WeakMap();
|
|
45
|
+
const hashOf = (attrs) => {
|
|
46
|
+
if (attrs === undefined)
|
|
47
|
+
return undefined;
|
|
48
|
+
let hash = hashes.get(attrs);
|
|
49
|
+
if (hash === undefined) {
|
|
50
|
+
hash = snapshotOf(attrs).hash;
|
|
51
|
+
hashes.set(attrs, hash);
|
|
52
|
+
}
|
|
53
|
+
return hash;
|
|
54
|
+
};
|
|
55
|
+
const paths = new Set([...a.keys(), ...o.keys(), ...t.keys()]);
|
|
56
|
+
for (const path of paths) {
|
|
57
|
+
const result = mergeEntity(path, a.get(path), o.get(path), t.get(path), hashOf);
|
|
58
|
+
autoOps.push(...result.ops);
|
|
59
|
+
conflicts.push(...result.conflicts);
|
|
60
|
+
touched += result.touched;
|
|
61
|
+
}
|
|
62
|
+
const escalated = escalateShadowedConflicts(a, o, t, autoOps, conflicts);
|
|
63
|
+
return {
|
|
64
|
+
autoOps: escalated.autoOps,
|
|
65
|
+
conflicts: escalated.conflicts,
|
|
66
|
+
stats: {
|
|
67
|
+
touched,
|
|
68
|
+
autoMerged: escalated.autoOps.length,
|
|
69
|
+
conflicting: escalated.conflicts.length,
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* A conflict on a path that is SHADOW-dead on one side cannot be decided
|
|
75
|
+
* on its own: subtree shadowing beats a child resurrect at composition
|
|
76
|
+
* time, so any per-child resolution against a dead ancestor is a silent
|
|
77
|
+
* no-op. The decision belongs to the tombstoned root:
|
|
78
|
+
*
|
|
79
|
+
* - Theirs tombstoned root R, ours edited descendants → R's auto
|
|
80
|
+
* tombstone is demoted to a `modify-vs-delete` conflict on R and the
|
|
81
|
+
* descendant conflicts fold into it (`subtree`): resolving R = theirs
|
|
82
|
+
* deletes the subtree knowingly; R = ours keeps it, edits and all.
|
|
83
|
+
* - Ours tombstoned root R, theirs edited descendants → R gains a
|
|
84
|
+
* `delete-vs-modify` conflict (it had none: theirs never touched R
|
|
85
|
+
* itself). The descendant conflicts stay — their theirs-resolutions
|
|
86
|
+
* carry the actual edits and become satisfiable once R is resurrected.
|
|
87
|
+
*/
|
|
88
|
+
function escalateShadowedConflicts(a, o, t, autoOps, conflicts) {
|
|
89
|
+
const shadowDeadIn = (state, path) => {
|
|
90
|
+
const entity = state.get(path);
|
|
91
|
+
return entity !== undefined && entity.deleted && !entity.explicitDeleted;
|
|
92
|
+
};
|
|
93
|
+
const needsPass = conflicts.some((c) => c.componentKey === undefined && (shadowDeadIn(t, c.path) || shadowDeadIn(o, c.path)));
|
|
94
|
+
if (!needsPass)
|
|
95
|
+
return { autoOps, conflicts };
|
|
96
|
+
const parentMaps = new Map();
|
|
97
|
+
const parentsOf = (state) => {
|
|
98
|
+
let parents = parentMaps.get(state);
|
|
99
|
+
if (!parents) {
|
|
100
|
+
parents = new Map();
|
|
101
|
+
for (const entity of state.values()) {
|
|
102
|
+
for (const child of entity.children.values()) {
|
|
103
|
+
if (!parents.has(child))
|
|
104
|
+
parents.set(child, entity.path);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
parentMaps.set(state, parents);
|
|
108
|
+
}
|
|
109
|
+
return parents;
|
|
110
|
+
};
|
|
111
|
+
const rootIn = (state, path) => {
|
|
112
|
+
// Nearest ancestor (via the state's children graph) whose death is
|
|
113
|
+
// explicit on its own path.
|
|
114
|
+
const parents = parentsOf(state);
|
|
115
|
+
const seen = new Set([path]);
|
|
116
|
+
let current = parents.get(path);
|
|
117
|
+
while (current !== undefined && !seen.has(current)) {
|
|
118
|
+
const entity = state.get(current);
|
|
119
|
+
if (entity?.explicitDeleted)
|
|
120
|
+
return current;
|
|
121
|
+
seen.add(current);
|
|
122
|
+
current = parents.get(current);
|
|
123
|
+
}
|
|
124
|
+
return undefined;
|
|
125
|
+
};
|
|
126
|
+
const keptConflicts = [];
|
|
127
|
+
// Root path → descendant conflict paths folded into its decision.
|
|
128
|
+
const theirsRoots = new Map();
|
|
129
|
+
const oursRoots = new Map();
|
|
130
|
+
for (const conflict of conflicts) {
|
|
131
|
+
if (conflict.componentKey === undefined && shadowDeadIn(t, conflict.path)) {
|
|
132
|
+
const root = rootIn(t, conflict.path);
|
|
133
|
+
if (root !== undefined) {
|
|
134
|
+
// Folded into the root's decision; the ours-side edits need no
|
|
135
|
+
// ops (they are already in the target stack).
|
|
136
|
+
const list = theirsRoots.get(root) ?? [];
|
|
137
|
+
list.push(conflict.path);
|
|
138
|
+
theirsRoots.set(root, list);
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (conflict.componentKey === undefined && shadowDeadIn(o, conflict.path)) {
|
|
143
|
+
const root = rootIn(o, conflict.path);
|
|
144
|
+
if (root !== undefined) {
|
|
145
|
+
const list = oursRoots.get(root) ?? [];
|
|
146
|
+
list.push(conflict.path);
|
|
147
|
+
oursRoots.set(root, list);
|
|
148
|
+
// Kept: its theirs-resolution carries the descendant's edits.
|
|
149
|
+
keptConflicts.push(conflict);
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
keptConflicts.push(conflict);
|
|
154
|
+
}
|
|
155
|
+
let ops = autoOps;
|
|
156
|
+
for (const [root, subtree] of theirsRoots) {
|
|
157
|
+
ops = ops.filter((op) => !(op.op === 'tombstone-entity' && op.path === root));
|
|
158
|
+
const existing = keptConflicts.find((c) => c.path === root && c.componentKey === undefined);
|
|
159
|
+
if (existing) {
|
|
160
|
+
existing.subtree = [...(existing.subtree ?? []), ...subtree].sort();
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
const oEntity = o.get(root);
|
|
164
|
+
const aEntity = a.get(root);
|
|
165
|
+
keptConflicts.push({
|
|
166
|
+
kind: 'modify-vs-delete',
|
|
167
|
+
path: root,
|
|
168
|
+
ours: snapshotOf(Object.fromEntries(componentsOf(oEntity).entries())),
|
|
169
|
+
...(aEntity && !aEntity.deleted
|
|
170
|
+
? { base: snapshotOf(Object.fromEntries(componentsOf(aEntity).entries())) }
|
|
171
|
+
: {}),
|
|
172
|
+
subtree: subtree.sort(),
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
for (const [root, subtree] of oursRoots) {
|
|
176
|
+
const existing = keptConflicts.find((c) => c.path === root && c.componentKey === undefined);
|
|
177
|
+
if (existing) {
|
|
178
|
+
existing.subtree = [...(existing.subtree ?? []), ...subtree].sort();
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
const tEntity = t.get(root);
|
|
182
|
+
const oEntity = o.get(root);
|
|
183
|
+
const aEntity = a.get(root);
|
|
184
|
+
keptConflicts.push({
|
|
185
|
+
kind: 'delete-vs-modify',
|
|
186
|
+
path: root,
|
|
187
|
+
theirs: snapshotOf(Object.fromEntries(componentsOf(tEntity).entries())),
|
|
188
|
+
ours: snapshotOf(Object.fromEntries(componentsOf(oEntity).entries())),
|
|
189
|
+
...(aEntity && !aEntity.deleted
|
|
190
|
+
? { base: snapshotOf(Object.fromEntries(componentsOf(aEntity).entries())) }
|
|
191
|
+
: {}),
|
|
192
|
+
subtree: subtree.sort(),
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
return { autoOps: ops, conflicts: keptConflicts };
|
|
196
|
+
}
|
|
197
|
+
function alive(entity) {
|
|
198
|
+
return entity !== undefined && !entity.deleted;
|
|
199
|
+
}
|
|
200
|
+
function componentsOf(entity) {
|
|
201
|
+
return entity ? componentEntries(entity) : new Map();
|
|
202
|
+
}
|
|
203
|
+
function componentsEqual(x, y, hashOf) {
|
|
204
|
+
if (x.size !== y.size)
|
|
205
|
+
return false;
|
|
206
|
+
for (const [key, attrs] of x) {
|
|
207
|
+
const other = y.get(key);
|
|
208
|
+
if (!other)
|
|
209
|
+
return false;
|
|
210
|
+
if (other === attrs)
|
|
211
|
+
continue;
|
|
212
|
+
if (hashOf(other) !== hashOf(attrs))
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
return true;
|
|
216
|
+
}
|
|
217
|
+
function mergeEntity(path, aEntity, oEntity, tEntity, hashOf) {
|
|
218
|
+
const aAlive = alive(aEntity);
|
|
219
|
+
const oAlive = alive(oEntity);
|
|
220
|
+
const tAlive = alive(tEntity);
|
|
221
|
+
const aComponents = componentsOf(aEntity);
|
|
222
|
+
const oComponents = componentsOf(oEntity);
|
|
223
|
+
const tComponents = componentsOf(tEntity);
|
|
224
|
+
// Both sides agree the entity is gone (or never both created it): fold.
|
|
225
|
+
// Component edits under a double-sided tombstone are unobservable and
|
|
226
|
+
// fold with the deletion.
|
|
227
|
+
if (!oAlive && !tAlive)
|
|
228
|
+
return { ops: [], conflicts: [], touched: aAlive ? 1 : 0 };
|
|
229
|
+
// A change is a component edit OR an alive-state flip: a resurrection
|
|
230
|
+
// carries no component delta but is still a change (05 §5.3) — comparing
|
|
231
|
+
// components alone would silently drop it (or worse, re-delete it).
|
|
232
|
+
const oChanged = oAlive !== aAlive || !componentsEqual(aComponents, oComponents, hashOf);
|
|
233
|
+
const tChanged = tAlive !== aAlive || !componentsEqual(aComponents, tComponents, hashOf);
|
|
234
|
+
// Theirs didn't touch it (same alive-state, same components as the
|
|
235
|
+
// ancestor): whatever ours did stands. Covers ours-only adds, edits,
|
|
236
|
+
// deletes AND resurrections.
|
|
237
|
+
if (!tChanged)
|
|
238
|
+
return { ops: [], conflicts: [], touched: oChanged ? 1 : 0 };
|
|
239
|
+
// Ours didn't touch it: take theirs wholesale.
|
|
240
|
+
if (!oChanged) {
|
|
241
|
+
if (oAlive && !tAlive) {
|
|
242
|
+
// Theirs stripped it to an empty shell (nulled every attribute and
|
|
243
|
+
// slot; the extraction drops such entities, no tombstone): express
|
|
244
|
+
// that as removal opinions. A tombstone here would shadow-kill
|
|
245
|
+
// children theirs kept alive.
|
|
246
|
+
if (tEntity === undefined) {
|
|
247
|
+
return {
|
|
248
|
+
ops: opsForComponentDelta(path, aComponents, new Map(), oComponents, hashOf),
|
|
249
|
+
conflicts: [],
|
|
250
|
+
touched: 1,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
// Shadow-only deaths (a deleted ancestor node, not a tombstone on
|
|
254
|
+
// this path) emit nothing: the parent's own merge decision carries
|
|
255
|
+
// the subtree at composition time. Emitting here would re-delete a
|
|
256
|
+
// child the target reparented, or pre-empt a parent still in
|
|
257
|
+
// conflict.
|
|
258
|
+
if (tEntity.deleted && !tEntity.explicitDeleted) {
|
|
259
|
+
return { ops: [], conflicts: [], touched: 1 };
|
|
260
|
+
}
|
|
261
|
+
return { ops: [{ op: 'tombstone-entity', path }], conflicts: [], touched: 1 };
|
|
262
|
+
}
|
|
263
|
+
// Theirs is alive: added, resurrected, and/or edited.
|
|
264
|
+
const ops = [];
|
|
265
|
+
if (oEntity?.deleted === true || aEntity?.deleted === true) {
|
|
266
|
+
ops.push({ op: 'resurrect-entity', path });
|
|
267
|
+
}
|
|
268
|
+
ops.push(...opsForComponentDelta(path, aComponents, tComponents, oComponents, hashOf));
|
|
269
|
+
return { ops, conflicts: [], touched: 1 };
|
|
270
|
+
}
|
|
271
|
+
// Both changed. Convergent outcomes fold.
|
|
272
|
+
if (oAlive === tAlive && componentsEqual(oComponents, tComponents, hashOf)) {
|
|
273
|
+
return { ops: [], conflicts: [], touched: 1 };
|
|
274
|
+
}
|
|
275
|
+
// Ours deleted (or still dead) while theirs edited/resurrected.
|
|
276
|
+
if (!oAlive && tAlive) {
|
|
277
|
+
return {
|
|
278
|
+
ops: [],
|
|
279
|
+
conflicts: [
|
|
280
|
+
{
|
|
281
|
+
kind: 'delete-vs-modify',
|
|
282
|
+
path,
|
|
283
|
+
theirs: snapshotOf(Object.fromEntries(tComponents.entries())),
|
|
284
|
+
// Ours' opinions stay in the stack under the tombstone and become
|
|
285
|
+
// visible again on resurrect — resolutions need them to null out.
|
|
286
|
+
ours: snapshotOf(Object.fromEntries(oComponents.entries())),
|
|
287
|
+
...(aAlive ? { base: snapshotOf(Object.fromEntries(aComponents.entries())) } : {}),
|
|
288
|
+
},
|
|
289
|
+
],
|
|
290
|
+
touched: 1,
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
// Ours edited/resurrected while theirs deleted.
|
|
294
|
+
if (oAlive && !tAlive) {
|
|
295
|
+
return {
|
|
296
|
+
ops: [],
|
|
297
|
+
conflicts: [
|
|
298
|
+
{
|
|
299
|
+
kind: 'modify-vs-delete',
|
|
300
|
+
path,
|
|
301
|
+
ours: snapshotOf(Object.fromEntries(oComponents.entries())),
|
|
302
|
+
...(aAlive ? { base: snapshotOf(Object.fromEntries(aComponents.entries())) } : {}),
|
|
303
|
+
// A shell-strip (no tombstone) records an empty theirs state so
|
|
304
|
+
// a theirs-resolution emits removal opinions, not a tombstone
|
|
305
|
+
// that would shadow-kill children theirs kept alive.
|
|
306
|
+
...(tEntity === undefined ? { theirs: snapshotOf({}) } : {}),
|
|
307
|
+
},
|
|
308
|
+
],
|
|
309
|
+
touched: 1,
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
// Both alive: component-level matrix over the union of touched keys.
|
|
313
|
+
const ops = [];
|
|
314
|
+
const conflicts = [];
|
|
315
|
+
let touched = 0;
|
|
316
|
+
const keys = new Set([
|
|
317
|
+
...aComponents.keys(),
|
|
318
|
+
...oComponents.keys(),
|
|
319
|
+
...tComponents.keys(),
|
|
320
|
+
]);
|
|
321
|
+
for (const key of keys) {
|
|
322
|
+
const aAttrs = aComponents.get(key);
|
|
323
|
+
const oAttrs = oComponents.get(key);
|
|
324
|
+
const tAttrs = tComponents.get(key);
|
|
325
|
+
// Reference equality first (shared objects from the projection fast
|
|
326
|
+
// path); only genuinely diverging references pay for hashing.
|
|
327
|
+
if (aAttrs === oAttrs && aAttrs === tAttrs)
|
|
328
|
+
continue;
|
|
329
|
+
const oChanged = aAttrs === oAttrs ? false : hashOf(aAttrs) !== hashOf(oAttrs);
|
|
330
|
+
const tChanged = aAttrs === tAttrs ? false : hashOf(aAttrs) !== hashOf(tAttrs);
|
|
331
|
+
if (!oChanged && !tChanged)
|
|
332
|
+
continue;
|
|
333
|
+
touched += 1;
|
|
334
|
+
if (oChanged && !tChanged)
|
|
335
|
+
continue; // keep ours
|
|
336
|
+
if (oChanged && tChanged && (oAttrs === tAttrs || hashOf(oAttrs) === hashOf(tAttrs)))
|
|
337
|
+
continue; // fold
|
|
338
|
+
if (!oChanged && tChanged) {
|
|
339
|
+
ops.push(...opsForComponentChange(path, key, aAttrs, tAttrs, oAttrs));
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
// Both changed, different values.
|
|
343
|
+
conflicts.push({
|
|
344
|
+
kind: key.startsWith('child:') || key.startsWith('inherit:') ? 'hierarchy' : 'concurrent-edit',
|
|
345
|
+
path,
|
|
346
|
+
componentKey: key,
|
|
347
|
+
...(aAttrs ? { base: snapshotOf(aAttrs) } : {}),
|
|
348
|
+
...(oAttrs ? { ours: snapshotOf(oAttrs) } : {}),
|
|
349
|
+
...(tAttrs ? { theirs: snapshotOf(tAttrs) } : {}),
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
return { ops, conflicts, touched };
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Ops that transform an entity's components from the ancestor state to the
|
|
356
|
+
* theirs state, given that ours matches the ancestor per entity-level
|
|
357
|
+
* change detection. Emits only genuinely differing keys.
|
|
358
|
+
*/
|
|
359
|
+
function opsForComponentDelta(path, ancestor, theirs, ours, hashOf) {
|
|
360
|
+
const ops = [];
|
|
361
|
+
const keys = new Set([...ancestor.keys(), ...theirs.keys()]);
|
|
362
|
+
for (const key of keys) {
|
|
363
|
+
const aAttrs = ancestor.get(key);
|
|
364
|
+
const tAttrs = theirs.get(key);
|
|
365
|
+
if (aAttrs === tAttrs)
|
|
366
|
+
continue;
|
|
367
|
+
if (aAttrs !== undefined && tAttrs !== undefined && hashOf(aAttrs) === hashOf(tAttrs))
|
|
368
|
+
continue;
|
|
369
|
+
ops.push(...opsForComponentChange(path, key, aAttrs, tAttrs, ours.get(key)));
|
|
370
|
+
}
|
|
371
|
+
return ops;
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Ops that move one component from its ancestor value to the new value.
|
|
375
|
+
*
|
|
376
|
+
* The emitted node applies on top of the TARGET stack, where composition
|
|
377
|
+
* is per-attribute LWW — setting the surviving attributes alone would
|
|
378
|
+
* leave stale opinions (an attribute the candidate removed, or one ours
|
|
379
|
+
* added inside a component being resolved to theirs) shining through.
|
|
380
|
+
* Every key visible on the ancestor or ours side that `next` no longer
|
|
381
|
+
* carries is therefore explicitly nulled.
|
|
382
|
+
*/
|
|
383
|
+
export function opsForComponentChange(path, componentKey, ancestor, next, oursVisible) {
|
|
384
|
+
if (componentKey.startsWith('child:')) {
|
|
385
|
+
const name = componentKey.slice('child:'.length);
|
|
386
|
+
if (next === undefined)
|
|
387
|
+
return [{ op: 'remove-child', path, name }];
|
|
388
|
+
return [{ op: 'set-child', path, name, child: String(next.child) }];
|
|
389
|
+
}
|
|
390
|
+
if (componentKey.startsWith('inherit:')) {
|
|
391
|
+
const name = componentKey.slice('inherit:'.length);
|
|
392
|
+
if (next === undefined)
|
|
393
|
+
return [{ op: 'remove-inherit', path, name }];
|
|
394
|
+
return [{ op: 'set-inherit', path, name, target: String(next.inherit) }];
|
|
395
|
+
}
|
|
396
|
+
if (next === undefined) {
|
|
397
|
+
const nulled = {};
|
|
398
|
+
for (const attr of Object.keys(ancestor ?? {}))
|
|
399
|
+
nulled[attr] = null;
|
|
400
|
+
for (const attr of Object.keys(oursVisible ?? {}))
|
|
401
|
+
nulled[attr] = null;
|
|
402
|
+
return [{ op: 'tombstone-component', path, componentKey, attributes: nulled }];
|
|
403
|
+
}
|
|
404
|
+
const attributes = {};
|
|
405
|
+
for (const attr of Object.keys(ancestor ?? {})) {
|
|
406
|
+
if (!(attr in next))
|
|
407
|
+
attributes[attr] = null;
|
|
408
|
+
}
|
|
409
|
+
for (const attr of Object.keys(oursVisible ?? {})) {
|
|
410
|
+
if (!(attr in next))
|
|
411
|
+
attributes[attr] = null;
|
|
412
|
+
}
|
|
413
|
+
Object.assign(attributes, next);
|
|
414
|
+
return [{ op: 'set-component', path, componentKey, attributes }];
|
|
415
|
+
}
|
|
416
|
+
//# sourceMappingURL=three-way.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"three-way.js","sourceRoot":"","sources":["../src/three-way.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Layer-merge type vocabulary.
|
|
3
|
+
*
|
|
4
|
+
* Ops are state-based at (entity, componentKey) granularity: a
|
|
5
|
+
* `set-component` carries the full component value, never a transform.
|
|
6
|
+
* Composing is a deterministic fold where stronger layers shadow weaker
|
|
7
|
+
* per (entity, componentKey) — no OT machinery.
|
|
8
|
+
*
|
|
9
|
+
* Spec: docs/architecture/layer-prs/02-layer-format.md §2.2,
|
|
10
|
+
* 05-merge.md §5.3.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Component key vocabulary shared with `@ifc-lite/diff` sub-hash mode:
|
|
14
|
+
* `attr:<group>`, `pset:<PsetName>`, `qset:<QsetName>`, `type-assignment`,
|
|
15
|
+
* `placement`, `geometry:<tier>`, plus `child:<name>` for relation slots.
|
|
16
|
+
*/
|
|
17
|
+
export type ComponentKey = string;
|
|
18
|
+
/** A component's merged value: the IFCX attributes that form it. */
|
|
19
|
+
export type ComponentAttributes = Record<string, unknown>;
|
|
20
|
+
/** Semantic reading of a layer's nodes — the op model of 02 §2.2. */
|
|
21
|
+
export type MergeOp = {
|
|
22
|
+
readonly op: 'set-component';
|
|
23
|
+
readonly path: string;
|
|
24
|
+
readonly componentKey: ComponentKey;
|
|
25
|
+
/** Full component value (state-based; LWW within a stack). */
|
|
26
|
+
readonly attributes: ComponentAttributes;
|
|
27
|
+
} | {
|
|
28
|
+
readonly op: 'tombstone-component';
|
|
29
|
+
readonly path: string;
|
|
30
|
+
readonly componentKey: ComponentKey;
|
|
31
|
+
/** Attribute keys to null out (IFCX null = remove opinion). */
|
|
32
|
+
readonly attributes: ComponentAttributes;
|
|
33
|
+
} | {
|
|
34
|
+
readonly op: 'tombstone-entity';
|
|
35
|
+
readonly path: string;
|
|
36
|
+
} | {
|
|
37
|
+
readonly op: 'resurrect-entity';
|
|
38
|
+
readonly path: string;
|
|
39
|
+
} | {
|
|
40
|
+
readonly op: 'set-child';
|
|
41
|
+
readonly path: string;
|
|
42
|
+
readonly name: string;
|
|
43
|
+
readonly child: string;
|
|
44
|
+
} | {
|
|
45
|
+
readonly op: 'remove-child';
|
|
46
|
+
readonly path: string;
|
|
47
|
+
readonly name: string;
|
|
48
|
+
} | {
|
|
49
|
+
readonly op: 'set-inherit';
|
|
50
|
+
readonly path: string;
|
|
51
|
+
readonly name: string;
|
|
52
|
+
readonly target: string;
|
|
53
|
+
} | {
|
|
54
|
+
readonly op: 'remove-inherit';
|
|
55
|
+
readonly path: string;
|
|
56
|
+
readonly name: string;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Conflict taxonomy, lifted from `collab/conflicts/detector.ts` kinds from
|
|
60
|
+
* session level to merge level (05 §5.3).
|
|
61
|
+
*/
|
|
62
|
+
export type MergeConflictKind = 'concurrent-edit' | 'delete-vs-modify' | 'modify-vs-delete' | 'hierarchy';
|
|
63
|
+
export interface ComponentSnapshot {
|
|
64
|
+
/** Stable sub-hash of the component value. */
|
|
65
|
+
hash: string;
|
|
66
|
+
attributes: ComponentAttributes;
|
|
67
|
+
}
|
|
68
|
+
export interface MergeConflict {
|
|
69
|
+
kind: MergeConflictKind;
|
|
70
|
+
/** Entity path the conflict is on. */
|
|
71
|
+
path: string;
|
|
72
|
+
/** Component the conflict is scoped to; absent for entity-level kinds. */
|
|
73
|
+
componentKey?: ComponentKey;
|
|
74
|
+
/** Ancestor value (undefined when the component did not exist in A). */
|
|
75
|
+
base?: ComponentSnapshot;
|
|
76
|
+
/** Target-side value (undefined when removed/absent on ours). */
|
|
77
|
+
ours?: ComponentSnapshot;
|
|
78
|
+
/** Candidate-side value (undefined when removed/absent on theirs). */
|
|
79
|
+
theirs?: ComponentSnapshot;
|
|
80
|
+
/**
|
|
81
|
+
* For an entity-level delete conflict whose tombstone shadow-kills
|
|
82
|
+
* descendants the other side touched: their paths. The delete decision
|
|
83
|
+
* on THIS entity carries the whole subtree - per-descendant choices
|
|
84
|
+
* against a dead ancestor would be unsatisfiable (subtree shadowing
|
|
85
|
+
* beats a child resurrect at composition time).
|
|
86
|
+
*/
|
|
87
|
+
subtree?: string[];
|
|
88
|
+
}
|
|
89
|
+
export interface MergePlanStats {
|
|
90
|
+
/** (entity, componentKey) pairs touched by either side. */
|
|
91
|
+
touched: number;
|
|
92
|
+
autoMerged: number;
|
|
93
|
+
conflicting: number;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Result of a three-way plan: ops that apply cleanly on top of the target
|
|
97
|
+
* state, plus explicit conflict records awaiting resolution. Zero
|
|
98
|
+
* conflicts + green checks means the merge can complete unattended
|
|
99
|
+
* (subject to ref policy).
|
|
100
|
+
*/
|
|
101
|
+
export interface MergePlan {
|
|
102
|
+
autoOps: MergeOp[];
|
|
103
|
+
conflicts: MergeConflict[];
|
|
104
|
+
stats: MergePlanStats;
|
|
105
|
+
}
|
|
106
|
+
/** A reviewer's (or policy's) decision for one conflict. */
|
|
107
|
+
export interface ResolutionInput {
|
|
108
|
+
path: string;
|
|
109
|
+
componentKey?: ComponentKey;
|
|
110
|
+
choice: 'ours' | 'theirs' | 'edited';
|
|
111
|
+
/** Replacement component value; required when `choice === 'edited'`. */
|
|
112
|
+
attributes?: ComponentAttributes;
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;+DAE+D"}
|