tinybase 1.3.4 → 2.0.0-beta.1

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.
Files changed (53) hide show
  1. package/lib/checkpoints.js +1 -1
  2. package/lib/checkpoints.js.gz +0 -0
  3. package/lib/debug/checkpoints.js +67 -54
  4. package/lib/debug/indexes.js +104 -67
  5. package/lib/debug/metrics.js +185 -129
  6. package/lib/debug/persisters.js +2 -1
  7. package/lib/debug/queries.d.ts +3054 -0
  8. package/lib/debug/queries.js +880 -0
  9. package/lib/debug/relationships.d.ts +6 -5
  10. package/lib/debug/relationships.js +102 -66
  11. package/lib/debug/store.d.ts +137 -66
  12. package/lib/debug/store.js +215 -119
  13. package/lib/debug/tinybase.d.ts +1 -0
  14. package/lib/debug/tinybase.js +892 -175
  15. package/lib/debug/ui-react.d.ts +2004 -195
  16. package/lib/debug/ui-react.js +273 -77
  17. package/lib/indexes.js +1 -1
  18. package/lib/indexes.js.gz +0 -0
  19. package/lib/metrics.js +1 -1
  20. package/lib/metrics.js.gz +0 -0
  21. package/lib/queries.d.ts +3054 -0
  22. package/lib/queries.js +1 -0
  23. package/lib/queries.js.gz +0 -0
  24. package/lib/relationships.d.ts +6 -5
  25. package/lib/relationships.js +1 -1
  26. package/lib/relationships.js.gz +0 -0
  27. package/lib/store.d.ts +137 -66
  28. package/lib/store.js +1 -1
  29. package/lib/store.js.gz +0 -0
  30. package/lib/tinybase.d.ts +1 -0
  31. package/lib/tinybase.js +1 -1
  32. package/lib/tinybase.js.gz +0 -0
  33. package/lib/ui-react.d.ts +2004 -195
  34. package/lib/ui-react.js +1 -1
  35. package/lib/ui-react.js.gz +0 -0
  36. package/lib/umd/checkpoints.js +1 -1
  37. package/lib/umd/checkpoints.js.gz +0 -0
  38. package/lib/umd/indexes.js +1 -1
  39. package/lib/umd/indexes.js.gz +0 -0
  40. package/lib/umd/metrics.js +1 -1
  41. package/lib/umd/metrics.js.gz +0 -0
  42. package/lib/umd/queries.js +1 -0
  43. package/lib/umd/queries.js.gz +0 -0
  44. package/lib/umd/relationships.js +1 -1
  45. package/lib/umd/relationships.js.gz +0 -0
  46. package/lib/umd/store.js +1 -1
  47. package/lib/umd/store.js.gz +0 -0
  48. package/lib/umd/tinybase.js +1 -1
  49. package/lib/umd/tinybase.js.gz +0 -0
  50. package/lib/umd/ui-react.js +1 -1
  51. package/lib/umd/ui-react.js.gz +0 -0
  52. package/package.json +22 -22
  53. package/readme.md +2 -2
@@ -15,22 +15,27 @@ const AVG = 'avg';
15
15
  const MIN = 'min';
16
16
  const MAX = 'max';
17
17
 
18
- const arrayPair = (value) => [value, value];
19
18
  const arrayHas = (array, value) => array.includes(value);
19
+ const arrayEvery = (array, cb) => array.every(cb);
20
+ const arrayIsEqual = (array1, array2) =>
21
+ arrayLength(array1) === arrayLength(array2) &&
22
+ arrayEvery(array1, (value1, index) => array2[index] === value1);
20
23
  const arrayIsSorted = (array, sorter) =>
