@oimdb/core 1.2.1 → 1.3.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/dist/index.cjs CHANGED
@@ -39,15 +39,23 @@ __export(index_exports, {
39
39
  OIMEventQueueSchedulerImmediate: () => OIMEventQueueSchedulerImmediate,
40
40
  OIMEventQueueSchedulerMicrotask: () => OIMEventQueueSchedulerMicrotask,
41
41
  OIMEventQueueSchedulerTimeout: () => OIMEventQueueSchedulerTimeout,
42
- OIMIndex: () => OIMIndex,
42
+ OIMIndexArrayBased: () => OIMIndexArrayBased,
43
43
  OIMIndexComparatorFactory: () => OIMIndexComparatorFactory,
44
- OIMIndexManual: () => OIMIndexManual,
44
+ OIMIndexManualArrayBased: () => OIMIndexManualArrayBased,
45
+ OIMIndexManualSetBased: () => OIMIndexManualSetBased,
46
+ OIMIndexSetBased: () => OIMIndexSetBased,
47
+ OIMIndexStoreArrayBased: () => OIMIndexStoreArrayBased,
48
+ OIMIndexStoreMapDrivenArrayBased: () => OIMIndexStoreMapDrivenArrayBased,
49
+ OIMIndexStoreMapDrivenSetBased: () => OIMIndexStoreMapDrivenSetBased,
50
+ OIMIndexStoreSetBased: () => OIMIndexStoreSetBased,
45
51
  OIMMap2Keys: () => OIMMap2Keys,
46
52
  OIMPkSelectorFactory: () => OIMPkSelectorFactory,
47
53
  OIMRICollection: () => OIMRICollection,
48
54
  OIMReactiveCollection: () => OIMReactiveCollection,
49
- OIMReactiveIndex: () => OIMReactiveIndex,
50
- OIMReactiveIndexManual: () => OIMReactiveIndexManual,
55
+ OIMReactiveIndexArrayBased: () => OIMReactiveIndexArrayBased,
56
+ OIMReactiveIndexManualArrayBased: () => OIMReactiveIndexManualArrayBased,
57
+ OIMReactiveIndexManualSetBased: () => OIMReactiveIndexManualSetBased,
58
+ OIMReactiveIndexSetBased: () => OIMReactiveIndexSetBased,
51
59
  OIMUpdateEventCoalescer: () => OIMUpdateEventCoalescer,
52
60
  OIMUpdateEventCoalescerCollection: () => OIMUpdateEventCoalescerCollection,
53
61
  OIMUpdateEventCoalescerIndex: () => OIMUpdateEventCoalescerIndex,
@@ -59,6 +67,14 @@ module.exports = __toCommonJS(index_exports);
59
67
  var OIMCollectionStore = class {
60
68
  };
61
69
 
70
+ // src/abstract/OIMIndexStoreSetBased.ts
71
+ var OIMIndexStoreSetBased = class {
72
+ };
73
+
74
+ // src/abstract/OIMIndexStoreArrayBased.ts
75
+ var OIMIndexStoreArrayBased = class {
76
+ };
77
+
62
78
  // src/core/OIMEventEmitter.ts
63
79
  var OIMEventEmitter = class {
64
80
  constructor() {
@@ -184,12 +200,8 @@ var EOIMIndexEventType = /* @__PURE__ */ ((EOIMIndexEventType2) => {
184
200
  return EOIMIndexEventType2;
185
201
  })(EOIMIndexEventType || {});
186
202
 
187
- // src/abstract/OIMIndexStore.ts
188
- var OIMIndexStore = class {
189
- };
190
-
191
- // src/core/OIMIndexStoreMapDriven.ts
192
- var OIMIndexStoreMapDriven = class extends OIMIndexStore {
203
+ // src/core/OIMIndexStoreMapDrivenSetBased.ts
204
+ var OIMIndexStoreMapDrivenSetBased = class extends OIMIndexStoreSetBased {
193
205
  constructor() {
194
206
  super(...arguments);
195
207
  this.pks = /* @__PURE__ */ new Map();
@@ -232,12 +244,12 @@ var OIMIndexStoreMapDriven = class extends OIMIndexStore {
232
244
  }
233
245
  };
234
246
 
235
- // src/abstract/OIMIndex.ts
236
- var OIMIndex = class {
247
+ // src/abstract/OIMIndexSetBased.ts
248
+ var OIMIndexSetBased = class {
237
249
  constructor(options = {}) {
238
250
  this.emitter = new OIMEventEmitter();
239
251
  this.comparePks = options.comparePks;
240
- this.store = options.store ?? new OIMIndexStoreMapDriven();
252
+ this.store = options.store ?? new OIMIndexStoreMapDrivenSetBased();
241
253
  }
242
254
  /**
243
255
  * Get primary keys for multiple index keys
@@ -346,6 +358,155 @@ var OIMIndex = class {
346
358
  }
347
359
  };
348
360
 
361
+ // src/core/OIMIndexStoreMapDrivenArrayBased.ts
362
+ var OIMIndexStoreMapDrivenArrayBased = class extends OIMIndexStoreArrayBased {
363
+ constructor() {
364
+ super(...arguments);
365
+ this.pks = /* @__PURE__ */ new Map();
366
+ }
367
+ setOneByKey(key, pks) {
368
+ this.pks.set(key, pks);
369
+ }
370
+ removeOneByKey(key) {
371
+ this.pks.delete(key);
372
+ }
373
+ removeManyByKeys(keys) {
374
+ for (const key of keys) {
375
+ this.pks.delete(key);
376
+ }
377
+ }
378
+ getOneByKey(key) {
379
+ return this.pks.get(key);
380
+ }
381
+ getManyByKeys(keys) {
382
+ const result = /* @__PURE__ */ new Map();
383
+ for (const key of keys) {
384
+ const pks = this.getOneByKey(key);
385
+ if (pks !== void 0) {
386
+ result.set(key, pks);
387
+ }
388
+ }
389
+ return result;
390
+ }
391
+ getAllKeys() {
392
+ return Array.from(this.pks.keys());
393
+ }
394
+ getAll() {
395
+ return new Map(this.pks);
396
+ }
397
+ countAll() {
398
+ return this.pks.size;
399
+ }
400
+ clear() {
401
+ this.pks.clear();
402
+ }
403
+ };
404
+
405
+ // src/abstract/OIMIndexArrayBased.ts
406
+ var OIMIndexArrayBased = class {
407
+ constructor(options = {}) {
408
+ this.emitter = new OIMEventEmitter();
409
+ this.comparePks = options.comparePks;
410
+ this.store = options.store ?? new OIMIndexStoreMapDrivenArrayBased();
411
+ }
412
+ /**
413
+ * Get primary keys for multiple index keys
414
+ */
415
+ getPksByKeys(keys) {
416
+ return this.store.getManyByKeys(keys);
417
+ }
418
+ /**
419
+ * Get primary keys for a specific index key
420
+ */
421
+ getPksByKey(key) {
422
+ const pksArray = this.store.getOneByKey(key);
423
+ return pksArray ? pksArray : [];
424
+ }
425
+ /**
426
+ * Check if an index key exists
427
+ */
428
+ hasKey(key) {
429
+ return this.store.getOneByKey(key) !== void 0;
430
+ }
431
+ /**
432
+ * Get all index keys
433
+ */
434
+ getKeys() {
435
+ return this.store.getAllKeys();
436
+ }
437
+ /**
438
+ * Get the number of primary keys for a specific index key
439
+ */
440
+ getKeySize(key) {
441
+ const pksArray = this.store.getOneByKey(key);
442
+ return pksArray ? pksArray.length : 0;
443
+ }
444
+ /**
445
+ * Get total number of index keys
446
+ */
447
+ get size() {
448
+ return this.store.countAll();
449
+ }
450
+ /**
451
+ * Check if the index is empty
452
+ */
453
+ get isEmpty() {
454
+ return this.store.countAll() === 0;
455
+ }
456
+ /**
457
+ * Get performance metrics for monitoring and debugging
458
+ */
459
+ getMetrics() {
460
+ let totalPks = 0;
461
+ let maxBucketSize = 0;
462
+ let minBucketSize = Infinity;
463
+ const allPks = this.store.getAll();
464
+ for (const pksArray of allPks.values()) {
465
+ totalPks += pksArray.length;
466
+ maxBucketSize = Math.max(maxBucketSize, pksArray.length);
467
+ minBucketSize = Math.min(minBucketSize, pksArray.length);
468
+ }
469
+ return {
470
+ totalKeys: allPks.size,
471
+ totalPks,
472
+ averagePksPerKey: allPks.size > 0 ? totalPks / allPks.size : 0,
473
+ maxBucketSize: maxBucketSize === -Infinity ? 0 : maxBucketSize,
474
+ minBucketSize: minBucketSize === Infinity ? 0 : minBucketSize
475
+ };
476
+ }
477
+ /**
478
+ * Clean up event listeners when index is no longer needed
479
+ */
480
+ destroy() {
481
+ this.emitter.offAll();
482
+ this.store.clear();
483
+ }
484
+ /**
485
+ * Set primary keys for a specific index key with optional comparison.
486
+ * If comparator is provided and returns true (no changes), skip the update.
487
+ */
488
+ setPksWithComparison(key, newPks) {
489
+ if (this.comparePks) {
490
+ const existingPksArray = this.store.getOneByKey(key);
491
+ if (existingPksArray && existingPksArray.length === newPks.length) {
492
+ if (this.comparePks(existingPksArray, newPks)) {
493
+ return false;
494
+ }
495
+ } else if (!existingPksArray && newPks.length === 0) {
496
+ return false;
497
+ }
498
+ }
499
+ this.store.setOneByKey(key, newPks);
500
+ return true;
501
+ }
502
+ /**
503
+ * Emit update event with changed keys
504
+ */
505
+ emitUpdate(keys) {
506
+ this.emitter.emit(0 /* UPDATE */, { keys });
507
+ }
508
+ };
509
+
349
510
  // src/enum/EOIMUpdateEventCoalescerEventType.ts
350
511
  var EOIMUpdateEventCoalescerEventType = /* @__PURE__ */ ((EOIMUpdateEventCoalescerEventType2) => {
351
512
  EOIMUpdateEventCoalescerEventType2[EOIMUpdateEventCoalescerEventType2["HAS_CHANGES"] = 0] = "HAS_CHANGES";
@@ -472,8 +633,8 @@ var OIMUpdateEventEmitter = class {
472
633
  }
473
634
  };
474
635
 
475
- // src/core/OIMIndexManual.ts
476
- var OIMIndexManual = class extends OIMIndex {
636
+ // src/core/OIMIndexManualSetBased.ts
637
+ var OIMIndexManualSetBased = class extends OIMIndexSetBased {
477
638
  constructor(options = {}) {
478
639
  super(options);
479
640
  }
@@ -565,8 +726,8 @@ var OIMUpdateEventCoalescer = class {
565
726
  constructor(index) {
566
727
  this.emitter = new OIMEventEmitter();
567
728
  this.hasEmittedChanges = false;
568
- this.updatedKeysIndex = index ?? new OIMIndexManual({
569
- store: new OIMIndexStoreMapDriven()
729
+ this.updatedKeysIndex = index ?? new OIMIndexManualSetBased({
730
+ store: new OIMIndexStoreMapDrivenSetBased()
570
731
  });
571
732
  if (!this.updatedKeysIndex.hasKey(OIMDBSettings.UPDATED_KEYS_INDEX_KEY)) {
572
733
  this.updatedKeysIndex.setPks(
@@ -636,8 +797,49 @@ var OIMUpdateEventCoalescerIndex = class extends OIMUpdateEventCoalescer {
636
797
  }
637
798
  };
638
799
 
639
- // src/abstract/OIMReactiveIndex.ts
640
- var OIMReactiveIndex = class {
800
+ // src/abstract/OIMReactiveIndexSetBased.ts
801
+ var OIMReactiveIndexSetBased = class {
802
+ constructor(queue, opts) {
803
+ this.index = opts?.index ?? this.createDefaultIndex();
804
+ this.coalescer = new OIMUpdateEventCoalescerIndex(
805
+ this.index.emitter
806
+ );
807
+ this.updateEventEmitter = new OIMUpdateEventEmitter({
808
+ coalescer: this.coalescer,
809
+ queue
810
+ });
811
+ }
812
+ getPksByKey(key) {
813
+ return this.index.getPksByKey(key);
814
+ }
815
+ getPksByKeys(keys) {
816
+ return this.index.getPksByKeys(keys);
817
+ }
818
+ hasKey(key) {
819
+ return this.index.hasKey(key);
820
+ }
821
+ getKeys() {
822
+ return this.index.getKeys();
823
+ }
824
+ getKeySize(key) {
825
+ return this.index.getKeySize(key);
826
+ }
827
+ get size() {
828
+ return this.index.size;
829
+ }
830
+ get isEmpty() {
831
+ return this.index.isEmpty;
832
+ }
833
+ getMetrics() {
834
+ return this.index.getMetrics();
835
+ }
836
+ destroy() {
837
+ this.index.destroy();
838
+ }
839
+ };
840
+
841
+ // src/abstract/OIMReactiveIndexArrayBased.ts
842
+ var OIMReactiveIndexArrayBased = class {
641
843
  constructor(queue, opts) {
642
844
  this.index = opts?.index ?? this.createDefaultIndex();
643
845
  this.coalescer = new OIMUpdateEventCoalescerIndex(
@@ -765,6 +967,10 @@ var OIMCollection = class {
765
967
  getManyByPks(pks) {
766
968
  return this.store.getManyByPks(pks);
767
969
  }
970
+ upsertOneByPk(pk, entity) {
971
+ this.upsertOneWithoutNotificationsByPk(pk, entity);
972
+ this.emitter.emit(0 /* UPDATE */, { pks: [pk] });
973
+ }
768
974
  upsertOne(entity) {
769
975
  const pk = this.upsertOneWithoutNotifications(entity);
770
976
  this.emitter.emit(0 /* UPDATE */, { pks: [pk] });
@@ -808,6 +1014,15 @@ var OIMCollection = class {
808
1014
  }
809
1015
  upsertOneWithoutNotifications(entity) {
810
1016
  const pk = this.selectPk(entity);
1017
+ this.upsertOneWithoutNotificationsByPk(pk, entity);
1018
+ return pk;
1019
+ }
1020
+ upsertOneWithoutNotificationsByPk(pk, entity) {
1021
+ if (!pk) {
1022
+ throw new Error(
1023
+ `[OIMCollection]: PK is required to upsert an entity ${JSON.stringify(entity)}`
1024
+ );
1025
+ }
811
1026
  const existingEntity = this.store.getOneByPk(pk);
812
1027
  if (existingEntity) {
813
1028
  this.store.setOneByPk(
@@ -817,7 +1032,6 @@ var OIMCollection = class {
817
1032
  } else {
818
1033
  this.store.setOneByPk(pk, entity);
819
1034
  }
820
- return pk;
821
1035
  }
822
1036
  };
823
1037
 
@@ -861,13 +1075,111 @@ var OIMReactiveCollection = class extends OIMCollection {
861
1075
  }
862
1076
  };
863
1077
 
864
- // src/core/OIMReactiveIndexManual.ts
865
- var OIMReactiveIndexManual = class extends OIMReactiveIndex {
1078
+ // src/core/OIMReactiveIndexManualSetBased.ts
1079
+ var OIMReactiveIndexManualSetBased = class extends OIMReactiveIndexSetBased {
1080
+ constructor(queue, opts) {
1081
+ super(queue, opts);
1082
+ }
1083
+ createDefaultIndex() {
1084
+ return new OIMIndexManualSetBased();
1085
+ }
1086
+ setPks(key, pks) {
1087
+ this.index.setPks(key, pks);
1088
+ }
1089
+ addPks(key, pks) {
1090
+ this.index.addPks(key, pks);
1091
+ }
1092
+ removePks(key, pks) {
1093
+ this.index.removePks(key, pks);
1094
+ }
1095
+ clear(key) {
1096
+ this.index.clear(key);
1097
+ }
1098
+ };
1099
+
1100
+ // src/core/OIMIndexManualArrayBased.ts
1101
+ var OIMIndexManualArrayBased = class extends OIMIndexArrayBased {
1102
+ constructor(options = {}) {
1103
+ super(options);
1104
+ }
1105
+ /**
1106
+ * Set primary keys for a specific index key, replacing any existing values.
1107
+ * Uses optional comparator to skip updates if PKs haven't actually changed.
1108
+ */
1109
+ setPks(key, pks) {
1110
+ const hasChanges = this.setPksWithComparison(key, pks);
1111
+ if (hasChanges) {
1112
+ this.emitUpdate([key]);
1113
+ }
1114
+ }
1115
+ /**
1116
+ * Add primary keys to a specific index key
1117
+ */
1118
+ addPks(key, pks) {
1119
+ if (pks.length === 0) return;
1120
+ let pksArray = this.store.getOneByKey(key);
1121
+ if (!pksArray) {
1122
+ pksArray = [];
1123
+ this.store.setOneByKey(key, pksArray);
1124
+ }
1125
+ const pksSet = new Set(pksArray);
1126
+ let hasChanges = false;
1127
+ for (const pk of pks) {
1128
+ if (!pksSet.has(pk)) {
1129
+ pksSet.add(pk);
1130
+ hasChanges = true;
1131
+ }
1132
+ }
1133
+ if (hasChanges) {
1134
+ this.store.setOneByKey(key, Array.from(pksSet));
1135
+ this.emitUpdate([key]);
1136
+ }
1137
+ }
1138
+ /**
1139
+ * Remove primary keys from a specific index key
1140
+ */
1141
+ removePks(key, pks) {
1142
+ if (pks.length === 0) return;
1143
+ const pksArray = this.store.getOneByKey(key);
1144
+ if (!pksArray) return;
1145
+ const pksSet = new Set(pks);
1146
+ const filtered = pksArray.filter((pk) => !pksSet.has(pk));
1147
+ const hasChanges = filtered.length !== pksArray.length;
1148
+ if (hasChanges) {
1149
+ if (filtered.length === 0) {
1150
+ this.store.removeOneByKey(key);
1151
+ } else {
1152
+ this.store.setOneByKey(key, filtered);
1153
+ }
1154
+ this.emitUpdate([key]);
1155
+ }
1156
+ }
1157
+ /**
1158
+ * Clear all primary keys for a specific index key, or all keys if no key specified
1159
+ */
1160
+ clear(key) {
1161
+ if (key === void 0) {
1162
+ const allKeys = this.store.getAllKeys();
1163
+ if (allKeys.length > 0) {
1164
+ this.store.clear();
1165
+ this.emitUpdate(allKeys);
1166
+ }
1167
+ } else {
1168
+ if (this.store.getOneByKey(key) !== void 0) {
1169
+ this.store.removeOneByKey(key);
1170
+ this.emitUpdate([key]);
1171
+ }
1172
+ }
1173
+ }
1174
+ };
1175
+
1176
+ // src/core/OIMReactiveIndexManualArrayBased.ts
1177
+ var OIMReactiveIndexManualArrayBased = class extends OIMReactiveIndexArrayBased {
866
1178
  constructor(queue, opts) {
867
1179
  super(queue, opts);
868
1180
  }
869
1181
  createDefaultIndex() {
870
- return new OIMIndexManual();
1182
+ return new OIMIndexManualArrayBased();
871
1183
  }
872
1184
  setPks(key, pks) {
873
1185
  this.index.setPks(key, pks);
@@ -1275,15 +1587,23 @@ var OIMEventQueueSchedulerFactory = class {
1275
1587
  OIMEventQueueSchedulerImmediate,
1276
1588
  OIMEventQueueSchedulerMicrotask,
1277
1589
  OIMEventQueueSchedulerTimeout,
1278
- OIMIndex,
1590
+ OIMIndexArrayBased,
1279
1591
  OIMIndexComparatorFactory,
1280
- OIMIndexManual,
1592
+ OIMIndexManualArrayBased,
1593
+ OIMIndexManualSetBased,
1594
+ OIMIndexSetBased,
1595
+ OIMIndexStoreArrayBased,
1596
+ OIMIndexStoreMapDrivenArrayBased,
1597
+ OIMIndexStoreMapDrivenSetBased,
1598
+ OIMIndexStoreSetBased,
1281
1599
  OIMMap2Keys,
1282
1600
  OIMPkSelectorFactory,
1283
1601
  OIMRICollection,
1284
1602
  OIMReactiveCollection,
1285
- OIMReactiveIndex,
1286
- OIMReactiveIndexManual,
1603
+ OIMReactiveIndexArrayBased,
1604
+ OIMReactiveIndexManualArrayBased,
1605
+ OIMReactiveIndexManualSetBased,
1606
+ OIMReactiveIndexSetBased,
1287
1607
  OIMUpdateEventCoalescer,
1288
1608
  OIMUpdateEventCoalescerCollection,
1289
1609
  OIMUpdateEventCoalescerIndex,