tinybase 1.3.3 → 2.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 +3066 -0
  8. package/lib/debug/queries.js +883 -0
  9. package/lib/debug/relationships.d.ts +6 -5
  10. package/lib/debug/relationships.js +103 -67
  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 +896 -176
  15. package/lib/debug/ui-react.d.ts +49 -2
  16. package/lib/debug/ui-react.js +85 -74
  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 +3066 -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 +49 -2
  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 +24 -24
  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,574 @@ 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, trackReorder) =>
1597
+ resultStore.addCellIdsListener(
1598
+ queryId,
1599
+ rowId,
1600
+ (_store, ...args) => listener(queries, ...args),
1601
+ trackReorder,
1602
+ );
1603
+ const addResultCellListener = (queryId, rowId, cellId, listener) =>
1604
+ resultStore.addCellListener(queryId, rowId, cellId, (_store, ...args) =>
1605
+ listener(queries, ...args),
1606
+ );
1607
+ const delListener = (listenerId) => {
1608
+ resultStore.delListener(listenerId);
1609
+ return queries;
1610
+ };
1611
+ const getListenerStats = () => {
1612
+ const {
1613
+ tables: _1,
1614
+ tableIds: _2,
1615
+ transaction: _3,
1616
+ ...stats
1617
+ } = resultStore.getListenerStats();
1618
+ return stats;
1619
+ };
1620
+ const queries = {
1621
+ setQueryDefinition,
1622
+ delQueryDefinition,
1623
+ getStore,
1624
+ getQueryIds,
1625
+ forEachQuery,
1626
+ hasQuery,
1627
+ getTableId,
1628
+ getResultTable,
1629
+ getResultRowIds,
1630
+ getResultRow,
1631
+ getResultCellIds,
1632
+ getResultCell,
1633
+ hasResultTable,
1634
+ hasResultRow,
1635
+ hasResultCell,
1636
+ forEachResultTable,
1637
+ forEachResultRow,
1638
+ forEachResultCell,
1639
+ addResultTableListener,
1640
+ addResultRowIdsListener,
1641
+ addResultRowListener,
1642
+ addResultCellIdsListener,
1643
+ addResultCellListener,
1644
+ delListener,
1645
+ destroy,
1646
+ getListenerStats,
1647
+ };
1648
+ return objFreeze(queries);
1649
+ });
1650
+
1014
1651
  const createRelationships = getCreateFunction((store) => {
1015
1652
  const remoteTableIds = mapNew();
1016
1653
  const remoteRowIdListeners = mapNew();
@@ -1023,8 +1660,9 @@ const createRelationships = getCreateFunction((store) => {
1023
1660
  hasRelationship,
1024
1661
  getLocalTableId,
1025
1662
  getRelationship,
1026
- _setRelationship,
1027
- setDefinition,
1663
+ ,
1664
+ ,
1665
+ setDefinitionAndListen,
1028
1666
  delDefinition,
1029
1667
  destroy,
1030
1668
  ] = getDefinableFunctions(
@@ -1071,7 +1709,7 @@ const createRelationships = getCreateFunction((store) => {
1071
1709
  getRemoteRowId2,
1072
1710
  ) => {
1073
1711
  mapSet(remoteTableIds, relationshipId, remoteTableId);
1074
- setDefinition(
1712
+ setDefinitionAndListen(
1075
1713
  relationshipId,
1076
1714
  localTableId,
1077
1715
  (change, changedRemoteRowIds) => {
@@ -1197,6 +1835,14 @@ const createRelationships = getCreateFunction((store) => {
1197
1835
  return objFreeze(relationships);
1198
1836
  });
1199
1837
 
1838
+ const pairNew = (value) => [value, value];
1839
+ const pairCollSize2 = (pair, func = collSize2) => func(pair[0]) + func(pair[1]);
1840
+ const pairCollIsEmpty = (pair) => pairCollSize2(pair) == 0;
1841
+ const pairNewMap = () => [mapNew(), mapNew()];
1842
+ const pair2CollSize2 = (pair2, func = collSize2) =>
1843
+ pairCollSize2(pair2[0], func) + pairCollSize2(pair2[1], func);
1844
+ const pair2NewMap = () => [pairNewMap(), pairNewMap()];
1845
+
1200
1846
  const transformMap = (map, toBeLikeObject, setId, delId = mapSet) => {
1201
1847
  const idsToDelete = arrayFilter(
1202
1848
  mapKeys(map),
@@ -1208,12 +1854,6 @@ const transformMap = (map, toBeLikeObject, setId, delId = mapSet) => {
1208
1854
  arrayForEach(idsToDelete, (id) => delId(map, id));
1209
1855
  return map;
1210
1856
  };
1211
- const getCellType = (cell) => {
1212
- const type = getTypeOf(cell);
1213
- return isTypeStringOrBoolean(type) || (type == NUMBER && isFiniteNumber(cell))
1214
- ? type
1215
- : void 0;
1216
- };
1217
1857
  const validate = (obj, validateChild, onInvalidObj) => {
1218
1858
  if (isUndefined(obj) || !isObject(obj) || objIsEmpty(obj) || objFrozen(obj)) {
1219
1859
  onInvalidObj?.();
@@ -1226,8 +1866,8 @@ const validate = (obj, validateChild, onInvalidObj) => {
1226
1866
  });
1227
1867
  return !objIsEmpty(obj);
1228
1868
  };
1229
- const idsChanged = (ids, id, added) =>
1230
- mapSet(ids, id, mapGet(ids, id) == -added ? void 0 : added);
1869
+ const idsChanged = (changedIds, id, added) =>
1870
+ mapSet(changedIds, id, mapGet(changedIds, id) == -added ? void 0 : added);
1231
1871
  const createStore = () => {
1232
1872
  let hasSchema;
1233
1873
  let cellsTouched;
@@ -1241,17 +1881,22 @@ const createStore = () => {
1241
1881
  const schemaMap = mapNew();
1242
1882
  const schemaRowCache = mapNew();
1243
1883
  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);
1884
+ const tablesListeners = pairNewMap();
1885
+ const tableIdsListeners = pair2NewMap();
1886
+ const tableListeners = pairNewMap();
1887
+ const rowIdsListeners = pair2NewMap();
1888
+ const rowListeners = pairNewMap();
1889
+ const cellIdsListeners = pair2NewMap();
1890
+ const cellListeners = pairNewMap();
1891
+ const invalidCellListeners = pairNewMap();
1892
+ const finishTransactionListeners = pairNewMap();
1893
+ const [
1894
+ addListener,
1895
+ callListeners,
1896
+ delListenerImpl,
1897
+ hasListeners,
1898
+ callListenerImpl,
1899
+ ] = getListenerFunctions(() => store);
1255
1900
  const validateSchema = (schema) =>
1256
1901
  validate(schema, (tableSchema) =>
1257
1902
  validate(tableSchema, (cellSchema) => {
@@ -1402,7 +2047,7 @@ const createStore = () => {
1402
2047
  ),
1403
2048
  );
1404
2049
  const getNewRowId = (tableMap) => {
1405
- const rowId = '' + nextRowId++;
2050
+ const rowId = EMPTY_STRING + nextRowId++;
1406
2051
  if (!collHas(tableMap, rowId)) {
1407
2052
  return rowId;
1408
2053
  }
@@ -1433,12 +2078,49 @@ const createStore = () => {
1433
2078
  }
1434
2079
  };
1435
2080
  const tableIdsChanged = (tableId, added) =>
1436
- idsChanged(changedTableIds, tableId, added);
2081
+ idsChanged(
2082
+ collIsEmpty(changedTableIds)
2083
+ ? mapSet(
2084
+ changedTableIds,
2085
+ null,
2086
+ hasListeners(tableIdsListeners[0][1]) ||
2087
+ hasListeners(tableIdsListeners[1][1])
2088
+ ? getTableIds()
2089
+ : 0,
2090
+ )
2091
+ : changedTableIds,
2092
+ tableId,
2093
+ added,
2094
+ );
1437
2095
  const rowIdsChanged = (tableId, rowId, added) =>
1438
- idsChanged(mapEnsure(changedRowIds, tableId, mapNew), rowId, added);
2096
+ idsChanged(
2097
+ mapEnsure(changedRowIds, tableId, () =>
2098
+ mapNew([
2099
+ [
2100
+ null,
2101
+ hasListeners(rowIdsListeners[0][1], [tableId]) ||
2102
+ hasListeners(rowIdsListeners[1][1], [tableId])
2103
+ ? getRowIds(tableId)
2104
+ : 0,
2105
+ ],
2106
+ ]),
2107
+ ),
2108
+ rowId,
2109
+ added,
2110
+ );
1439
2111
  const cellIdsChanged = (tableId, rowId, cellId, added) =>
1440
2112
  idsChanged(
1441
- mapEnsure(mapEnsure(changedCellIds, tableId, mapNew), rowId, mapNew),
2113
+ mapEnsure(mapEnsure(changedCellIds, tableId, mapNew), rowId, () =>
2114
+ mapNew([
2115
+ [
2116
+ null,
2117
+ hasListeners(cellIdsListeners[0][1], [tableId, rowId]) ||
2118
+ hasListeners(cellIdsListeners[1][1], [tableId, rowId])
2119
+ ? getCellIds(tableId, rowId)
2120
+ : 0,
2121
+ ],
2122
+ ]),
2123
+ ),
1442
2124
  cellId,
1443
2125
  added,
1444
2126
  );
@@ -1463,7 +2145,7 @@ const createStore = () => {
1463
2145
  ifNotUndefined(
1464
2146
  mapGet(mapGet(mapGet(changedCells, tableId), rowId), cellId),
1465
2147
  ([oldCell, newCell]) => [true, oldCell, newCell],
1466
- () => [false, ...arrayPair(getCell(tableId, rowId, cellId))],
2148
+ () => [false, ...pairNew(getCell(tableId, rowId, cellId))],
1467
2149
  );
1468
2150
  const callInvalidCellListeners = (mutator) =>
1469
2151
  !collIsEmpty(invalidCells) && !collIsEmpty(invalidCellListeners[mutator])
@@ -1481,17 +2163,29 @@ const createStore = () => {
1481
2163
  ),
1482
2164
  )
1483
2165
  : 0;
2166
+ const callIdsListenersIfChanged = (listeners, changedIds, getIds, ids) => {
2167
+ if (collSize(changedIds) > 1) {
2168
+ callListeners(listeners[0], ids);
2169
+ callListeners(listeners[1], ids);
2170
+ } else if (
2171
+ !collIsEmpty(changedIds) &&
2172
+ mapGet(changedIds, null) != 0 &&
2173
+ !arrayIsEqual(mapGet(changedIds, null), getIds(...(ids ?? [])))
2174
+ ) {
2175
+ callListeners(listeners[1], ids);
2176
+ }
2177
+ };
1484
2178
  const callListenersForChanges = (mutator) => {
1485
2179
  const emptyIdListeners =
1486
- collIsEmpty(cellIdsListeners[mutator]) &&
1487
- collIsEmpty(rowIdsListeners[mutator]) &&
1488
- collIsEmpty(tableIdsListeners[mutator]);
2180
+ pairCollIsEmpty(cellIdsListeners[mutator]) &&
2181
+ pairCollIsEmpty(rowIdsListeners[mutator]) &&
2182
+ pairCollIsEmpty(tableIdsListeners[mutator]);
1489
2183
  const emptyOtherListeners =
1490
2184
  collIsEmpty(cellListeners[mutator]) &&
1491
2185
  collIsEmpty(rowListeners[mutator]) &&
1492
2186
  collIsEmpty(tableListeners[mutator]) &&
1493
2187
  collIsEmpty(tablesListeners[mutator]);
1494
- if (!(emptyIdListeners && emptyOtherListeners)) {
2188
+ if (!emptyIdListeners || !emptyOtherListeners) {
1495
2189
  const changes = mutator
1496
2190
  ? [
1497
2191
  mapClone(changedTableIds),
@@ -1502,20 +2196,28 @@ const createStore = () => {
1502
2196
  : [changedTableIds, changedRowIds, changedCellIds, changedCells];
1503
2197
  if (!emptyIdListeners) {
1504
2198
  collForEach(changes[2], (rowCellIds, tableId) =>
1505
- collForEach(rowCellIds, (changedIds, rowId) => {
1506
- if (!collIsEmpty(changedIds)) {
1507
- callListeners(cellIdsListeners[mutator], [tableId, rowId]);
1508
- }
1509
- }),
2199
+ collForEach(rowCellIds, (changedIds, rowId) =>
2200
+ callIdsListenersIfChanged(
2201
+ cellIdsListeners[mutator],
2202
+ changedIds,
2203
+ getCellIds,
2204
+ [tableId, rowId],
2205
+ ),
2206
+ ),
2207
+ );
2208
+ collForEach(changes[1], (changedIds, tableId) =>
2209
+ callIdsListenersIfChanged(
2210
+ rowIdsListeners[mutator],
2211
+ changedIds,
2212
+ getRowIds,
2213
+ [tableId],
2214
+ ),
2215
+ );
2216
+ callIdsListenersIfChanged(
2217
+ tableIdsListeners[mutator],
2218
+ changes[0],
2219
+ getTableIds,
1510
2220
  );
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
2221
  }
1520
2222
  if (!emptyOtherListeners) {
1521
2223
  let tablesChanged;
@@ -1548,7 +2250,7 @@ const createStore = () => {
1548
2250
  }
1549
2251
  });
1550
2252
  if (tablesChanged) {
1551
- callListeners(tablesListeners[mutator], [], getCellChange);
2253
+ callListeners(tablesListeners[mutator], void 0, getCellChange);
1552
2254
  }
1553
2255
  }
1554
2256
  }
@@ -1589,16 +2291,15 @@ const createStore = () => {
1589
2291
  ? setValidRow(tableId, getOrCreateTable(tableId), rowId, row)
1590
2292
  : 0,
1591
2293
  );
1592
- const addRow = (tableId, row) =>
2294
+ const addRow = (tableId, row, forceId) =>
1593
2295
  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
- );
2296
+ const isValidRow = validateRow(tableId, void 0, row);
2297
+ const rowId =
2298
+ isValidRow || forceId
2299
+ ? getNewRowId(mapGet(tablesMap, tableId))
2300
+ : void 0;
2301
+ if (isValidRow) {
2302
+ setValidRow(tableId, getOrCreateTable(tableId), rowId, row);
1602
2303
  }
1603
2304
  return rowId;
1604
2305
  });
@@ -1730,12 +2431,12 @@ const createStore = () => {
1730
2431
  transactions = -1;
1731
2432
  cellsTouched = false;
1732
2433
  }
1733
- callListeners(finishTransactionListeners[0], [], cellsTouched);
2434
+ callListeners(finishTransactionListeners[0], void 0, cellsTouched);
1734
2435
  callInvalidCellListeners(0);
1735
2436
  if (cellsTouched) {
1736
2437
  callListenersForChanges(0);
1737
2438
  }
1738
- callListeners(finishTransactionListeners[1], [], cellsTouched);
2439
+ callListeners(finishTransactionListeners[1], void 0, cellsTouched);
1739
2440
  transactions = 0;
1740
2441
  arrayForEach(
1741
2442
  [
@@ -1769,16 +2470,33 @@ const createStore = () => {
1769
2470
  mapForEach(mapGet(mapGet(tablesMap, tableId), rowId), cellCallback);
1770
2471
  const addTablesListener = (listener, mutator) =>
1771
2472
  addListener(listener, tablesListeners[mutator ? 1 : 0]);
1772
- const addTableIdsListener = (listener, mutator) =>
1773
- addListener(listener, tableIdsListeners[mutator ? 1 : 0]);
2473
+ const addTableIdsListener = (listener, trackReorder, mutator) =>
2474
+ addListener(
2475
+ listener,
2476
+ tableIdsListeners[mutator ? 1 : 0][trackReorder ? 1 : 0],
2477
+ );
1774
2478
  const addTableListener = (tableId, listener, mutator) =>
1775
2479
  addListener(listener, tableListeners[mutator ? 1 : 0], [tableId]);
1776
- const addRowIdsListener = (tableId, listener, mutator) =>
1777
- addListener(listener, rowIdsListeners[mutator ? 1 : 0], [tableId]);
2480
+ const addRowIdsListener = (tableId, listener, trackReorder, mutator) =>
2481
+ addListener(
2482
+ listener,
2483
+ rowIdsListeners[mutator ? 1 : 0][trackReorder ? 1 : 0],
2484
+ [tableId],
2485
+ );
1778
2486
  const addRowListener = (tableId, rowId, listener, mutator) =>
1779
2487
  addListener(listener, rowListeners[mutator ? 1 : 0], [tableId, rowId]);
1780
- const addCellIdsListener = (tableId, rowId, listener, mutator) =>
1781
- addListener(listener, cellIdsListeners[mutator ? 1 : 0], [tableId, rowId]);
2488
+ const addCellIdsListener = (
2489
+ tableId,
2490
+ rowId,
2491
+ listener,
2492
+ trackReorder,
2493
+ mutator,
2494
+ ) =>
2495
+ addListener(
2496
+ listener,
2497
+ cellIdsListeners[mutator ? 1 : 0][trackReorder ? 1 : 0],
2498
+ [tableId, rowId],
2499
+ );
1782
2500
  const addCellListener = (tableId, rowId, cellId, listener, mutator) =>
1783
2501
  addListener(listener, cellListeners[mutator ? 1 : 0], [
1784
2502
  tableId,
@@ -1797,7 +2515,7 @@ const createStore = () => {
1797
2515
  addListener(listener, finishTransactionListeners[1]);
1798
2516
  const callListener = (listenerId) => {
1799
2517
  callListenerImpl(listenerId, [getTableIds, getRowIds, getCellIds], (ids) =>
1800
- isUndefined(ids[2]) ? [] : arrayPair(getCell(...ids)),
2518
+ isUndefined(ids[2]) ? [] : pairNew(getCell(...ids)),
1801
2519
  );
1802
2520
  return store;
1803
2521
  };
@@ -1806,15 +2524,15 @@ const createStore = () => {
1806
2524
  return store;
1807
2525
  };
1808
2526
  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),
2527
+ tables: pairCollSize2(tablesListeners),
2528
+ tableIds: pair2CollSize2(tableIdsListeners),
2529
+ table: pairCollSize2(tableListeners),
2530
+ rowIds: pair2CollSize2(rowIdsListeners),
2531
+ row: pairCollSize2(rowListeners, collSize3),
2532
+ cellIds: pair2CollSize2(cellIdsListeners, collSize3),
2533
+ cell: pairCollSize2(cellListeners, collSize4),
2534
+ invalidCell: pairCollSize2(invalidCellListeners, collSize4),
2535
+ transaction: pairCollSize2(finishTransactionListeners),
1818
2536
  });
1819
2537
  const store = {
1820
2538
  getTables,
@@ -1862,6 +2580,7 @@ const createStore = () => {
1862
2580
  callListener,
1863
2581
  delListener,
1864
2582
  getListenerStats,
2583
+ createStore,
1865
2584
  };
1866
2585
  return objFreeze(store);
1867
2586
  };
@@ -1873,6 +2592,7 @@ export {
1873
2592
  createIndexes,
1874
2593
  createLocalPersister,
1875
2594
  createMetrics,
2595
+ createQueries,
1876
2596
  createRelationships,
1877
2597
  createRemotePersister,
1878
2598
  createSessionPersister,