@oimdb/core 1.4.1 → 2.0.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.js CHANGED
@@ -2,14 +2,6 @@
2
2
  var OIMCollectionStore = class {
3
3
  };
4
4
 
5
- // src/abstract/OIMIndexStoreSetBased.ts
6
- var OIMIndexStoreSetBased = class {
7
- };
8
-
9
- // src/abstract/OIMIndexStoreArrayBased.ts
10
- var OIMIndexStoreArrayBased = class {
11
- };
12
-
13
5
  // src/core/OIMEventEmitter.ts
14
6
  var OIMEventEmitter = class {
15
7
  constructor() {
@@ -115,7 +107,94 @@ var OIMEventEmitter = class {
115
107
  }
116
108
  };
117
109
 
118
- // src/enum/EOIMEventQueueSchedulerEventType.ts
110
+ // src/enums/EOIMIndexEventType.ts
111
+ var EOIMIndexEventType = /* @__PURE__ */ ((EOIMIndexEventType2) => {
112
+ EOIMIndexEventType2[EOIMIndexEventType2["UPDATE"] = 0] = "UPDATE";
113
+ return EOIMIndexEventType2;
114
+ })(EOIMIndexEventType || {});
115
+
116
+ // src/abstract/OIMIndex.ts
117
+ var OIMIndex = class {
118
+ constructor(store, comparePks) {
119
+ this.emitter = new OIMEventEmitter();
120
+ this.store = store;
121
+ this.comparePks = comparePks;
122
+ }
123
+ hasKey(key) {
124
+ return this.store.getOneByKey(key) !== void 0;
125
+ }
126
+ getKeys() {
127
+ return this.store.getAllKeys();
128
+ }
129
+ getKeySize(key) {
130
+ const bucket = this.store.getOneByKey(key);
131
+ return bucket ? this.getBucketSize(bucket) : 0;
132
+ }
133
+ get size() {
134
+ return this.store.countAll();
135
+ }
136
+ get isEmpty() {
137
+ return this.store.countAll() === 0;
138
+ }
139
+ getEntitiesByKey(key) {
140
+ const bucket = this.store.getOneByKey(key);
141
+ return bucket ? this.slotsToEntities(bucket) : [];
142
+ }
143
+ getEntitiesByKeys(keys) {
144
+ const result = /* @__PURE__ */ new Map();
145
+ const bucketsByKey = this.store.getManyByKeys(keys);
146
+ for (const [key, bucket] of bucketsByKey) {
147
+ result.set(key, this.slotsToEntities(bucket));
148
+ }
149
+ return result;
150
+ }
151
+ getMetrics() {
152
+ let totalPks = 0;
153
+ let maxBucketSize = 0;
154
+ let minBucketSize = Infinity;
155
+ const all = this.store.getAll();
156
+ for (const bucket of all.values()) {
157
+ const size = this.getBucketSize(bucket);
158
+ totalPks += size;
159
+ maxBucketSize = Math.max(maxBucketSize, size);
160
+ minBucketSize = Math.min(minBucketSize, size);
161
+ }
162
+ return {
163
+ totalKeys: all.size,
164
+ totalPks,
165
+ averagePksPerKey: all.size > 0 ? totalPks / all.size : 0,
166
+ maxBucketSize: maxBucketSize === -Infinity ? 0 : maxBucketSize,
167
+ minBucketSize: minBucketSize === Infinity ? 0 : minBucketSize
168
+ };
169
+ }
170
+ destroy() {
171
+ this.emitter.offAll();
172
+ this.store.clear();
173
+ }
174
+ emitUpdate(keys) {
175
+ this.emitter.emit(0 /* UPDATE */, { keys });
176
+ }
177
+ emitUpdateOne(key) {
178
+ this.emitUpdate([key]);
179
+ }
180
+ slotsToEntities(slots) {
181
+ const entities = [];
182
+ for (const slot of slots) {
183
+ if (slot.item !== void 0) entities.push(slot.item);
184
+ }
185
+ return entities;
186
+ }
187
+ };
188
+
189
+ // src/abstract/OIMIndexStoreSetBased.ts
190
+ var OIMIndexStoreSetBased = class {
191
+ };
192
+
193
+ // src/abstract/OIMIndexStoreArrayBased.ts
194
+ var OIMIndexStoreArrayBased = class {
195
+ };
196
+
197
+ // src/enums/EOIMEventQueueSchedulerEventType.ts
119
198
  var EOIMEventQueueSchedulerEventType = /* @__PURE__ */ ((EOIMEventQueueSchedulerEventType2) => {
120
199
  EOIMEventQueueSchedulerEventType2[EOIMEventQueueSchedulerEventType2["FLUSH"] = 0] = "FLUSH";
121
200
  return EOIMEventQueueSchedulerEventType2;
@@ -140,332 +219,202 @@ var OIMEventQueueScheduler = class {
140
219
  }
141
220
  };
142
221
 
143
- // src/enum/EOIMIndexEventType.ts
144
- var EOIMIndexEventType = /* @__PURE__ */ ((EOIMIndexEventType2) => {
145
- EOIMIndexEventType2[EOIMIndexEventType2["UPDATE"] = 0] = "UPDATE";
146
- return EOIMIndexEventType2;
147
- })(EOIMIndexEventType || {});
148
-
149
222
  // src/core/OIMIndexStoreMapDrivenSetBased.ts
150
223
  var OIMIndexStoreMapDrivenSetBased = class extends OIMIndexStoreSetBased {
151
224
  constructor() {
152
225
  super(...arguments);
153
- this.pks = /* @__PURE__ */ new Map();
226
+ this.slots = /* @__PURE__ */ new Map();
154
227
  }
155
- setOneByKey(key, pks) {
156
- this.pks.set(key, pks);
228
+ setOneByKey(key, slots) {
229
+ this.slots.set(key, slots);
157
230
  }
158
231
  removeOneByKey(key) {
159
- this.pks.delete(key);
232
+ this.slots.delete(key);
160
233
  }
161
234
  removeManyByKeys(keys) {
162
235
  for (const key of keys) {
163
- this.pks.delete(key);
236
+ this.slots.delete(key);
164
237
  }
165
238
  }
166
239
  getOneByKey(key) {
167
- return this.pks.get(key);
240
+ return this.slots.get(key);
168
241
  }
169
242
  getManyByKeys(keys) {
170
243
  const result = /* @__PURE__ */ new Map();
171
244
  for (const key of keys) {
172
- const pks = this.getOneByKey(key);
173
- if (pks !== void 0) {
174
- result.set(key, pks);
245
+ const slots = this.getOneByKey(key);
246
+ if (slots !== void 0) {
247
+ result.set(key, slots);
175
248
  }
176
249
  }
177
250
  return result;
178
251
  }
179
252
  getAllKeys() {
180
- return Array.from(this.pks.keys());
253
+ return Array.from(this.slots.keys());
181
254
  }
182
255
  getAll() {
183
- return new Map(this.pks);
256
+ return new Map(this.slots);
184
257
  }
185
258
  countAll() {
186
- return this.pks.size;
259
+ return this.slots.size;
187
260
  }
188
261
  clear() {
189
- this.pks.clear();
262
+ this.slots.clear();
190
263
  }
191
264
  };
192
265
 
193
266
  // src/abstract/OIMIndexSetBased.ts
194
- var OIMIndexSetBased = class {
267
+ var OIMIndexSetBased = class extends OIMIndex {
195
268
  constructor(options = {}) {
196
- this.emitter = new OIMEventEmitter();
197
- this.comparePks = options.comparePks;
198
- this.store = options.store ?? new OIMIndexStoreMapDrivenSetBased();
199
- }
200
- /**
201
- * Get primary keys for multiple index keys
202
- */
203
- getPksByKeys(keys) {
204
- return this.store.getManyByKeys(keys);
269
+ super(
270
+ options.store ?? new OIMIndexStoreMapDrivenSetBased(),
271
+ options.comparePks
272
+ );
205
273
  }
206
- /**
207
- * @deprecated Use getPksByKey instead
208
- * Get primary keys for a specific index key
209
- */
210
- getPks(key) {
211
- return this.getPksByKey(key);
274
+ getBucketSize(bucket) {
275
+ return bucket.size;
212
276
  }
213
- /**
214
- * Get primary keys for a specific index key
215
- */
216
277
  getPksByKey(key) {
217
- const pksSet = this.store.getOneByKey(key);
218
- return pksSet ? pksSet : /* @__PURE__ */ new Set();
219
- }
220
- /**
221
- * Check if an index key exists
222
- */
223
- hasKey(key) {
224
- return this.store.getOneByKey(key) !== void 0;
225
- }
226
- /**
227
- * Get all index keys
228
- */
229
- getKeys() {
230
- return this.store.getAllKeys();
231
- }
232
- /**
233
- * Get the number of primary keys for a specific index key
234
- */
235
- getKeySize(key) {
236
- const pksSet = this.store.getOneByKey(key);
237
- return pksSet ? pksSet.size : 0;
278
+ const slotsSet = this.store.getOneByKey(key);
279
+ return slotsSet ? this.slotsToPks(slotsSet) : /* @__PURE__ */ new Set();
238
280
  }
239
- /**
240
- * Get total number of index keys
241
- */
242
- get size() {
243
- return this.store.countAll();
281
+ getPksByKeys(keys) {
282
+ const result = /* @__PURE__ */ new Map();
283
+ const slotsByKey = this.store.getManyByKeys(keys);
284
+ for (const [key, slots] of slotsByKey) {
285
+ result.set(key, this.slotsToPks(slots));
286
+ }
287
+ return result;
244
288
  }
245
- /**
246
- * Check if the index is empty
247
- */
248
- get isEmpty() {
249
- return this.store.countAll() === 0;
289
+ getSlotsByKey(key) {
290
+ return this.store.getOneByKey(key) ?? /* @__PURE__ */ new Set();
250
291
  }
251
- /**
252
- * Get performance metrics for monitoring and debugging
253
- */
254
- getMetrics() {
255
- let totalPks = 0;
256
- let maxBucketSize = 0;
257
- let minBucketSize = Infinity;
258
- const allPks = this.store.getAll();
259
- for (const pksSet of allPks.values()) {
260
- totalPks += pksSet.size;
261
- maxBucketSize = Math.max(maxBucketSize, pksSet.size);
262
- minBucketSize = Math.min(minBucketSize, pksSet.size);
263
- }
264
- return {
265
- totalKeys: allPks.size,
266
- totalPks,
267
- averagePksPerKey: allPks.size > 0 ? totalPks / allPks.size : 0,
268
- maxBucketSize: maxBucketSize === -Infinity ? 0 : maxBucketSize,
269
- minBucketSize: minBucketSize === Infinity ? 0 : minBucketSize
270
- };
292
+ getSlotsByKeys(keys) {
293
+ return this.store.getManyByKeys(keys);
271
294
  }
272
- /**
273
- * Clean up event listeners when index is no longer needed
274
- */
275
- destroy() {
276
- this.emitter.offAll();
277
- this.store.clear();
295
+ slotsToPks(slots) {
296
+ const pks = /* @__PURE__ */ new Set();
297
+ for (const slot of slots) pks.add(slot.pk);
298
+ return pks;
278
299
  }
279
- /**
280
- * Set primary keys for a specific index key with optional comparison.
281
- * If comparator is provided and returns true (no changes), skip the update.
282
- */
283
- setPksWithComparison(key, newPks) {
300
+ setSlotsWithComparison(key, newSlots) {
284
301
  if (this.comparePks) {
285
- const existingPksSet = this.store.getOneByKey(key);
286
- if (existingPksSet && existingPksSet.size === newPks.size) {
287
- const existingPksArray = existingPksSet.size > 0 ? [...existingPksSet] : [];
288
- const newPksArray = newPks.size > 0 ? [...newPks] : [];
289
- if (this.comparePks(existingPksArray, newPksArray)) {
302
+ const existingSlotsSet = this.store.getOneByKey(key);
303
+ if (existingSlotsSet && existingSlotsSet.size === newSlots.size) {
304
+ if (this.comparePks(
305
+ Array.from(this.slotsToPks(existingSlotsSet)),
306
+ Array.from(this.slotsToPks(newSlots))
307
+ )) {
290
308
  return false;
291
309
  }
292
- } else if (!existingPksSet && newPks.size === 0) {
310
+ } else if (!existingSlotsSet && newSlots.size === 0) {
293
311
  return false;
294
312
  }
295
313
  }
296
- this.store.setOneByKey(key, newPks);
314
+ this.store.setOneByKey(key, newSlots);
297
315
  return true;
298
316
  }
299
- /**
300
- * Emit update event with changed keys
301
- */
302
- emitUpdate(keys) {
303
- this.emitter.emit(0 /* UPDATE */, { keys });
304
- }
305
- /**
306
- * Emit update event for a single key (fast-path override point).
307
- */
308
- emitUpdateOne(key) {
309
- this.emitUpdate([key]);
310
- }
311
317
  };
312
318
 
313
319
  // src/core/OIMIndexStoreMapDrivenArrayBased.ts
314
320
  var OIMIndexStoreMapDrivenArrayBased = class extends OIMIndexStoreArrayBased {
315
321
  constructor() {
316
322
  super(...arguments);
317
- this.pks = /* @__PURE__ */ new Map();
323
+ this.slots = /* @__PURE__ */ new Map();
318
324
  }
319
- setOneByKey(key, pks) {
320
- this.pks.set(key, pks);
325
+ setOneByKey(key, slots) {
326
+ this.slots.set(key, slots);
321
327
  }
322
328
  removeOneByKey(key) {
323
- this.pks.delete(key);
329
+ this.slots.delete(key);
324
330
  }
325
331
  removeManyByKeys(keys) {
326
332
  for (const key of keys) {
327
- this.pks.delete(key);
333
+ this.slots.delete(key);
328
334
  }
329
335
  }
330
336
  getOneByKey(key) {
331
- return this.pks.get(key);
337
+ return this.slots.get(key);
332
338
  }
333
339
  getManyByKeys(keys) {
334
340
  const result = /* @__PURE__ */ new Map();
335
341
  for (const key of keys) {
336
- const pks = this.getOneByKey(key);
337
- if (pks !== void 0) {
338
- result.set(key, pks);
342
+ const slots = this.getOneByKey(key);
343
+ if (slots !== void 0) {
344
+ result.set(key, slots);
339
345
  }
340
346
  }
341
347
  return result;
342
348
  }
343
349
  getAllKeys() {
344
- return Array.from(this.pks.keys());
350
+ return Array.from(this.slots.keys());
345
351
  }
346
352
  getAll() {
347
- return new Map(this.pks);
353
+ return new Map(this.slots);
348
354
  }
349
355
  countAll() {
350
- return this.pks.size;
356
+ return this.slots.size;
351
357
  }
352
358
  clear() {
353
- this.pks.clear();
359
+ this.slots.clear();
354
360
  }
355
361
  };
356
362
 
357
363
  // src/abstract/OIMIndexArrayBased.ts
358
- var OIMIndexArrayBased = class {
364
+ var OIMIndexArrayBased = class extends OIMIndex {
359
365
  constructor(options = {}) {
360
- this.emitter = new OIMEventEmitter();
361
- this.comparePks = options.comparePks;
362
- this.store = options.store ?? new OIMIndexStoreMapDrivenArrayBased();
366
+ super(
367
+ options.store ?? new OIMIndexStoreMapDrivenArrayBased(),
368
+ options.comparePks
369
+ );
363
370
  }
364
- /**
365
- * Get primary keys for multiple index keys
366
- */
367
- getPksByKeys(keys) {
368
- return this.store.getManyByKeys(keys);
371
+ getBucketSize(bucket) {
372
+ return bucket.length;
369
373
  }
370
- /**
371
- * Get primary keys for a specific index key
372
- */
373
374
  getPksByKey(key) {
374
- const pksArray = this.store.getOneByKey(key);
375
- return pksArray ? pksArray : [];
376
- }
377
- /**
378
- * Check if an index key exists
379
- */
380
- hasKey(key) {
381
- return this.store.getOneByKey(key) !== void 0;
382
- }
383
- /**
384
- * Get all index keys
385
- */
386
- getKeys() {
387
- return this.store.getAllKeys();
388
- }
389
- /**
390
- * Get the number of primary keys for a specific index key
391
- */
392
- getKeySize(key) {
393
- const pksArray = this.store.getOneByKey(key);
394
- return pksArray ? pksArray.length : 0;
375
+ const slotsArray = this.store.getOneByKey(key);
376
+ return slotsArray ? this.slotsToPks(slotsArray) : [];
395
377
  }
396
- /**
397
- * Get total number of index keys
398
- */
399
- get size() {
400
- return this.store.countAll();
378
+ getPksByKeys(keys) {
379
+ const result = /* @__PURE__ */ new Map();
380
+ const slotsByKey = this.store.getManyByKeys(keys);
381
+ for (const [key, slots] of slotsByKey) {
382
+ result.set(key, this.slotsToPks(slots));
383
+ }
384
+ return result;
401
385
  }
402
- /**
403
- * Check if the index is empty
404
- */
405
- get isEmpty() {
406
- return this.store.countAll() === 0;
386
+ getSlotsByKey(key) {
387
+ return this.store.getOneByKey(key) ?? [];
407
388
  }
408
- /**
409
- * Get performance metrics for monitoring and debugging
410
- */
411
- getMetrics() {
412
- let totalPks = 0;
413
- let maxBucketSize = 0;
414
- let minBucketSize = Infinity;
415
- const allPks = this.store.getAll();
416
- for (const pksArray of allPks.values()) {
417
- totalPks += pksArray.length;
418
- maxBucketSize = Math.max(maxBucketSize, pksArray.length);
419
- minBucketSize = Math.min(minBucketSize, pksArray.length);
420
- }
421
- return {
422
- totalKeys: allPks.size,
423
- totalPks,
424
- averagePksPerKey: allPks.size > 0 ? totalPks / allPks.size : 0,
425
- maxBucketSize: maxBucketSize === -Infinity ? 0 : maxBucketSize,
426
- minBucketSize: minBucketSize === Infinity ? 0 : minBucketSize
427
- };
389
+ getSlotsByKeys(keys) {
390
+ return this.store.getManyByKeys(keys);
428
391
  }
429
- /**
430
- * Clean up event listeners when index is no longer needed
431
- */
432
- destroy() {
433
- this.emitter.offAll();
434
- this.store.clear();
392
+ slotsToPks(slots) {
393
+ const pks = [];
394
+ pks.length = slots.length;
395
+ for (let i = 0; i < slots.length; i++) pks[i] = slots[i].pk;
396
+ return pks;
435
397
  }
436
- /**
437
- * Set primary keys for a specific index key with optional comparison.
438
- * If comparator is provided and returns true (no changes), skip the update.
439
- */
440
- setPksWithComparison(key, newPks) {
398
+ setSlotsWithComparison(key, newSlots) {
441
399
  if (this.comparePks) {
442
- const existingPksArray = this.store.getOneByKey(key);
443
- if (existingPksArray && existingPksArray.length === newPks.length) {
444
- if (this.comparePks(existingPksArray, newPks)) {
400
+ const existingSlotsArray = this.store.getOneByKey(key);
401
+ if (existingSlotsArray && existingSlotsArray.length === newSlots.length) {
402
+ if (this.comparePks(
403
+ this.slotsToPks(existingSlotsArray),
404
+ this.slotsToPks(newSlots)
405
+ )) {
445
406
  return false;
446
407
  }
447
- } else if (!existingPksArray && newPks.length === 0) {
408
+ } else if (!existingSlotsArray && newSlots.length === 0) {
448
409
  return false;
449
410
  }
450
411
  }
451
- this.store.setOneByKey(key, newPks);
412
+ this.store.setOneByKey(key, newSlots);
452
413
  return true;
453
414
  }
454
- /**
455
- * Emit update event with changed keys
456
- */
457
- emitUpdate(keys) {
458
- this.emitter.emit(0 /* UPDATE */, { keys });
459
- }
460
- /**
461
- * Emit update event for a single key (fast-path override point).
462
- */
463
- emitUpdateOne(key) {
464
- this.emitUpdate([key]);
465
- }
466
415
  };
467
416
 
468
- // src/enum/EOIMEventQueueEventType.ts
417
+ // src/enums/EOIMEventQueueEventType.ts
469
418
  var EOIMEventQueueEventType = /* @__PURE__ */ ((EOIMEventQueueEventType2) => {
470
419
  EOIMEventQueueEventType2[EOIMEventQueueEventType2["AFTER_FLUSH"] = 0] = "AFTER_FLUSH";
471
420
  EOIMEventQueueEventType2[EOIMEventQueueEventType2["BEFORE_FLUSH"] = 1] = "BEFORE_FLUSH";
@@ -719,17 +668,17 @@ var OIMUpdateEventEmitter = class {
719
668
  }
720
669
  };
721
670
 
722
- // src/abstract/OIMReactiveIndexSetBased.ts
723
- var OIMReactiveIndexSetBased = class {
671
+ // src/abstract/OIMReactiveIndex.ts
672
+ var OIMReactiveIndex = class {
724
673
  constructor(queue, createIndex) {
725
674
  this.updateEmitter = new OIMUpdateEventEmitter(queue);
726
675
  this.index = createIndex(this.updateEmitter);
727
676
  }
728
- getPksByKey(key) {
729
- return this.index.getPksByKey(key);
677
+ getEntitiesByKey(key) {
678
+ return this.index.getEntitiesByKey(key);
730
679
  }
731
- getPksByKeys(keys) {
732
- return this.index.getPksByKeys(keys);
680
+ getEntitiesByKeys(keys) {
681
+ return this.index.getEntitiesByKeys(keys);
733
682
  }
734
683
  hasKey(key) {
735
684
  return this.index.hasKey(key);
@@ -776,11 +725,10 @@ var OIMReactiveIndexSetBased = class {
776
725
  }
777
726
  };
778
727
 
779
- // src/abstract/OIMReactiveIndexArrayBased.ts
780
- var OIMReactiveIndexArrayBased = class {
728
+ // src/abstract/OIMReactiveIndexSetBased.ts
729
+ var OIMReactiveIndexSetBased = class extends OIMReactiveIndex {
781
730
  constructor(queue, createIndex) {
782
- this.updateEmitter = new OIMUpdateEventEmitter(queue);
783
- this.index = createIndex(this.updateEmitter);
731
+ super(queue, createIndex);
784
732
  }
785
733
  getPksByKey(key) {
786
734
  return this.index.getPksByKey(key);
@@ -788,59 +736,41 @@ var OIMReactiveIndexArrayBased = class {
788
736
  getPksByKeys(keys) {
789
737
  return this.index.getPksByKeys(keys);
790
738
  }
791
- hasKey(key) {
792
- return this.index.hasKey(key);
793
- }
794
- getKeys() {
795
- return this.index.getKeys();
796
- }
797
- getKeySize(key) {
798
- return this.index.getKeySize(key);
739
+ getSlotsByKey(key) {
740
+ return this.index.getSlotsByKey(key);
799
741
  }
800
- get size() {
801
- return this.index.size;
742
+ getSlotsByKeys(keys) {
743
+ return this.index.getSlotsByKeys(keys);
802
744
  }
803
- get isEmpty() {
804
- return this.index.isEmpty;
745
+ };
746
+
747
+ // src/abstract/OIMReactiveIndexArrayBased.ts
748
+ var OIMReactiveIndexArrayBased = class extends OIMReactiveIndex {
749
+ constructor(queue, createIndex) {
750
+ super(queue, createIndex);
805
751
  }
806
- getMetrics() {
807
- return this.index.getMetrics();
752
+ getPksByKey(key) {
753
+ return this.index.getPksByKey(key);
808
754
  }
809
- subscribeOnKey(key, handler) {
810
- return this.updateEmitter.subscribeOnKey(key, handler);
755
+ getPksByKeys(keys) {
756
+ return this.index.getPksByKeys(keys);
811
757
  }
812
- subscribeOnKeys(keys, handler) {
813
- return this.updateEmitter.subscribeOnKeys(keys, handler);
758
+ getSlotsByKey(key) {
759
+ return this.index.getSlotsByKey(key);
814
760
  }
815
- unsubscribeFromKey(key, handler) {
816
- this.updateEmitter.unsubscribeFromKey(key, handler);
761
+ getSlotsByKeys(keys) {
762
+ return this.index.getSlotsByKeys(keys);
817
763
  }
818
- unsubscribeFromKeys(keys, handler) {
819
- this.updateEmitter.unsubscribeFromKeys(keys, handler);
820
- }
821
- hasSubscriptions() {
822
- return this.updateEmitter.hasSubscriptions();
823
- }
824
- getHandlerCount(key) {
825
- return this.updateEmitter.getHandlerCount(key);
826
- }
827
- getSubscriptionMetrics() {
828
- return this.updateEmitter.getMetrics();
829
- }
830
- destroy() {
831
- this.updateEmitter.destroy();
832
- this.index.destroy();
833
- }
834
- };
835
-
836
- // src/abstract/OIMObjectStore.ts
837
- var OIMObjectStore = class {
838
- };
839
-
840
- // src/core/OIMPkSelectorFactory.ts
841
- var OIMPkSelectorFactory = class {
842
- createIdSelector() {
843
- return (entity) => entity.id;
764
+ };
765
+
766
+ // src/abstract/OIMObjectStore.ts
767
+ var OIMObjectStore = class {
768
+ };
769
+
770
+ // src/core/OIMPkSelectorFactory.ts
771
+ var OIMPkSelectorFactory = class {
772
+ createIdSelector() {
773
+ return (entity) => entity.id;
844
774
  }
845
775
  };
846
776
 
@@ -848,26 +778,58 @@ var OIMPkSelectorFactory = class {
848
778
  var OIMCollectionStoreMapDriven = class extends OIMCollectionStore {
849
779
  constructor() {
850
780
  super(...arguments);
851
- this.entities = /* @__PURE__ */ new Map();
781
+ this.slots = /* @__PURE__ */ new Map();
852
782
  }
853
783
  setOneByPk(pk, entity) {
854
- this.entities.set(pk, entity);
784
+ const slot = this.slots.get(pk);
785
+ if (slot) {
786
+ slot.item = entity;
787
+ return slot;
788
+ } else {
789
+ const nextSlot = { pk, item: entity };
790
+ this.slots.set(pk, nextSlot);
791
+ return nextSlot;
792
+ }
855
793
  }
856
794
  setManyByPks(pks, entities) {
795
+ const slots = [];
796
+ slots.length = pks.length;
797
+ for (let i = 0; i < pks.length; i++) {
798
+ slots[i] = this.setOneByPk(pks[i], entities[i]);
799
+ }
800
+ return slots;
801
+ }
802
+ getSlotByPk(pk) {
803
+ return this.slots.get(pk);
804
+ }
805
+ getSlotsByPks(pks) {
806
+ const result = [];
807
+ result.length = pks.length;
808
+ let writeIndex = 0;
857
809
  for (let i = 0; i < pks.length; i++) {
858
- this.entities.set(pks[i], entities[i]);
810
+ const slot = this.slots.get(pks[i]);
811
+ if (slot !== void 0) {
812
+ result[writeIndex++] = slot;
813
+ }
859
814
  }
815
+ result.length = writeIndex;
816
+ return result;
817
+ }
818
+ getAllSlots() {
819
+ return Array.from(this.slots.values());
860
820
  }
861
821
  removeOneByPk(pk) {
862
- this.entities.delete(pk);
822
+ const slot = this.slots.get(pk);
823
+ if (slot) slot.item = void 0;
824
+ this.slots.delete(pk);
863
825
  }
864
826
  removeManyByPks(pks) {
865
827
  for (const pk of pks) {
866
- this.entities.delete(pk);
828
+ this.removeOneByPk(pk);
867
829
  }
868
830
  }
869
831
  getOneByPk(pk) {
870
- return this.entities.get(pk);
832
+ return this.slots.get(pk)?.item;
871
833
  }
872
834
  getManyByPks(pks) {
873
835
  const result = [];
@@ -883,16 +845,23 @@ var OIMCollectionStoreMapDriven = class extends OIMCollectionStore {
883
845
  return result;
884
846
  }
885
847
  getAll() {
886
- return Array.from(this.entities.values());
848
+ const result = [];
849
+ for (const slot of this.slots.values()) {
850
+ if (slot.item !== void 0) result.push(slot.item);
851
+ }
852
+ return result;
887
853
  }
888
854
  countAll() {
889
- return this.entities.size;
855
+ return this.slots.size;
890
856
  }
891
857
  clear() {
892
- this.entities.clear();
858
+ for (const slot of this.slots.values()) {
859
+ slot.item = void 0;
860
+ }
861
+ this.slots.clear();
893
862
  }
894
863
  getAllPks() {
895
- return Array.from(this.entities.keys());
864
+ return Array.from(this.slots.keys());
896
865
  }
897
866
  destroy() {
898
867
  this.clear();
@@ -908,7 +877,7 @@ var OIMEntityUpdaterFactory = class {
908
877
  }
909
878
  };
910
879
 
911
- // src/enum/EOIMCollectionEventType.ts
880
+ // src/enums/EOIMCollectionEventType.ts
912
881
  var EOIMCollectionEventType = /* @__PURE__ */ ((EOIMCollectionEventType2) => {
913
882
  EOIMCollectionEventType2[EOIMCollectionEventType2["UPDATE"] = 0] = "UPDATE";
914
883
  return EOIMCollectionEventType2;
@@ -928,19 +897,33 @@ var OIMCollection = class {
928
897
  getManyByPks(pks) {
929
898
  return this.store.getManyByPks(pks);
930
899
  }
900
+ getSlotByPk(pk) {
901
+ return this.store.getSlotByPk(pk);
902
+ }
903
+ getSlotsByPks(pks) {
904
+ return this.store.getSlotsByPks(pks);
905
+ }
906
+ getAllSlots() {
907
+ return this.store.getAllSlots();
908
+ }
931
909
  upsertOneByPk(pk, entity) {
932
- this.upsertOneWithoutNotificationsByPk(pk, entity);
910
+ const slot = this.upsertOneWithoutNotificationsByPk(pk, entity);
933
911
  this.emitter.emit(0 /* UPDATE */, { pks: [pk] });
912
+ return slot;
934
913
  }
935
914
  upsertOne(entity) {
936
- const pk = this.upsertOneWithoutNotifications(entity);
937
- this.emitter.emit(0 /* UPDATE */, { pks: [pk] });
915
+ const slot = this.upsertOneWithoutNotifications(entity);
916
+ this.emitter.emit(0 /* UPDATE */, { pks: [slot.pk] });
917
+ return slot;
938
918
  }
939
919
  upsertMany(entities) {
940
- const pks = entities.map(
920
+ if (entities.length === 0) return [];
921
+ const slots = entities.map(
941
922
  (entity) => this.upsertOneWithoutNotifications(entity)
942
923
  );
924
+ const pks = slots.map((slot) => slot.pk);
943
925
  this.emitter.emit(0 /* UPDATE */, { pks });
926
+ return slots;
944
927
  }
945
928
  removeOne(entity) {
946
929
  const pk = this.selectPk(entity);
@@ -948,6 +931,7 @@ var OIMCollection = class {
948
931
  this.emitter.emit(0 /* UPDATE */, { pks: [pk] });
949
932
  }
950
933
  removeMany(entities) {
934
+ if (entities.length === 0) return;
951
935
  const pks = entities.map(this.selectPk);
952
936
  this.store.removeManyByPks(pks);
953
937
  this.emitter.emit(0 /* UPDATE */, { pks });
@@ -957,6 +941,7 @@ var OIMCollection = class {
957
941
  this.emitter.emit(0 /* UPDATE */, { pks: [pk] });
958
942
  }
959
943
  removeManyByPks(pks) {
944
+ if (pks.length === 0) return;
960
945
  this.store.removeManyByPks(pks);
961
946
  this.emitter.emit(0 /* UPDATE */, { pks });
962
947
  }
@@ -975,8 +960,7 @@ var OIMCollection = class {
975
960
  }
976
961
  upsertOneWithoutNotifications(entity) {
977
962
  const pk = this.selectPk(entity);
978
- this.upsertOneWithoutNotificationsByPk(pk, entity);
979
- return pk;
963
+ return this.upsertOneWithoutNotificationsByPk(pk, entity);
980
964
  }
981
965
  upsertOneWithoutNotificationsByPk(pk, entity) {
982
966
  if (!pk) {
@@ -986,12 +970,12 @@ var OIMCollection = class {
986
970
  }
987
971
  const existingEntity = this.store.getOneByPk(pk);
988
972
  if (existingEntity) {
989
- this.store.setOneByPk(
973
+ return this.store.setOneByPk(
990
974
  pk,
991
975
  this.updateEntity(entity, existingEntity)
992
976
  );
993
977
  } else {
994
- this.store.setOneByPk(pk, entity);
978
+ return this.store.setOneByPk(pk, entity);
995
979
  }
996
980
  }
997
981
  destroy() {
@@ -1052,46 +1036,61 @@ var OIMReactiveCollection = class extends OIMCollection {
1052
1036
  };
1053
1037
  }
1054
1038
  upsertOneByPk(pk, entity) {
1055
- this.upsertOneWithoutNotificationsByPk(pk, entity);
1039
+ const slot = this.upsertOneWithoutNotificationsByPk(pk, entity);
1040
+ this.emitter.emit(0 /* UPDATE */, { pks: [pk] });
1056
1041
  this.updateEmitter.markUpdatedKey(pk);
1057
1042
  this.trackAnyUpdatePk(pk);
1043
+ return slot;
1058
1044
  }
1059
1045
  upsertOne(entity) {
1060
- const pk = this.upsertOneWithoutNotifications(entity);
1061
- this.updateEmitter.markUpdatedKey(pk);
1062
- this.trackAnyUpdatePk(pk);
1046
+ const slot = this.upsertOneWithoutNotifications(entity);
1047
+ this.emitter.emit(0 /* UPDATE */, { pks: [slot.pk] });
1048
+ this.updateEmitter.markUpdatedKey(slot.pk);
1049
+ this.trackAnyUpdatePk(slot.pk);
1050
+ return slot;
1063
1051
  }
1064
1052
  upsertMany(entities) {
1065
- const pks = entities.map(
1053
+ if (entities.length === 0) return [];
1054
+ const slots = entities.map(
1066
1055
  (entity) => this.upsertOneWithoutNotifications(entity)
1067
1056
  );
1057
+ const pks = slots.map((slot) => slot.pk);
1058
+ this.emitter.emit(0 /* UPDATE */, { pks });
1068
1059
  this.updateEmitter.markUpdatedKeys(pks);
1069
1060
  this.trackAnyUpdatePks(pks);
1061
+ return slots;
1070
1062
  }
1071
1063
  removeOne(entity) {
1072
1064
  const pk = this.selectPk(entity);
1073
1065
  this.store.removeOneByPk(pk);
1066
+ this.emitter.emit(0 /* UPDATE */, { pks: [pk] });
1074
1067
  this.updateEmitter.markUpdatedKey(pk);
1075
1068
  this.trackAnyUpdatePk(pk);
1076
1069
  }
1077
1070
  removeMany(entities) {
1071
+ if (entities.length === 0) return;
1078
1072
  const pks = entities.map(this.selectPk);
1079
1073
  this.store.removeManyByPks(pks);
1074
+ this.emitter.emit(0 /* UPDATE */, { pks });
1080
1075
  this.updateEmitter.markUpdatedKeys(pks);
1081
1076
  this.trackAnyUpdatePks(pks);
1082
1077
  }
1083
1078
  removeOneByPk(pk) {
1084
1079
  this.store.removeOneByPk(pk);
1080
+ this.emitter.emit(0 /* UPDATE */, { pks: [pk] });
1085
1081
  this.updateEmitter.markUpdatedKey(pk);
1086
1082
  this.trackAnyUpdatePk(pk);
1087
1083
  }
1088
1084
  removeManyByPks(pks) {
1085
+ if (pks.length === 0) return;
1089
1086
  this.store.removeManyByPks(pks);
1087
+ this.emitter.emit(0 /* UPDATE */, { pks });
1090
1088
  this.updateEmitter.markUpdatedKeys(pks);
1091
1089
  this.trackAnyUpdatePks(pks);
1092
1090
  }
1093
1091
  clear() {
1094
1092
  this.store.clear();
1093
+ this.emitter.emit(0 /* UPDATE */, { pks: [] });
1095
1094
  this.updateEmitter.markAllUpdated();
1096
1095
  this.trackAnyUpdateClear();
1097
1096
  }
@@ -1129,193 +1128,347 @@ var OIMReactiveCollection = class extends OIMCollection {
1129
1128
  }
1130
1129
  };
1131
1130
 
1132
- // src/core/OIMObjectStoreRecordDriven.ts
1133
- var OIMObjectStoreRecordDriven = class extends OIMObjectStore {
1134
- constructor(initial) {
1135
- super();
1136
- this.properties = Object.assign(/* @__PURE__ */ Object.create(null), initial ?? {});
1137
- }
1138
- setProperty(key, value) {
1139
- this.properties[key] = value;
1140
- }
1141
- merge(draft) {
1142
- Object.assign(this.properties, draft);
1143
- }
1144
- delete(key) {
1145
- delete this.properties[key];
1146
- }
1147
- get(key) {
1148
- return this.properties[key];
1149
- }
1150
- getAll() {
1151
- return { ...this.properties };
1131
+ // src/modules/wrapper/index/OIMIndexManualOrderedArrayBased.ts
1132
+ var OIMIndexManualOrderedArrayBased = class extends OIMIndexArrayBased {
1133
+ clear(key) {
1134
+ if (key === void 0) {
1135
+ const keys = this.getKeys();
1136
+ if (keys.length > 0) {
1137
+ this.store.clear();
1138
+ this.emitUpdate(Array.from(keys));
1139
+ }
1140
+ return;
1141
+ }
1142
+ if (this.store.getOneByKey(key) !== void 0) {
1143
+ this.store.removeOneByKey(key);
1144
+ this.emitUpdate([key]);
1145
+ }
1152
1146
  }
1153
- clear() {
1154
- this.properties = /* @__PURE__ */ Object.create(null);
1147
+ pushSlot(key, slot) {
1148
+ const list = this.getOrCreateList(key);
1149
+ const index = list.length;
1150
+ list.push(slot);
1151
+ this.emitUpdate([key]);
1152
+ return index;
1155
1153
  }
1156
- count() {
1157
- return Object.keys(this.properties).length;
1154
+ insertSlotAt(key, index, slot) {
1155
+ const list = this.getOrCreateList(key);
1156
+ const safeIndex = Math.max(0, Math.min(index, list.length));
1157
+ list.splice(safeIndex, 0, slot);
1158
+ this.emitUpdate([key]);
1159
+ return safeIndex;
1158
1160
  }
1159
- keys() {
1160
- return Object.keys(this.properties);
1161
+ removeAt(key, index) {
1162
+ const list = this.store.getOneByKey(key);
1163
+ if (!list) return void 0;
1164
+ if (index < 0 || index >= list.length) return void 0;
1165
+ const [removed] = list.splice(index, 1);
1166
+ if (list.length === 0) {
1167
+ this.store.removeOneByKey(key);
1168
+ }
1169
+ this.emitUpdate([key]);
1170
+ return removed;
1161
1171
  }
1162
- values() {
1163
- return Object.values(this.properties);
1172
+ move(key, fromIndex, toIndex) {
1173
+ const list = this.store.getOneByKey(key);
1174
+ if (!list) return void 0;
1175
+ if (fromIndex < 0 || fromIndex >= list.length) return void 0;
1176
+ if (toIndex < 0 || toIndex >= list.length) return void 0;
1177
+ if (fromIndex === toIndex) return list[fromIndex];
1178
+ const [slot] = list.splice(fromIndex, 1);
1179
+ if (slot === void 0) return void 0;
1180
+ list.splice(toIndex, 0, slot);
1181
+ this.emitUpdate([key]);
1182
+ return slot;
1164
1183
  }
1165
- entries() {
1166
- return Object.entries(this.properties);
1184
+ resetSlots(key, slots) {
1185
+ if (slots.length === 0) {
1186
+ if (this.store.getOneByKey(key) !== void 0) {
1187
+ this.store.removeOneByKey(key);
1188
+ this.emitUpdate([key]);
1189
+ }
1190
+ return;
1191
+ }
1192
+ this.store.setOneByKey(key, slots.slice());
1193
+ this.emitUpdate([key]);
1167
1194
  }
1168
- destroy() {
1169
- this.clear();
1195
+ getOrCreateList(key) {
1196
+ let list = this.store.getOneByKey(key);
1197
+ if (!list) {
1198
+ list = [];
1199
+ this.store.setOneByKey(key, list);
1200
+ }
1201
+ return list;
1170
1202
  }
1171
1203
  };
1172
1204
 
1173
- // src/enum/EOIMObjectEventType.ts
1174
- var EOIMObjectEventType = /* @__PURE__ */ ((EOIMObjectEventType2) => {
1175
- EOIMObjectEventType2[EOIMObjectEventType2["UPDATE"] = 0] = "UPDATE";
1176
- return EOIMObjectEventType2;
1177
- })(EOIMObjectEventType || {});
1178
-
1179
- // src/core/OIMObject.ts
1180
- var OIMObject = class {
1205
+ // src/modules/wrapper/index/OIMCollectionIndexManualOrderedArrayBased.ts
1206
+ var OIMCollectionIndexManualOrderedArrayBased = class extends OIMIndexManualOrderedArrayBased {
1181
1207
  constructor(opts) {
1182
- this.emitter = new OIMEventEmitter();
1183
- this.store = opts?.store ?? new OIMObjectStoreRecordDriven();
1184
- }
1185
- setProperty(key, value) {
1186
- this.store.setProperty(key, value);
1187
- this.emitter.emit(0 /* UPDATE */, { keys: [key] });
1188
- }
1189
- merge(draft) {
1190
- this.store.merge(draft);
1191
- this.emitter.emit(0 /* UPDATE */, {
1192
- keys: Object.keys(draft)
1193
- });
1194
- }
1195
- delete(key) {
1196
- this.store.delete(key);
1197
- this.emitter.emit(0 /* UPDATE */, { keys: [key] });
1208
+ super();
1209
+ this.resolveSlot = opts.collection !== void 0 ? (pk) => opts.collection.getSlotByPk(pk) : opts.resolveSlot;
1198
1210
  }
1199
- get(key) {
1200
- return this.store.get(key);
1211
+ push(key, pk) {
1212
+ return this.pushSlot(key, this.resolveRequiredSlot(pk));
1201
1213
  }
1202
- getAll() {
1203
- return this.store.getAll();
1214
+ insertAt(key, index, pk) {
1215
+ return this.insertSlotAt(key, index, this.resolveRequiredSlot(pk));
1204
1216
  }
1205
- clear() {
1206
- this.store.clear();
1207
- this.emitter.emit(0 /* UPDATE */, { keys: [] });
1217
+ reset(key, pks) {
1218
+ this.resetSlots(key, this.resolveSlots(pks));
1208
1219
  }
1209
- count() {
1210
- return this.store.count();
1220
+ getEntitiesByKey(key) {
1221
+ return super.getEntitiesByKey(key);
1211
1222
  }
1212
- keys() {
1213
- return this.store.keys();
1223
+ getEntitiesByKeys(keys) {
1224
+ return super.getEntitiesByKeys(keys);
1214
1225
  }
1215
- values() {
1216
- return this.store.values();
1226
+ resolvePk(pk) {
1227
+ return this.resolveRequiredSlot(pk);
1217
1228
  }
1218
- entries() {
1219
- return this.store.entries();
1229
+ resolveSlots(pks) {
1230
+ const slots = [];
1231
+ slots.length = pks.length;
1232
+ for (let i = 0; i < pks.length; i++) {
1233
+ slots[i] = this.resolveRequiredSlot(pks[i]);
1234
+ }
1235
+ return slots;
1220
1236
  }
1221
- destroy() {
1222
- this.store.destroy();
1223
- this.emitter.offAll();
1237
+ resolveRequiredSlot(pk) {
1238
+ const slot = this.resolveSlot(pk);
1239
+ if (!slot) {
1240
+ throw new Error(
1241
+ `[OIMCollectionIndexManualOrderedArrayBased]: Unable to resolve slot for PK "${String(pk)}".`
1242
+ );
1243
+ }
1244
+ return slot;
1224
1245
  }
1225
1246
  };
1226
1247
 
1227
- // src/core/OIMReactiveObject.ts
1228
- var OIMReactiveObject = class extends OIMObject {
1229
- constructor(queue, opts) {
1230
- super(opts);
1231
- this.updateEmitter = new OIMUpdateEventEmitter(queue, "queue");
1232
- this.emitter.on(0 /* UPDATE */, (payload) => {
1233
- this.updateEmitter.markUpdatedKeys(payload.keys);
1234
- });
1235
- }
1236
- subscribeOnKey(key, handler) {
1237
- return this.updateEmitter.subscribeOnKey(key, handler);
1248
+ // src/modules/wrapper/index/OIMOrderedListCommandStream.ts
1249
+ var OIMOrderedListCommandStream = class {
1250
+ constructor(queue, index) {
1251
+ this.isInWrite = false;
1252
+ this.commandsByKey = /* @__PURE__ */ new Map();
1253
+ this.onAfterFlush = () => {
1254
+ this.commandsByKey.clear();
1255
+ };
1256
+ this.onIndexUpdate = (payload) => {
1257
+ if (this.isInWrite) return;
1258
+ if (payload.keys.length === 0) return;
1259
+ for (let i = 0; i < payload.keys.length; i++) {
1260
+ const key = payload.keys[i];
1261
+ this.appendSetCommand(key, this.getSlotsByKey(key));
1262
+ }
1263
+ };
1264
+ this.commandsEventEmitter = new OIMUpdateEventEmitter(
1265
+ queue,
1266
+ "after_flush"
1267
+ );
1268
+ this.index = index ?? new OIMIndexManualOrderedArrayBased();
1269
+ this.unsubscribeAfterFlush = queue.emitter.on(
1270
+ 0 /* AFTER_FLUSH */,
1271
+ this.onAfterFlush
1272
+ );
1273
+ this.unsubscribeFromIndexEmitter = this.index.emitter.on(
1274
+ 0 /* UPDATE */,
1275
+ this.onIndexUpdate
1276
+ );
1238
1277
  }
1239
- subscribeOnKeys(keys, handler) {
1240
- return this.updateEmitter.subscribeOnKeys(keys, handler);
1278
+ getPksByKey(key) {
1279
+ return this.index.getPksByKey(key);
1241
1280
  }
1242
- unsubscribeFromKey(key, handler) {
1243
- this.updateEmitter.unsubscribeFromKey(key, handler);
1281
+ getSlotsByKey(key) {
1282
+ return this.index.getSlotsByKey(
1283
+ key
1284
+ );
1244
1285
  }
1245
- unsubscribeFromKeys(keys, handler) {
1246
- this.updateEmitter.unsubscribeFromKeys(keys, handler);
1286
+ getEntitiesByKey(key) {
1287
+ return this.index.getEntitiesByKey(key);
1247
1288
  }
1248
- hasSubscriptions() {
1249
- return this.updateEmitter.hasSubscriptions();
1289
+ hasKey(key) {
1290
+ return this.index.hasKey(key);
1250
1291
  }
1251
- getHandlerCount(key) {
1252
- return this.updateEmitter.getHandlerCount(key);
1292
+ getKeys() {
1293
+ return this.index.getKeys();
1253
1294
  }
1254
- getMetrics() {
1255
- return this.updateEmitter.getMetrics();
1295
+ pushSlot(key, slot) {
1296
+ this.withWrite(() => {
1297
+ const index = this.index.pushSlot(key, slot);
1298
+ this.appendCommand(key, {
1299
+ type: "insert",
1300
+ pk: slot.pk,
1301
+ slot,
1302
+ index
1303
+ });
1304
+ });
1256
1305
  }
1257
- destroy() {
1258
- this.updateEmitter.destroy();
1259
- super.destroy();
1306
+ insertSlotAt(key, index, slot) {
1307
+ this.withWrite(() => {
1308
+ const safeIndex = this.index.insertSlotAt(key, index, slot);
1309
+ this.appendCommand(key, {
1310
+ type: "insert",
1311
+ pk: slot.pk,
1312
+ slot,
1313
+ index: safeIndex
1314
+ });
1315
+ });
1260
1316
  }
1261
- };
1262
-
1263
- // src/core/OIMIndexManualSetBased.ts
1264
- var OIMIndexManualSetBased = class extends OIMIndexSetBased {
1265
- constructor(options = {}) {
1266
- super(options);
1317
+ removeAt(key, index) {
1318
+ this.withWrite(() => {
1319
+ const slot = this.index.removeAt(key, index);
1320
+ if (slot === void 0) return;
1321
+ this.appendCommand(key, {
1322
+ type: "remove",
1323
+ pk: slot.pk,
1324
+ slot,
1325
+ index
1326
+ });
1327
+ });
1267
1328
  }
1268
- /**
1269
- * Set primary keys for a specific index key, replacing any existing values.
1270
- * Uses optional comparator to skip updates if PKs haven't actually changed.
1271
- */
1272
- setPks(key, pks) {
1273
- const hasChanges = this.setPksWithComparison(key, new Set(pks));
1274
- if (hasChanges) {
1275
- this.emitUpdateOne(key);
1276
- }
1329
+ move(key, fromIndex, toIndex) {
1330
+ this.withWrite(() => {
1331
+ const slot = this.index.move(key, fromIndex, toIndex);
1332
+ if (slot === void 0) return;
1333
+ this.appendCommand(key, {
1334
+ type: "move",
1335
+ pk: slot.pk,
1336
+ slot,
1337
+ fromIndex,
1338
+ toIndex
1339
+ });
1340
+ });
1277
1341
  }
1278
- /**
1279
- * Add primary keys to a specific index key
1280
- */
1281
- addPks(key, pks) {
1282
- if (pks.length === 0) return;
1283
- let pksSet = this.store.getOneByKey(key);
1284
- if (!pksSet) {
1285
- pksSet = /* @__PURE__ */ new Set();
1286
- this.store.setOneByKey(key, pksSet);
1342
+ setSlots(key, slots) {
1343
+ this.withWrite(() => {
1344
+ this.index.resetSlots(key, slots);
1345
+ this.appendSetCommand(key, slots);
1346
+ });
1347
+ }
1348
+ getBufferedCommands(key) {
1349
+ return this.commandsByKey.get(key) ?? [];
1350
+ }
1351
+ consumeCommands(key) {
1352
+ const cmds = this.commandsByKey.get(key);
1353
+ if (!cmds || cmds.length === 0) return [];
1354
+ return cmds.slice();
1355
+ }
1356
+ clear(key) {
1357
+ if (key === void 0) {
1358
+ this.index.destroy();
1359
+ this.commandsByKey.clear();
1360
+ return;
1287
1361
  }
1288
- let hasChanges = false;
1289
- for (const pk of pks) {
1290
- if (!pksSet.has(pk)) {
1291
- pksSet.add(pk);
1292
- hasChanges = true;
1293
- }
1362
+ this.index.clear(key);
1363
+ this.commandsByKey.delete(key);
1364
+ }
1365
+ destroy() {
1366
+ this.commandsEventEmitter.destroy();
1367
+ this.unsubscribeAfterFlush();
1368
+ this.unsubscribeFromIndexEmitter();
1369
+ this.clear();
1370
+ }
1371
+ withWrite(fn) {
1372
+ if (this.isInWrite) {
1373
+ fn();
1374
+ return;
1294
1375
  }
1295
- if (hasChanges) {
1296
- this.store.setOneByKey(key, pksSet);
1297
- this.emitUpdateOne(key);
1376
+ this.isInWrite = true;
1377
+ try {
1378
+ fn();
1379
+ } finally {
1380
+ this.isInWrite = false;
1298
1381
  }
1299
1382
  }
1300
- /**
1301
- * Remove primary keys from a specific index key
1302
- */
1303
- removePks(key, pks) {
1304
- if (pks.length === 0) return;
1305
- const pksSet = this.store.getOneByKey(key);
1306
- if (!pksSet) return;
1307
- let hasChanges = false;
1308
- for (const pk of pks) {
1309
- if (pksSet.delete(pk)) {
1310
- hasChanges = true;
1311
- }
1383
+ appendCommand(key, cmd) {
1384
+ let cmds = this.commandsByKey.get(key);
1385
+ if (!cmds) {
1386
+ cmds = [];
1387
+ this.commandsByKey.set(key, cmds);
1312
1388
  }
1313
- if (pksSet.size === 0) {
1314
- this.store.removeOneByKey(key);
1315
- hasChanges = true;
1316
- } else if (hasChanges) {
1317
- this.store.setOneByKey(key, pksSet);
1389
+ if (cmd.type === "set") {
1390
+ cmds.length = 0;
1391
+ cmds.push(cmd);
1392
+ } else if (cmds.length > 0 && cmds[0].type === "set") {
1393
+ const slots = this.getSlotsByKey(key);
1394
+ cmds[0] = this.createSetCommand(slots);
1395
+ } else {
1396
+ cmds.push(cmd);
1318
1397
  }
1398
+ this.commandsEventEmitter.markUpdatedKeys([key]);
1399
+ }
1400
+ appendSetCommand(key, slots) {
1401
+ this.appendCommand(key, this.createSetCommand(slots));
1402
+ }
1403
+ createSetCommand(slots) {
1404
+ return {
1405
+ type: "set",
1406
+ pks: slots.map((slot) => slot.pk),
1407
+ slots
1408
+ };
1409
+ }
1410
+ };
1411
+
1412
+ // src/modules/wrapper/index/OIMCollectionOrderedListCommandStream.ts
1413
+ var OIMCollectionOrderedListCommandStream = class extends OIMOrderedListCommandStream {
1414
+ constructor(queue, opts) {
1415
+ const index = opts.index ?? new OIMCollectionIndexManualOrderedArrayBased(
1416
+ opts.collection !== void 0 ? { collection: opts.collection } : { resolveSlot: opts.resolveSlot }
1417
+ );
1418
+ super(queue, index);
1419
+ this.index = index;
1420
+ }
1421
+ push(key, pk) {
1422
+ this.withWrite(() => {
1423
+ const slot = this.resolvePk(pk);
1424
+ const index = this.index.pushSlot(key, slot);
1425
+ this.appendCommand(key, {
1426
+ type: "insert",
1427
+ pk,
1428
+ slot,
1429
+ index
1430
+ });
1431
+ });
1432
+ }
1433
+ insertAt(key, index, pk) {
1434
+ this.withWrite(() => {
1435
+ const slot = this.resolvePk(pk);
1436
+ const safeIndex = this.index.insertSlotAt(key, index, slot);
1437
+ this.appendCommand(key, {
1438
+ type: "insert",
1439
+ pk,
1440
+ slot,
1441
+ index: safeIndex
1442
+ });
1443
+ });
1444
+ }
1445
+ set(key, pks) {
1446
+ this.withWrite(() => {
1447
+ const slots = pks.map((pk) => this.resolvePk(pk));
1448
+ this.index.resetSlots(key, slots);
1449
+ this.appendSetCommand(key, slots);
1450
+ });
1451
+ }
1452
+ getSlotsByKey(key) {
1453
+ return this.index.getSlotsByKey(
1454
+ key
1455
+ );
1456
+ }
1457
+ getEntitiesByKey(key) {
1458
+ return this.index.getEntitiesByKey(key);
1459
+ }
1460
+ resolvePk(pk) {
1461
+ return this.index.resolvePk(pk);
1462
+ }
1463
+ };
1464
+
1465
+ // src/core/OIMIndexManualArrayBased.ts
1466
+ var OIMIndexManualArrayBased = class extends OIMIndexArrayBased {
1467
+ constructor(options = {}) {
1468
+ super(options);
1469
+ }
1470
+ setSlots(key, slots) {
1471
+ const hasChanges = this.setSlotsWithComparison(key, slots);
1319
1472
  if (hasChanges) {
1320
1473
  this.emitUpdateOne(key);
1321
1474
  }
@@ -1339,8 +1492,8 @@ var OIMIndexManualSetBased = class extends OIMIndexSetBased {
1339
1492
  }
1340
1493
  };
1341
1494
 
1342
- // src/core/OIMReactiveIndexManualSetBased.ts
1343
- var OIMIndexManualSetBasedReactive = class extends OIMIndexManualSetBased {
1495
+ // src/core/OIMReactiveIndexManualArrayBased.ts
1496
+ var OIMIndexManualArrayBasedReactive = class extends OIMIndexManualArrayBased {
1344
1497
  constructor(updateEmitter, opts) {
1345
1498
  super(opts);
1346
1499
  this.updateEmitter = updateEmitter;
@@ -1352,83 +1505,93 @@ var OIMIndexManualSetBasedReactive = class extends OIMIndexManualSetBased {
1352
1505
  this.updateEmitter.markUpdatedKey(key);
1353
1506
  }
1354
1507
  };
1355
- var OIMReactiveIndexManualSetBased = class extends OIMReactiveIndexSetBased {
1508
+ var OIMReactiveIndexManualArrayBased = class extends OIMReactiveIndexArrayBased {
1356
1509
  constructor(queue, opts) {
1357
1510
  super(queue, (updateEmitter) => {
1358
- return new OIMIndexManualSetBasedReactive(
1511
+ return new OIMIndexManualArrayBasedReactive(
1359
1512
  updateEmitter,
1360
1513
  opts?.indexOptions
1361
1514
  );
1362
1515
  });
1363
1516
  }
1364
- setPks(key, pks) {
1365
- this.index.setPks(key, pks);
1366
- }
1367
- addPks(key, pks) {
1368
- this.index.addPks(key, pks);
1369
- }
1370
- removePks(key, pks) {
1371
- this.index.removePks(key, pks);
1517
+ setSlots(key, slots) {
1518
+ this.index.setSlots(key, slots);
1372
1519
  }
1373
1520
  clear(key) {
1374
1521
  this.index.clear(key);
1375
1522
  }
1376
1523
  };
1377
1524
 
1378
- // src/core/OIMIndexManualArrayBased.ts
1379
- var OIMIndexManualArrayBased = class extends OIMIndexArrayBased {
1380
- constructor(options = {}) {
1381
- super(options);
1525
+ // src/core/OIMReactiveCollectionIndexManualArrayBased.ts
1526
+ var OIMReactiveCollectionIndexManualArrayBased = class extends OIMReactiveIndexManualArrayBased {
1527
+ constructor(queue, opts) {
1528
+ const resolveSlot = opts.collection !== void 0 ? (pk) => opts.collection.getSlotByPk(pk) : opts.resolveSlot;
1529
+ super(queue, {
1530
+ indexOptions: opts.indexOptions
1531
+ });
1532
+ this.resolveSlot = resolveSlot;
1382
1533
  }
1383
- /**
1384
- * Set primary keys for a specific index key, replacing any existing values.
1385
- * Uses optional comparator to skip updates if PKs haven't actually changed.
1386
- */
1387
1534
  setPks(key, pks) {
1388
- const hasChanges = this.setPksWithComparison(key, pks);
1389
- if (hasChanges) {
1390
- this.emitUpdateOne(key);
1391
- }
1535
+ this.setSlots(key, this.resolveSlots(pks));
1392
1536
  }
1393
- /**
1394
- * Add primary keys to a specific index key
1395
- */
1396
1537
  addPks(key, pks) {
1397
1538
  if (pks.length === 0) return;
1398
- let pksArray = this.store.getOneByKey(key);
1399
- if (!pksArray) {
1400
- pksArray = [];
1401
- this.store.setOneByKey(key, pksArray);
1402
- }
1403
- const pksSet = new Set(pksArray);
1539
+ const existingSlots = this.index.getSlotsByKey(key);
1540
+ const existingPks = new Set(existingSlots.map((slot) => slot.pk));
1541
+ const nextSlots = existingSlots.slice();
1404
1542
  let hasChanges = false;
1405
1543
  for (const pk of pks) {
1406
- if (!pksSet.has(pk)) {
1407
- pksSet.add(pk);
1408
- hasChanges = true;
1409
- }
1410
- }
1411
- if (hasChanges) {
1412
- this.store.setOneByKey(key, Array.from(pksSet));
1413
- this.emitUpdateOne(key);
1544
+ if (existingPks.has(pk)) continue;
1545
+ nextSlots.push(this.resolveRequiredSlot(pk));
1546
+ existingPks.add(pk);
1547
+ hasChanges = true;
1414
1548
  }
1549
+ if (hasChanges) this.setSlots(key, nextSlots);
1415
1550
  }
1416
- /**
1417
- * Remove primary keys from a specific index key
1418
- */
1419
1551
  removePks(key, pks) {
1420
1552
  if (pks.length === 0) return;
1421
- const pksArray = this.store.getOneByKey(key);
1422
- if (!pksArray) return;
1423
- const pksSet = new Set(pks);
1424
- const filtered = pksArray.filter((pk) => !pksSet.has(pk));
1425
- const hasChanges = filtered.length !== pksArray.length;
1553
+ const pksToRemove = new Set(pks);
1554
+ const existingSlots = this.index.getSlotsByKey(key);
1555
+ const nextSlots = existingSlots.filter(
1556
+ (slot) => !pksToRemove.has(slot.pk)
1557
+ );
1558
+ if (nextSlots.length === existingSlots.length) return;
1559
+ if (nextSlots.length === 0) this.clear(key);
1560
+ else this.setSlots(key, nextSlots);
1561
+ }
1562
+ getEntitiesByKey(key) {
1563
+ return super.getEntitiesByKey(key);
1564
+ }
1565
+ getEntitiesByKeys(keys) {
1566
+ return super.getEntitiesByKeys(keys);
1567
+ }
1568
+ resolveSlots(pks) {
1569
+ const slots = [];
1570
+ slots.length = pks.length;
1571
+ for (let i = 0; i < pks.length; i++) {
1572
+ slots[i] = this.resolveRequiredSlot(pks[i]);
1573
+ }
1574
+ return slots;
1575
+ }
1576
+ resolveRequiredSlot(pk) {
1577
+ const slot = this.resolveSlot(pk);
1578
+ if (!slot) {
1579
+ throw new Error(
1580
+ `[OIMReactiveCollectionIndexManualArrayBased]: Unable to resolve slot for PK "${String(pk)}".`
1581
+ );
1582
+ }
1583
+ return slot;
1584
+ }
1585
+ };
1586
+
1587
+ // src/core/OIMIndexManualSetBased.ts
1588
+ var OIMIndexManualSetBased = class extends OIMIndexSetBased {
1589
+ constructor(options = {}) {
1590
+ super(options);
1591
+ }
1592
+ setSlots(key, slots) {
1593
+ const hasChanges = this.setSlotsWithComparison(key, new Set(slots));
1426
1594
  if (hasChanges) {
1427
- if (filtered.length === 0) {
1428
- this.store.removeOneByKey(key);
1429
- } else {
1430
- this.store.setOneByKey(key, filtered);
1431
- }
1432
1595
  this.emitUpdateOne(key);
1433
1596
  }
1434
1597
  }
@@ -1451,8 +1614,8 @@ var OIMIndexManualArrayBased = class extends OIMIndexArrayBased {
1451
1614
  }
1452
1615
  };
1453
1616
 
1454
- // src/core/OIMReactiveIndexManualArrayBased.ts
1455
- var OIMIndexManualArrayBasedReactive = class extends OIMIndexManualArrayBased {
1617
+ // src/core/OIMReactiveIndexManualSetBased.ts
1618
+ var OIMIndexManualSetBasedReactive = class extends OIMIndexManualSetBased {
1456
1619
  constructor(updateEmitter, opts) {
1457
1620
  super(opts);
1458
1621
  this.updateEmitter = updateEmitter;
@@ -1464,303 +1627,1256 @@ var OIMIndexManualArrayBasedReactive = class extends OIMIndexManualArrayBased {
1464
1627
  this.updateEmitter.markUpdatedKey(key);
1465
1628
  }
1466
1629
  };
1467
- var OIMReactiveIndexManualArrayBased = class extends OIMReactiveIndexArrayBased {
1630
+ var OIMReactiveIndexManualSetBased = class extends OIMReactiveIndexSetBased {
1468
1631
  constructor(queue, opts) {
1469
1632
  super(queue, (updateEmitter) => {
1470
- return new OIMIndexManualArrayBasedReactive(
1633
+ return new OIMIndexManualSetBasedReactive(
1471
1634
  updateEmitter,
1472
1635
  opts?.indexOptions
1473
1636
  );
1474
1637
  });
1475
1638
  }
1476
- setPks(key, pks) {
1477
- this.index.setPks(key, pks);
1478
- }
1479
- addPks(key, pks) {
1480
- this.index.addPks(key, pks);
1481
- }
1482
- removePks(key, pks) {
1483
- this.index.removePks(key, pks);
1639
+ setSlots(key, slots) {
1640
+ this.index.setSlots(key, slots);
1484
1641
  }
1485
1642
  clear(key) {
1486
1643
  this.index.clear(key);
1487
1644
  }
1488
1645
  };
1489
1646
 
1490
- // src/core/OIMRICollection.ts
1491
- var OIMRICollection = class extends OIMReactiveCollection {
1647
+ // src/core/OIMReactiveCollectionIndexManualSetBased.ts
1648
+ var OIMReactiveCollectionIndexManualSetBased = class extends OIMReactiveIndexManualSetBased {
1492
1649
  constructor(queue, opts) {
1493
- super(queue, opts.collectionOpts);
1494
- this.indexes = opts.indexes || {};
1495
- }
1496
- };
1497
-
1498
- // src/core/OIMObjectStoreMapDriven.ts
1499
- var OIMObjectStoreMapDriven = class extends OIMObjectStore {
1500
- constructor() {
1501
- super(...arguments);
1502
- this.properties = /* @__PURE__ */ new Map();
1650
+ const resolveSlot = opts.collection !== void 0 ? (pk) => opts.collection.getSlotByPk(pk) : opts.resolveSlot;
1651
+ super(queue, {
1652
+ indexOptions: opts.indexOptions
1653
+ });
1654
+ this.resolveSlot = resolveSlot;
1503
1655
  }
1504
- setProperty(key, value) {
1505
- this.properties.set(key, value);
1656
+ setPks(key, pks) {
1657
+ this.setSlots(key, this.resolveSlots(pks));
1506
1658
  }
1507
- merge(draft) {
1508
- for (const key in draft) {
1509
- if (!Object.prototype.hasOwnProperty.call(draft, key)) continue;
1510
- this.properties.set(
1511
- key,
1512
- draft[key]
1513
- );
1659
+ addPks(key, pks) {
1660
+ if (pks.length === 0) return;
1661
+ const existingSlots = this.index.getSlotsByKey(key);
1662
+ const existingPks = this.index.getPksByKey(key);
1663
+ const nextSlots = new Set(existingSlots);
1664
+ let hasChanges = false;
1665
+ for (const pk of pks) {
1666
+ if (existingPks.has(pk)) continue;
1667
+ const slot = this.resolveRequiredSlot(pk);
1668
+ nextSlots.add(slot);
1669
+ existingPks.add(pk);
1670
+ hasChanges = true;
1514
1671
  }
1672
+ if (hasChanges) this.setSlots(key, nextSlots);
1515
1673
  }
1516
- delete(key) {
1517
- this.properties.delete(key);
1518
- }
1519
- get(key) {
1520
- return this.properties.get(key);
1521
- }
1522
- getAll() {
1523
- return Object.fromEntries(this.properties.entries());
1524
- }
1525
- clear() {
1526
- this.properties.clear();
1527
- }
1528
- count() {
1529
- return this.properties.size;
1674
+ removePks(key, pks) {
1675
+ if (pks.length === 0) return;
1676
+ const pksToRemove = new Set(pks);
1677
+ const existingSlots = this.index.getSlotsByKey(key);
1678
+ const nextSlots = /* @__PURE__ */ new Set();
1679
+ let hasChanges = false;
1680
+ for (const slot of existingSlots) {
1681
+ if (pksToRemove.has(slot.pk)) {
1682
+ hasChanges = true;
1683
+ } else {
1684
+ nextSlots.add(slot);
1685
+ }
1686
+ }
1687
+ if (!hasChanges) return;
1688
+ if (nextSlots.size === 0) this.clear(key);
1689
+ else this.setSlots(key, nextSlots);
1530
1690
  }
1531
- keys() {
1532
- return Array.from(this.properties.keys());
1691
+ getEntitiesByKey(key) {
1692
+ return super.getEntitiesByKey(key);
1533
1693
  }
1534
- values() {
1535
- return Array.from(this.properties.values());
1694
+ getEntitiesByKeys(keys) {
1695
+ return super.getEntitiesByKeys(keys);
1536
1696
  }
1537
- entries() {
1538
- return Array.from(this.properties.entries());
1697
+ resolveSlots(pks) {
1698
+ const slots = /* @__PURE__ */ new Set();
1699
+ for (const pk of pks) slots.add(this.resolveRequiredSlot(pk));
1700
+ return slots;
1539
1701
  }
1540
- destroy() {
1541
- this.properties.clear();
1702
+ resolveRequiredSlot(pk) {
1703
+ const slot = this.resolveSlot(pk);
1704
+ if (!slot) {
1705
+ throw new Error(
1706
+ `[OIMReactiveCollectionIndexManualSetBased]: Unable to resolve slot for PK "${String(pk)}".`
1707
+ );
1708
+ }
1709
+ return slot;
1542
1710
  }
1543
1711
  };
1544
1712
 
1545
- // src/core/OIMComparatorFactory.ts
1546
- var OIMComparatorFactory = class {
1547
- createShallowComparator() {
1548
- return (a, b) => {
1549
- if (a === b) return false;
1550
- const ka = Object.keys(a);
1551
- const kb = Object.keys(b);
1552
- if (ka.length !== kb.length) return true;
1553
- for (let i = 0; i < ka.length; i++) {
1554
- const k = ka[i];
1555
- if (b[k] !== a[k])
1556
- return true;
1713
+ // src/core/OIMDerivedCollectionIndexSetBased.ts
1714
+ var OIMDerivedCollectionIndexSetBased = class extends OIMReactiveCollectionIndexManualSetBased {
1715
+ constructor(queue, collection, opts) {
1716
+ super(queue, {
1717
+ collection,
1718
+ indexOptions: opts.indexOptions
1719
+ });
1720
+ this.keysByPk = /* @__PURE__ */ new Map();
1721
+ this.onCollectionUpdate = (pks) => {
1722
+ if (pks.length === 0) {
1723
+ this.rebuildFromCollection();
1724
+ return;
1725
+ }
1726
+ for (const pk of pks) {
1727
+ this.syncPk(pk);
1557
1728
  }
1558
- return false;
1559
1729
  };
1730
+ this.collection = collection;
1731
+ this.selectIndexKeys = opts.selectIndexKeys;
1732
+ this.unsubscribeFromCollection = collection.emitter.on(
1733
+ 0 /* UPDATE */,
1734
+ (payload) => this.onCollectionUpdate(payload.pks)
1735
+ );
1736
+ if (opts.buildInitial !== false) {
1737
+ this.rebuildFromCollection();
1738
+ }
1739
+ }
1740
+ rebuildFromCollection() {
1741
+ this.keysByPk.clear();
1742
+ super.clear();
1743
+ const slotsByKey = /* @__PURE__ */ new Map();
1744
+ const slots = this.collection.getAllSlots();
1745
+ for (const slot of slots) {
1746
+ if (slot.item === void 0) continue;
1747
+ const keys = this.normalizeKeys(this.selectIndexKeys(slot.item));
1748
+ if (keys.length === 0) continue;
1749
+ this.keysByPk.set(slot.pk, new Set(keys));
1750
+ for (const key of keys) {
1751
+ let slotsForKey = slotsByKey.get(key);
1752
+ if (!slotsForKey) {
1753
+ slotsForKey = /* @__PURE__ */ new Set();
1754
+ slotsByKey.set(key, slotsForKey);
1755
+ }
1756
+ slotsForKey.add(slot);
1757
+ }
1758
+ }
1759
+ for (const [key, keySlots] of slotsByKey) {
1760
+ this.setSlots(key, keySlots);
1761
+ }
1560
1762
  }
1561
- };
1562
-
1563
- // src/core/OIMEventQueue.ts
1564
- var OIMEventQueue = class {
1565
- constructor(options = {}) {
1566
- this.emitter = new OIMEventEmitter();
1567
- this.tasks = /* @__PURE__ */ new Set();
1568
- this.isFlushing = false;
1569
- this.scheduler = options.scheduler;
1570
- if (this.scheduler) {
1571
- this.flushBound = () => this.flush();
1572
- this.scheduler.on(
1573
- 0 /* FLUSH */,
1574
- this.flushBound
1575
- );
1763
+ clear(key) {
1764
+ if (key === void 0) {
1765
+ this.keysByPk.clear();
1766
+ } else {
1767
+ for (const keys of this.keysByPk.values()) {
1768
+ keys.delete(key);
1769
+ }
1576
1770
  }
1771
+ super.clear(key);
1577
1772
  }
1578
- /**
1579
- * Enqueue a one-shot task to be executed on the next flush.
1580
- * Returns a dequeue function that can be called to cancel the task before it runs.
1581
- */
1582
- enqueue(fn) {
1583
- let isActive = true;
1584
- const task = () => {
1585
- if (!isActive) return;
1586
- fn();
1587
- };
1588
- this.tasks.add(task);
1589
- this.ensureScheduled();
1590
- return () => {
1591
- if (!isActive) return;
1592
- isActive = false;
1593
- this.tasks.delete(task);
1594
- };
1773
+ destroy() {
1774
+ this.unsubscribeFromCollection();
1775
+ this.keysByPk.clear();
1776
+ super.destroy();
1595
1777
  }
1596
- get isInFlush() {
1597
- return this.isFlushing;
1778
+ syncPk(pk) {
1779
+ const prevKeys = this.keysByPk.get(pk) ?? /* @__PURE__ */ new Set();
1780
+ const slot = this.collection.getSlotByPk(pk);
1781
+ const entity = slot?.item;
1782
+ const nextKeys = entity === void 0 ? /* @__PURE__ */ new Set() : new Set(this.normalizeKeys(this.selectIndexKeys(entity)));
1783
+ for (const key of prevKeys) {
1784
+ if (!nextKeys.has(key)) {
1785
+ this.removePks(key, [pk]);
1786
+ }
1787
+ }
1788
+ if (entity !== void 0) {
1789
+ for (const key of nextKeys) {
1790
+ if (!prevKeys.has(key)) {
1791
+ this.addPks(key, [pk]);
1792
+ }
1793
+ }
1794
+ }
1795
+ if (nextKeys.size === 0) {
1796
+ this.keysByPk.delete(pk);
1797
+ } else {
1798
+ this.keysByPk.set(pk, nextKeys);
1799
+ }
1598
1800
  }
1599
- /**
1600
- * Dequeue (cancel) a previously enqueued task by its dequeue function.
1601
- */
1602
- dequeue(dequeueFn) {
1603
- dequeueFn();
1801
+ normalizeKeys(keys) {
1802
+ if (keys === void 0 || keys === null) return [];
1803
+ const rawKeys = Array.isArray(keys) ? keys : [keys];
1804
+ const result = [];
1805
+ const seen = /* @__PURE__ */ new Set();
1806
+ for (const key of rawKeys) {
1807
+ if (seen.has(key)) continue;
1808
+ seen.add(key);
1809
+ result.push(key);
1810
+ }
1811
+ return result;
1604
1812
  }
1605
- /**
1606
- * Execute all pending tasks.
1607
- *
1608
- * This flush drains until the queue is empty. If tasks enqueue more tasks, they will be
1609
- * executed within the same flush.
1610
- */
1611
- flush() {
1612
- this.scheduler?.cancel();
1613
- this.isFlushing = true;
1614
- this.emitter.emit(1 /* BEFORE_FLUSH */, void 0);
1615
- const snapshot = Array.from(this.tasks);
1616
- this.tasks.clear();
1617
- for (let i = 0; i < snapshot.length; i++) {
1618
- snapshot[i]();
1813
+ };
1814
+
1815
+ // src/core/OIMDerivedCollectionIndexArrayBased.ts
1816
+ var OIMDerivedCollectionIndexArrayBased = class extends OIMReactiveCollectionIndexManualArrayBased {
1817
+ constructor(queue, collection, opts) {
1818
+ super(queue, {
1819
+ collection,
1820
+ indexOptions: opts.indexOptions
1821
+ });
1822
+ this.keysByPk = /* @__PURE__ */ new Map();
1823
+ this.onCollectionUpdate = (pks) => {
1824
+ if (pks.length === 0) {
1825
+ this.rebuildFromCollection();
1826
+ return;
1827
+ }
1828
+ const affectedKeys = /* @__PURE__ */ new Set();
1829
+ for (const pk of pks) {
1830
+ this.trackPkKeys(pk, affectedKeys);
1831
+ }
1832
+ for (const key of affectedKeys) {
1833
+ this.rebuildKey(key);
1834
+ }
1835
+ };
1836
+ this.collection = collection;
1837
+ this.selectIndexKeys = opts.selectIndexKeys;
1838
+ this.compareEntities = opts.compareEntities;
1839
+ this.orderBy = opts.orderBy;
1840
+ this.unsubscribeFromCollection = collection.emitter.on(
1841
+ 0 /* UPDATE */,
1842
+ (payload) => this.onCollectionUpdate(payload.pks)
1843
+ );
1844
+ if (opts.buildInitial !== false) {
1845
+ this.rebuildFromCollection();
1846
+ }
1847
+ }
1848
+ rebuildFromCollection() {
1849
+ this.keysByPk.clear();
1850
+ super.clear();
1851
+ const slotsByKey = /* @__PURE__ */ new Map();
1852
+ const slots = this.collection.getAllSlots();
1853
+ for (const slot of slots) {
1854
+ if (slot.item === void 0) continue;
1855
+ const keys = this.normalizeKeys(this.selectIndexKeys(slot.item));
1856
+ if (keys.length === 0) continue;
1857
+ this.keysByPk.set(slot.pk, new Set(keys));
1858
+ for (const key of keys) {
1859
+ let slotsForKey = slotsByKey.get(key);
1860
+ if (!slotsForKey) {
1861
+ slotsForKey = [];
1862
+ slotsByKey.set(key, slotsForKey);
1863
+ }
1864
+ slotsForKey.push(slot);
1865
+ }
1866
+ }
1867
+ for (const [key, keySlots] of slotsByKey) {
1868
+ this.setSlots(key, this.sortSlots(keySlots));
1619
1869
  }
1620
- this.isFlushing = false;
1621
- this.emitter.emit(0 /* AFTER_FLUSH */, void 0);
1622
1870
  }
1623
- /**
1624
- * Get the current number of queued functions.
1625
- */
1626
- get length() {
1627
- return this.tasks.size;
1871
+ clear(key) {
1872
+ if (key === void 0) {
1873
+ this.keysByPk.clear();
1874
+ } else {
1875
+ for (const keys of this.keysByPk.values()) {
1876
+ keys.delete(key);
1877
+ }
1878
+ }
1879
+ super.clear(key);
1628
1880
  }
1629
- /**
1630
- * Check if the queue is empty.
1631
- */
1632
- get isEmpty() {
1633
- return this.tasks.size === 0;
1881
+ destroy() {
1882
+ this.unsubscribeFromCollection();
1883
+ this.keysByPk.clear();
1884
+ super.destroy();
1634
1885
  }
1635
- /**
1636
- * Clear the queue without executing functions and cancel any scheduled flush.
1637
- */
1638
- clear() {
1639
- this.tasks.clear();
1640
- this.scheduler?.cancel();
1886
+ trackPkKeys(pk, affectedKeys) {
1887
+ const prevKeys = this.keysByPk.get(pk) ?? /* @__PURE__ */ new Set();
1888
+ for (const key of prevKeys) affectedKeys.add(key);
1889
+ const slot = this.collection.getSlotByPk(pk);
1890
+ const entity = slot?.item;
1891
+ const nextKeys = entity === void 0 ? /* @__PURE__ */ new Set() : new Set(this.normalizeKeys(this.selectIndexKeys(entity)));
1892
+ for (const key of nextKeys) affectedKeys.add(key);
1893
+ if (nextKeys.size === 0) {
1894
+ this.keysByPk.delete(pk);
1895
+ } else {
1896
+ this.keysByPk.set(pk, nextKeys);
1897
+ }
1641
1898
  }
1642
- ensureScheduled() {
1643
- if (!this.scheduler) return;
1644
- if (this.isFlushing) return;
1645
- if (this.tasks.size === 1) {
1646
- this.scheduler.schedule();
1899
+ rebuildKey(key) {
1900
+ const keySlots = [];
1901
+ const slots = this.collection.getAllSlots();
1902
+ for (const slot of slots) {
1903
+ if (slot.item === void 0) continue;
1904
+ const keys = this.keysByPk.get(slot.pk);
1905
+ if (keys?.has(key)) keySlots.push(slot);
1906
+ }
1907
+ if (keySlots.length === 0) {
1908
+ super.clear(key);
1909
+ } else {
1910
+ this.setSlots(key, this.sortSlots(keySlots));
1647
1911
  }
1648
1912
  }
1649
- /**
1650
- * Clean up scheduler subscription when queue is no longer needed.
1651
- */
1652
- destroy() {
1653
- if (this.scheduler && this.flushBound) {
1654
- this.scheduler.off(
1655
- 0 /* FLUSH */,
1656
- this.flushBound
1657
- );
1913
+ sortSlots(slots) {
1914
+ if (!this.compareEntities && !this.orderBy) return slots;
1915
+ return slots.slice().sort((a, b) => {
1916
+ const aItem = a.item;
1917
+ const bItem = b.item;
1918
+ if (aItem === void 0 || bItem === void 0) return 0;
1919
+ if (this.compareEntities) {
1920
+ return this.compareEntities(aItem, bItem);
1921
+ }
1922
+ const aValue = this.orderBy(aItem);
1923
+ const bValue = this.orderBy(bItem);
1924
+ if (aValue < bValue) return -1;
1925
+ if (aValue > bValue) return 1;
1926
+ return 0;
1927
+ });
1928
+ }
1929
+ normalizeKeys(keys) {
1930
+ if (keys === void 0 || keys === null) return [];
1931
+ const rawKeys = Array.isArray(keys) ? keys : [keys];
1932
+ const result = [];
1933
+ const seen = /* @__PURE__ */ new Set();
1934
+ for (const key of rawKeys) {
1935
+ if (seen.has(key)) continue;
1936
+ seen.add(key);
1937
+ result.push(key);
1658
1938
  }
1659
- this.clear();
1939
+ return result;
1660
1940
  }
1661
1941
  };
1662
1942
 
1663
- // src/core/OIMIndexComparatorFactory.ts
1664
- var OIMIndexComparatorFactory = class {
1665
- /**
1666
- * Create an element-wise comparator that checks arrays for strict equality.
1667
- * Compares array lengths and each element using strict equality (===).
1668
- */
1669
- static createElementWiseComparator() {
1670
- return (existingPks, newPks) => {
1671
- if (existingPks.length !== newPks.length) {
1672
- return false;
1673
- }
1674
- for (let i = 0; i < existingPks.length; i++) {
1675
- if (existingPks[i] !== newPks[i]) {
1676
- return false;
1677
- }
1678
- }
1679
- return true;
1680
- };
1943
+ // src/core/OIMCollectionIndexFactory.ts
1944
+ var OIMCollectionIndexFactory = class {
1945
+ constructor(queue, collection) {
1946
+ this.queue = queue;
1947
+ this.collection = collection;
1681
1948
  }
1682
- /**
1683
- * Create a set-based comparator that checks if arrays contain the same elements.
1684
- * Order doesn't matter, only the presence of elements.
1685
- */
1686
- static createSetBasedComparator() {
1687
- return (existingPks, newPks) => {
1688
- if (existingPks.length !== newPks.length) {
1689
- return false;
1690
- }
1691
- const existingSet = new Set(existingPks);
1692
- const newSet = new Set(newPks);
1693
- if (existingSet.size !== newSet.size || existingSet.size !== existingPks.length) {
1694
- return false;
1949
+ setBasedIndex(opts = {}) {
1950
+ return new OIMReactiveCollectionIndexManualSetBased(this.queue, {
1951
+ collection: this.collection,
1952
+ indexOptions: opts.indexOptions
1953
+ });
1954
+ }
1955
+ derivedSetIndex(selectIndexKeys, opts = {}) {
1956
+ return new OIMDerivedCollectionIndexSetBased(
1957
+ this.queue,
1958
+ this.collection,
1959
+ {
1960
+ selectIndexKeys,
1961
+ buildInitial: opts.buildInitial,
1962
+ indexOptions: opts.indexOptions
1695
1963
  }
1696
- for (const pk of Array.from(existingSet)) {
1697
- if (!newSet.has(pk)) {
1698
- return false;
1699
- }
1964
+ );
1965
+ }
1966
+ arrayBasedIndex(opts = {}) {
1967
+ return new OIMReactiveCollectionIndexManualArrayBased(this.queue, {
1968
+ collection: this.collection,
1969
+ indexOptions: opts.indexOptions
1970
+ });
1971
+ }
1972
+ derivedArrayIndex(selectIndexKeys, opts = {}) {
1973
+ return new OIMDerivedCollectionIndexArrayBased(
1974
+ this.queue,
1975
+ this.collection,
1976
+ {
1977
+ selectIndexKeys,
1978
+ buildInitial: opts.buildInitial,
1979
+ compareEntities: opts.compareEntities,
1980
+ orderBy: opts.orderBy,
1981
+ indexOptions: opts.indexOptions
1700
1982
  }
1701
- return true;
1702
- };
1983
+ );
1703
1984
  }
1704
- /**
1705
- * Create a shallow comparator that only checks array references.
1706
- * Fastest but only works if you're reusing the same array instances.
1707
- */
1708
- static createShallowComparator() {
1709
- return (existingPks, newPks) => {
1710
- return existingPks === newPks;
1985
+ orderedIndex() {
1986
+ return new OIMCollectionIndexManualOrderedArrayBased({
1987
+ collection: this.collection
1988
+ });
1989
+ }
1990
+ orderedList(opts = {}) {
1991
+ return new OIMCollectionOrderedListCommandStream(
1992
+ this.queue,
1993
+ opts.index ? { collection: this.collection, index: opts.index } : { collection: this.collection }
1994
+ );
1995
+ }
1996
+ };
1997
+ function createOIMCollectionIndexFactory(queue, collection) {
1998
+ return new OIMCollectionIndexFactory(queue, collection);
1999
+ }
2000
+
2001
+ // src/core/OIMObjectStoreRecordDriven.ts
2002
+ var OIMObjectStoreRecordDriven = class extends OIMObjectStore {
2003
+ constructor(initial) {
2004
+ super();
2005
+ this.properties = Object.assign(/* @__PURE__ */ Object.create(null), initial ?? {});
2006
+ }
2007
+ setProperty(key, value) {
2008
+ this.properties[key] = value;
2009
+ }
2010
+ merge(draft) {
2011
+ Object.assign(this.properties, draft);
2012
+ }
2013
+ delete(key) {
2014
+ delete this.properties[key];
2015
+ }
2016
+ get(key) {
2017
+ return this.properties[key];
2018
+ }
2019
+ getAll() {
2020
+ return { ...this.properties };
2021
+ }
2022
+ clear() {
2023
+ this.properties = /* @__PURE__ */ Object.create(null);
2024
+ }
2025
+ count() {
2026
+ return Object.keys(this.properties).length;
2027
+ }
2028
+ keys() {
2029
+ return Object.keys(this.properties);
2030
+ }
2031
+ values() {
2032
+ return Object.values(this.properties);
2033
+ }
2034
+ entries() {
2035
+ return Object.entries(this.properties);
2036
+ }
2037
+ destroy() {
2038
+ this.clear();
2039
+ }
2040
+ };
2041
+
2042
+ // src/enums/EOIMObjectEventType.ts
2043
+ var EOIMObjectEventType = /* @__PURE__ */ ((EOIMObjectEventType2) => {
2044
+ EOIMObjectEventType2[EOIMObjectEventType2["UPDATE"] = 0] = "UPDATE";
2045
+ return EOIMObjectEventType2;
2046
+ })(EOIMObjectEventType || {});
2047
+
2048
+ // src/core/OIMObject.ts
2049
+ var OIMObject = class {
2050
+ constructor(opts) {
2051
+ this.emitter = new OIMEventEmitter();
2052
+ this.store = opts?.store ?? new OIMObjectStoreRecordDriven();
2053
+ }
2054
+ setProperty(key, value) {
2055
+ this.store.setProperty(key, value);
2056
+ this.emitter.emit(0 /* UPDATE */, { keys: [key] });
2057
+ }
2058
+ merge(draft) {
2059
+ this.store.merge(draft);
2060
+ this.emitter.emit(0 /* UPDATE */, {
2061
+ keys: Object.keys(draft)
2062
+ });
2063
+ }
2064
+ delete(key) {
2065
+ this.store.delete(key);
2066
+ this.emitter.emit(0 /* UPDATE */, { keys: [key] });
2067
+ }
2068
+ get(key) {
2069
+ return this.store.get(key);
2070
+ }
2071
+ getAll() {
2072
+ return this.store.getAll();
2073
+ }
2074
+ clear() {
2075
+ this.store.clear();
2076
+ this.emitter.emit(0 /* UPDATE */, { keys: [] });
2077
+ }
2078
+ count() {
2079
+ return this.store.count();
2080
+ }
2081
+ keys() {
2082
+ return this.store.keys();
2083
+ }
2084
+ values() {
2085
+ return this.store.values();
2086
+ }
2087
+ entries() {
2088
+ return this.store.entries();
2089
+ }
2090
+ destroy() {
2091
+ this.store.destroy();
2092
+ this.emitter.offAll();
2093
+ }
2094
+ };
2095
+
2096
+ // src/core/OIMReactiveObject.ts
2097
+ var OIMReactiveObject = class extends OIMObject {
2098
+ constructor(queue, opts) {
2099
+ super(opts);
2100
+ this.updateEmitter = new OIMUpdateEventEmitter(queue, "queue");
2101
+ this.emitter.on(0 /* UPDATE */, (payload) => {
2102
+ this.updateEmitter.markUpdatedKeys(payload.keys);
2103
+ });
2104
+ }
2105
+ subscribeOnKey(key, handler) {
2106
+ return this.updateEmitter.subscribeOnKey(key, handler);
2107
+ }
2108
+ subscribeOnKeys(keys, handler) {
2109
+ return this.updateEmitter.subscribeOnKeys(keys, handler);
2110
+ }
2111
+ unsubscribeFromKey(key, handler) {
2112
+ this.updateEmitter.unsubscribeFromKey(key, handler);
2113
+ }
2114
+ unsubscribeFromKeys(keys, handler) {
2115
+ this.updateEmitter.unsubscribeFromKeys(keys, handler);
2116
+ }
2117
+ hasSubscriptions() {
2118
+ return this.updateEmitter.hasSubscriptions();
2119
+ }
2120
+ getHandlerCount(key) {
2121
+ return this.updateEmitter.getHandlerCount(key);
2122
+ }
2123
+ getMetrics() {
2124
+ return this.updateEmitter.getMetrics();
2125
+ }
2126
+ destroy() {
2127
+ this.updateEmitter.destroy();
2128
+ super.destroy();
2129
+ }
2130
+ };
2131
+
2132
+ // src/core/OIMObjectStoreMapDriven.ts
2133
+ var OIMObjectStoreMapDriven = class extends OIMObjectStore {
2134
+ constructor() {
2135
+ super(...arguments);
2136
+ this.properties = /* @__PURE__ */ new Map();
2137
+ }
2138
+ setProperty(key, value) {
2139
+ this.properties.set(key, value);
2140
+ }
2141
+ merge(draft) {
2142
+ for (const key in draft) {
2143
+ if (!Object.prototype.hasOwnProperty.call(draft, key)) continue;
2144
+ this.properties.set(
2145
+ key,
2146
+ draft[key]
2147
+ );
2148
+ }
2149
+ }
2150
+ delete(key) {
2151
+ this.properties.delete(key);
2152
+ }
2153
+ get(key) {
2154
+ return this.properties.get(key);
2155
+ }
2156
+ getAll() {
2157
+ return Object.fromEntries(this.properties.entries());
2158
+ }
2159
+ clear() {
2160
+ this.properties.clear();
2161
+ }
2162
+ count() {
2163
+ return this.properties.size;
2164
+ }
2165
+ keys() {
2166
+ return Array.from(this.properties.keys());
2167
+ }
2168
+ values() {
2169
+ return Array.from(this.properties.values());
2170
+ }
2171
+ entries() {
2172
+ return Array.from(this.properties.entries());
2173
+ }
2174
+ destroy() {
2175
+ this.properties.clear();
2176
+ }
2177
+ };
2178
+
2179
+ // src/core/OIMComparatorFactory.ts
2180
+ var OIMComparatorFactory = class {
2181
+ createShallowComparator() {
2182
+ return (a, b) => {
2183
+ if (a === b) return false;
2184
+ const ka = Object.keys(a);
2185
+ const kb = Object.keys(b);
2186
+ if (ka.length !== kb.length) return true;
2187
+ for (let i = 0; i < ka.length; i++) {
2188
+ const k = ka[i];
2189
+ if (b[k] !== a[k])
2190
+ return true;
2191
+ }
2192
+ return false;
2193
+ };
2194
+ }
2195
+ };
2196
+
2197
+ // src/core/OIMEventQueue.ts
2198
+ var OIMEventQueue = class {
2199
+ constructor(options = {}) {
2200
+ this.emitter = new OIMEventEmitter();
2201
+ this.tasks = /* @__PURE__ */ new Set();
2202
+ this.isFlushing = false;
2203
+ this.scheduler = options.scheduler;
2204
+ if (this.scheduler) {
2205
+ this.flushBound = () => this.flush();
2206
+ this.scheduler.on(
2207
+ 0 /* FLUSH */,
2208
+ this.flushBound
2209
+ );
2210
+ }
2211
+ }
2212
+ /**
2213
+ * Enqueue a one-shot task to be executed on the next flush.
2214
+ * Returns a dequeue function that can be called to cancel the task before it runs.
2215
+ */
2216
+ enqueue(fn) {
2217
+ let isActive = true;
2218
+ const task = () => {
2219
+ if (!isActive) return;
2220
+ fn();
2221
+ };
2222
+ this.tasks.add(task);
2223
+ this.ensureScheduled();
2224
+ return () => {
2225
+ if (!isActive) return;
2226
+ isActive = false;
2227
+ this.tasks.delete(task);
2228
+ };
2229
+ }
2230
+ get isInFlush() {
2231
+ return this.isFlushing;
2232
+ }
2233
+ /**
2234
+ * Dequeue (cancel) a previously enqueued task by its dequeue function.
2235
+ */
2236
+ dequeue(dequeueFn) {
2237
+ dequeueFn();
2238
+ }
2239
+ /**
2240
+ * Execute all pending tasks.
2241
+ *
2242
+ * This flush drains until the queue is empty. If tasks enqueue more tasks, they will be
2243
+ * executed within the same flush.
2244
+ */
2245
+ flush() {
2246
+ this.scheduler?.cancel();
2247
+ this.isFlushing = true;
2248
+ this.emitter.emit(1 /* BEFORE_FLUSH */, void 0);
2249
+ const snapshot = Array.from(this.tasks);
2250
+ this.tasks.clear();
2251
+ for (let i = 0; i < snapshot.length; i++) {
2252
+ snapshot[i]();
2253
+ }
2254
+ this.isFlushing = false;
2255
+ this.emitter.emit(0 /* AFTER_FLUSH */, void 0);
2256
+ }
2257
+ /**
2258
+ * Get the current number of queued functions.
2259
+ */
2260
+ get length() {
2261
+ return this.tasks.size;
2262
+ }
2263
+ /**
2264
+ * Check if the queue is empty.
2265
+ */
2266
+ get isEmpty() {
2267
+ return this.tasks.size === 0;
2268
+ }
2269
+ /**
2270
+ * Clear the queue without executing functions and cancel any scheduled flush.
2271
+ */
2272
+ clear() {
2273
+ this.tasks.clear();
2274
+ this.scheduler?.cancel();
2275
+ }
2276
+ ensureScheduled() {
2277
+ if (!this.scheduler) return;
2278
+ if (this.isFlushing) return;
2279
+ if (this.tasks.size === 1) {
2280
+ this.scheduler.schedule();
2281
+ }
2282
+ }
2283
+ /**
2284
+ * Clean up scheduler subscription when queue is no longer needed.
2285
+ */
2286
+ destroy() {
2287
+ if (this.scheduler && this.flushBound) {
2288
+ this.scheduler.off(
2289
+ 0 /* FLUSH */,
2290
+ this.flushBound
2291
+ );
2292
+ }
2293
+ this.clear();
2294
+ }
2295
+ };
2296
+
2297
+ // src/core/OIMIndexComparatorFactory.ts
2298
+ var OIMIndexComparatorFactory = class {
2299
+ /**
2300
+ * Create an element-wise comparator that checks arrays for strict equality.
2301
+ * Compares array lengths and each element using strict equality (===).
2302
+ */
2303
+ static createElementWiseComparator() {
2304
+ return (existingPks, newPks) => {
2305
+ if (existingPks.length !== newPks.length) {
2306
+ return false;
2307
+ }
2308
+ for (let i = 0; i < existingPks.length; i++) {
2309
+ if (existingPks[i] !== newPks[i]) {
2310
+ return false;
2311
+ }
2312
+ }
2313
+ return true;
2314
+ };
2315
+ }
2316
+ /**
2317
+ * Create a set-based comparator that checks if arrays contain the same elements.
2318
+ * Order doesn't matter, only the presence of elements.
2319
+ */
2320
+ static createSetBasedComparator() {
2321
+ return (existingPks, newPks) => {
2322
+ if (existingPks.length !== newPks.length) {
2323
+ return false;
2324
+ }
2325
+ const existingSet = new Set(existingPks);
2326
+ const newSet = new Set(newPks);
2327
+ if (existingSet.size !== newSet.size || existingSet.size !== existingPks.length) {
2328
+ return false;
2329
+ }
2330
+ for (const pk of Array.from(existingSet)) {
2331
+ if (!newSet.has(pk)) {
2332
+ return false;
2333
+ }
2334
+ }
2335
+ return true;
2336
+ };
2337
+ }
2338
+ /**
2339
+ * Create a shallow comparator that only checks array references.
2340
+ * Fastest but only works if you're reusing the same array instances.
2341
+ */
2342
+ static createShallowComparator() {
2343
+ return (existingPks, newPks) => {
2344
+ return existingPks === newPks;
2345
+ };
2346
+ }
2347
+ /**
2348
+ * Create a no-comparison comparator that always returns false (always updates).
2349
+ * Useful for disabling comparison while keeping the same interface.
2350
+ */
2351
+ static createAlwaysUpdateComparator() {
2352
+ return () => false;
2353
+ }
2354
+ };
2355
+
2356
+ // src/core/OIMMap2Keys.ts
2357
+ var OIMMap2Keys = class {
2358
+ constructor() {
2359
+ this.map = /* @__PURE__ */ new Map();
2360
+ this._size = 0;
2361
+ }
2362
+ get size() {
2363
+ return this._size;
2364
+ }
2365
+ set(k1, k2, v) {
2366
+ let m = this.map.get(k1);
2367
+ if (!m) {
2368
+ m = /* @__PURE__ */ new Map();
2369
+ this.map.set(k1, m);
2370
+ }
2371
+ if (!m.has(k2)) this._size++;
2372
+ m.set(k2, v);
2373
+ }
2374
+ get(k1, k2) {
2375
+ return this.map.get(k1)?.get(k2);
2376
+ }
2377
+ has(k1, k2) {
2378
+ return this.map.get(k1)?.has(k2);
2379
+ }
2380
+ delete(k1, k2) {
2381
+ const m = this.map.get(k1);
2382
+ if (!m) return false;
2383
+ const had = m.delete(k2);
2384
+ if (had) {
2385
+ this._size--;
2386
+ if (m.size === 0) this.map.delete(k1);
2387
+ }
2388
+ return had;
2389
+ }
2390
+ clear() {
2391
+ this.map.clear();
2392
+ this._size = 0;
2393
+ }
2394
+ };
2395
+
2396
+ // src/modules/compute/core/OIMComputeRuntime.ts
2397
+ var OIMComputeRuntime = class {
2398
+ constructor(queue) {
2399
+ this.queue = queue;
2400
+ this.isFlushScheduled = false;
2401
+ this.isFlushing = false;
2402
+ this.minLevel = Infinity;
2403
+ this.maxLevel = -Infinity;
2404
+ this.dirtyByLevel = /* @__PURE__ */ new Map();
2405
+ this.afterFlushTasks = /* @__PURE__ */ new Set();
2406
+ this.queue.emitter.on(0 /* AFTER_FLUSH */, () => {
2407
+ if (!this.isFlushScheduled) return;
2408
+ this.flush();
2409
+ });
2410
+ }
2411
+ /**
2412
+ * Schedule a one-shot task to run on the next `queue.flush()` (AFTER_FLUSH),
2413
+ * ordered by `level` ascending.
2414
+ */
2415
+ schedule(task, level = 0) {
2416
+ let isActive = true;
2417
+ const wrapped = () => {
2418
+ if (!isActive) return;
2419
+ task();
2420
+ };
2421
+ let bucket = this.dirtyByLevel.get(level);
2422
+ if (!bucket) {
2423
+ bucket = /* @__PURE__ */ new Set();
2424
+ this.dirtyByLevel.set(level, bucket);
2425
+ }
2426
+ bucket.add(wrapped);
2427
+ if (level < this.minLevel) this.minLevel = level;
2428
+ if (level > this.maxLevel) this.maxLevel = level;
2429
+ if (!this.isFlushing) this.isFlushScheduled = true;
2430
+ return () => {
2431
+ if (!isActive) return;
2432
+ isActive = false;
2433
+ bucket.delete(wrapped);
2434
+ };
2435
+ }
2436
+ /**
2437
+ * Run once at the start of the next runtime flush (i.e. after queue.flush()).
2438
+ * Useful for resubscribing dependencies exactly once per source flush.
2439
+ */
2440
+ scheduleAfterFlush(task) {
2441
+ let isActive = true;
2442
+ const wrapped = () => {
2443
+ if (!isActive) return;
2444
+ task();
2445
+ };
2446
+ this.afterFlushTasks.add(wrapped);
2447
+ if (!this.isFlushing) this.isFlushScheduled = true;
2448
+ return () => {
2449
+ if (!isActive) return;
2450
+ isActive = false;
2451
+ this.afterFlushTasks.delete(wrapped);
2452
+ };
2453
+ }
2454
+ flush() {
2455
+ this.isFlushScheduled = false;
2456
+ if (this.dirtyByLevel.size === 0 && this.afterFlushTasks.size === 0)
2457
+ return;
2458
+ this.isFlushing = true;
2459
+ try {
2460
+ if (this.afterFlushTasks.size > 0) {
2461
+ const snapshot = Array.from(this.afterFlushTasks);
2462
+ this.afterFlushTasks.clear();
2463
+ for (let i = 0; i < snapshot.length; i++) snapshot[i]();
2464
+ }
2465
+ if (this.dirtyByLevel.size === 0) return;
2466
+ let level = this.minLevel;
2467
+ let max = this.maxLevel;
2468
+ while (level <= max) {
2469
+ for (; ; ) {
2470
+ const bucket = this.dirtyByLevel.get(level);
2471
+ if (!bucket || bucket.size === 0) break;
2472
+ this.dirtyByLevel.delete(level);
2473
+ const snapshot = Array.from(bucket);
2474
+ for (let i = 0; i < snapshot.length; i++) snapshot[i]();
2475
+ }
2476
+ if (this.maxLevel > max) max = this.maxLevel;
2477
+ level++;
2478
+ }
2479
+ } finally {
2480
+ this.isFlushing = false;
2481
+ if (this.dirtyByLevel.size === 0) {
2482
+ this.minLevel = Infinity;
2483
+ this.maxLevel = -Infinity;
2484
+ } else {
2485
+ let nextMin = Infinity;
2486
+ let nextMax = -Infinity;
2487
+ this.dirtyByLevel.forEach((_bucket, lvl) => {
2488
+ if (lvl < nextMin) nextMin = lvl;
2489
+ if (lvl > nextMax) nextMax = lvl;
2490
+ });
2491
+ this.minLevel = nextMin;
2492
+ this.maxLevel = nextMax;
2493
+ this.isFlushScheduled = true;
2494
+ }
2495
+ }
2496
+ }
2497
+ };
2498
+
2499
+ // src/modules/selector/core/OIMSelector.ts
2500
+ var OIMSelector = class {
2501
+ constructor(runtime, sourceDeps, computedDeps = []) {
2502
+ this.runtime = runtime;
2503
+ this.sourceDeps = sourceDeps;
2504
+ this.computedDeps = computedDeps;
2505
+ this.listeners = /* @__PURE__ */ new Set();
2506
+ this.sourceUnsubscribers = [];
2507
+ this.computedUnsubscribers = [];
2508
+ this.isResubscribeEnqueued = false;
2509
+ this.isTemporarilyUnsubscribedForFlush = false;
2510
+ this.isDeliveryEnqueued = false;
2511
+ this.hasLastValue = false;
2512
+ this.onSourceInvalidate = () => {
2513
+ if (this.listeners.size === 0) return;
2514
+ if (!this.isTemporarilyUnsubscribedForFlush) {
2515
+ this.isTemporarilyUnsubscribedForFlush = true;
2516
+ this.unsubscribeFromSourceDeps();
2517
+ this.ensureResubscribeAfterFlush();
2518
+ }
2519
+ if (this.isDeliveryEnqueued) return;
2520
+ this.isDeliveryEnqueued = true;
2521
+ this.dequeueDelivery = this.runtime.schedule(this.deliver);
2522
+ };
2523
+ this.onComputedInvalidate = () => {
2524
+ if (this.listeners.size === 0) return;
2525
+ if (this.isDeliveryEnqueued) return;
2526
+ this.isDeliveryEnqueued = true;
2527
+ this.dequeueDelivery = this.runtime.schedule(this.deliver);
2528
+ };
2529
+ this.deliver = () => {
2530
+ this.isDeliveryEnqueued = false;
2531
+ this.dequeueDelivery = void 0;
2532
+ if (this.listeners.size === 0) return;
2533
+ const next = this.getValue();
2534
+ if (this.hasLastValue && this.areEqual(this.lastValue, next)) return;
2535
+ this.setLastValue(next);
2536
+ const snapshot = Array.from(this.listeners);
2537
+ for (let i = 0; i < snapshot.length; i++) {
2538
+ const listener = snapshot[i];
2539
+ if (this.listeners.has(listener)) listener(next);
2540
+ }
2541
+ };
2542
+ this.onAfterFlush = () => {
2543
+ if (!this.isResubscribeEnqueued) return;
2544
+ this.isResubscribeEnqueued = false;
2545
+ this.dequeueResubscribe = void 0;
2546
+ if (this.listeners.size === 0) return;
2547
+ this.isTemporarilyUnsubscribedForFlush = false;
2548
+ if (this.sourceUnsubscribers.length === 0) {
2549
+ for (const dep of this.sourceDeps) {
2550
+ this.sourceUnsubscribers.push(
2551
+ dep.subscribe(this.onSourceInvalidate)
2552
+ );
2553
+ }
2554
+ }
2555
+ };
2556
+ }
2557
+ get queue() {
2558
+ return this.runtime.queue;
2559
+ }
2560
+ /**
2561
+ * Start watching this selector. The callback is called immediately with the current value,
2562
+ * then coalesced and delivered on `queue.flush()` when the selector invalidates.
2563
+ */
2564
+ watch(onValue) {
2565
+ this.listeners.add(onValue);
2566
+ if (this.listeners.size === 1) {
2567
+ this.subscribeToDeps();
2568
+ }
2569
+ const current = this.getValue();
2570
+ this.setLastValue(current);
2571
+ onValue(current);
2572
+ return () => {
2573
+ this.listeners.delete(onValue);
2574
+ if (this.listeners.size > 0) return;
2575
+ this.unsubscribeFromAllDeps();
2576
+ if (this.dequeueResubscribe) {
2577
+ this.dequeueResubscribe();
2578
+ this.dequeueResubscribe = void 0;
2579
+ }
2580
+ this.isResubscribeEnqueued = false;
2581
+ this.isTemporarilyUnsubscribedForFlush = false;
2582
+ if (this.dequeueDelivery) {
2583
+ this.dequeueDelivery();
2584
+ this.dequeueDelivery = void 0;
2585
+ }
2586
+ this.isDeliveryEnqueued = false;
2587
+ this.hasLastValue = false;
2588
+ };
2589
+ }
2590
+ areEqual(prev, next) {
2591
+ return Object.is(prev, next);
2592
+ }
2593
+ setLastValue(value) {
2594
+ this.lastValue = value;
2595
+ this.hasLastValue = true;
2596
+ }
2597
+ subscribeToDeps() {
2598
+ if (this.sourceUnsubscribers.length > 0 || this.computedUnsubscribers.length > 0)
2599
+ return;
2600
+ for (const dep of this.sourceDeps) {
2601
+ this.sourceUnsubscribers.push(
2602
+ dep.subscribe(this.onSourceInvalidate)
2603
+ );
2604
+ }
2605
+ for (const dep of this.computedDeps) {
2606
+ this.computedUnsubscribers.push(
2607
+ dep.subscribe(this.onComputedInvalidate)
2608
+ );
2609
+ }
2610
+ }
2611
+ unsubscribeFromSourceDeps() {
2612
+ if (this.sourceUnsubscribers.length === 0) return;
2613
+ for (const unsub of this.sourceUnsubscribers) unsub();
2614
+ this.sourceUnsubscribers = [];
2615
+ }
2616
+ unsubscribeFromAllDeps() {
2617
+ this.unsubscribeFromSourceDeps();
2618
+ if (this.computedUnsubscribers.length === 0) return;
2619
+ for (const unsub of this.computedUnsubscribers) unsub();
2620
+ this.computedUnsubscribers = [];
2621
+ }
2622
+ ensureResubscribeAfterFlush() {
2623
+ if (this.isResubscribeEnqueued) return;
2624
+ this.isResubscribeEnqueued = true;
2625
+ this.dequeueResubscribe = this.runtime.scheduleAfterFlush(
2626
+ this.onAfterFlush
2627
+ );
2628
+ }
2629
+ };
2630
+
2631
+ // src/modules/selector/core/OIMSelectorSourceDependencyUpdateEventEmitterKey.ts
2632
+ var OIMSelectorSourceDependencyUpdateEventEmitterKey = class {
2633
+ constructor(subscription, key) {
2634
+ this.subscription = subscription;
2635
+ this.key = key;
2636
+ }
2637
+ subscribe(onUpdate) {
2638
+ return this.subscription.subscribeOnKey(this.key, onUpdate);
2639
+ }
2640
+ };
2641
+
2642
+ // src/modules/selector/core/OIMCollectionByPkSelector.ts
2643
+ var OIMCollectionByPkSelector = class extends OIMSelector {
2644
+ constructor(runtime, collection, pk) {
2645
+ super(runtime, [
2646
+ new OIMSelectorSourceDependencyUpdateEventEmitterKey(
2647
+ collection,
2648
+ pk
2649
+ )
2650
+ ]);
2651
+ this.collection = collection;
2652
+ this.pk = pk;
2653
+ }
2654
+ getValue() {
2655
+ return this.collection.getOneByPk(this.pk);
2656
+ }
2657
+ };
2658
+
2659
+ // src/modules/selector/core/OIMSelectorSourceDependencyUpdateEventEmitterKeys.ts
2660
+ var OIMSelectorSourceDependencyUpdateEventEmitterKeys = class {
2661
+ constructor(subscription, keys) {
2662
+ this.subscription = subscription;
2663
+ this.keys = keys;
2664
+ }
2665
+ subscribe(onUpdate) {
2666
+ return this.subscription.subscribeOnKeys(this.keys, onUpdate);
2667
+ }
2668
+ };
2669
+
2670
+ // src/modules/selector/core/OIMCollectionByPksSelector.ts
2671
+ var OIMCollectionByPksSelector = class extends OIMSelector {
2672
+ constructor(runtime, collection, pks) {
2673
+ super(runtime, [
2674
+ new OIMSelectorSourceDependencyUpdateEventEmitterKeys(
2675
+ collection,
2676
+ pks
2677
+ )
2678
+ ]);
2679
+ this.collection = collection;
2680
+ this.pks = pks;
2681
+ }
2682
+ getValue() {
2683
+ return this.pks.map((pk) => this.collection.getOneByPk(pk));
2684
+ }
2685
+ areEqual(prev, next) {
2686
+ if (prev === next) return true;
2687
+ if (prev.length !== next.length) return false;
2688
+ for (let i = 0; i < prev.length; i++) {
2689
+ if (prev[i] !== next[i]) return false;
2690
+ }
2691
+ return true;
2692
+ }
2693
+ };
2694
+
2695
+ // src/modules/selector/core/OIMSelectorSourceDependencyEntitiesByIndexKeyArrayBased.ts
2696
+ var OIMSelectorSourceDependencyEntitiesByIndexKeyArrayBased = class {
2697
+ constructor(collection, reactiveIndex, key) {
2698
+ this.collection = collection;
2699
+ this.reactiveIndex = reactiveIndex;
2700
+ this.key = key;
2701
+ this.currentPks = [];
2702
+ }
2703
+ subscribe(onUpdate) {
2704
+ const resubscribeCollection = () => {
2705
+ const nextPks = this.reactiveIndex.getPksByKey(this.key);
2706
+ if (this.sameArray(this.currentPks, nextPks)) return;
2707
+ this.unsubscribeFromCollectionKeys?.();
2708
+ this.unsubscribeFromCollectionKeys = void 0;
2709
+ this.currentPks = nextPks;
2710
+ if (this.currentPks.length === 0) return;
2711
+ this.unsubscribeFromCollectionKeys = this.collection.subscribeOnKeys(this.currentPks, onUpdate);
2712
+ };
2713
+ const onIndexUpdate = () => {
2714
+ resubscribeCollection();
2715
+ onUpdate();
2716
+ };
2717
+ resubscribeCollection();
2718
+ const unsubscribeFromIndex = this.reactiveIndex.subscribeOnKey(
2719
+ this.key,
2720
+ onIndexUpdate
2721
+ );
2722
+ return () => {
2723
+ unsubscribeFromIndex();
2724
+ this.unsubscribeFromCollectionKeys?.();
2725
+ this.unsubscribeFromCollectionKeys = void 0;
2726
+ this.currentPks = [];
2727
+ };
2728
+ }
2729
+ sameArray(a, b) {
2730
+ if (a === b) return true;
2731
+ if (a.length !== b.length) return false;
2732
+ for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
2733
+ return true;
2734
+ }
2735
+ };
2736
+
2737
+ // src/modules/selector/core/OIMEntitiesByIndexKeyArrayBasedSelector.ts
2738
+ var OIMEntitiesByIndexKeyArrayBasedSelector = class extends OIMSelector {
2739
+ constructor(runtime, collection, reactiveIndex, key) {
2740
+ super(runtime, [
2741
+ new OIMSelectorSourceDependencyEntitiesByIndexKeyArrayBased(collection, reactiveIndex, key)
2742
+ ]);
2743
+ this.collection = collection;
2744
+ this.reactiveIndex = reactiveIndex;
2745
+ this.key = key;
2746
+ }
2747
+ getValue() {
2748
+ const pks = this.reactiveIndex.getPksByKey(this.key);
2749
+ const entities = [];
2750
+ entities.length = pks.length;
2751
+ for (let i = 0; i < pks.length; i++) {
2752
+ entities[i] = this.collection.getOneByPk(pks[i]);
2753
+ }
2754
+ return entities;
2755
+ }
2756
+ areEqual(prev, next) {
2757
+ if (prev === next) return true;
2758
+ if (prev.length !== next.length) return false;
2759
+ for (let i = 0; i < prev.length; i++)
2760
+ if (prev[i] !== next[i]) return false;
2761
+ return true;
2762
+ }
2763
+ };
2764
+
2765
+ // src/modules/selector/core/OIMSelectorSourceDependencyEntitiesByIndexKeySetBased.ts
2766
+ var OIMSelectorSourceDependencyEntitiesByIndexKeySetBased = class {
2767
+ constructor(collection, reactiveIndex, key) {
2768
+ this.collection = collection;
2769
+ this.reactiveIndex = reactiveIndex;
2770
+ this.key = key;
2771
+ this.currentPks = [];
2772
+ }
2773
+ subscribe(onUpdate) {
2774
+ const resubscribeCollection = () => {
2775
+ const nextPks = Array.from(this.reactiveIndex.getPksByKey(this.key));
2776
+ if (this.sameArray(this.currentPks, nextPks)) return;
2777
+ this.unsubscribeFromCollectionKeys?.();
2778
+ this.unsubscribeFromCollectionKeys = void 0;
2779
+ this.currentPks = nextPks;
2780
+ if (this.currentPks.length === 0) return;
2781
+ this.unsubscribeFromCollectionKeys = this.collection.subscribeOnKeys(this.currentPks, onUpdate);
2782
+ };
2783
+ const onIndexUpdate = () => {
2784
+ resubscribeCollection();
2785
+ onUpdate();
2786
+ };
2787
+ resubscribeCollection();
2788
+ const unsubscribeFromIndex = this.reactiveIndex.subscribeOnKey(
2789
+ this.key,
2790
+ onIndexUpdate
2791
+ );
2792
+ return () => {
2793
+ unsubscribeFromIndex();
2794
+ this.unsubscribeFromCollectionKeys?.();
2795
+ this.unsubscribeFromCollectionKeys = void 0;
2796
+ this.currentPks = [];
1711
2797
  };
1712
2798
  }
1713
- /**
1714
- * Create a no-comparison comparator that always returns false (always updates).
1715
- * Useful for disabling comparison while keeping the same interface.
1716
- */
1717
- static createAlwaysUpdateComparator() {
1718
- return () => false;
2799
+ sameArray(a, b) {
2800
+ if (a === b) return true;
2801
+ if (a.length !== b.length) return false;
2802
+ for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
2803
+ return true;
1719
2804
  }
1720
2805
  };
1721
2806
 
1722
- // src/core/OIMMap2Keys.ts
1723
- var OIMMap2Keys = class {
1724
- constructor() {
1725
- this.map = /* @__PURE__ */ new Map();
1726
- this._size = 0;
1727
- }
1728
- get size() {
1729
- return this._size;
2807
+ // src/modules/selector/core/OIMEntitiesByIndexKeySetBasedSelector.ts
2808
+ var OIMEntitiesByIndexKeySetBasedSelector = class extends OIMSelector {
2809
+ constructor(runtime, collection, reactiveIndex, key) {
2810
+ super(runtime, [
2811
+ new OIMSelectorSourceDependencyEntitiesByIndexKeySetBased(collection, reactiveIndex, key)
2812
+ ]);
2813
+ this.collection = collection;
2814
+ this.reactiveIndex = reactiveIndex;
2815
+ this.key = key;
1730
2816
  }
1731
- set(k1, k2, v) {
1732
- let m = this.map.get(k1);
1733
- if (!m) {
1734
- m = /* @__PURE__ */ new Map();
1735
- this.map.set(k1, m);
2817
+ getValue() {
2818
+ const pks = this.reactiveIndex.getPksByKey(this.key);
2819
+ const entities = [];
2820
+ entities.length = pks.size;
2821
+ let writeIndex = 0;
2822
+ for (const pk of pks) {
2823
+ entities[writeIndex++] = this.collection.getOneByPk(pk);
1736
2824
  }
1737
- if (!m.has(k2)) this._size++;
1738
- m.set(k2, v);
2825
+ return entities;
1739
2826
  }
1740
- get(k1, k2) {
1741
- return this.map.get(k1)?.get(k2);
2827
+ areEqual(prev, next) {
2828
+ if (prev === next) return true;
2829
+ if (prev.length !== next.length) return false;
2830
+ for (let i = 0; i < prev.length; i++)
2831
+ if (prev[i] !== next[i]) return false;
2832
+ return true;
1742
2833
  }
1743
- has(k1, k2) {
1744
- return this.map.get(k1)?.has(k2);
2834
+ };
2835
+
2836
+ // src/dx/OIMCollectionSelectors.ts
2837
+ var OIMCollectionSelectors = class {
2838
+ constructor(queue, collection) {
2839
+ this.collection = collection;
2840
+ this.runtime = new OIMComputeRuntime(queue);
1745
2841
  }
1746
- delete(k1, k2) {
1747
- const m = this.map.get(k1);
1748
- if (!m) return false;
1749
- const had = m.delete(k2);
1750
- if (had) {
1751
- this._size--;
1752
- if (m.size === 0) this.map.delete(k1);
1753
- }
1754
- return had;
2842
+ byPk(pk) {
2843
+ return new OIMCollectionByPkSelector(this.runtime, this.collection, pk);
1755
2844
  }
1756
- clear() {
1757
- this.map.clear();
1758
- this._size = 0;
2845
+ byPks(pks) {
2846
+ return new OIMCollectionByPksSelector(
2847
+ this.runtime,
2848
+ this.collection,
2849
+ pks
2850
+ );
2851
+ }
2852
+ entitiesBySetIndexKey(index, key) {
2853
+ return new OIMEntitiesByIndexKeySetBasedSelector(
2854
+ this.runtime,
2855
+ this.collection,
2856
+ index,
2857
+ key
2858
+ );
2859
+ }
2860
+ entitiesByArrayIndexKey(index, key) {
2861
+ return new OIMEntitiesByIndexKeyArrayBasedSelector(
2862
+ this.runtime,
2863
+ this.collection,
2864
+ index,
2865
+ key
2866
+ );
1759
2867
  }
1760
2868
  };
1761
2869
 
1762
- // src/modules/wrapper/collection/OIMCollectionChangedFieldsWrapper.ts
1763
- var OIMCollectionChangedFieldsWrapper = class {
2870
+ // src/dx/OIMCollectionContext.ts
2871
+ function createOIMCollectionContext(queue, opts) {
2872
+ const collection = new OIMReactiveCollection(queue, opts);
2873
+ const indexFactory = new OIMCollectionIndexFactory(queue, collection);
2874
+ const select = new OIMCollectionSelectors(queue, collection);
2875
+ return { queue, collection, indexFactory, select };
2876
+ }
2877
+
2878
+ // src/modules/wrapper/collection/OIMCollectionChangedFields.ts
2879
+ var OIMCollectionChangedFields = class {
1764
2880
  constructor(queue, collection, opts) {
1765
2881
  this.isInWrite = false;
1766
2882
  this.lastEntitySnapshotByPk = /* @__PURE__ */ new Map();
@@ -1780,7 +2896,7 @@ var OIMCollectionChangedFieldsWrapper = class {
1780
2896
  this.collection = collection;
1781
2897
  this.selectPk = opts?.selectPk ?? (() => {
1782
2898
  throw new Error(
1783
- `[OIMCollectionChangedFieldsWrapper]: selectPk was not provided. Use upsertOneByPk(...) or pass opts.selectPk.`
2899
+ `[OIMCollectionChangedFields]: selectPk was not provided. Use upsertOneByPk(...) or pass opts.selectPk.`
1784
2900
  );
1785
2901
  });
1786
2902
  this.changedPksEventEmitter = new OIMUpdateEventEmitter(
@@ -1803,31 +2919,34 @@ var OIMCollectionChangedFieldsWrapper = class {
1803
2919
  );
1804
2920
  }
1805
2921
  upsertOneByPk(pk, patch) {
1806
- this.withWrite(() => {
2922
+ const slot = this.withWrite(() => {
1807
2923
  this.trackPatch(pk, patch);
1808
- this.collection.upsertOneByPk(pk, patch);
2924
+ return this.collection.upsertOneByPk(pk, patch);
1809
2925
  });
1810
2926
  this.updateEntitySnapshot(pk);
2927
+ return slot;
1811
2928
  }
1812
2929
  upsertOne(entity) {
1813
- this.withWrite(() => {
2930
+ const slot = this.withWrite(() => {
1814
2931
  const pk = this.selectPk(entity);
1815
2932
  this.trackPatch(pk, entity);
1816
- this.collection.upsertOne(entity);
2933
+ return this.collection.upsertOne(entity);
1817
2934
  });
1818
- this.updateEntitySnapshot(this.selectPk(entity));
2935
+ this.updateEntitySnapshot(slot.pk);
2936
+ return slot;
1819
2937
  }
1820
2938
  upsertMany(entities) {
1821
2939
  const pks = [];
1822
- this.withWrite(() => {
2940
+ const slots = this.withWrite(() => {
1823
2941
  for (const entity of entities) {
1824
2942
  const pk = this.selectPk(entity);
1825
2943
  pks.push(pk);
1826
2944
  this.trackPatch(pk, entity);
1827
2945
  }
1828
- this.collection.upsertMany(entities);
2946
+ return this.collection.upsertMany(entities);
1829
2947
  });
1830
2948
  for (let i = 0; i < pks.length; i++) this.updateEntitySnapshot(pks[i]);
2949
+ return slots;
1831
2950
  }
1832
2951
  getChangedFieldsByPk(pk) {
1833
2952
  return this.changedFieldsByPk.get(pk) ?? /* @__PURE__ */ new Set();
@@ -1858,12 +2977,11 @@ var OIMCollectionChangedFieldsWrapper = class {
1858
2977
  }
1859
2978
  withWrite(fn) {
1860
2979
  if (this.isInWrite) {
1861
- fn();
1862
- return;
2980
+ return fn();
1863
2981
  }
1864
2982
  this.isInWrite = true;
1865
2983
  try {
1866
- fn();
2984
+ return fn();
1867
2985
  } finally {
1868
2986
  this.isInWrite = false;
1869
2987
  }
@@ -1905,238 +3023,33 @@ var OIMCollectionChangedFieldsWrapper = class {
1905
3023
  ...entity
1906
3024
  });
1907
3025
  }
1908
- trackChangedKeys(pk, keys) {
1909
- let pkFields = this.changedFieldsByPk.get(pk);
1910
- if (!pkFields) {
1911
- pkFields = /* @__PURE__ */ new Set();
1912
- this.changedFieldsByPk.set(pk, pkFields);
1913
- }
1914
- for (const key of keys) {
1915
- pkFields.add(key);
1916
- let fieldPks = this.changedPksByField.get(key);
1917
- if (!fieldPks) {
1918
- fieldPks = /* @__PURE__ */ new Set();
1919
- this.changedPksByField.set(key, fieldPks);
1920
- }
1921
- fieldPks.add(pk);
1922
- }
1923
- this.changedPksEventEmitter.markUpdatedKeys([pk]);
1924
- this.changedFieldsEventEmitter.markUpdatedKeys(keys);
1925
- }
1926
- trackPatch(pk, patch) {
1927
- if (pk === void 0 || pk === null || pk === "") {
1928
- throw new Error(
1929
- `[OIMCollectionChangedFieldsWrapper]: PK is required to track changes.`
1930
- );
1931
- }
1932
- const keys = Object.keys(patch);
1933
- if (keys.length === 0) return;
1934
- this.trackChangedKeys(pk, keys);
1935
- }
1936
- };
1937
-
1938
- // src/modules/wrapper/index/OIMIndexManualOrderedArrayBased.ts
1939
- var OIMIndexManualOrderedArrayBased = class extends OIMIndexArrayBased {
1940
- clear(key) {
1941
- if (key === void 0) {
1942
- const keys = this.getKeys();
1943
- if (keys.length > 0) {
1944
- this.store.clear();
1945
- this.emitUpdate(Array.from(keys));
1946
- }
1947
- return;
1948
- }
1949
- if (this.store.getOneByKey(key) !== void 0) {
1950
- this.store.removeOneByKey(key);
1951
- this.emitUpdate([key]);
1952
- }
1953
- }
1954
- push(key, itemKey) {
1955
- const list = this.getOrCreateList(key);
1956
- const index = list.length;
1957
- list.push(itemKey);
1958
- this.emitUpdate([key]);
1959
- return index;
1960
- }
1961
- insertAt(key, index, itemKey) {
1962
- const list = this.getOrCreateList(key);
1963
- const safeIndex = Math.max(0, Math.min(index, list.length));
1964
- list.splice(safeIndex, 0, itemKey);
1965
- this.emitUpdate([key]);
1966
- }
1967
- removeAt(key, index) {
1968
- const list = this.store.getOneByKey(key);
1969
- if (!list) return void 0;
1970
- if (index < 0 || index >= list.length) return void 0;
1971
- const [removed] = list.splice(index, 1);
1972
- if (list.length === 0) {
1973
- this.store.removeOneByKey(key);
1974
- }
1975
- this.emitUpdate([key]);
1976
- return removed;
1977
- }
1978
- move(key, fromIndex, toIndex) {
1979
- const list = this.store.getOneByKey(key);
1980
- if (!list) return void 0;
1981
- if (fromIndex < 0 || fromIndex >= list.length) return void 0;
1982
- if (toIndex < 0 || toIndex >= list.length) return void 0;
1983
- if (fromIndex === toIndex) return list[fromIndex];
1984
- const [itemKey] = list.splice(fromIndex, 1);
1985
- if (itemKey === void 0) return void 0;
1986
- list.splice(toIndex, 0, itemKey);
1987
- this.emitUpdate([key]);
1988
- return itemKey;
1989
- }
1990
- reset(key, itemKeys) {
1991
- if (itemKeys.length === 0) {
1992
- if (this.store.getOneByKey(key) !== void 0) {
1993
- this.store.removeOneByKey(key);
1994
- this.emitUpdate([key]);
1995
- }
1996
- return;
1997
- }
1998
- this.store.setOneByKey(key, Array.from(itemKeys));
1999
- this.emitUpdate([key]);
2000
- }
2001
- getOrCreateList(key) {
2002
- let list = this.store.getOneByKey(key);
2003
- if (!list) {
2004
- list = [];
2005
- this.store.setOneByKey(key, list);
2006
- }
2007
- return list;
2008
- }
2009
- };
2010
-
2011
- // src/modules/wrapper/index/OIMOrderedListIndexCommandStreamWrapper.ts
2012
- var OIMOrderedListIndexCommandStreamWrapper = class {
2013
- constructor(queue, index) {
2014
- this.isInWrite = false;
2015
- this.commandsByKey = /* @__PURE__ */ new Map();
2016
- this.onAfterFlush = () => {
2017
- this.commandsByKey.clear();
2018
- };
2019
- this.onIndexUpdate = (payload) => {
2020
- if (this.isInWrite) return;
2021
- if (payload.keys.length === 0) return;
2022
- for (let i = 0; i < payload.keys.length; i++) {
2023
- const key = payload.keys[i];
2024
- const list = this.index.getPksByKey(key);
2025
- this.appendCommand(key, { type: "set", keys: Array.from(list) });
2026
- }
2027
- };
2028
- this.commandsEventEmitter = new OIMUpdateEventEmitter(
2029
- queue,
2030
- "after_flush"
2031
- );
2032
- this.index = index ?? new OIMIndexManualOrderedArrayBased();
2033
- this.unsubscribeAfterFlush = queue.emitter.on(
2034
- 0 /* AFTER_FLUSH */,
2035
- this.onAfterFlush
2036
- );
2037
- this.unsubscribeFromIndexEmitter = this.index.emitter.on(
2038
- 0 /* UPDATE */,
2039
- this.onIndexUpdate
2040
- );
2041
- }
2042
- getPksByKey(key) {
2043
- return this.index.getPksByKey(key);
2044
- }
2045
- hasKey(key) {
2046
- return this.index.hasKey(key);
2047
- }
2048
- getKeys() {
2049
- return this.index.getKeys();
2050
- }
2051
- push(key, itemKey) {
2052
- this.withWrite(() => {
2053
- const index = this.index.push(key, itemKey);
2054
- this.appendCommand(key, { type: "add", key: itemKey, index });
2055
- });
2056
- }
2057
- removeAt(key, index) {
2058
- this.withWrite(() => {
2059
- const itemKey = this.index.removeAt(key, index);
2060
- if (itemKey === void 0) return;
2061
- this.appendCommand(key, { type: "remove", key: itemKey, index });
2062
- });
2063
- }
2064
- move(key, fromIndex, toIndex) {
2065
- this.withWrite(() => {
2066
- const itemKey = this.index.move(key, fromIndex, toIndex);
2067
- if (itemKey === void 0) return;
2068
- this.appendCommand(key, {
2069
- type: "move",
2070
- key: itemKey,
2071
- fromIndex,
2072
- toIndex
2073
- });
2074
- });
2075
- }
2076
- set(key, keys) {
2077
- this.withWrite(() => {
2078
- this.index.reset(key, keys);
2079
- this.appendCommand(key, { type: "set", keys });
2080
- });
2081
- }
2082
- /**
2083
- * Read-only peek at buffered commands without clearing them.
2084
- */
2085
- getBufferedCommands(key) {
2086
- return this.commandsByKey.get(key) ?? [];
2087
- }
2088
- /**
2089
- * Drain buffered commands for a key, preserving order.
2090
- * Intended usage: call inside a `commandsEventEmitter.subscribeOnKey(key, ...)` handler.
2091
- */
2092
- consumeCommands(key) {
2093
- const cmds = this.commandsByKey.get(key);
2094
- if (!cmds || cmds.length === 0) return [];
2095
- return cmds.slice();
2096
- }
2097
- clear(key) {
2098
- if (key === void 0) {
2099
- this.index.destroy();
2100
- this.commandsByKey.clear();
2101
- return;
2102
- }
2103
- this.index.clear(key);
2104
- this.commandsByKey.delete(key);
2105
- }
2106
- destroy() {
2107
- this.commandsEventEmitter.destroy();
2108
- this.unsubscribeAfterFlush();
2109
- this.unsubscribeFromIndexEmitter();
2110
- this.clear();
2111
- }
2112
- withWrite(fn) {
2113
- if (this.isInWrite) {
2114
- fn();
2115
- return;
3026
+ trackChangedKeys(pk, keys) {
3027
+ let pkFields = this.changedFieldsByPk.get(pk);
3028
+ if (!pkFields) {
3029
+ pkFields = /* @__PURE__ */ new Set();
3030
+ this.changedFieldsByPk.set(pk, pkFields);
2116
3031
  }
2117
- this.isInWrite = true;
2118
- try {
2119
- fn();
2120
- } finally {
2121
- this.isInWrite = false;
3032
+ for (const key of keys) {
3033
+ pkFields.add(key);
3034
+ let fieldPks = this.changedPksByField.get(key);
3035
+ if (!fieldPks) {
3036
+ fieldPks = /* @__PURE__ */ new Set();
3037
+ this.changedPksByField.set(key, fieldPks);
3038
+ }
3039
+ fieldPks.add(pk);
2122
3040
  }
3041
+ this.changedPksEventEmitter.markUpdatedKeys([pk]);
3042
+ this.changedFieldsEventEmitter.markUpdatedKeys(keys);
2123
3043
  }
2124
- appendCommand(key, cmd) {
2125
- let cmds = this.commandsByKey.get(key);
2126
- if (!cmds) {
2127
- cmds = [];
2128
- this.commandsByKey.set(key, cmds);
2129
- }
2130
- if (cmd.type === "set") {
2131
- cmds.length = 0;
2132
- cmds.push(cmd);
2133
- } else if (cmds.length > 0 && cmds[0].type === "set") {
2134
- const setCmd = cmds[0];
2135
- setCmd.keys = Array.from(this.index.getPksByKey(key));
2136
- } else {
2137
- cmds.push(cmd);
3044
+ trackPatch(pk, patch) {
3045
+ if (pk === void 0 || pk === null || pk === "") {
3046
+ throw new Error(
3047
+ `[OIMCollectionChangedFields]: PK is required to track changes.`
3048
+ );
2138
3049
  }
2139
- this.commandsEventEmitter.markUpdatedKeys([key]);
3050
+ const keys = Object.keys(patch);
3051
+ if (keys.length === 0) return;
3052
+ this.trackChangedKeys(pk, keys);
2140
3053
  }
2141
3054
  };
2142
3055
 
@@ -2252,7 +3165,9 @@ var OIMEventQueueSchedulerImmediate = class extends OIMEventQueueScheduler {
2252
3165
  cancel() {
2253
3166
  if (this.immediateId === void 0) return;
2254
3167
  if (this.useSetImmediate) {
2255
- clearImmediate(this.immediateId);
3168
+ clearImmediate(
3169
+ this.immediateId
3170
+ );
2256
3171
  } else if (this.useMessageChannel) {
2257
3172
  this.pendingCallback = void 0;
2258
3173
  } else {
@@ -2309,7 +3224,7 @@ var OIMEventQueueSchedulerFactory = class {
2309
3224
  }
2310
3225
  };
2311
3226
 
2312
- // src/const/OIMDBSettings.ts
3227
+ // src/constants/OIMDBSettings.ts
2313
3228
  var OIMDBSettings = class {
2314
3229
  };
2315
3230
  /**
@@ -2321,11 +3236,11 @@ OIMDBSettings.FLUSHING_KEYS_INDEX_KEY = "__flushingKeys__";
2321
3236
 
2322
3237
  // src/modules/effect/core/OIMEffectDependencyComputed.ts
2323
3238
  var OIMEffectDependencyComputed = class {
2324
- constructor(computed) {
2325
- this.computed = computed;
3239
+ constructor(source) {
3240
+ this.source = source;
2326
3241
  }
2327
3242
  subscribe(onUpdate) {
2328
- return this.computed.emitter.on("update", onUpdate);
3243
+ return this.source.emitter.on("update", onUpdate);
2329
3244
  }
2330
3245
  };
2331
3246
 
@@ -2437,7 +3352,7 @@ var OIMEffect = class {
2437
3352
  }
2438
3353
  };
2439
3354
 
2440
- // src/modules/computed/enum/EOIMComputedEventType.ts
3355
+ // src/modules/computed/enums/EOIMComputedEventType.ts
2441
3356
  var EOIMComputedEventType = /* @__PURE__ */ ((EOIMComputedEventType2) => {
2442
3357
  EOIMComputedEventType2["UPDATE"] = "update";
2443
3358
  return EOIMComputedEventType2;
@@ -2452,13 +3367,13 @@ var OIMComputed = class {
2452
3367
  this.runtime = runtime;
2453
3368
  this.compute = opts.compute;
2454
3369
  this.compare = opts.compare ?? Object.is;
2455
- this.deps = opts.deps ?? [];
3370
+ this._deps = opts.deps ?? [];
2456
3371
  this.updateEventEmitter = new OIMUpdateEventEmitter(
2457
3372
  this.runtime.queue,
2458
3373
  "immediate"
2459
3374
  );
2460
3375
  this.effect = new OIMEffect(this.runtime, {
2461
- deps: this.deps,
3376
+ deps: this._deps,
2462
3377
  onUpdate: () => {
2463
3378
  this.isDirty = true;
2464
3379
  },
@@ -2467,6 +3382,15 @@ var OIMComputed = class {
2467
3382
  }
2468
3383
  });
2469
3384
  }
3385
+ get needsRecompute() {
3386
+ return this.isDirty;
3387
+ }
3388
+ get isReady() {
3389
+ return this.hasValue;
3390
+ }
3391
+ get deps() {
3392
+ return this._deps;
3393
+ }
2470
3394
  get() {
2471
3395
  this.recomputeAndEmitIfChanged();
2472
3396
  return this.value;
@@ -2501,8 +3425,8 @@ function isReadonlyArray(value) {
2501
3425
  return Array.isArray(value);
2502
3426
  }
2503
3427
  var OIMEffectDependencyKeyed = class {
2504
- constructor(subscription, keyOrKeys) {
2505
- this.subscription = subscription;
3428
+ constructor(source, keyOrKeys) {
3429
+ this.source = source;
2506
3430
  if (isReadonlyArray(keyOrKeys)) {
2507
3431
  this.keys = keyOrKeys;
2508
3432
  } else {
@@ -2511,11 +3435,11 @@ var OIMEffectDependencyKeyed = class {
2511
3435
  }
2512
3436
  subscribe(onUpdate) {
2513
3437
  if (this.keys !== void 0) {
2514
- return this.subscription.subscribeOnKeys(this.keys, onUpdate);
3438
+ return this.source.subscribeOnKeys(this.keys, onUpdate);
2515
3439
  }
2516
3440
  if (this.key === void 0) return () => {
2517
3441
  };
2518
- return this.subscription.subscribeOnKey(this.key, onUpdate);
3442
+ return this.source.subscribeOnKey(this.key, onUpdate);
2519
3443
  }
2520
3444
  };
2521
3445
 
@@ -2527,6 +3451,9 @@ var OIMEffectDependencyKeyedObject = class {
2527
3451
  keyOrKeys
2528
3452
  );
2529
3453
  }
3454
+ get source() {
3455
+ return this.dep.source;
3456
+ }
2530
3457
  subscribe(onUpdate) {
2531
3458
  return this.dep.subscribe(onUpdate);
2532
3459
  }
@@ -2536,221 +3463,31 @@ var OIMEffectDependencyKeyedObject = class {
2536
3463
  var OIMEffectDependencyKeyedCollection = class {
2537
3464
  constructor(collection, pkOrPks) {
2538
3465
  this.dep = new OIMEffectDependencyKeyed(
2539
- collection,
2540
- pkOrPks
2541
- );
2542
- }
2543
- subscribe(onUpdate) {
2544
- return this.dep.subscribe(onUpdate);
2545
- }
2546
- };
2547
-
2548
- // src/modules/effect/core/OIMEffectDependencyKeyedIndex.ts
2549
- var OIMEffectDependencyKeyedIndex = class {
2550
- constructor(index, keyOrKeys) {
2551
- this.dep = new OIMEffectDependencyKeyed(
2552
- index,
2553
- keyOrKeys
2554
- );
2555
- }
2556
- subscribe(onUpdate) {
2557
- return this.dep.subscribe(onUpdate);
2558
- }
2559
- };
2560
-
2561
- // src/modules/selector/core/OIMSelector.ts
2562
- var OIMSelector = class {
2563
- constructor(runtime, sourceDeps, computedDeps = []) {
2564
- this.runtime = runtime;
2565
- this.sourceDeps = sourceDeps;
2566
- this.computedDeps = computedDeps;
2567
- this.listeners = /* @__PURE__ */ new Set();
2568
- this.sourceUnsubscribers = [];
2569
- this.computedUnsubscribers = [];
2570
- this.isResubscribeEnqueued = false;
2571
- this.isTemporarilyUnsubscribedForFlush = false;
2572
- this.isDeliveryEnqueued = false;
2573
- this.hasLastValue = false;
2574
- this.onSourceInvalidate = () => {
2575
- if (this.listeners.size === 0) return;
2576
- if (!this.isTemporarilyUnsubscribedForFlush) {
2577
- this.isTemporarilyUnsubscribedForFlush = true;
2578
- this.unsubscribeFromSourceDeps();
2579
- this.ensureResubscribeAfterFlush();
2580
- }
2581
- if (this.isDeliveryEnqueued) return;
2582
- this.isDeliveryEnqueued = true;
2583
- this.dequeueDelivery = this.runtime.schedule(this.deliver);
2584
- };
2585
- this.onComputedInvalidate = () => {
2586
- if (this.listeners.size === 0) return;
2587
- if (this.isDeliveryEnqueued) return;
2588
- this.isDeliveryEnqueued = true;
2589
- this.dequeueDelivery = this.runtime.schedule(this.deliver);
2590
- };
2591
- this.deliver = () => {
2592
- this.isDeliveryEnqueued = false;
2593
- this.dequeueDelivery = void 0;
2594
- if (this.listeners.size === 0) return;
2595
- const next = this.getValue();
2596
- if (this.hasLastValue && this.areEqual(this.lastValue, next)) return;
2597
- this.setLastValue(next);
2598
- const snapshot = Array.from(this.listeners);
2599
- for (let i = 0; i < snapshot.length; i++) {
2600
- const listener = snapshot[i];
2601
- if (this.listeners.has(listener)) listener(next);
2602
- }
2603
- };
2604
- this.onAfterFlush = () => {
2605
- if (!this.isResubscribeEnqueued) return;
2606
- this.isResubscribeEnqueued = false;
2607
- this.dequeueResubscribe = void 0;
2608
- if (this.listeners.size === 0) return;
2609
- this.isTemporarilyUnsubscribedForFlush = false;
2610
- if (this.sourceUnsubscribers.length === 0) {
2611
- for (const dep of this.sourceDeps) {
2612
- this.sourceUnsubscribers.push(
2613
- dep.subscribe(this.onSourceInvalidate)
2614
- );
2615
- }
2616
- }
2617
- };
2618
- }
2619
- get queue() {
2620
- return this.runtime.queue;
2621
- }
2622
- /**
2623
- * Start watching this selector. The callback is called immediately with the current value,
2624
- * then coalesced and delivered on `queue.flush()` when the selector invalidates.
2625
- */
2626
- watch(onValue) {
2627
- this.listeners.add(onValue);
2628
- if (this.listeners.size === 1) {
2629
- this.subscribeToDeps();
2630
- }
2631
- const current = this.getValue();
2632
- this.setLastValue(current);
2633
- onValue(current);
2634
- return () => {
2635
- this.listeners.delete(onValue);
2636
- if (this.listeners.size > 0) return;
2637
- this.unsubscribeFromAllDeps();
2638
- if (this.dequeueResubscribe) {
2639
- this.dequeueResubscribe();
2640
- this.dequeueResubscribe = void 0;
2641
- }
2642
- this.isResubscribeEnqueued = false;
2643
- this.isTemporarilyUnsubscribedForFlush = false;
2644
- if (this.dequeueDelivery) {
2645
- this.dequeueDelivery();
2646
- this.dequeueDelivery = void 0;
2647
- }
2648
- this.isDeliveryEnqueued = false;
2649
- this.hasLastValue = false;
2650
- };
2651
- }
2652
- areEqual(prev, next) {
2653
- return Object.is(prev, next);
2654
- }
2655
- setLastValue(value) {
2656
- this.lastValue = value;
2657
- this.hasLastValue = true;
2658
- }
2659
- subscribeToDeps() {
2660
- if (this.sourceUnsubscribers.length > 0 || this.computedUnsubscribers.length > 0)
2661
- return;
2662
- for (const dep of this.sourceDeps) {
2663
- this.sourceUnsubscribers.push(
2664
- dep.subscribe(this.onSourceInvalidate)
2665
- );
2666
- }
2667
- for (const dep of this.computedDeps) {
2668
- this.computedUnsubscribers.push(
2669
- dep.subscribe(this.onComputedInvalidate)
2670
- );
2671
- }
2672
- }
2673
- unsubscribeFromSourceDeps() {
2674
- if (this.sourceUnsubscribers.length === 0) return;
2675
- for (const unsub of this.sourceUnsubscribers) unsub();
2676
- this.sourceUnsubscribers = [];
2677
- }
2678
- unsubscribeFromAllDeps() {
2679
- this.unsubscribeFromSourceDeps();
2680
- if (this.computedUnsubscribers.length === 0) return;
2681
- for (const unsub of this.computedUnsubscribers) unsub();
2682
- this.computedUnsubscribers = [];
2683
- }
2684
- ensureResubscribeAfterFlush() {
2685
- if (this.isResubscribeEnqueued) return;
2686
- this.isResubscribeEnqueued = true;
2687
- this.dequeueResubscribe = this.runtime.scheduleAfterFlush(
2688
- this.onAfterFlush
2689
- );
2690
- }
2691
- };
2692
-
2693
- // src/modules/selector/core/OIMSelectorSourceDependencyUpdateEventEmitterKey.ts
2694
- var OIMSelectorSourceDependencyUpdateEventEmitterKey = class {
2695
- constructor(subscription, key) {
2696
- this.subscription = subscription;
2697
- this.key = key;
2698
- }
2699
- subscribe(onUpdate) {
2700
- return this.subscription.subscribeOnKey(this.key, onUpdate);
2701
- }
2702
- };
2703
-
2704
- // src/modules/selector/core/OIMCollectionByPkSelector.ts
2705
- var OIMCollectionByPkSelector = class extends OIMSelector {
2706
- constructor(runtime, collection, pk) {
2707
- super(runtime, [
2708
- new OIMSelectorSourceDependencyUpdateEventEmitterKey(
2709
- collection,
2710
- pk
2711
- )
2712
- ]);
2713
- this.collection = collection;
2714
- this.pk = pk;
2715
- }
2716
- getValue() {
2717
- return this.collection.getOneByPk(this.pk);
2718
- }
2719
- };
2720
-
2721
- // src/modules/selector/core/OIMSelectorSourceDependencyUpdateEventEmitterKeys.ts
2722
- var OIMSelectorSourceDependencyUpdateEventEmitterKeys = class {
2723
- constructor(subscription, keys) {
2724
- this.subscription = subscription;
2725
- this.keys = keys;
2726
- }
2727
- subscribe(onUpdate) {
2728
- return this.subscription.subscribeOnKeys(this.keys, onUpdate);
2729
- }
2730
- };
2731
-
2732
- // src/modules/selector/core/OIMCollectionByPksSelector.ts
2733
- var OIMCollectionByPksSelector = class extends OIMSelector {
2734
- constructor(runtime, collection, pks) {
2735
- super(runtime, [
2736
- new OIMSelectorSourceDependencyUpdateEventEmitterKeys(
2737
- collection,
2738
- pks
2739
- )
2740
- ]);
2741
- this.collection = collection;
2742
- this.pks = pks;
3466
+ collection,
3467
+ pkOrPks
3468
+ );
2743
3469
  }
2744
- getValue() {
2745
- return this.pks.map((pk) => this.collection.getOneByPk(pk));
3470
+ get source() {
3471
+ return this.dep.source;
2746
3472
  }
2747
- areEqual(prev, next) {
2748
- if (prev === next) return true;
2749
- if (prev.length !== next.length) return false;
2750
- for (let i = 0; i < prev.length; i++) {
2751
- if (prev[i] !== next[i]) return false;
2752
- }
2753
- return true;
3473
+ subscribe(onUpdate) {
3474
+ return this.dep.subscribe(onUpdate);
3475
+ }
3476
+ };
3477
+
3478
+ // src/modules/effect/core/OIMEffectDependencyKeyedIndex.ts
3479
+ var OIMEffectDependencyKeyedIndex = class {
3480
+ constructor(index, keyOrKeys) {
3481
+ this.dep = new OIMEffectDependencyKeyed(
3482
+ index,
3483
+ keyOrKeys
3484
+ );
3485
+ }
3486
+ get source() {
3487
+ return this.dep.source;
3488
+ }
3489
+ subscribe(onUpdate) {
3490
+ return this.dep.subscribe(onUpdate);
2754
3491
  }
2755
3492
  };
2756
3493
 
@@ -2884,241 +3621,6 @@ var OIMIndexPksByKeysArrayBasedSelector = class extends OIMSelector {
2884
3621
  return true;
2885
3622
  }
2886
3623
  };
2887
-
2888
- // src/modules/selector/core/OIMSelectorSourceDependencyEntitiesByIndexKeySetBased.ts
2889
- var OIMSelectorSourceDependencyEntitiesByIndexKeySetBased = class {
2890
- constructor(collection, reactiveIndex, key) {
2891
- this.collection = collection;
2892
- this.reactiveIndex = reactiveIndex;
2893
- this.key = key;
2894
- this.currentPks = [];
2895
- }
2896
- subscribe(onUpdate) {
2897
- const resubscribeCollection = () => {
2898
- const nextPks = Array.from(
2899
- this.reactiveIndex.getPksByKey(this.key)
2900
- );
2901
- if (this.sameArray(this.currentPks, nextPks)) return;
2902
- this.unsubscribeFromCollectionKeys?.();
2903
- this.unsubscribeFromCollectionKeys = void 0;
2904
- this.currentPks = nextPks;
2905
- if (this.currentPks.length === 0) return;
2906
- this.unsubscribeFromCollectionKeys = this.collection.subscribeOnKeys(this.currentPks, onUpdate);
2907
- };
2908
- const onIndexUpdate = () => {
2909
- resubscribeCollection();
2910
- onUpdate();
2911
- };
2912
- resubscribeCollection();
2913
- const unsubscribeFromIndex = this.reactiveIndex.subscribeOnKey(
2914
- this.key,
2915
- onIndexUpdate
2916
- );
2917
- return () => {
2918
- unsubscribeFromIndex();
2919
- this.unsubscribeFromCollectionKeys?.();
2920
- this.unsubscribeFromCollectionKeys = void 0;
2921
- this.currentPks = [];
2922
- };
2923
- }
2924
- sameArray(a, b) {
2925
- if (a === b) return true;
2926
- if (a.length !== b.length) return false;
2927
- for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
2928
- return true;
2929
- }
2930
- };
2931
-
2932
- // src/modules/selector/core/OIMEntitiesByIndexKeySetBasedSelector.ts
2933
- var OIMEntitiesByIndexKeySetBasedSelector = class extends OIMSelector {
2934
- constructor(runtime, collection, reactiveIndex, key) {
2935
- super(runtime, [
2936
- new OIMSelectorSourceDependencyEntitiesByIndexKeySetBased(collection, reactiveIndex, key)
2937
- ]);
2938
- this.collection = collection;
2939
- this.reactiveIndex = reactiveIndex;
2940
- this.key = key;
2941
- }
2942
- getValue() {
2943
- const pks = Array.from(this.reactiveIndex.getPksByKey(this.key));
2944
- return pks.map((pk) => this.collection.getOneByPk(pk));
2945
- }
2946
- areEqual(prev, next) {
2947
- if (prev === next) return true;
2948
- if (prev.length !== next.length) return false;
2949
- for (let i = 0; i < prev.length; i++)
2950
- if (prev[i] !== next[i]) return false;
2951
- return true;
2952
- }
2953
- };
2954
-
2955
- // src/modules/selector/core/OIMSelectorSourceDependencyEntitiesByIndexKeyArrayBased.ts
2956
- var OIMSelectorSourceDependencyEntitiesByIndexKeyArrayBased = class {
2957
- constructor(collection, reactiveIndex, key) {
2958
- this.collection = collection;
2959
- this.reactiveIndex = reactiveIndex;
2960
- this.key = key;
2961
- this.currentPks = [];
2962
- }
2963
- subscribe(onUpdate) {
2964
- const resubscribeCollection = () => {
2965
- const nextPks = this.reactiveIndex.getPksByKey(this.key);
2966
- if (this.sameArray(this.currentPks, nextPks)) return;
2967
- this.unsubscribeFromCollectionKeys?.();
2968
- this.unsubscribeFromCollectionKeys = void 0;
2969
- this.currentPks = nextPks;
2970
- if (this.currentPks.length === 0) return;
2971
- this.unsubscribeFromCollectionKeys = this.collection.subscribeOnKeys(this.currentPks, onUpdate);
2972
- };
2973
- const onIndexUpdate = () => {
2974
- resubscribeCollection();
2975
- onUpdate();
2976
- };
2977
- resubscribeCollection();
2978
- const unsubscribeFromIndex = this.reactiveIndex.subscribeOnKey(
2979
- this.key,
2980
- onIndexUpdate
2981
- );
2982
- return () => {
2983
- unsubscribeFromIndex();
2984
- this.unsubscribeFromCollectionKeys?.();
2985
- this.unsubscribeFromCollectionKeys = void 0;
2986
- this.currentPks = [];
2987
- };
2988
- }
2989
- sameArray(a, b) {
2990
- if (a === b) return true;
2991
- if (a.length !== b.length) return false;
2992
- for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
2993
- return true;
2994
- }
2995
- };
2996
-
2997
- // src/modules/selector/core/OIMEntitiesByIndexKeyArrayBasedSelector.ts
2998
- var OIMEntitiesByIndexKeyArrayBasedSelector = class extends OIMSelector {
2999
- constructor(runtime, collection, reactiveIndex, key) {
3000
- super(runtime, [
3001
- new OIMSelectorSourceDependencyEntitiesByIndexKeyArrayBased(collection, reactiveIndex, key)
3002
- ]);
3003
- this.collection = collection;
3004
- this.reactiveIndex = reactiveIndex;
3005
- this.key = key;
3006
- }
3007
- getValue() {
3008
- const pks = this.reactiveIndex.getPksByKey(this.key);
3009
- return pks.map((pk) => this.collection.getOneByPk(pk));
3010
- }
3011
- areEqual(prev, next) {
3012
- if (prev === next) return true;
3013
- if (prev.length !== next.length) return false;
3014
- for (let i = 0; i < prev.length; i++)
3015
- if (prev[i] !== next[i]) return false;
3016
- return true;
3017
- }
3018
- };
3019
-
3020
- // src/modules/computative/core/OIMComputativeRuntime.ts
3021
- var OIMComputativeRuntime = class {
3022
- constructor(queue) {
3023
- this.queue = queue;
3024
- this.isFlushScheduled = false;
3025
- this.isFlushing = false;
3026
- this.minLevel = Infinity;
3027
- this.maxLevel = -Infinity;
3028
- this.dirtyByLevel = /* @__PURE__ */ new Map();
3029
- this.afterFlushTasks = /* @__PURE__ */ new Set();
3030
- this.queue.emitter.on(0 /* AFTER_FLUSH */, () => {
3031
- if (!this.isFlushScheduled) return;
3032
- this.flush();
3033
- });
3034
- }
3035
- /**
3036
- * Schedule a one-shot task to run on the next `queue.flush()` (AFTER_FLUSH),
3037
- * ordered by `level` ascending.
3038
- */
3039
- schedule(task, level = 0) {
3040
- let isActive = true;
3041
- const wrapped = () => {
3042
- if (!isActive) return;
3043
- task();
3044
- };
3045
- let bucket = this.dirtyByLevel.get(level);
3046
- if (!bucket) {
3047
- bucket = /* @__PURE__ */ new Set();
3048
- this.dirtyByLevel.set(level, bucket);
3049
- }
3050
- bucket.add(wrapped);
3051
- if (level < this.minLevel) this.minLevel = level;
3052
- if (level > this.maxLevel) this.maxLevel = level;
3053
- if (!this.isFlushing) this.isFlushScheduled = true;
3054
- return () => {
3055
- if (!isActive) return;
3056
- isActive = false;
3057
- bucket.delete(wrapped);
3058
- };
3059
- }
3060
- /**
3061
- * Run once at the start of the next runtime flush (i.e. after queue.flush()).
3062
- * Useful for resubscribing dependencies exactly once per source flush.
3063
- */
3064
- scheduleAfterFlush(task) {
3065
- let isActive = true;
3066
- const wrapped = () => {
3067
- if (!isActive) return;
3068
- task();
3069
- };
3070
- this.afterFlushTasks.add(wrapped);
3071
- if (!this.isFlushing) this.isFlushScheduled = true;
3072
- return () => {
3073
- if (!isActive) return;
3074
- isActive = false;
3075
- this.afterFlushTasks.delete(wrapped);
3076
- };
3077
- }
3078
- flush() {
3079
- this.isFlushScheduled = false;
3080
- if (this.dirtyByLevel.size === 0 && this.afterFlushTasks.size === 0)
3081
- return;
3082
- this.isFlushing = true;
3083
- try {
3084
- if (this.afterFlushTasks.size > 0) {
3085
- const snapshot = Array.from(this.afterFlushTasks);
3086
- this.afterFlushTasks.clear();
3087
- for (let i = 0; i < snapshot.length; i++) snapshot[i]();
3088
- }
3089
- if (this.dirtyByLevel.size === 0) return;
3090
- let level = this.minLevel;
3091
- let max = this.maxLevel;
3092
- while (level <= max) {
3093
- for (; ; ) {
3094
- const bucket = this.dirtyByLevel.get(level);
3095
- if (!bucket || bucket.size === 0) break;
3096
- this.dirtyByLevel.delete(level);
3097
- const snapshot = Array.from(bucket);
3098
- for (let i = 0; i < snapshot.length; i++) snapshot[i]();
3099
- }
3100
- if (this.maxLevel > max) max = this.maxLevel;
3101
- level++;
3102
- }
3103
- } finally {
3104
- this.isFlushing = false;
3105
- if (this.dirtyByLevel.size === 0) {
3106
- this.minLevel = Infinity;
3107
- this.maxLevel = -Infinity;
3108
- } else {
3109
- let nextMin = Infinity;
3110
- let nextMax = -Infinity;
3111
- this.dirtyByLevel.forEach((_bucket, lvl) => {
3112
- if (lvl < nextMin) nextMin = lvl;
3113
- if (lvl > nextMax) nextMax = lvl;
3114
- });
3115
- this.minLevel = nextMin;
3116
- this.maxLevel = nextMax;
3117
- this.isFlushScheduled = true;
3118
- }
3119
- }
3120
- }
3121
- };
3122
3624
  export {
3123
3625
  EOIMCollectionEventType,
3124
3626
  EOIMComputedEventType,
@@ -3129,13 +3631,19 @@ export {
3129
3631
  OIMCollection,
3130
3632
  OIMCollectionByPkSelector,
3131
3633
  OIMCollectionByPksSelector,
3132
- OIMCollectionChangedFieldsWrapper,
3634
+ OIMCollectionChangedFields,
3635
+ OIMCollectionIndexFactory,
3636
+ OIMCollectionIndexManualOrderedArrayBased,
3637
+ OIMCollectionOrderedListCommandStream,
3638
+ OIMCollectionSelectors,
3133
3639
  OIMCollectionStore,
3134
3640
  OIMCollectionStoreMapDriven,
3135
3641
  OIMComparatorFactory,
3136
- OIMComputativeRuntime,
3642
+ OIMComputeRuntime,
3137
3643
  OIMComputed,
3138
3644
  OIMDBSettings,
3645
+ OIMDerivedCollectionIndexArrayBased,
3646
+ OIMDerivedCollectionIndexSetBased,
3139
3647
  OIMEffect,
3140
3648
  OIMEffectDependencyComputed,
3141
3649
  OIMEffectDependencyKeyed,
@@ -3153,6 +3661,7 @@ export {
3153
3661
  OIMEventQueueSchedulerImmediate,
3154
3662
  OIMEventQueueSchedulerMicrotask,
3155
3663
  OIMEventQueueSchedulerTimeout,
3664
+ OIMIndex,
3156
3665
  OIMIndexArrayBased,
3157
3666
  OIMIndexComparatorFactory,
3158
3667
  OIMIndexManualArrayBased,
@@ -3174,14 +3683,18 @@ export {
3174
3683
  OIMObjectStoreRecordDriven,
3175
3684
  OIMObjectValueByKeySelector,
3176
3685
  OIMObjectValuesByKeysSelector,
3177
- OIMOrderedListIndexCommandStreamWrapper,
3686
+ OIMOrderedListCommandStream,
3178
3687
  OIMPkSelectorFactory,
3179
- OIMRICollection,
3180
3688
  OIMReactiveCollection,
3689
+ OIMReactiveCollectionIndexManualArrayBased,
3690
+ OIMReactiveCollectionIndexManualSetBased,
3691
+ OIMReactiveIndex,
3181
3692
  OIMReactiveIndexArrayBased,
3182
3693
  OIMReactiveIndexManualArrayBased,
3183
3694
  OIMReactiveIndexManualSetBased,
3184
3695
  OIMReactiveIndexSetBased,
3185
3696
  OIMReactiveObject,
3186
- OIMSelector
3697
+ OIMSelector,
3698
+ createOIMCollectionContext,
3699
+ createOIMCollectionIndexFactory
3187
3700
  };