@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 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
- addIfNew(s, item);
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
- addIfNew(s, item);
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
- throw new Error("Richtext value must be a string, Uint8Array, ArrayBuffer, null, or undefined.");
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 import_core2 = require("@korajs/core");
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 import_core3 = require("@korajs/core");
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 = import_core3.HybridLogicalClock.compare(localOp.timestamp, remoteOp.timestamp);
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 = import_core3.HybridLogicalClock.compare(localOp.timestamp, remoteOp.timestamp);
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 = import_core3.HybridLogicalClock.compare(localOp.timestamp, remoteOp.timestamp);
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 = import_core3.HybridLogicalClock.compare(localOp.timestamp, remoteOp.timestamp);
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 import_core4 = require("@korajs/core");
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 import_core6 = require("@korajs/core");
1071
+ var import_core7 = require("@korajs/core");
1061
1072
 
1062
1073
  // src/constraints/state-machine-constraint.ts
1063
- var import_core5 = require("@korajs/core");
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, import_core5.validateTransition)(constraint, from, to).valid;
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 = import_core5.HybridLogicalClock.compare(localOp.timestamp, remoteOp.timestamp);
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, baseState, collectionDef);
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 = import_core6.HybridLogicalClock.compare(local.timestamp, remote.timestamp);
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, baseState, collectionDef) {
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) {