@oimdb/core 2.1.0 → 2.2.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
@@ -879,17 +879,39 @@ var OIMCollectionStoreMapDriven = class extends OIMCollectionStore {
879
879
  constructor() {
880
880
  super(...arguments);
881
881
  this.slots = /* @__PURE__ */ new Map();
882
+ // Slots reserved by indexes for PKs whose entity has not arrived yet.
883
+ // They carry `item: undefined` and live apart from `slots` so they do not
884
+ // leak into entity enumeration (getAll/getAllPks/countAll). When the entity
885
+ // is finally written, the reserved slot is promoted into `slots` — keeping
886
+ // the reference that indexes already hold, so it fills in live.
887
+ this.reservedSlots = /* @__PURE__ */ new Map();
882
888
  }
883
889
  setOneByPk(pk, entity) {
884
890
  const slot = this.slots.get(pk);
885
891
  if (slot) {
886
892
  slot.item = entity;
887
893
  return slot;
888
- } else {
889
- const nextSlot = { pk, item: entity };
890
- this.slots.set(pk, nextSlot);
891
- return nextSlot;
892
894
  }
895
+ const reserved = this.reservedSlots.get(pk);
896
+ if (reserved) {
897
+ reserved.item = entity;
898
+ this.reservedSlots.delete(pk);
899
+ this.slots.set(pk, reserved);
900
+ return reserved;
901
+ }
902
+ const nextSlot = { pk, item: entity };
903
+ this.slots.set(pk, nextSlot);
904
+ return nextSlot;
905
+ }
906
+ getOrReserveSlotByPk(pk) {
907
+ const slot = this.slots.get(pk);
908
+ if (slot) return slot;
909
+ let reserved = this.reservedSlots.get(pk);
910
+ if (!reserved) {
911
+ reserved = { pk, item: void 0 };
912
+ this.reservedSlots.set(pk, reserved);
913
+ }
914
+ return reserved;
893
915
  }
894
916
  setManyByPks(pks, entities) {
895
917
  const slots = [];
@@ -922,6 +944,11 @@ var OIMCollectionStoreMapDriven = class extends OIMCollectionStore {
922
944
  const slot = this.slots.get(pk);
923
945
  if (slot) slot.item = void 0;
924
946
  this.slots.delete(pk);
947
+ const reserved = this.reservedSlots.get(pk);
948
+ if (reserved) {
949
+ reserved.item = void 0;
950
+ this.reservedSlots.delete(pk);
951
+ }
925
952
  }
926
953
  removeManyByPks(pks) {
927
954
  for (const pk of pks) {
@@ -959,6 +986,10 @@ var OIMCollectionStoreMapDriven = class extends OIMCollectionStore {
959
986
  slot.item = void 0;
960
987
  }
961
988
  this.slots.clear();
989
+ for (const slot of this.reservedSlots.values()) {
990
+ slot.item = void 0;
991
+ }
992
+ this.reservedSlots.clear();
962
993
  }
963
994
  getAllPks() {
964
995
  return Array.from(this.slots.keys());
@@ -1000,6 +1031,9 @@ var OIMCollection = class {
1000
1031
  getSlotByPk(pk) {
1001
1032
  return this.store.getSlotByPk(pk);
1002
1033
  }
1034
+ getOrReserveSlotByPk(pk) {
1035
+ return this.store.getOrReserveSlotByPk(pk);
1036
+ }
1003
1037
  getSlotsByPks(pks) {
1004
1038
  return this.store.getSlotsByPks(pks);
1005
1039
  }
@@ -1625,7 +1659,7 @@ var OIMReactiveIndexManualArrayBased = class extends OIMReactiveIndexArrayBased
1625
1659
  // src/core/OIMReactiveCollectionIndexManualArrayBased.ts
1626
1660
  var OIMReactiveCollectionIndexManualArrayBased = class extends OIMReactiveIndexManualArrayBased {
1627
1661
  constructor(queue, opts) {
1628
- const resolveSlot = opts.collection !== void 0 ? (pk) => opts.collection.getSlotByPk(pk) : opts.resolveSlot;
1662
+ const resolveSlot = opts.collection !== void 0 ? (pk) => opts.collection.getOrReserveSlotByPk(pk) : opts.resolveSlot;
1629
1663
  super(queue, {
1630
1664
  indexOptions: opts.indexOptions
1631
1665
  });
@@ -1675,11 +1709,7 @@ var OIMReactiveCollectionIndexManualArrayBased = class extends OIMReactiveIndexM
1675
1709
  }
1676
1710
  resolveRequiredSlot(pk) {
1677
1711
  const slot = this.resolveSlot(pk);
1678
- if (!slot) {
1679
- throw new Error(
1680
- `[OIMReactiveCollectionIndexManualArrayBased]: Unable to resolve slot for PK "${String(pk)}".`
1681
- );
1682
- }
1712
+ if (!slot) return { pk, item: void 0 };
1683
1713
  return slot;
1684
1714
  }
1685
1715
  };
@@ -1747,7 +1777,7 @@ var OIMReactiveIndexManualSetBased = class extends OIMReactiveIndexSetBased {
1747
1777
  // src/core/OIMReactiveCollectionIndexManualSetBased.ts
1748
1778
  var OIMReactiveCollectionIndexManualSetBased = class extends OIMReactiveIndexManualSetBased {
1749
1779
  constructor(queue, opts) {
1750
- const resolveSlot = opts.collection !== void 0 ? (pk) => opts.collection.getSlotByPk(pk) : opts.resolveSlot;
1780
+ const resolveSlot = opts.collection !== void 0 ? (pk) => opts.collection.getOrReserveSlotByPk(pk) : opts.resolveSlot;
1751
1781
  super(queue, {
1752
1782
  indexOptions: opts.indexOptions
1753
1783
  });
@@ -1801,11 +1831,7 @@ var OIMReactiveCollectionIndexManualSetBased = class extends OIMReactiveIndexMan
1801
1831
  }
1802
1832
  resolveRequiredSlot(pk) {
1803
1833
  const slot = this.resolveSlot(pk);
1804
- if (!slot) {
1805
- throw new Error(
1806
- `[OIMReactiveCollectionIndexManualSetBased]: Unable to resolve slot for PK "${String(pk)}".`
1807
- );
1808
- }
1834
+ if (!slot) return { pk, item: void 0 };
1809
1835
  return slot;
1810
1836
  }
1811
1837
  };
package/dist/index.d.cts CHANGED
@@ -10,6 +10,13 @@ type TOIMEntitySlotResolver<TPk extends TOIMPk> = (pk: TPk) => TOIMAnyEntitySlot
10
10
  declare abstract class OIMCollectionStore<TEntity extends object, TPk extends TOIMPk> {
11
11
  abstract setOneByPk(pk: TPk, entity: TEntity): TOIMEntitySlot<TEntity, TPk>;
12
12
  abstract getSlotByPk(pk: TPk): TOIMEntitySlot<TEntity, TPk> | undefined;
13
+ /**
14
+ * Returns the canonical slot for `pk`, creating a reserved empty slot
15
+ * (`item: undefined`) if no entity exists yet. The reserved slot is a stable
16
+ * reference that fills in when the entity is later written, so indexes can
17
+ * hold it ahead of the entity's arrival without crashing.
18
+ */
19
+ abstract getOrReserveSlotByPk(pk: TPk): TOIMEntitySlot<TEntity, TPk>;
13
20
  abstract getSlotsByPks(pks: readonly TPk[]): TOIMEntitySlot<TEntity, TPk>[];
14
21
  abstract getAllSlots(): TOIMEntitySlot<TEntity, TPk>[];
15
22
  abstract removeOneByPk(pk: TPk): void;
@@ -381,6 +388,7 @@ declare class OIMCollection<TEntity extends object, TPk extends TOIMPk> {
381
388
  getOneByPk(pk: TPk): TEntity | undefined;
382
389
  getManyByPks(pks: readonly TPk[]): TEntity[];
383
390
  getSlotByPk(pk: TPk): TOIMEntitySlot<TEntity, TPk> | undefined;
391
+ getOrReserveSlotByPk(pk: TPk): TOIMEntitySlot<TEntity, TPk>;
384
392
  getSlotsByPks(pks: readonly TPk[]): TOIMEntitySlot<TEntity, TPk>[];
385
393
  getAllSlots(): TOIMEntitySlot<TEntity, TPk>[];
386
394
  upsertOneByPk(pk: TPk, entity: Partial<TEntity>): TOIMEntitySlot<TEntity, TPk>;
@@ -849,7 +857,9 @@ declare class OIMReactiveObject<TKey extends string, TValue> extends OIMObject<T
849
857
 
850
858
  declare class OIMCollectionStoreMapDriven<TEntity extends object, TPk extends TOIMPk> extends OIMCollectionStore<TEntity, TPk> {
851
859
  protected readonly slots: Map<TPk, TOIMEntitySlot<TEntity, TPk>>;
860
+ protected readonly reservedSlots: Map<TPk, TOIMEntitySlot<TEntity, TPk>>;
852
861
  setOneByPk(pk: TPk, entity: TEntity): TOIMEntitySlot<TEntity, TPk>;
862
+ getOrReserveSlotByPk(pk: TPk): TOIMEntitySlot<TEntity, TPk>;
853
863
  setManyByPks(pks: readonly TPk[], entities: TEntity[]): TOIMEntitySlot<TEntity, TPk>[];
854
864
  getSlotByPk(pk: TPk): TOIMEntitySlot<TEntity, TPk> | undefined;
855
865
  getSlotsByPks(pks: readonly TPk[]): TOIMEntitySlot<TEntity, TPk>[];
package/dist/index.d.ts CHANGED
@@ -10,6 +10,13 @@ type TOIMEntitySlotResolver<TPk extends TOIMPk> = (pk: TPk) => TOIMAnyEntitySlot
10
10
  declare abstract class OIMCollectionStore<TEntity extends object, TPk extends TOIMPk> {
11
11
  abstract setOneByPk(pk: TPk, entity: TEntity): TOIMEntitySlot<TEntity, TPk>;
12
12
  abstract getSlotByPk(pk: TPk): TOIMEntitySlot<TEntity, TPk> | undefined;
13
+ /**
14
+ * Returns the canonical slot for `pk`, creating a reserved empty slot
15
+ * (`item: undefined`) if no entity exists yet. The reserved slot is a stable
16
+ * reference that fills in when the entity is later written, so indexes can
17
+ * hold it ahead of the entity's arrival without crashing.
18
+ */
19
+ abstract getOrReserveSlotByPk(pk: TPk): TOIMEntitySlot<TEntity, TPk>;
13
20
  abstract getSlotsByPks(pks: readonly TPk[]): TOIMEntitySlot<TEntity, TPk>[];
14
21
  abstract getAllSlots(): TOIMEntitySlot<TEntity, TPk>[];
15
22
  abstract removeOneByPk(pk: TPk): void;
@@ -381,6 +388,7 @@ declare class OIMCollection<TEntity extends object, TPk extends TOIMPk> {
381
388
  getOneByPk(pk: TPk): TEntity | undefined;
382
389
  getManyByPks(pks: readonly TPk[]): TEntity[];
383
390
  getSlotByPk(pk: TPk): TOIMEntitySlot<TEntity, TPk> | undefined;
391
+ getOrReserveSlotByPk(pk: TPk): TOIMEntitySlot<TEntity, TPk>;
384
392
  getSlotsByPks(pks: readonly TPk[]): TOIMEntitySlot<TEntity, TPk>[];
385
393
  getAllSlots(): TOIMEntitySlot<TEntity, TPk>[];
386
394
  upsertOneByPk(pk: TPk, entity: Partial<TEntity>): TOIMEntitySlot<TEntity, TPk>;
@@ -849,7 +857,9 @@ declare class OIMReactiveObject<TKey extends string, TValue> extends OIMObject<T
849
857
 
850
858
  declare class OIMCollectionStoreMapDriven<TEntity extends object, TPk extends TOIMPk> extends OIMCollectionStore<TEntity, TPk> {
851
859
  protected readonly slots: Map<TPk, TOIMEntitySlot<TEntity, TPk>>;
860
+ protected readonly reservedSlots: Map<TPk, TOIMEntitySlot<TEntity, TPk>>;
852
861
  setOneByPk(pk: TPk, entity: TEntity): TOIMEntitySlot<TEntity, TPk>;
862
+ getOrReserveSlotByPk(pk: TPk): TOIMEntitySlot<TEntity, TPk>;
853
863
  setManyByPks(pks: readonly TPk[], entities: TEntity[]): TOIMEntitySlot<TEntity, TPk>[];
854
864
  getSlotByPk(pk: TPk): TOIMEntitySlot<TEntity, TPk> | undefined;
855
865
  getSlotsByPks(pks: readonly TPk[]): TOIMEntitySlot<TEntity, TPk>[];
package/dist/index.js CHANGED
@@ -779,17 +779,39 @@ var OIMCollectionStoreMapDriven = class extends OIMCollectionStore {
779
779
  constructor() {
780
780
  super(...arguments);
781
781
  this.slots = /* @__PURE__ */ new Map();
782
+ // Slots reserved by indexes for PKs whose entity has not arrived yet.
783
+ // They carry `item: undefined` and live apart from `slots` so they do not
784
+ // leak into entity enumeration (getAll/getAllPks/countAll). When the entity
785
+ // is finally written, the reserved slot is promoted into `slots` — keeping
786
+ // the reference that indexes already hold, so it fills in live.
787
+ this.reservedSlots = /* @__PURE__ */ new Map();
782
788
  }
783
789
  setOneByPk(pk, entity) {
784
790
  const slot = this.slots.get(pk);
785
791
  if (slot) {
786
792
  slot.item = entity;
787
793
  return slot;
788
- } else {
789
- const nextSlot = { pk, item: entity };
790
- this.slots.set(pk, nextSlot);
791
- return nextSlot;
792
794
  }
795
+ const reserved = this.reservedSlots.get(pk);
796
+ if (reserved) {
797
+ reserved.item = entity;
798
+ this.reservedSlots.delete(pk);
799
+ this.slots.set(pk, reserved);
800
+ return reserved;
801
+ }
802
+ const nextSlot = { pk, item: entity };
803
+ this.slots.set(pk, nextSlot);
804
+ return nextSlot;
805
+ }
806
+ getOrReserveSlotByPk(pk) {
807
+ const slot = this.slots.get(pk);
808
+ if (slot) return slot;
809
+ let reserved = this.reservedSlots.get(pk);
810
+ if (!reserved) {
811
+ reserved = { pk, item: void 0 };
812
+ this.reservedSlots.set(pk, reserved);
813
+ }
814
+ return reserved;
793
815
  }
794
816
  setManyByPks(pks, entities) {
795
817
  const slots = [];
@@ -822,6 +844,11 @@ var OIMCollectionStoreMapDriven = class extends OIMCollectionStore {
822
844
  const slot = this.slots.get(pk);
823
845
  if (slot) slot.item = void 0;
824
846
  this.slots.delete(pk);
847
+ const reserved = this.reservedSlots.get(pk);
848
+ if (reserved) {
849
+ reserved.item = void 0;
850
+ this.reservedSlots.delete(pk);
851
+ }
825
852
  }
826
853
  removeManyByPks(pks) {
827
854
  for (const pk of pks) {
@@ -859,6 +886,10 @@ var OIMCollectionStoreMapDriven = class extends OIMCollectionStore {
859
886
  slot.item = void 0;
860
887
  }
861
888
  this.slots.clear();
889
+ for (const slot of this.reservedSlots.values()) {
890
+ slot.item = void 0;
891
+ }
892
+ this.reservedSlots.clear();
862
893
  }
863
894
  getAllPks() {
864
895
  return Array.from(this.slots.keys());
@@ -900,6 +931,9 @@ var OIMCollection = class {
900
931
  getSlotByPk(pk) {
901
932
  return this.store.getSlotByPk(pk);
902
933
  }
934
+ getOrReserveSlotByPk(pk) {
935
+ return this.store.getOrReserveSlotByPk(pk);
936
+ }
903
937
  getSlotsByPks(pks) {
904
938
  return this.store.getSlotsByPks(pks);
905
939
  }
@@ -1525,7 +1559,7 @@ var OIMReactiveIndexManualArrayBased = class extends OIMReactiveIndexArrayBased
1525
1559
  // src/core/OIMReactiveCollectionIndexManualArrayBased.ts
1526
1560
  var OIMReactiveCollectionIndexManualArrayBased = class extends OIMReactiveIndexManualArrayBased {
1527
1561
  constructor(queue, opts) {
1528
- const resolveSlot = opts.collection !== void 0 ? (pk) => opts.collection.getSlotByPk(pk) : opts.resolveSlot;
1562
+ const resolveSlot = opts.collection !== void 0 ? (pk) => opts.collection.getOrReserveSlotByPk(pk) : opts.resolveSlot;
1529
1563
  super(queue, {
1530
1564
  indexOptions: opts.indexOptions
1531
1565
  });
@@ -1575,11 +1609,7 @@ var OIMReactiveCollectionIndexManualArrayBased = class extends OIMReactiveIndexM
1575
1609
  }
1576
1610
  resolveRequiredSlot(pk) {
1577
1611
  const slot = this.resolveSlot(pk);
1578
- if (!slot) {
1579
- throw new Error(
1580
- `[OIMReactiveCollectionIndexManualArrayBased]: Unable to resolve slot for PK "${String(pk)}".`
1581
- );
1582
- }
1612
+ if (!slot) return { pk, item: void 0 };
1583
1613
  return slot;
1584
1614
  }
1585
1615
  };
@@ -1647,7 +1677,7 @@ var OIMReactiveIndexManualSetBased = class extends OIMReactiveIndexSetBased {
1647
1677
  // src/core/OIMReactiveCollectionIndexManualSetBased.ts
1648
1678
  var OIMReactiveCollectionIndexManualSetBased = class extends OIMReactiveIndexManualSetBased {
1649
1679
  constructor(queue, opts) {
1650
- const resolveSlot = opts.collection !== void 0 ? (pk) => opts.collection.getSlotByPk(pk) : opts.resolveSlot;
1680
+ const resolveSlot = opts.collection !== void 0 ? (pk) => opts.collection.getOrReserveSlotByPk(pk) : opts.resolveSlot;
1651
1681
  super(queue, {
1652
1682
  indexOptions: opts.indexOptions
1653
1683
  });
@@ -1701,11 +1731,7 @@ var OIMReactiveCollectionIndexManualSetBased = class extends OIMReactiveIndexMan
1701
1731
  }
1702
1732
  resolveRequiredSlot(pk) {
1703
1733
  const slot = this.resolveSlot(pk);
1704
- if (!slot) {
1705
- throw new Error(
1706
- `[OIMReactiveCollectionIndexManualSetBased]: Unable to resolve slot for PK "${String(pk)}".`
1707
- );
1708
- }
1734
+ if (!slot) return { pk, item: void 0 };
1709
1735
  return slot;
1710
1736
  }
1711
1737
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oimdb/core",
3
- "version": "2.1.0",
3
+ "version": "2.2.0",
4
4
  "description": "Core in-memory data library with type-safe indices, reactive subscriptions, and event processing",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",