@korajs/merge 0.6.0 → 1.0.0-beta.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 +21 -0
- package/dist/index.cjs +30 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +22 -13
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Dr. Obed Ehoneah
|
|
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/dist/index.cjs
CHANGED
|
@@ -109,18 +109,22 @@ function addWinsSet(localArray, remoteArray, baseArray) {
|
|
|
109
109
|
for (const item of baseArray) {
|
|
110
110
|
addIfNew(serialize(item), item);
|
|
111
111
|
}
|
|
112
|
+
const additions = /* @__PURE__ */ new Map();
|
|
112
113
|
for (const item of localArray) {
|
|
113
114
|
const s = serialize(item);
|
|
114
|
-
if (addedLocal.has(s)) {
|
|
115
|
-
|
|
115
|
+
if (addedLocal.has(s) && !additions.has(s)) {
|
|
116
|
+
additions.set(s, item);
|
|
116
117
|
}
|
|
117
118
|
}
|
|
118
119
|
for (const item of remoteArray) {
|
|
119
120
|
const s = serialize(item);
|
|
120
|
-
if (addedRemote.has(s)) {
|
|
121
|
-
|
|
121
|
+
if (addedRemote.has(s) && !additions.has(s)) {
|
|
122
|
+
additions.set(s, item);
|
|
122
123
|
}
|
|
123
124
|
}
|
|
125
|
+
for (const s of [...additions.keys()].sort()) {
|
|
126
|
+
addIfNew(s, additions.get(s));
|
|
127
|
+
}
|
|
124
128
|
return result;
|
|
125
129
|
}
|
|
126
130
|
|
|
@@ -187,6 +191,7 @@ function mergeAtomicOps(localAtomicOp, remoteAtomicOp, baseValue) {
|
|
|
187
191
|
}
|
|
188
192
|
|
|
189
193
|
// src/strategies/yjs-richtext.ts
|
|
194
|
+
var import_core2 = require("@korajs/core");
|
|
190
195
|
var Y = __toESM(require("yjs"), 1);
|
|
191
196
|
var TEXT_KEY = "content";
|
|
192
197
|
function mergeRichtext(localValue, remoteValue, baseValue) {
|
|
@@ -219,11 +224,17 @@ function toYjsUpdate(value) {
|
|
|
219
224
|
if (value instanceof ArrayBuffer) {
|
|
220
225
|
return new Uint8Array(value);
|
|
221
226
|
}
|
|
222
|
-
|
|
227
|
+
if ((0, import_core2.isKoraBytesValue)(value) || (0, import_core2.isLegacyNumericByteObject)(value)) {
|
|
228
|
+
const decoded = (0, import_core2.decodeBytesFromOpData)(value);
|
|
229
|
+
return decoded instanceof Uint8Array ? decoded : stringToRichtextUpdate(decoded);
|
|
230
|
+
}
|
|
231
|
+
throw new Error(
|
|
232
|
+
"Richtext value must be a string, Uint8Array, ArrayBuffer, tagged { $koraBytes } object, null, or undefined."
|
|
233
|
+
);
|
|
223
234
|
}
|
|
224
235
|
|
|
225
236
|
// src/strategies/schema-strategies.ts
|
|
226
|
-
var
|
|
237
|
+
var import_core3 = require("@korajs/core");
|
|
227
238
|
function counterMerge(localValue, remoteValue, baseValue) {
|
|
228
239
|
const base = typeof baseValue === "number" ? baseValue : 0;
|
|
229
240
|
const local = typeof localValue === "number" ? localValue : base;
|
|
@@ -614,13 +625,13 @@ function matchesWhere(record, where) {
|
|
|
614
625
|
}
|
|
615
626
|
|
|
616
627
|
// src/constraints/resolvers.ts
|
|
617
|
-
var
|
|
628
|
+
var import_core4 = require("@korajs/core");
|
|
618
629
|
function resolveConstraintViolation(violation, mergedRecord, localOp, remoteOp, baseState) {
|
|
619
630
|
const startTime = Date.now();
|
|
620
631
|
const { constraint } = violation;
|
|
621
632
|
switch (constraint.onConflict) {
|
|
622
633
|
case "last-write-wins": {
|
|
623
|
-
const comparison =
|
|
634
|
+
const comparison = import_core4.HybridLogicalClock.compare(localOp.timestamp, remoteOp.timestamp);
|
|
624
635
|
const winner = comparison >= 0 ? localOp : remoteOp;
|
|
625
636
|
const resolvedRecord = applyWinnerFields(mergedRecord, winner, violation.fields);
|
|
626
637
|
return createResolution(
|
|
@@ -634,7 +645,7 @@ function resolveConstraintViolation(violation, mergedRecord, localOp, remoteOp,
|
|
|
634
645
|
);
|
|
635
646
|
}
|
|
636
647
|
case "first-write-wins": {
|
|
637
|
-
const comparison =
|
|
648
|
+
const comparison = import_core4.HybridLogicalClock.compare(localOp.timestamp, remoteOp.timestamp);
|
|
638
649
|
const winner = comparison <= 0 ? localOp : remoteOp;
|
|
639
650
|
const resolvedRecord = applyWinnerFields(mergedRecord, winner, violation.fields);
|
|
640
651
|
return createResolution(
|
|
@@ -650,7 +661,7 @@ function resolveConstraintViolation(violation, mergedRecord, localOp, remoteOp,
|
|
|
650
661
|
case "priority-field": {
|
|
651
662
|
const priorityField = constraint.priorityField;
|
|
652
663
|
if (priorityField === void 0) {
|
|
653
|
-
const comparison =
|
|
664
|
+
const comparison = import_core4.HybridLogicalClock.compare(localOp.timestamp, remoteOp.timestamp);
|
|
654
665
|
const winner2 = comparison >= 0 ? localOp : remoteOp;
|
|
655
666
|
const resolvedRecord2 = applyWinnerFields(mergedRecord, winner2, violation.fields);
|
|
656
667
|
return createResolution(
|
|
@@ -694,7 +705,7 @@ function resolveConstraintViolation(violation, mergedRecord, localOp, remoteOp,
|
|
|
694
705
|
}
|
|
695
706
|
case "custom": {
|
|
696
707
|
if (constraint.resolve === void 0) {
|
|
697
|
-
const comparison =
|
|
708
|
+
const comparison = import_core4.HybridLogicalClock.compare(localOp.timestamp, remoteOp.timestamp);
|
|
698
709
|
const winner = comparison >= 0 ? localOp : remoteOp;
|
|
699
710
|
const resolvedRecord2 = applyWinnerFields(mergedRecord, winner, violation.fields);
|
|
700
711
|
return createResolution(
|
|
@@ -786,7 +797,7 @@ function extractFields(record, fields) {
|
|
|
786
797
|
}
|
|
787
798
|
|
|
788
799
|
// src/constraints/referential-integrity.ts
|
|
789
|
-
var
|
|
800
|
+
var import_core5 = require("@korajs/core");
|
|
790
801
|
function buildMergeRelationLookup(schema) {
|
|
791
802
|
const lookup = /* @__PURE__ */ new Map();
|
|
792
803
|
const relationNames = Object.keys(schema.relations).sort();
|
|
@@ -1057,17 +1068,17 @@ function createDeleteVsInsertTrace(deleteOp, insertOp, relation, strategy, actio
|
|
|
1057
1068
|
}
|
|
1058
1069
|
|
|
1059
1070
|
// src/engine/merge-engine.ts
|
|
1060
|
-
var
|
|
1071
|
+
var import_core7 = require("@korajs/core");
|
|
1061
1072
|
|
|
1062
1073
|
// src/constraints/state-machine-constraint.ts
|
|
1063
|
-
var
|
|
1074
|
+
var import_core6 = require("@korajs/core");
|
|
1064
1075
|
function isValid(sm, from, to) {
|
|
1065
1076
|
const constraint = {
|
|
1066
1077
|
field: sm.field,
|
|
1067
1078
|
collection: "",
|
|
1068
1079
|
transitions: sm.transitions
|
|
1069
1080
|
};
|
|
1070
|
-
return (0,
|
|
1081
|
+
return (0, import_core6.validateTransition)(constraint, from, to).valid;
|
|
1071
1082
|
}
|
|
1072
1083
|
function resolveStateMachineMerge(fieldName, localOp, remoteOp, baseState, stateMachine) {
|
|
1073
1084
|
const startTime = Date.now();
|
|
@@ -1128,7 +1139,7 @@ function resolveStateMachineMerge(fieldName, localOp, remoteOp, baseState, state
|
|
|
1128
1139
|
const localValid = isValid(stateMachine, baseStateStr, localStr);
|
|
1129
1140
|
const remoteValid = isValid(stateMachine, baseStateStr, remoteStr);
|
|
1130
1141
|
if (localValid && remoteValid) {
|
|
1131
|
-
const comparison =
|
|
1142
|
+
const comparison = import_core6.HybridLogicalClock.compare(localOp.timestamp, remoteOp.timestamp);
|
|
1132
1143
|
const winner = comparison >= 0 ? localStr : remoteStr;
|
|
1133
1144
|
return makeResult(
|
|
1134
1145
|
winner,
|
|
@@ -1280,7 +1291,7 @@ var MergeEngine = class {
|
|
|
1280
1291
|
*/
|
|
1281
1292
|
mergeFields(input) {
|
|
1282
1293
|
const { local, remote, baseState, collectionDef } = input;
|
|
1283
|
-
const allFields = collectAffectedFields(local, remote
|
|
1294
|
+
const allFields = collectAffectedFields(local, remote);
|
|
1284
1295
|
const mergedData = {};
|
|
1285
1296
|
const traces = [];
|
|
1286
1297
|
for (const fieldName of allFields) {
|
|
@@ -1322,7 +1333,7 @@ var MergeEngine = class {
|
|
|
1322
1333
|
*/
|
|
1323
1334
|
mergeWithDelete(input) {
|
|
1324
1335
|
const { local, remote } = input;
|
|
1325
|
-
const comparison =
|
|
1336
|
+
const comparison = import_core7.HybridLogicalClock.compare(local.timestamp, remote.timestamp);
|
|
1326
1337
|
if (comparison >= 0) {
|
|
1327
1338
|
if (local.type === "delete") {
|
|
1328
1339
|
return { mergedData: {}, traces: [], appliedOperation: "local", sideEffects: [] };
|
|
@@ -1345,11 +1356,8 @@ var MergeEngine = class {
|
|
|
1345
1356
|
};
|
|
1346
1357
|
}
|
|
1347
1358
|
};
|
|
1348
|
-
function collectAffectedFields(local, remote
|
|
1359
|
+
function collectAffectedFields(local, remote) {
|
|
1349
1360
|
const fields = /* @__PURE__ */ new Set();
|
|
1350
|
-
for (const fieldName of Object.keys(collectionDef.fields)) {
|
|
1351
|
-
fields.add(fieldName);
|
|
1352
|
-
}
|
|
1353
1361
|
if (local.data !== null) {
|
|
1354
1362
|
for (const fieldName of Object.keys(local.data)) {
|
|
1355
1363
|
fields.add(fieldName);
|
|
@@ -1360,9 +1368,6 @@ function collectAffectedFields(local, remote, baseState, collectionDef) {
|
|
|
1360
1368
|
fields.add(fieldName);
|
|
1361
1369
|
}
|
|
1362
1370
|
}
|
|
1363
|
-
for (const fieldName of Object.keys(baseState)) {
|
|
1364
|
-
fields.add(fieldName);
|
|
1365
|
-
}
|
|
1366
1371
|
return fields;
|
|
1367
1372
|
}
|
|
1368
1373
|
function determineAppliedOperation(traces) {
|