21
- array.every(
24
+ arrayEvery(
25
+ array,
22
26
  (value, index) => index == 0 || sorter(array[index - 1], value) <= 0,
23
27
  );
24
28
  const arraySort = (array, sorter) => array.sort(sorter);
25
29
  const arrayForEach = (array, cb) => array.forEach(cb);
30
+ const arrayMap = (array, cb) => array.map(cb);
26
31
  const arraySum = (array) => arrayReduce(array, (i, j) => i + j, 0);
27
32
  const arrayLength = (array) => array.length;
28
33
  const arrayIsEmpty = (array) => arrayLength(array) == 0;
29
34
  const arrayReduce = (array, cb, initial) => array.reduce(cb, initial);
30
35
  const arrayFilter = (array, cb) => array.filter(cb);
31
- const arrayFromSecond = (ids) => ids.slice(1);
36
+ const arraySlice = (array, start, end) => array.slice(start, end);
32
37
  const arrayClear = (array, to) => array.splice(0, to);
33
- const arrayPush = (array, value) => array.push(value);
38
+ const arrayPush = (array, ...values) => array.push(...values);
34
39
  const arrayPop = (array) => array.pop();
35
40
 
36
41
  const jsonString = (obj) =>
@@ -65,7 +70,6 @@ const collSize = (coll) => coll.size;
65
70
  const collSize2 = collSizeN(collSize);
66
71
  const collSize3 = collSizeN(collSize2);
67
72
  const collSize4 = collSizeN(collSize3);
68
- const collPairSize = (pair, func = collSize) => func(pair[0]) + func(pair[1]);
69
73
  const collHas = (coll, keyOrValue) => coll?.has(keyOrValue) ?? false;
70
74
  const collIsEmpty = (coll) => isUndefined(coll) || collSize(coll) == 0;
71
75
  const collValues = (coll) => [...(coll?.values() ?? [])];
@@ -74,7 +78,6 @@ const collForEach = (coll, cb) => coll?.forEach(cb);
74
78
  const collDel = (coll, keyOrValue) => coll?.delete(keyOrValue);
75
79
 
76
80
  const mapNew = (entries) => new Map(entries);
77
- const mapNewPair = (newFunction = mapNew) => [newFunction(), newFunction()];
78
81
  const mapKeys = (map) => [...(map?.keys() ?? [])];
79
82
  const mapGet = (map, key) => map?.get(key);
80
83
  const mapForEach = (map, cb) =>
@@ -104,6 +107,27 @@ const mapClone = (map, childMapper) => {
104
107
  return map2;
105
108
  };
106
109
  const mapClone2 = (map) => mapClone(map, mapClone);
110
+ const visitTree = (node, path, ensureLeaf, pruneLeaf, p = 0) =>
111
+ ifNotUndefined(
112
+ (ensureLeaf ? mapEnsure : mapGet)(
113
+ node,
114
+ path[p],
115
+ p > arrayLength(path) - 2 ? ensureLeaf : mapNew,
116
+ ),
117
+ (nodeOrLeaf) => {
118
+ if (p > arrayLength(path) - 2) {
119
+ if (pruneLeaf?.(nodeOrLeaf)) {
120
+ mapSet(node, path[p]);
121
+ }
122
+ return nodeOrLeaf;
123
+ }
124
+ const leaf = visitTree(nodeOrLeaf, path, ensureLeaf, pruneLeaf, p + 1);
125
+ if (collIsEmpty(nodeOrLeaf)) {
126
+ mapSet(node, path[p]);
127
+ }
128
+ return leaf;
129
+ },
130
+ );
107
131
 
108
132
  const setNew = (entries) => new Set(entries);
109
133
  const setAdd = (set, value) => set?.add(value);
@@ -122,20 +146,46 @@ const getDefinableFunctions = (store, getDefaultThing, validateRowValue) => {
122
146
  const getTableId = (id) => mapGet(tableIds, id);
123
147
  const getThing = (id) => mapGet(things, id);
124
148
  const setThing = (id, thing) => mapSet(things, id, thing);
125
- const removeStoreListeners = (id) =>
126
- ifNotUndefined(mapGet(storeListenerIds, id), (listenerIds) => {
127
- collForEach(listenerIds, store.delListener);
128
- mapSet(storeListenerIds, id);
149
+ const addStoreListeners = (id, andCall, ...listenerIds) => {
150
+ const set = mapEnsure(storeListenerIds, id, setNew);
151
+ arrayForEach(
152
+ listenerIds,
153
+ (listenerId) =>
154
+ setAdd(set, listenerId) && andCall && store.callListener(listenerId),
155
+ );
156
+ return listenerIds;
157
+ };
158
+ const delStoreListeners = (id, ...listenerIds) =>
159
+ ifNotUndefined(mapGet(storeListenerIds, id), (allListenerIds) => {
160
+ arrayForEach(
161
+ arrayIsEmpty(listenerIds) ? collValues(allListenerIds) : listenerIds,
162
+ (listenerId) => {
163
+ store.delListener(listenerId);
164
+ collDel(allListenerIds, listenerId);
165
+ },
166
+ );
167
+ if (collIsEmpty(allListenerIds)) {
168
+ mapSet(storeListenerIds, id);
169
+ }
129
170
  });
130
- const setDefinition = (id, tableId, onChanged, getRowValue, getSortKey) => {
131
- const changedRowValues = mapNew();
132
- const changedSortKeys = mapNew();
171
+ const setDefinition = (id, tableId) => {
133
172
  mapSet(tableIds, id, tableId);
134
173
  if (!collHas(things, id)) {
135
174
  mapSet(things, id, getDefaultThing());
136
175
  mapSet(allRowValues, id, mapNew());
137
176
  mapSet(allSortKeys, id, mapNew());
138
177
  }
178
+ };
179
+ const setDefinitionAndListen = (
180
+ id,
181
+ tableId,
182
+ onChanged,
183
+ getRowValue,
184
+ getSortKey,
185
+ ) => {
186
+ setDefinition(id, tableId);
187
+ const changedRowValues = mapNew();
188
+ const changedSortKeys = mapNew();
139
189
  const rowValues = mapGet(allRowValues, id);
140
190
  const sortKeys = mapGet(allSortKeys, id);
141
191
  const processRow = (rowId) => {
@@ -185,16 +235,14 @@ const getDefinableFunctions = (store, getDefaultThing, validateRowValue) => {
185
235
  });
186
236
  }
187
237
  processTable(true);
188
- removeStoreListeners(id);
189
- mapSet(
190
- storeListenerIds,
238
+ delStoreListeners(id);
239
+ addStoreListeners(
191
240
  id,
192
- setNew([
193
- store.addRowListener(tableId, null, (_store, _tableId, rowId) =>
194
- processRow(rowId),
195
- ),
196
- store.addTableListener(tableId, () => processTable()),
197
- ]),
241
+ 0,
242
+ store.addRowListener(tableId, null, (_store, _tableId, rowId) =>
243
+ processRow(rowId),
244
+ ),
245
+ store.addTableListener(tableId, () => processTable()),
198
246
  );
199
247
  };
200
248
  const delDefinition = (id) => {
@@ -202,7 +250,7 @@ const getDefinableFunctions = (store, getDefaultThing, validateRowValue) => {
202
250
  mapSet(things, id);
203
251
  mapSet(allRowValues, id);
204
252
  mapSet(allSortKeys, id);
205
- removeStoreListeners(id);
253
+ delStoreListeners(id);
206
254
  };
207
255
  const destroy = () => mapForEach(storeListenerIds, delDefinition);
208
256
  return [
@@ -214,8 +262,11 @@ const getDefinableFunctions = (store, getDefaultThing, validateRowValue) => {
214
262
  getThing,
215
263
  setThing,
216
264
  setDefinition,
265
+ setDefinitionAndListen,
217
266
  delDefinition,
218
267
  destroy,
268
+ addStoreListeners,
269
+ delStoreListeners,
219
270
  ];
220
271
  };
221
272
  const getRowCellFunction = (getRowCell, defaultCellValue) =>
@@ -232,64 +283,51 @@ const getCreateFunction = (getFunction) => {
232
283
  };
233
284
  };
234
285
 
235
- const addDeepSet = (deepSet, value, ids) =>
236
- arrayLength(ids) < 2
237
- ? setAdd(
238
- arrayIsEmpty(ids) ? deepSet : mapEnsure(deepSet, ids[0], setNew),
239
- value,
240
- )
241
- : addDeepSet(
242
- mapEnsure(deepSet, ids[0], mapNew),
243
- value,
244
- arrayFromSecond(ids),
245
- );
246
- const forDeepSet = (valueDo) => {
247
- const deep = (deepIdSet, arg, ...ids) =>
248
- ifNotUndefined(deepIdSet, (deepIdSet2) =>
249
- arrayIsEmpty(ids)
250
- ? valueDo(deepIdSet2, arg)
251
- : arrayForEach([ids[0], null], (id) =>
252
- deep(mapGet(deepIdSet2, id), arg, ...arrayFromSecond(ids)),
253
- ),
254
- );
255
- return deep;
286
+ const getWildcardedLeaves = (deepIdSet, path = [EMPTY_STRING]) => {
287
+ const sets = [];
288
+ const deep = (set, p) =>
289
+ p == arrayLength(path)
290
+ ? arrayPush(sets, set)
291
+ : arrayForEach([path[p], null], (id) => deep(mapGet(set, id), p + 1));
292
+ deep(deepIdSet, 0);
293
+ return sets;
256
294
  };
257
295
  const getListenerFunctions = (getThing) => {
258
296
  let thing;
259
297
  let nextId = 0;
260
298
  const listenerPool = [];
261
299
  const allListeners = mapNew();
262
- const addListener = (listener, deepSet, idOrNulls = []) => {
300
+ const addListener = (listener, idSetNode, idOrNulls) => {
263
301
  thing ??= getThing();
264
- const id = arrayPop(listenerPool) ?? '' + nextId++;
265
- mapSet(allListeners, id, [listener, deepSet, idOrNulls]);
266
- addDeepSet(deepSet, id, idOrNulls);
302
+ const id = arrayPop(listenerPool) ?? EMPTY_STRING + nextId++;
303
+ mapSet(allListeners, id, [listener, idSetNode, idOrNulls]);
304
+ setAdd(visitTree(idSetNode, idOrNulls ?? [EMPTY_STRING], setNew), id);
267
305
  return id;
268
306
  };
269
- const callListeners = (deepSet, ids = [], ...extraArgs) =>
270
- forDeepSet(collForEach)(
271
- deepSet,
272
- (id) =>
307
+ const callListeners = (idSetNode, ids, ...extraArgs) =>
308
+ arrayForEach(getWildcardedLeaves(idSetNode, ids), (set) =>
309
+ collForEach(set, (id) =>
273
310
  ifNotUndefined(mapGet(allListeners, id), ([listener]) =>
274
- listener(thing, ...ids, ...extraArgs),
311
+ listener(thing, ...(ids ?? []), ...extraArgs),
275
312
  ),
276
- ...ids,
313
+ ),
277
314
  );
278
315
  const delListener = (id) =>
279
- ifNotUndefined(
280
- mapGet(allListeners, id),
281
- ([, deepSet, idOrNulls]) => {
282
- forDeepSet(collDel)(deepSet, id, ...idOrNulls);
283
- mapSet(allListeners, id);
284
- if (arrayLength(listenerPool) < 1e3) {
285
- arrayPush(listenerPool, id);
286
- }
287
- return idOrNulls;
288
- },
289
- () => [],
290
- );
316
+ ifNotUndefined(mapGet(allListeners, id), ([, idSetNode, idOrNulls]) => {
317
+ visitTree(idSetNode, idOrNulls ?? [EMPTY_STRING], void 0, (idSet) => {
318
+ collDel(idSet, id);
319
+ return collIsEmpty(idSet) ? 1 : 0;
320
+ });
321
+ mapSet(allListeners, id);
322
+ if (arrayLength(listenerPool) < 1e3) {
323
+ arrayPush(listenerPool, id);
324
+ }
325
+ return idOrNulls;
326
+ });
327
+ const hasListeners = (idSetNode, ids) =>
328
+ !arrayEvery(getWildcardedLeaves(idSetNode, ids), isUndefined);
291
329
  const callListener = (id, idNullGetters, extraArgsGetter) =>
292
- ifNotUndefined(mapGet(allListeners, id), ([listener, , idOrNulls]) => {
330
+ ifNotUndefined(mapGet(allListeners, id), ([listener, , idOrNulls = []]) => {
293
331
  const callWithIds = (...ids) => {
294
332
  const index = arrayLength(ids);
295
333
  index == arrayLength(idOrNulls)
@@ -302,7 +340,7 @@ const getListenerFunctions = (getThing) => {
302
340
  };
303
341
  callWithIds();
304
342
  });
305
- return [addListener, callListeners, delListener, callListener];
343
+ return [addListener, callListeners, delListener, hasListeners, callListener];
306
344
  };
307
345
 
308
346
  const object = Object;
@@ -318,6 +356,17 @@ const objForEach = (obj, cb) =>
318
356
  arrayForEach(object.entries(obj), ([id, value]) => cb(value, id));
319
357
  const objIsEmpty = (obj) => arrayIsEmpty(objIds(obj));
320
358
 
359
+ const getCellType = (cell) => {
360
+ const type = getTypeOf(cell);
361
+ return isTypeStringOrBoolean(type) || (type == NUMBER && isFiniteNumber(cell))
362
+ ? type
363
+ : void 0;
364
+ };
365
+ const setOrDelCell = (store, tableId, rowId, cellId, cell) =>
366
+ isUndefined(cell)
367
+ ? store.delCell(tableId, rowId, cellId, true)
368
+ : store.setCell(tableId, rowId, cellId, cell);
369
+
321
370
  const createCheckpoints = getCreateFunction((store) => {
322
371
  let backwardIdsSize = 100;
323
372
  let currentId;
@@ -325,7 +374,7 @@ const createCheckpoints = getCreateFunction((store) => {
325
374
  let listening = 1;
326
375
  let nextCheckpointId;
327
376
  let checkpointsChanged;
328
- const checkpointIdsListeners = setNew();
377
+ const checkpointIdsListeners = mapNew();
329
378
  const checkpointListeners = mapNew();
330
379
  const [addListener, callListeners, delListenerImpl] = getListenerFunctions(
331
380
  () => checkpoints,
@@ -340,9 +389,7 @@ const createCheckpoints = getCreateFunction((store) => {
340
389
  collForEach(mapGet(deltas, checkpointId), (table, tableId) =>
341
390
  collForEach(table, (row, rowId) =>
342
391
  collForEach(row, (oldNew, cellId) =>
343
- isUndefined(oldNew[oldOrNew])
344
- ? store.delCell(tableId, rowId, cellId, true)
345
- : store.setCell(tableId, rowId, cellId, oldNew[oldOrNew]),
392
+ setOrDelCell(store, tableId, rowId, cellId, oldNew[oldOrNew]),
346
393
  ),
347
394
  ),
348
395
  ),
@@ -392,9 +439,9 @@ const createCheckpoints = getCreateFunction((store) => {
392
439
  }
393
440
  },
394
441
  );
395
- const addCheckpointImpl = (label = '') => {
442
+ const addCheckpointImpl = (label = EMPTY_STRING) => {
396
443
  if (isUndefined(currentId)) {
397
- currentId = '' + nextCheckpointId++;
444
+ currentId = EMPTY_STRING + nextCheckpointId++;
398
445
  mapSet(deltas, currentId, delta);
399
446
  setCheckpoint(currentId, label);
400
447
  delta = mapNew();
@@ -492,7 +539,7 @@ const createCheckpoints = getCreateFunction((store) => {
492
539
  store.delListener(listenerId);
493
540
  };
494
541
  const getListenerStats = () => ({
495
- checkpointIds: collSize(checkpointIdsListeners),
542
+ checkpointIds: collSize2(checkpointIdsListeners),
496
543
  checkpoint: collSize2(checkpointListeners),
497
544
  });
498
545
  const checkpoints = {
@@ -530,7 +577,8 @@ const createIndexes = getCreateFunction((store) => {
530
577
  getTableId,
531
578
  getIndex,
532
579
  setIndex,
533
- setDefinition,
580
+ ,
581
+ setDefinitionAndListen,
534
582
  delDefinition,
535
583
  destroy,
536
584
  ] = getDefinableFunctions(store, mapNew, (value) =>
@@ -551,7 +599,7 @@ const createIndexes = getCreateFunction((store) => {
551
599
  const sliceIdArraySorter = isUndefined(sliceIdSorter)
552
600
  ? void 0
553
601
  : ([id1], [id2]) => sliceIdSorter(id1, id2);
554
- setDefinition(
602
+ setDefinitionAndListen(
555
603
  indexId,
556
604
  tableId,
557
605
  (change, changedSliceIds, changedSortKeys, sliceIds, sortKeys, force) => {
@@ -695,7 +743,7 @@ const createIndexes = getCreateFunction((store) => {
695
743
  return objFreeze(indexes);
696
744
  });
697
745
 
698
- const aggregators = mapNew([
746
+ const numericAggregators = mapNew([
699
747
  [
700
748
  AVG,
701
749
  [
@@ -735,6 +783,35 @@ const aggregators = mapNew([
735
783
  ],
736
784
  ],
737
785
  ]);
786
+ const getAggregateValue = (
787
+ aggregateValue,
788
+ oldLength,
789
+ newValues,
790
+ changedValues,
791
+ aggregators,
792
+ force = false,
793
+ ) => {
794
+ if (collIsEmpty(newValues)) {
795
+ return void 0;
796
+ }
797
+ const [aggregate, aggregateAdd, aggregateRemove, aggregateReplace] =
798
+ aggregators;
799
+ force ||= isUndefined(aggregateValue);
800
+ collForEach(changedValues, ([oldValue, newValue]) => {
801
+ if (!force) {
802
+ aggregateValue = isUndefined(oldValue)
803
+ ? aggregateAdd?.(aggregateValue, newValue, oldLength++)
804
+ : isUndefined(newValue)
805
+ ? aggregateRemove?.(aggregateValue, oldValue, oldLength--)
806
+ : aggregateReplace?.(aggregateValue, newValue, oldValue, oldLength);
807
+ force ||= isUndefined(aggregateValue);
808
+ }
809
+ });
810
+ return force
811
+ ? aggregate(collValues(newValues), collSize(newValues))
812
+ : aggregateValue;
813
+ };
814
+
738
815
  const createMetrics = getCreateFunction((store) => {
739
816
  const metricListeners = mapNew();
740
817
  const [
@@ -745,7 +822,8 @@ const createMetrics = getCreateFunction((store) => {
745
822
  getTableId,
746
823
  getMetric,
747
824
  setMetric,
748
- setDefinition,
825
+ ,
826
+ setDefinitionAndListen,
749
827
  delDefinition,
750
828
  destroy,
751
829
  ] = getDefinableFunctions(store, getUndefined, (value) =>
@@ -769,38 +847,29 @@ const createMetrics = getCreateFunction((store) => {
769
847
  aggregateRemove,
770
848
  aggregateReplace,
771
849
  ) => {
772
- const metricAggregators = isFunction(aggregate)
850
+ const aggregators = isFunction(aggregate)
773
851
  ? [aggregate, aggregateAdd, aggregateRemove, aggregateReplace]
774
- : mapGet(aggregators, aggregate) ?? mapGet(aggregators, SUM);
775
- setDefinition(
852
+ : mapGet(numericAggregators, aggregate) ??
853
+ mapGet(numericAggregators, SUM);
854
+ setDefinitionAndListen(
776
855
  metricId,
777
856
  tableId,
778
857
  (change, changedNumbers, _changedSortKeys, numbers, _sortKeys, force) => {
779
- let newMetric = getMetric(metricId);
780
- let length = collSize(numbers);
781
- const [aggregate2, aggregateAdd2, aggregateRemove2, aggregateReplace2] =
782
- metricAggregators;
783
- force = force || isUndefined(newMetric);
784
- collForEach(changedNumbers, ([oldNumber, newNumber]) => {
785
- if (!force) {
786
- newMetric = isUndefined(oldNumber)
787
- ? aggregateAdd2?.(newMetric, newNumber, length++)
788
- : isUndefined(newNumber)
789
- ? aggregateRemove2?.(newMetric, oldNumber, length--)
790
- : aggregateReplace2?.(newMetric, newNumber, oldNumber, length);
791
- }
792
- force = force || isUndefined(newMetric);
793
- });
858
+ const oldMetric = getMetric(metricId);
859
+ const oldLength = collSize(numbers);
860
+ force ||= isUndefined(oldMetric);
794
861
  change();
795
- if (collIsEmpty(numbers)) {
796
- newMetric = void 0;
797
- } else if (force) {
798
- newMetric = aggregate2(collValues(numbers), collSize(numbers));
799
- }
862
+ let newMetric = getAggregateValue(
863
+ oldMetric,
864
+ oldLength,
865
+ numbers,
866
+ changedNumbers,
867
+ aggregators,
868
+ force,
869
+ );
800
870
  if (!isFiniteNumber(newMetric)) {
801
871
  newMetric = void 0;
802
872
  }
803
- const oldMetric = getMetric(metricId);
804
873
  if (newMetric != oldMetric) {
805
874
  setMetric(metricId, newMetric);
806
875
  callListeners(metricListeners, [metricId], newMetric, oldMetric);
@@ -858,7 +927,7 @@ const createCustomPersister = (
858
927
  loads++;
859
928
  }
860
929
  const body = await getPersisted();
861
- if (!isUndefined(body) && body != '') {
930
+ if (!isUndefined(body) && body != EMPTY_STRING) {
862
931
  store.setJson(body);
863
932
  } else {
864
933
  store.setTables(initialTables);
@@ -1011,6 +1080,571 @@ const createRemotePersister = (
1011
1080
  );
1012
1081
  };
1013
1082
 
1083
+ const createQueries = getCreateFunction((store) => {
1084
+ const [
1085
+ getStore,
1086
+ getQueryIds,
1087
+ forEachQuery,
1088
+ hasQuery,
1089
+ getTableId,
1090
+ ,
1091
+ ,
1092
+ setDefinition,
1093
+ ,
1094
+ delDefinition,
1095
+ destroy,
1096
+ addStoreListeners,
1097
+ delStoreListeners,
1098
+ ] = getDefinableFunctions(store, () => true, getUndefined);
1099
+ const preStore1 = store.createStore();
1100
+ const preStore2 = store.createStore();
1101
+ const resultStore = store.createStore();
1102
+ const preStoreListenerIds = mapNew();
1103
+ const addPreStoreListener = (preStore, queryId, ...listenerIds) =>
1104
+ arrayForEach(listenerIds, (listenerId) =>
1105
+ setAdd(
1106
+ mapEnsure(
1107
+ mapEnsure(preStoreListenerIds, queryId, mapNew),
1108
+ preStore,
1109
+ setNew,
1110
+ ),
1111
+ listenerId,
1112
+ ),
1113
+ );
1114
+ const synchronizeTransactions = (queryId, fromStore, toStore) =>
1115
+ addStoreListeners(
1116
+ queryId,
1117
+ 0,
1118
+ fromStore.addWillFinishTransactionListener(toStore.startTransaction),
1119
+ fromStore.addDidFinishTransactionListener(() =>
1120
+ toStore.finishTransaction(),
1121
+ ),
1122
+ );
1123
+ const setQueryDefinition = (queryId, tableId, build) => {
1124
+ setDefinition(queryId, tableId);
1125
+ preStore1.delTable(queryId);
1126
+ preStore2.delTable(queryId);
1127
+ resultStore.delTable(queryId);
1128
+ let offsetLimit;
1129
+ const selectEntries = [];
1130
+ const joinEntries = [[null, [tableId, null, null, [], mapNew()]]];
1131
+ const wheres = [];
1132
+ const groupEntries = [];
1133
+ const havings = [];
1134
+ const orders = [];
1135
+ const select = (arg1, arg2) => {
1136
+ const selectEntry = isFunction(arg1)
1137
+ ? [arrayLength(selectEntries) + EMPTY_STRING, arg1]
1138
+ : [
1139
+ isUndefined(arg2) ? arg1 : arg2,
1140
+ (getTableCell) => getTableCell(arg1, arg2),
1141
+ ];
1142
+ arrayPush(selectEntries, selectEntry);
1143
+ return {as: (selectedCellId) => (selectEntry[0] = selectedCellId)};
1144
+ };
1145
+ const join = (joinedTableId, arg1, arg2) => {
1146
+ const fromIntermediateJoinedTableId =
1147
+ isUndefined(arg2) || isFunction(arg1) ? null : arg1;
1148
+ const onArg = isUndefined(fromIntermediateJoinedTableId) ? arg1 : arg2;
1149
+ const joinEntry = [
1150
+ joinedTableId,
1151
+ [
1152
+ joinedTableId,
1153
+ fromIntermediateJoinedTableId,
1154
+ isFunction(onArg) ? onArg : (getCell) => getCell(onArg),
1155
+ [],
1156
+ mapNew(),
1157
+ ],
1158
+ ];
1159
+ arrayPush(joinEntries, joinEntry);
1160
+ return {as: (joinedTableId2) => (joinEntry[0] = joinedTableId2)};
1161
+ };
1162
+ const where = (arg1, arg2, arg3) =>
1163
+ arrayPush(
1164
+ wheres,
1165
+ isFunction(arg1)
1166
+ ? arg1
1167
+ : isUndefined(arg3)
1168
+ ? (getTableCell) => getTableCell(arg1) === arg2
1169
+ : (getTableCell) => getTableCell(arg1, arg2) === arg3,
1170
+ );
1171
+ const group = (
1172
+ selectedCellId,
1173
+ aggregate,
1174
+ aggregateAdd,
1175
+ aggregateRemove,
1176
+ aggregateReplace,
1177
+ ) => {
1178
+ const groupEntry = [
1179
+ selectedCellId,
1180
+ [
1181
+ selectedCellId,
1182
+ isFunction(aggregate)
1183
+ ? [aggregate, aggregateAdd, aggregateRemove, aggregateReplace]
1184
+ : mapGet(numericAggregators, aggregate) ?? [
1185
+ (_cells, length) => length,
1186
+ ],
1187
+ ],
1188
+ ];
1189
+ arrayPush(groupEntries, groupEntry);
1190
+ return {as: (groupedCellId) => (groupEntry[0] = groupedCellId)};
1191
+ };
1192
+ const having = (arg1, arg2) =>
1193
+ arrayPush(
1194
+ havings,
1195
+ isFunction(arg1)
1196
+ ? arg1
1197
+ : (getSelectedOrGroupedCell) =>
1198
+ getSelectedOrGroupedCell(arg1) === arg2,
1199
+ );
1200
+ const order = (arg1, descending) =>
1201
+ arrayPush(orders, [
1202
+ isFunction(arg1)
1203
+ ? arg1
1204
+ : (getSelectedOrGroupedCell) => getSelectedOrGroupedCell(arg1) ?? 0,
1205
+ descending,
1206
+ ]);
1207
+ const limit = (arg1, arg2) => {
1208
+ offsetLimit = isUndefined(arg2) ? [0, arg1] : [arg1, arg2];
1209
+ };
1210
+ build({select, join, where, group, having, order, limit});
1211
+ const selects = mapNew(selectEntries);
1212
+ if (collIsEmpty(selects)) {
1213
+ return queries;
1214
+ }
1215
+ const joins = mapNew(joinEntries);
1216
+ mapForEach(joins, (asTableId, [, fromAsTableId]) =>
1217
+ ifNotUndefined(mapGet(joins, fromAsTableId), ({3: toAsTableIds}) =>
1218
+ isUndefined(asTableId) ? 0 : arrayPush(toAsTableIds, asTableId),
1219
+ ),
1220
+ );
1221
+ const groups = mapNew(groupEntries);
1222
+ let selectJoinWhereStore = preStore1;
1223
+ let groupHavingStore = preStore2;
1224
+ if (arrayIsEmpty(orders) && isUndefined(offsetLimit)) {
1225
+ groupHavingStore = resultStore;
1226
+ } else {
1227
+ synchronizeTransactions(queryId, groupHavingStore, resultStore);
1228
+ const groupRowIdSorter = (rowId1, rowId2) => {
1229
+ const sortKeys1 = mapGet(sortKeysByGroupRowId, rowId1) ?? [];
1230
+ const sortKeys2 = mapGet(sortKeysByGroupRowId, rowId2) ?? [];
1231
+ const orderIndex = orders.findIndex(
1232
+ (_order, index) => sortKeys1[index] !== sortKeys2[index],
1233
+ );
1234
+ return orderIndex < 0
1235
+ ? 0
1236
+ : defaultSorter(sortKeys1[orderIndex], sortKeys2[orderIndex]) *
1237
+ (orders[orderIndex][1] ? -1 : 1);
1238
+ };
1239
+ const sortKeysByGroupRowId = mapNew();
1240
+ const sortedGroupRowIds = mapNew();
1241
+ addPreStoreListener(
1242
+ groupHavingStore,
1243
+ queryId,
1244
+ arrayIsEmpty(orders)
1245
+ ? groupHavingStore.addRowIdsListener(queryId, () =>
1246
+ collClear(sortedGroupRowIds),
1247
+ )
1248
+ : groupHavingStore.addRowListener(
1249
+ queryId,
1250
+ null,
1251
+ (_store, _tableId, groupRowId) => {
1252
+ let newSortKeys = null;
1253
+ if (groupHavingStore.hasRow(queryId, groupRowId)) {
1254
+ const oldSortKeys =
1255
+ mapGet(sortKeysByGroupRowId, groupRowId) ?? [];
1256
+ const groupRow = groupHavingStore.getRow(queryId, groupRowId);
1257
+ const getCell = (getSelectedOrGroupedCell) =>
1258
+ groupRow[getSelectedOrGroupedCell];
1259
+ newSortKeys = arrayMap(orders, ([getSortKey]) =>
1260
+ getSortKey(getCell, groupRowId),
1261
+ );
1262
+ if (arrayIsEqual(oldSortKeys, newSortKeys)) {
1263
+ if (mapGet(sortedGroupRowIds, groupRowId)) {
1264
+ resultStore.setRow(queryId, groupRowId, groupRow);
1265
+ }
1266
+ return;
1267
+ }
1268
+ }
1269
+ mapSet(sortKeysByGroupRowId, groupRowId, newSortKeys);
1270
+ collClear(sortedGroupRowIds);
1271
+ },
1272
+ ),
1273
+ groupHavingStore.addTableListener(queryId, () => {
1274
+ if (collIsEmpty(sortedGroupRowIds)) {
1275
+ resultStore.delTable(queryId);
1276
+ if (groupHavingStore.hasTable(queryId)) {
1277
+ const groupTable = groupHavingStore.getTable(queryId);
1278
+ arrayForEach(
1279
+ arraySort(objIds(groupTable), groupRowIdSorter),
1280
+ (id) => mapSet(sortedGroupRowIds, id, 0),
1281
+ );
1282
+ arrayForEach(
1283
+ ifNotUndefined(
1284
+ offsetLimit,
1285
+ ([offset, limit2]) =>
1286
+ arraySlice(
1287
+ mapKeys(sortedGroupRowIds),
1288
+ offset,
1289
+ offset + limit2,
1290
+ ),
1291
+ () => mapKeys(sortedGroupRowIds),
1292
+ ),
1293
+ (groupRowId) => {
1294
+ resultStore.setRow(
1295
+ queryId,
1296
+ groupRowId,
1297
+ groupTable[groupRowId],
1298
+ );
1299
+ mapSet(sortedGroupRowIds, groupRowId, 1);
1300
+ },
1301
+ );
1302
+ }
1303
+ }
1304
+ }),
1305
+ );
1306
+ }
1307
+ if (collIsEmpty(groups) && arrayIsEmpty(havings)) {
1308
+ selectJoinWhereStore = groupHavingStore;
1309
+ } else {
1310
+ synchronizeTransactions(queryId, selectJoinWhereStore, groupHavingStore);
1311
+ const groupedSelectedCellIds = mapNew();
1312
+ mapForEach(groups, (groupedCellId, [selectedCellId, aggregators]) =>
1313
+ setAdd(mapEnsure(groupedSelectedCellIds, selectedCellId, setNew), [
1314
+ groupedCellId,
1315
+ aggregators,
1316
+ ]),
1317
+ );
1318
+ const groupBySelectedCellIds = setNew();
1319
+ mapForEach(selects, (selectedCellId) =>
1320
+ collHas(groupedSelectedCellIds, selectedCellId)
1321
+ ? 0
1322
+ : setAdd(groupBySelectedCellIds, selectedCellId),
1323
+ );
1324
+ const tree = mapNew();
1325
+ const writeGroupRow = (
1326
+ leaf,
1327
+ changedGroupedSelectedCells,
1328
+ selectedRowId,
1329
+ forceRemove,
1330
+ ) =>
1331
+ ifNotUndefined(
1332
+ leaf,
1333
+ ([selectedCells, selectedRowIds, groupRowId, groupRow]) => {
1334
+ mapForEach(
1335
+ changedGroupedSelectedCells,
1336
+ (selectedCellId, [newCell]) => {
1337
+ const selectedCell = mapEnsure(
1338
+ selectedCells,
1339
+ selectedCellId,
1340
+ mapNew,
1341
+ );
1342
+ const oldLeafCell = mapGet(selectedCell, selectedRowId);
1343
+ const newLeafCell = forceRemove ? void 0 : newCell;
1344
+ if (oldLeafCell !== newLeafCell) {
1345
+ const oldNewSet = setNew([[oldLeafCell, newLeafCell]]);
1346
+ const oldLength = collSize(selectedCell);
1347
+ mapSet(selectedCell, selectedRowId, newLeafCell);
1348
+ collForEach(
1349
+ mapGet(groupedSelectedCellIds, selectedCellId),
1350
+ ([groupedCellId, aggregators]) => {
1351
+ const aggregateValue = getAggregateValue(
1352
+ groupRow[groupedCellId],
1353
+ oldLength,
1354
+ selectedCell,
1355
+ oldNewSet,
1356
+ aggregators,
1357
+ );
1358
+ groupRow[groupedCellId] = isUndefined(
1359
+ getCellType(aggregateValue),
1360
+ )
1361
+ ? null
1362
+ : aggregateValue;
1363
+ },
1364
+ );
1365
+ }
1366
+ },
1367
+ );
1368
+ (collIsEmpty(selectedRowIds) ||
1369
+ !arrayEvery(havings, (having2) =>
1370
+ having2((cellId) => groupRow[cellId]),
1371
+ )
1372
+ ? groupHavingStore.delRow
1373
+ : groupHavingStore.setRow)(queryId, groupRowId, groupRow);
1374
+ },
1375
+ );
1376
+ addPreStoreListener(
1377
+ selectJoinWhereStore,
1378
+ queryId,
1379
+ selectJoinWhereStore.addRowListener(
1380
+ queryId,
1381
+ null,
1382
+ (_store, _tableId, selectedRowId, getCellChange) => {
1383
+ const oldPath = [];
1384
+ const newPath = [];
1385
+ const changedGroupedSelectedCells = mapNew();
1386
+ const rowExists = selectJoinWhereStore.hasRow(
1387
+ queryId,
1388
+ selectedRowId,
1389
+ );
1390
+ let changedLeaf = !rowExists;
1391
+ collForEach(groupBySelectedCellIds, (selectedCellId) => {
1392
+ const [changed, oldCell, newCell] = getCellChange(
1393
+ queryId,
1394
+ selectedRowId,
1395
+ selectedCellId,
1396
+ );
1397
+ arrayPush(oldPath, oldCell);
1398
+ arrayPush(newPath, newCell);
1399
+ changedLeaf ||= changed;
1400
+ });
1401
+ mapForEach(groupedSelectedCellIds, (selectedCellId) => {
1402
+ const [changed, , newCell] = getCellChange(
1403
+ queryId,
1404
+ selectedRowId,
1405
+ selectedCellId,
1406
+ );
1407
+ if (changedLeaf || changed) {
1408
+ mapSet(changedGroupedSelectedCells, selectedCellId, [newCell]);
1409
+ }
1410
+ });
1411
+ if (changedLeaf) {
1412
+ writeGroupRow(
1413
+ visitTree(
1414
+ tree,
1415
+ oldPath,
1416
+ void 0,
1417
+ ([, selectedRowIds, groupRowId]) => {
1418
+ collDel(selectedRowIds, selectedRowId);
1419
+ if (collIsEmpty(selectedRowIds)) {
1420
+ groupHavingStore.delRow(queryId, groupRowId);
1421
+ return 1;
1422
+ }
1423
+ },
1424
+ ),
1425
+ changedGroupedSelectedCells,
1426
+ selectedRowId,
1427
+ 1,
1428
+ );
1429
+ }
1430
+ if (rowExists) {
1431
+ writeGroupRow(
1432
+ visitTree(
1433
+ tree,
1434
+ newPath,
1435
+ () => {
1436
+ const groupRow = {};
1437
+ collForEach(
1438
+ groupBySelectedCellIds,
1439
+ (selectedCellId) =>
1440
+ (groupRow[selectedCellId] =
1441
+ selectJoinWhereStore.getCell(
1442
+ queryId,
1443
+ selectedRowId,
1444
+ selectedCellId,
1445
+ )),
1446
+ );
1447
+ return [
1448
+ mapNew(),
1449
+ setNew(),
1450
+ groupHavingStore.addRow(queryId, groupRow, 1),
1451
+ groupRow,
1452
+ ];
1453
+ },
1454
+ ([, selectedRowIds]) => {
1455
+ setAdd(selectedRowIds, selectedRowId);
1456
+ },
1457
+ ),
1458
+ changedGroupedSelectedCells,
1459
+ selectedRowId,
1460
+ );
1461
+ }
1462
+ },
1463
+ ),
1464
+ );
1465
+ }
1466
+ synchronizeTransactions(queryId, store, selectJoinWhereStore);
1467
+ const writeSelectRow = (rootRowId) => {
1468
+ const getTableCell = (arg1, arg2) =>
1469
+ store.getCell(
1470
+ ...(isUndefined(arg2)
1471
+ ? [tableId, rootRowId, arg1]
1472
+ : arg1 === tableId
1473
+ ? [tableId, rootRowId, arg2]
1474
+ : [
1475
+ mapGet(joins, arg1)?.[0],
1476
+ mapGet(mapGet(joins, arg1)?.[4], rootRowId)?.[0],
1477
+ arg2,
1478
+ ]),
1479
+ );
1480
+ selectJoinWhereStore.transaction(() =>
1481
+ arrayEvery(wheres, (where2) => where2(getTableCell))
1482
+ ? mapForEach(selects, (asCellId, tableCellGetter) =>
1483
+ setOrDelCell(
1484
+ selectJoinWhereStore,
1485
+ queryId,
1486
+ rootRowId,
1487
+ asCellId,
1488
+ tableCellGetter(getTableCell, rootRowId),
1489
+ ),
1490
+ )
1491
+ : selectJoinWhereStore.delRow(queryId, rootRowId),
1492
+ );
1493
+ };
1494
+ const listenToTable = (rootRowId, tableId2, rowId, joinedTableIds2) => {
1495
+ const getCell = (cellId) => store.getCell(tableId2, rowId, cellId);
1496
+ arrayForEach(joinedTableIds2, (remoteAsTableId) => {
1497
+ const [realJoinedTableId, , on, nextJoinedTableIds, remoteIdPair] =
1498
+ mapGet(joins, remoteAsTableId);
1499
+ const remoteRowId = on?.(getCell, rootRowId);
1500
+ const [previousRemoteRowId, previousRemoteListenerId] =
1501
+ mapGet(remoteIdPair, rootRowId) ?? [];
1502
+ if (remoteRowId != previousRemoteRowId) {
1503
+ if (!isUndefined(previousRemoteListenerId)) {
1504
+ delStoreListeners(queryId, previousRemoteListenerId);
1505
+ }
1506
+ mapSet(
1507
+ remoteIdPair,
1508
+ rootRowId,
1509
+ isUndefined(remoteRowId)
1510
+ ? null
1511
+ : [
1512
+ remoteRowId,
1513
+ ...addStoreListeners(
1514
+ queryId,
1515
+ 1,
1516
+ store.addRowListener(realJoinedTableId, remoteRowId, () =>
1517
+ listenToTable(
1518
+ rootRowId,
1519
+ realJoinedTableId,
1520
+ remoteRowId,
1521
+ nextJoinedTableIds,
1522
+ ),
1523
+ ),
1524
+ ),
1525
+ ],
1526
+ );
1527
+ }
1528
+ });
1529
+ writeSelectRow(rootRowId);
1530
+ };
1531
+ const {3: joinedTableIds} = mapGet(joins, null);
1532
+ selectJoinWhereStore.transaction(() =>
1533
+ addStoreListeners(
1534
+ queryId,
1535
+ 1,
1536
+ store.addRowListener(tableId, null, (_store, _tableId, rootRowId) => {
1537
+ if (store.hasRow(tableId, rootRowId)) {
1538
+ listenToTable(rootRowId, tableId, rootRowId, joinedTableIds);
1539
+ } else {
1540
+ selectJoinWhereStore.delRow(queryId, rootRowId);
1541
+ collForEach(joins, ({4: idsByRootRowId}) =>
1542
+ ifNotUndefined(
1543
+ mapGet(idsByRootRowId, rootRowId),
1544
+ ([, listenerId]) => {
1545
+ delStoreListeners(queryId, listenerId);
1546
+ mapSet(idsByRootRowId, rootRowId);
1547
+ },
1548
+ ),
1549
+ );
1550
+ }
1551
+ }),
1552
+ ),
1553
+ );
1554
+ return queries;
1555
+ };
1556
+ const delQueryDefinition = (queryId) => {
1557
+ mapForEach(mapGet(preStoreListenerIds, queryId), (preStore, listenerIds) =>
1558
+ collForEach(listenerIds, (listenerId) =>
1559
+ preStore.delListener(listenerId),
1560
+ ),
1561
+ );
1562
+ delDefinition(queryId);
1563
+ return queries;
1564
+ };
1565
+ const getResultTable = (queryId) => resultStore.getTable(queryId);
1566
+ const getResultRowIds = (queryId) => resultStore.getRowIds(queryId);
1567
+ const getResultRow = (queryId, rowId) => resultStore.getRow(queryId, rowId);
1568
+ const getResultCellIds = (queryId, rowId) =>
1569
+ resultStore.getCellIds(queryId, rowId);
1570
+ const getResultCell = (queryId, rowId, cellId) =>
1571
+ resultStore.getCell(queryId, rowId, cellId);
1572
+ const hasResultTable = (queryId) => resultStore.hasTable(queryId);
1573
+ const hasResultRow = (queryId, rowId) => resultStore.hasRow(queryId, rowId);
1574
+ const hasResultCell = (queryId, rowId, cellId) =>
1575
+ resultStore.hasCell(queryId, rowId, cellId);
1576
+ const forEachResultTable = (tableCallback) =>
1577
+ resultStore.forEachTable(tableCallback);
1578
+ const forEachResultRow = (queryId, rowCallback) =>
1579
+ resultStore.forEachRow(queryId, rowCallback);
1580
+ const forEachResultCell = (queryId, rowId, cellCallback) =>
1581
+ resultStore.forEachCell(queryId, rowId, cellCallback);
1582
+ const addResultTableListener = (queryId, listener) =>
1583
+ resultStore.addTableListener(queryId, (_store, ...args) =>
1584
+ listener(queries, ...args),
1585
+ );
1586
+ const addResultRowIdsListener = (queryId, listener, trackReorder) =>
1587
+ resultStore.addRowIdsListener(
1588
+ queryId,
1589
+ (_store, ...args) => listener(queries, ...args),
1590
+ trackReorder,
1591
+ );
1592
+ const addResultRowListener = (queryId, rowId, listener) =>
1593
+ resultStore.addRowListener(queryId, rowId, (_store, ...args) =>
1594
+ listener(queries, ...args),
1595
+ );
1596
+ const addResultCellIdsListener = (queryId, rowId, listener) =>
1597
+ resultStore.addCellIdsListener(queryId, rowId, (_store, ...args) =>
1598
+ listener(queries, ...args),
1599
+ );
1600
+ const addResultCellListener = (queryId, rowId, cellId, listener) =>
1601
+ resultStore.addCellListener(queryId, rowId, cellId, (_store, ...args) =>
1602
+ listener(queries, ...args),
1603
+ );
1604
+ const delListener = (listenerId) => {
1605
+ resultStore.delListener(listenerId);
1606
+ return queries;
1607
+ };
1608
+ const getListenerStats = () => {
1609
+ const {
1610
+ tables: _1,
1611
+ tableIds: _2,
1612
+ transaction: _3,
1613
+ ...stats
1614
+ } = resultStore.getListenerStats();
1615
+ return stats;
1616
+ };
1617
+ const queries = {
1618
+ setQueryDefinition,
1619
+ delQueryDefinition,
1620
+ getStore,
1621
+ getQueryIds,
1622
+ forEachQuery,
1623
+ hasQuery,
1624
+ getTableId,
1625
+ getResultTable,
1626
+ getResultRowIds,
1627
+ getResultRow,
1628
+ getResultCellIds,
1629
+ getResultCell,
1630
+ hasResultTable,
1631
+ hasResultRow,
1632
+ hasResultCell,
1633
+ forEachResultTable,
1634
+ forEachResultRow,
1635
+ forEachResultCell,
1636
+ addResultTableListener,
1637
+ addResultRowIdsListener,
1638
+ addResultRowListener,
1639
+ addResultCellIdsListener,
1640
+ addResultCellListener,
1641
+ delListener,
1642
+ destroy,
1643
+ getListenerStats,
1644
+ };
1645
+ return objFreeze(queries);
1646
+ });
1647
+
1014
1648
  const createRelationships = getCreateFunction((store) => {
1015
1649
  const remoteTableIds = mapNew();
1016
1650
  const remoteRowIdListeners = mapNew();
@@ -1024,7 +1658,8 @@ const createRelationships = getCreateFunction((store) => {
1024
1658
  getLocalTableId,
1025
1659
  getRelationship,
1026
1660
  ,
1027
- setDefinition,
1661
+ ,
1662
+ setDefinitionAndListen,
1028
1663
  delDefinition,
1029
1664
  destroy,
1030
1665
  ] = getDefinableFunctions(
@@ -1071,7 +1706,7 @@ const createRelationships = getCreateFunction((store) => {
1071
1706
  getRemoteRowId2,
1072
1707
  ) => {
1073
1708
  mapSet(remoteTableIds, relationshipId, remoteTableId);
1074
- setDefinition(
1709
+ setDefinitionAndListen(
1075
1710
  relationshipId,
1076
1711
  localTableId,
1077
1712
  (change, changedRemoteRowIds) => {
@@ -1197,6 +1832,14 @@ const createRelationships = getCreateFunction((store) => {
1197
1832
  return objFreeze(relationships);
1198
1833
  });
1199
1834
 
1835
+ const pairNew = (value) => [value, value];
1836
+ const pairCollSize2 = (pair, func = collSize2) => func(pair[0]) + func(pair[1]);
1837
+ const pairCollIsEmpty = (pair) => pairCollSize2(pair) == 0;
1838
+ const pairNewMap = () => [mapNew(), mapNew()];
1839
+ const pair2CollSize2 = (pair2, func = collSize2) =>
1840
+ pairCollSize2(pair2[0], func) + pairCollSize2(pair2[1], func);
1841
+ const pair2NewMap = () => [pairNewMap(), pairNewMap()];
1842
+
1200
1843
  const transformMap = (map, toBeLikeObject, setId, delId = mapSet) => {
1201
1844
  const idsToDelete = arrayFilter(
1202
1845
  mapKeys(map),
@@ -1208,12 +1851,6 @@ const transformMap = (map, toBeLikeObject, setId, delId = mapSet) => {
1208
1851
  arrayForEach(idsToDelete, (id) => delId(map, id));
1209
1852
  return map;
1210
1853
  };
1211
- const getCellType = (cell) => {
1212
- const type = getTypeOf(cell);
1213
- return isTypeStringOrBoolean(type) || (type == NUMBER && isFiniteNumber(cell))
1214
- ? type
1215
- : void 0;
1216
- };
1217
1854
  const validate = (obj, validateChild, onInvalidObj) => {
1218
1855
  if (isUndefined(obj) || !isObject(obj) || objIsEmpty(obj) || objFrozen(obj)) {
1219
1856
  onInvalidObj?.();
@@ -1226,8 +1863,8 @@ const validate = (obj, validateChild, onInvalidObj) => {
1226
1863
  });
1227
1864
  return !objIsEmpty(obj);
1228
1865
  };
1229
- const idsChanged = (ids, id, added) =>
1230
- mapSet(ids, id, mapGet(ids, id) == -added ? void 0 : added);
1866
+ const idsChanged = (changedIds, id, added) =>
1867
+ mapSet(changedIds, id, mapGet(changedIds, id) == -added ? void 0 : added);
1231
1868
  const createStore = () => {
1232
1869
  let hasSchema;
1233
1870
  let cellsTouched;
@@ -1241,17 +1878,22 @@ const createStore = () => {
1241
1878
  const schemaMap = mapNew();
1242
1879
  const schemaRowCache = mapNew();
1243
1880
  const tablesMap = mapNew();
1244
- const tablesListeners = mapNewPair(setNew);
1245
- const tableIdsListeners = mapNewPair(setNew);
1246
- const tableListeners = mapNewPair();
1247
- const rowIdsListeners = mapNewPair();
1248
- const rowListeners = mapNewPair();
1249
- const cellIdsListeners = mapNewPair();
1250
- const cellListeners = mapNewPair();
1251
- const invalidCellListeners = mapNewPair();
1252
- const finishTransactionListeners = mapNewPair(setNew);
1253
- const [addListener, callListeners, delListenerImpl, callListenerImpl] =
1254
- getListenerFunctions(() => store);
1881
+ const tablesListeners = pairNewMap();
1882
+ const tableIdsListeners = pair2NewMap();
1883
+ const tableListeners = pairNewMap();
1884
+ const rowIdsListeners = pair2NewMap();
1885
+ const rowListeners = pairNewMap();
1886
+ const cellIdsListeners = pair2NewMap();
1887
+ const cellListeners = pairNewMap();
1888
+ const invalidCellListeners = pairNewMap();
1889
+ const finishTransactionListeners = pairNewMap();
1890
+ const [
1891
+ addListener,
1892
+ callListeners,
1893
+ delListenerImpl,
1894
+ hasListeners,
1895
+ callListenerImpl,
1896
+ ] = getListenerFunctions(() => store);
1255
1897
  const validateSchema = (schema) =>
1256
1898
  validate(schema, (tableSchema) =>
1257
1899
  validate(tableSchema, (cellSchema) => {
@@ -1402,7 +2044,7 @@ const createStore = () => {
1402
2044
  ),
1403
2045
  );
1404
2046
  const getNewRowId = (tableMap) => {
1405
- const rowId = '' + nextRowId++;
2047
+ const rowId = EMPTY_STRING + nextRowId++;
1406
2048
  if (!collHas(tableMap, rowId)) {
1407
2049
  return rowId;
1408
2050
  }
@@ -1433,12 +2075,49 @@ const createStore = () => {
1433
2075
  }
1434
2076
  };
1435
2077
  const tableIdsChanged = (tableId, added) =>
1436
- idsChanged(changedTableIds, tableId, added);
2078
+ idsChanged(
2079
+ collIsEmpty(changedTableIds)
2080
+ ? mapSet(
2081
+ changedTableIds,
2082
+ null,
2083
+ hasListeners(tableIdsListeners[0][1]) ||
2084
+ hasListeners(tableIdsListeners[1][1])
2085
+ ? getTableIds()
2086
+ : 0,
2087
+ )
2088
+ : changedTableIds,
2089
+ tableId,
2090
+ added,
2091
+ );
1437
2092
  const rowIdsChanged = (tableId, rowId, added) =>
1438
- idsChanged(mapEnsure(changedRowIds, tableId, mapNew), rowId, added);
2093
+ idsChanged(
2094
+ mapEnsure(changedRowIds, tableId, () =>
2095
+ mapNew([
2096
+ [
2097
+ null,
2098
+ hasListeners(rowIdsListeners[0][1], [tableId]) ||
2099
+ hasListeners(rowIdsListeners[1][1], [tableId])
2100
+ ? getRowIds(tableId)
2101
+ : 0,
2102
+ ],
2103
+ ]),
2104
+ ),
2105
+ rowId,
2106
+ added,
2107
+ );
1439
2108
  const cellIdsChanged = (tableId, rowId, cellId, added) =>
1440
2109
  idsChanged(
1441
- mapEnsure(mapEnsure(changedCellIds, tableId, mapNew), rowId, mapNew),
2110
+ mapEnsure(mapEnsure(changedCellIds, tableId, mapNew), rowId, () =>
2111
+ mapNew([
2112
+ [
2113
+ null,
2114
+ hasListeners(cellIdsListeners[0][1], [tableId, rowId]) ||
2115
+ hasListeners(cellIdsListeners[1][1], [tableId, rowId])
2116
+ ? getCellIds(tableId, rowId)
2117
+ : 0,
2118
+ ],
2119
+ ]),
2120
+ ),
1442
2121
  cellId,
1443
2122
  added,
1444
2123
  );
@@ -1463,7 +2142,7 @@ const createStore = () => {
1463
2142
  ifNotUndefined(
1464
2143
  mapGet(mapGet(mapGet(changedCells, tableId), rowId), cellId),
1465
2144
  ([oldCell, newCell]) => [true, oldCell, newCell],
1466
- () => [false, ...arrayPair(getCell(tableId, rowId, cellId))],
2145
+ () => [false, ...pairNew(getCell(tableId, rowId, cellId))],
1467
2146
  );
1468
2147
  const callInvalidCellListeners = (mutator) =>
1469
2148
  !collIsEmpty(invalidCells) && !collIsEmpty(invalidCellListeners[mutator])
@@ -1481,17 +2160,29 @@ const createStore = () => {
1481
2160
  ),
1482
2161
  )
1483
2162
  : 0;
2163
+ const callIdsListenersIfChanged = (listeners, changedIds, getIds, ids) => {
2164
+ if (collSize(changedIds) > 1) {
2165
+ callListeners(listeners[0], ids);
2166
+ callListeners(listeners[1], ids);
2167
+ } else if (
2168
+ !collIsEmpty(changedIds) &&
2169
+ mapGet(changedIds, null) != 0 &&
2170
+ !arrayIsEqual(mapGet(changedIds, null), getIds(...(ids ?? [])))
2171
+ ) {
2172
+ callListeners(listeners[1], ids);
2173
+ }
2174
+ };
1484
2175
  const callListenersForChanges = (mutator) => {
1485
2176
  const emptyIdListeners =
1486
- collIsEmpty(cellIdsListeners[mutator]) &&
1487
- collIsEmpty(rowIdsListeners[mutator]) &&
1488
- collIsEmpty(tableIdsListeners[mutator]);
2177
+ pairCollIsEmpty(cellIdsListeners[mutator]) &&
2178
+ pairCollIsEmpty(rowIdsListeners[mutator]) &&
2179
+ pairCollIsEmpty(tableIdsListeners[mutator]);
1489
2180
  const emptyOtherListeners =
1490
2181
  collIsEmpty(cellListeners[mutator]) &&
1491
2182
  collIsEmpty(rowListeners[mutator]) &&
1492
2183
  collIsEmpty(tableListeners[mutator]) &&
1493
2184
  collIsEmpty(tablesListeners[mutator]);
1494
- if (!(emptyIdListeners && emptyOtherListeners)) {
2185
+ if (!emptyIdListeners || !emptyOtherListeners) {
1495
2186
  const changes = mutator
1496
2187
  ? [
1497
2188
  mapClone(changedTableIds),
@@ -1502,20 +2193,28 @@ const createStore = () => {
1502
2193
  : [changedTableIds, changedRowIds, changedCellIds, changedCells];
1503
2194
  if (!emptyIdListeners) {
1504
2195
  collForEach(changes[2], (rowCellIds, tableId) =>
1505
- collForEach(rowCellIds, (changedIds, rowId) => {
1506
- if (!collIsEmpty(changedIds)) {
1507
- callListeners(cellIdsListeners[mutator], [tableId, rowId]);
1508
- }
1509
- }),
2196
+ collForEach(rowCellIds, (changedIds, rowId) =>
2197
+ callIdsListenersIfChanged(
2198
+ cellIdsListeners[mutator],
2199
+ changedIds,
2200
+ getCellIds,
2201
+ [tableId, rowId],
2202
+ ),
2203
+ ),
2204
+ );
2205
+ collForEach(changes[1], (changedIds, tableId) =>
2206
+ callIdsListenersIfChanged(
2207
+ rowIdsListeners[mutator],
2208
+ changedIds,
2209
+ getRowIds,
2210
+ [tableId],
2211
+ ),
2212
+ );
2213
+ callIdsListenersIfChanged(
2214
+ tableIdsListeners[mutator],
2215
+ changes[0],
2216
+ getTableIds,
1510
2217
  );
1511
- collForEach(changes[1], (changedIds, tableId) => {
1512
- if (!collIsEmpty(changedIds)) {
1513
- callListeners(rowIdsListeners[mutator], [tableId]);
1514
- }
1515
- });
1516
- if (!collIsEmpty(changes[0])) {
1517
- callListeners(tableIdsListeners[mutator]);
1518
- }
1519
2218
  }
1520
2219
  if (!emptyOtherListeners) {
1521
2220
  let tablesChanged;
@@ -1548,7 +2247,7 @@ const createStore = () => {
1548
2247
  }
1549
2248
  });
1550
2249
  if (tablesChanged) {
1551
- callListeners(tablesListeners[mutator], [], getCellChange);
2250
+ callListeners(tablesListeners[mutator], void 0, getCellChange);
1552
2251
  }
1553
2252
  }
1554
2253
  }
@@ -1589,16 +2288,15 @@ const createStore = () => {
1589
2288
  ? setValidRow(tableId, getOrCreateTable(tableId), rowId, row)
1590
2289
  : 0,
1591
2290
  );
1592
- const addRow = (tableId, row) =>
2291
+ const addRow = (tableId, row, forceId) =>
1593
2292
  transaction(() => {
1594
- let rowId = void 0;
1595
- if (validateRow(tableId, rowId, row)) {
1596
- setValidRow(
1597
- tableId,
1598
- getOrCreateTable(tableId),
1599
- (rowId = getNewRowId(mapGet(tablesMap, tableId))),
1600
- row,
1601
- );
2293
+ const isValidRow = validateRow(tableId, void 0, row);
2294
+ const rowId =
2295
+ isValidRow || forceId
2296
+ ? getNewRowId(mapGet(tablesMap, tableId))
2297
+ : void 0;
2298
+ if (isValidRow) {
2299
+ setValidRow(tableId, getOrCreateTable(tableId), rowId, row);
1602
2300
  }
1603
2301
  return rowId;
1604
2302
  });
@@ -1730,12 +2428,12 @@ const createStore = () => {
1730
2428
  transactions = -1;
1731
2429
  cellsTouched = false;
1732
2430
  }
1733
- callListeners(finishTransactionListeners[0], [], cellsTouched);
2431
+ callListeners(finishTransactionListeners[0], void 0, cellsTouched);
1734
2432
  callInvalidCellListeners(0);
1735
2433
  if (cellsTouched) {
1736
2434
  callListenersForChanges(0);
1737
2435
  }
1738
- callListeners(finishTransactionListeners[1], [], cellsTouched);
2436
+ callListeners(finishTransactionListeners[1], void 0, cellsTouched);
1739
2437
  transactions = 0;
1740
2438
  arrayForEach(
1741
2439
  [
@@ -1769,16 +2467,33 @@ const createStore = () => {
1769
2467
  mapForEach(mapGet(mapGet(tablesMap, tableId), rowId), cellCallback);
1770
2468
  const addTablesListener = (listener, mutator) =>
1771
2469
  addListener(listener, tablesListeners[mutator ? 1 : 0]);
1772
- const addTableIdsListener = (listener, mutator) =>
1773
- addListener(listener, tableIdsListeners[mutator ? 1 : 0]);
2470
+ const addTableIdsListener = (listener, trackReorder, mutator) =>
2471
+ addListener(
2472
+ listener,
2473
+ tableIdsListeners[mutator ? 1 : 0][trackReorder ? 1 : 0],
2474
+ );
1774
2475
  const addTableListener = (tableId, listener, mutator) =>
1775
2476
  addListener(listener, tableListeners[mutator ? 1 : 0], [tableId]);
1776
- const addRowIdsListener = (tableId, listener, mutator) =>
1777
- addListener(listener, rowIdsListeners[mutator ? 1 : 0], [tableId]);
2477
+ const addRowIdsListener = (tableId, listener, trackReorder, mutator) =>
2478
+ addListener(
2479
+ listener,
2480
+ rowIdsListeners[mutator ? 1 : 0][trackReorder ? 1 : 0],
2481
+ [tableId],
2482
+ );
1778
2483
  const addRowListener = (tableId, rowId, listener, mutator) =>
1779
2484
  addListener(listener, rowListeners[mutator ? 1 : 0], [tableId, rowId]);
1780
- const addCellIdsListener = (tableId, rowId, listener, mutator) =>
1781
- addListener(listener, cellIdsListeners[mutator ? 1 : 0], [tableId, rowId]);
2485
+ const addCellIdsListener = (
2486
+ tableId,
2487
+ rowId,
2488
+ listener,
2489
+ trackReorder,
2490
+ mutator,
2491
+ ) =>
2492
+ addListener(
2493
+ listener,
2494
+ cellIdsListeners[mutator ? 1 : 0][trackReorder ? 1 : 0],
2495
+ [tableId, rowId],
2496
+ );
1782
2497
  const addCellListener = (tableId, rowId, cellId, listener, mutator) =>
1783
2498
  addListener(listener, cellListeners[mutator ? 1 : 0], [
1784
2499
  tableId,
@@ -1797,7 +2512,7 @@ const createStore = () => {
1797
2512
  addListener(listener, finishTransactionListeners[1]);
1798
2513
  const callListener = (listenerId) => {
1799
2514
  callListenerImpl(listenerId, [getTableIds, getRowIds, getCellIds], (ids) =>
1800
- isUndefined(ids[2]) ? [] : arrayPair(getCell(...ids)),
2515
+ isUndefined(ids[2]) ? [] : pairNew(getCell(...ids)),
1801
2516
  );
1802
2517
  return store;
1803
2518
  };
@@ -1806,15 +2521,15 @@ const createStore = () => {
1806
2521
  return store;
1807
2522
  };
1808
2523
  const getListenerStats = () => ({
1809
- tables: collPairSize(tablesListeners),
1810
- tableIds: collPairSize(tableIdsListeners),
1811
- table: collPairSize(tableListeners, collSize2),
1812
- rowIds: collPairSize(rowIdsListeners, collSize2),
1813
- row: collPairSize(rowListeners, collSize3),
1814
- cellIds: collPairSize(cellIdsListeners, collSize3),
1815
- cell: collPairSize(cellListeners, collSize4),
1816
- invalidCell: collPairSize(invalidCellListeners, collSize4),
1817
- transaction: collPairSize(finishTransactionListeners),
2524
+ tables: pairCollSize2(tablesListeners),
2525
+ tableIds: pair2CollSize2(tableIdsListeners),
2526
+ table: pairCollSize2(tableListeners),
2527
+ rowIds: pair2CollSize2(rowIdsListeners),
2528
+ row: pairCollSize2(rowListeners, collSize3),
2529
+ cellIds: pair2CollSize2(cellIdsListeners, collSize3),
2530
+ cell: pairCollSize2(cellListeners, collSize4),
2531
+ invalidCell: pairCollSize2(invalidCellListeners, collSize4),
2532
+ transaction: pairCollSize2(finishTransactionListeners),
1818
2533
  });
1819
2534
  const store = {
1820
2535
  getTables,
@@ -1862,6 +2577,7 @@ const createStore = () => {
1862
2577
  callListener,
1863
2578
  delListener,
1864
2579
  getListenerStats,
2580
+ createStore,
1865
2581
  };
1866
2582
  return objFreeze(store);
1867
2583
  };
@@ -1873,6 +2589,7 @@ export {
1873
2589
  createIndexes,
1874
2590
  createLocalPersister,
1875
2591
  createMetrics,
2592
+ createQueries,
1876
2593
  createRelationships,
1877
2594
  createRemotePersister,
1878
2595
  createSessionPersister,