tinybase 2.0.0-beta.1 → 2.0.0-beta.2

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 (63) hide show
  1. package/lib/checkpoints.d.ts +4 -3
  2. package/lib/checkpoints.js +1 -1
  3. package/lib/checkpoints.js.gz +0 -0
  4. package/lib/common.js +1 -1
  5. package/lib/common.js.gz +0 -0
  6. package/lib/debug/checkpoints.d.ts +4 -3
  7. package/lib/debug/checkpoints.js +12 -12
  8. package/lib/debug/common.js +4 -1
  9. package/lib/debug/indexes.d.ts +4 -2
  10. package/lib/debug/indexes.js +12 -12
  11. package/lib/debug/metrics.d.ts +1 -1
  12. package/lib/debug/metrics.js +12 -12
  13. package/lib/debug/persisters.d.ts +6 -0
  14. package/lib/debug/queries.d.ts +206 -9
  15. package/lib/debug/queries.js +29 -9
  16. package/lib/debug/relationships.d.ts +6 -5
  17. package/lib/debug/relationships.js +12 -12
  18. package/lib/debug/store.d.ts +287 -9
  19. package/lib/debug/store.js +210 -89
  20. package/lib/debug/tinybase.js +231 -97
  21. package/lib/debug/ui-react.d.ts +767 -5
  22. package/lib/debug/ui-react.js +158 -26
  23. package/lib/indexes.d.ts +4 -2
  24. package/lib/indexes.js +1 -1
  25. package/lib/indexes.js.gz +0 -0
  26. package/lib/metrics.d.ts +1 -1
  27. package/lib/metrics.js +1 -1
  28. package/lib/metrics.js.gz +0 -0
  29. package/lib/persisters.d.ts +6 -0
  30. package/lib/queries.d.ts +206 -9
  31. package/lib/queries.js +1 -1
  32. package/lib/queries.js.gz +0 -0
  33. package/lib/relationships.d.ts +6 -5
  34. package/lib/relationships.js +1 -1
  35. package/lib/relationships.js.gz +0 -0
  36. package/lib/store.d.ts +287 -9
  37. package/lib/store.js +1 -1
  38. package/lib/store.js.gz +0 -0
  39. package/lib/tinybase.js +1 -1
  40. package/lib/tinybase.js.gz +0 -0
  41. package/lib/ui-react.d.ts +767 -5
  42. package/lib/ui-react.js +1 -1
  43. package/lib/ui-react.js.gz +0 -0
  44. package/lib/umd/checkpoints.js +1 -1
  45. package/lib/umd/checkpoints.js.gz +0 -0
  46. package/lib/umd/common.js +1 -1
  47. package/lib/umd/common.js.gz +0 -0
  48. package/lib/umd/indexes.js +1 -1
  49. package/lib/umd/indexes.js.gz +0 -0
  50. package/lib/umd/metrics.js +1 -1
  51. package/lib/umd/metrics.js.gz +0 -0
  52. package/lib/umd/queries.js +1 -1
  53. package/lib/umd/queries.js.gz +0 -0
  54. package/lib/umd/relationships.js +1 -1
  55. package/lib/umd/relationships.js.gz +0 -0
  56. package/lib/umd/store.js +1 -1
  57. package/lib/umd/store.js.gz +0 -0
  58. package/lib/umd/tinybase.js +1 -1
  59. package/lib/umd/tinybase.js.gz +0 -0
  60. package/lib/umd/ui-react.js +1 -1
  61. package/lib/umd/ui-react.js.gz +0 -0
  62. package/package.json +25 -25
  63. package/readme.md +2 -2
@@ -93,12 +93,13 @@ export type CheckpointListener = (
93
93
  */
94
94
  export type CheckpointsListenerStats = {
95
95
  /**
96
- * The number of CheckpointIdsListeners registered with the Checkpoints
97
- * object.
96
+ * The number of CheckpointIdsListener functions registered with the
97
+ * Checkpoints object.
98
98
  */
99
99
  checkpointIds?: number;
100
100
  /**
101
- * The number of CheckpointListeners registered with the Checkpoints object.
101
+ * The number of CheckpointListener functions registered with the Checkpoints
102
+ * object.
102
103
  */
103
104
  checkpoint?: number;
104
105
  };
@@ -1 +1 @@
1
- const e=(e,t)=>e.includes(t),t=(e,t)=>e.forEach(t),n=e=>e.length,r=e=>0==n(e),o=(e,...t)=>e.push(...t),s=e=>e.pop(),c=e=>null==e,l=(e,t,n)=>c(e)?n?.():t(e),i=(e,t)=>e?.has(t)??!1,a=e=>c(e)||0==(e=>e.size)(e),d=(e,t)=>e?.forEach(t),u=(e,t)=>e?.delete(t),h=e=>new Map(e),p=(e,t)=>e?.get(t),C=(e,t,n)=>c(n)?(u(e,t),e):e?.set(t,n),g=(e,t,n)=>(i(e,t)||e.set(t,n()),p(e,t)),k=(e,t,r,o,s=0)=>l((r?g:p)(e,t[s],s>n(t)-2?r:h),(c=>{if(s>n(t)-2)return o?.(c)&&C(e,t[s]),c;const l=k(c,t,r,o,s+1);return a(c)&&C(e,t[s]),l})),f=e=>new Set(e),v=(e,r=[""])=>{const s=[],c=(e,l)=>l==n(r)?o(s,e):t([r[l],null],(t=>c(p(e,t),l+1)));return c(e,0),s},L=Object.freeze,w=(e=>{const t=new WeakMap;return n=>(t.has(n)||t.set(n,e(n)),t.get(n))})((w=>{let S,z,E,y=100,I=h(),M=1;const b=h(),j=h(),[x,B,F]=(e=>{let r,i=0;const g=[],L=h();return[(t,n,o)=>{r??=e();const c=s(g)??""+i++;var l;return C(L,c,[t,n,o]),l=c,k(n,o??[""],f)?.add(l),c},(e,n,...o)=>t(v(e,n),(e=>d(e,(e=>l(p(L,e),(([e])=>e(r,...n??[],...o))))))),e=>l(p(L,e),(([,t,r])=>(k(t,r??[""],void 0,(t=>(u(t,e),a(t)?1:0))),C(L,e),n(g)<1e3&&o(g,e),r))),(e,t)=>{return n=v(e,t),r=c,!n.every(r);var n,r},(e,o,s)=>l(p(L,e),(([e,,l=[]])=>{const i=(...a)=>{const d=n(a);d==n(l)?e(r,...a,...s(a)):c(l[d])?t(o[d](...a),(e=>i(...a,e))):i(...a,l[d])};i()}))]})((()=>V)),O=h(),T=h(),W=[],m=[],q=(e,t)=>{M=0,w.transaction((()=>d(p(O,t),((t,n)=>d(t,((t,r)=>d(t,((t,o)=>((e,t,n,r,o)=>c(o)?e.delCell(t,n,r,!0):e.setCell(t,n,r,o))(w,n,r,o,t[e]))))))))),M=1},A=e=>{C(O,e),C(T,e),B(j,[e])},D=(e,r)=>t(((e,t)=>e.splice(0,t))(e,r??n(e)),A),G=()=>D(W,n(W)-y),H=w.addCellListener(null,null,null,((e,t,n,r,c,i)=>{if(M){l(S,(()=>{o(W,S),G(),D(m),S=void 0,E=1}));const e=g(I,t,h),d=g(e,n,h),u=g(d,r,(()=>[i,void 0]));u[1]=c,u[0]===c&&a(C(d,r))&&a(C(e,n))&&a(C(I,t))&&(S=s(W),E=1),P()}})),J=(e="")=>(c(S)&&(S=""+z++,C(O,S,I),R(S,e),I=h(),E=1),S),K=()=>{r(W)||(m.unshift(J()),q(0,S),S=s(W),E=1)},N=()=>{r(m)||(o(W,S),S=m.shift(),q(1,S),E=1)},P=()=>{E&&(B(b),E=0)},Q=e=>{const t=J(e);return P(),t},R=(e,t)=>(U(e)&&p(T,e)!==t&&(C(T,e,t),B(j,[e])),V),U=e=>i(O,e),V={setSize:e=>(y=e,G(),V),addCheckpoint:Q,setCheckpoint:R,getStore:()=>w,getCheckpointIds:()=>[[...W],S,[...m]],forEachCheckpoint:e=>{return t=e,d(T,((e,n)=>t(n,e)));var t},hasCheckpoint:U,getCheckpoint:e=>p(T,e),goBackward:()=>(K(),P(),V),goForward:()=>(N(),P(),V),goTo:t=>{const n=e(W,t)?K:e(m,t)?N:null;for(;!c(n)&&t!=S;)n();return P(),V},addCheckpointIdsListener:e=>x(e,b),addCheckpointListener:(e,t)=>x(t,j,[e]),delListener:e=>(F(e),V),clear:()=>(D(W),D(m),c(S)||A(S),S=void 0,z=0,Q(),V),destroy:()=>{w.delListener(H)},getListenerStats:()=>({})};return L(V.clear())}));export{w as createCheckpoints};
1
+ const e=(e,t)=>e.includes(t),t=(e,t)=>e.forEach(t),n=e=>e.length,r=e=>0==n(e),o=(e,...t)=>e.push(...t),s=e=>e.pop(),l=e=>null==e,c=(e,t,n)=>l(e)?n?.():t(e),i=(e,t)=>e?.has(t)??!1,a=e=>l(e)||0==(e=>e.size)(e),d=(e,t)=>e?.forEach(t),u=(e,t)=>e?.delete(t),h=e=>new Map(e),p=(e,t)=>e?.get(t),C=(e,t,n)=>l(n)?(u(e,t),e):e?.set(t,n),g=(e,t,n)=>(i(e,t)||C(e,t,n()),p(e,t)),k=(e,t,r,o,s=0)=>c((r?g:p)(e,t[s],s>n(t)-2?r:h),(l=>{if(s>n(t)-2)return o?.(l)&&C(e,t[s]),l;const c=k(l,t,r,o,s+1);return a(l)&&C(e,t[s]),c})),f=e=>new Set(e),v=(e,r=[""])=>{const s=[],l=(e,c)=>c==n(r)?o(s,e):null===r[c]?d(e,(e=>l(e,c+1))):t([r[c],null],(t=>l(p(e,t),c+1)));return l(e,0),s},L=Object.freeze,w=(e=>{const t=new WeakMap;return n=>(t.has(n)||t.set(n,e(n)),t.get(n))})((w=>{let S,z,E,y=100,I=h(),M=1;const b=h(),j=h(),[x,B,F]=(e=>{let r,i=0;const g=[],L=h();return[(t,n,o)=>{r??=e();const l=s(g)??""+i++;var c;return C(L,l,[t,n,o]),c=l,k(n,o??[""],f)?.add(c),l},(e,n,...o)=>t(v(e,n),(e=>d(e,(e=>p(L,e)[0](r,...n??[],...o))))),e=>c(p(L,e),(([,t,r])=>(k(t,r??[""],void 0,(t=>(u(t,e),a(t)?1:0))),C(L,e),n(g)<1e3&&o(g,e),r))),(e,t)=>{return n=v(e,t),r=l,!n.every(r);var n,r},(e,o,s)=>c(p(L,e),(([e,,c=[]])=>{const i=(...a)=>{const d=n(a);d==n(c)?e(r,...a,...s(a)):l(c[d])?t(o[d](...a),(e=>i(...a,e))):i(...a,c[d])};i()}))]})((()=>V)),O=h(),T=h(),W=[],m=[],q=(e,t)=>{M=0,w.transaction((()=>d(p(O,t),((t,n)=>d(t,((t,r)=>d(t,((t,o)=>((e,t,n,r,o)=>l(o)?e.delCell(t,n,r,!0):e.setCell(t,n,r,o))(w,n,r,o,t[e]))))))))),M=1},A=e=>{C(O,e),C(T,e),B(j,[e])},D=(e,r)=>t(((e,t)=>e.splice(0,t))(e,r??n(e)),A),G=()=>D(W,n(W)-y),H=w.addCellListener(null,null,null,((e,t,n,r,l,i)=>{if(M){c(S,(()=>{o(W,S),G(),D(m),S=void 0,E=1}));const e=g(I,t,h),d=g(e,n,h),u=g(d,r,(()=>[i,void 0]));u[1]=l,u[0]===l&&a(C(d,r))&&a(C(e,n))&&a(C(I,t))&&(S=s(W),E=1),P()}})),J=(e="")=>(l(S)&&(S=""+z++,C(O,S,I),R(S,e),I=h(),E=1),S),K=()=>{r(W)||(m.unshift(J()),q(0,S),S=s(W),E=1)},N=()=>{r(m)||(o(W,S),S=m.shift(),q(1,S),E=1)},P=()=>{E&&(B(b),E=0)},Q=e=>{const t=J(e);return P(),t},R=(e,t)=>(U(e)&&p(T,e)!==t&&(C(T,e,t),B(j,[e])),V),U=e=>i(O,e),V={setSize:e=>(y=e,G(),V),addCheckpoint:Q,setCheckpoint:R,getStore:()=>w,getCheckpointIds:()=>[[...W],S,[...m]],forEachCheckpoint:e=>{return t=e,d(T,((e,n)=>t(n,e)));var t},hasCheckpoint:U,getCheckpoint:e=>p(T,e),goBackward:()=>(K(),P(),V),goForward:()=>(N(),P(),V),goTo:t=>{const n=e(W,t)?K:e(m,t)?N:null;for(;!l(n)&&t!=S;)n();return P(),V},addCheckpointIdsListener:e=>x(e,b),addCheckpointListener:(e,t)=>x(t,j,[e]),delListener:e=>(F(e),V),clear:()=>(D(W),D(m),l(S)||A(S),S=void 0,z=0,Q(),V),destroy:()=>{w.delListener(H)},getListenerStats:()=>({})};return L(V.clear())}));export{w as createCheckpoints};
Binary file
package/lib/common.js CHANGED
@@ -1 +1 @@
1
- const o=(o,t)=>o<t?-1:1;export{o as defaultSorter};
1
+ const o=(o,t)=>o<t?-1:1,t=o=>""+o;export{o as defaultSorter,t as id};
package/lib/common.js.gz CHANGED
Binary file
@@ -93,12 +93,13 @@ export type CheckpointListener = (
93
93
  */
94
94
  export type CheckpointsListenerStats = {
95
95
  /**
96
- * The number of CheckpointIdsListeners registered with the Checkpoints
97
- * object.
96
+ * The number of CheckpointIdsListener functions registered with the
97
+ * Checkpoints object.
98
98
  */
99
99
  checkpointIds?: number;
100
100
  /**
101
- * The number of CheckpointListeners registered with the Checkpoints object.
101
+ * The number of CheckpointListener functions registered with the Checkpoints
102
+ * object.
102
103
  */
103
104
  checkpoint?: number;
104
105
  };
@@ -32,7 +32,7 @@ const mapSet = (map, key, value) =>
32
32
  isUndefined(value) ? (collDel(map, key), map) : map?.set(key, value);
33
33
  const mapEnsure = (map, key, getDefaultValue) => {
34
34
  if (!collHas(map, key)) {
35
- map.set(key, getDefaultValue());
35
+ mapSet(map, key, getDefaultValue());
36
36
  }
37
37
  return mapGet(map, key);
38
38
  };
@@ -72,32 +72,32 @@ const getCreateFunction = (getFunction) => {
72
72
  };
73
73
 
74
74
  const getWildcardedLeaves = (deepIdSet, path = [EMPTY_STRING]) => {
75
- const sets = [];
76
- const deep = (set, p) =>
75
+ const leaves = [];
76
+ const deep = (node, p) =>
77
77
  p == arrayLength(path)
78
- ? arrayPush(sets, set)
79
- : arrayForEach([path[p], null], (id) => deep(mapGet(set, id), p + 1));
78
+ ? arrayPush(leaves, node)
79
+ : path[p] === null
80
+ ? collForEach(node, (node2) => deep(node2, p + 1))
81
+ : arrayForEach([path[p], null], (id) => deep(mapGet(node, id), p + 1));
80
82
  deep(deepIdSet, 0);
81
- return sets;
83
+ return leaves;
82
84
  };
83
85
  const getListenerFunctions = (getThing) => {
84
86
  let thing;
85
87
  let nextId = 0;
86
88
  const listenerPool = [];
87
89
  const allListeners = mapNew();
88
- const addListener = (listener, idSetNode, idOrNulls) => {
90
+ const addListener = (listener, idSetNode, path) => {
89
91
  thing ??= getThing();
90
92
  const id = arrayPop(listenerPool) ?? EMPTY_STRING + nextId++;
91
- mapSet(allListeners, id, [listener, idSetNode, idOrNulls]);
92
- setAdd(visitTree(idSetNode, idOrNulls ?? [EMPTY_STRING], setNew), id);
93
+ mapSet(allListeners, id, [listener, idSetNode, path]);
94
+ setAdd(visitTree(idSetNode, path ?? [EMPTY_STRING], setNew), id);
93
95
  return id;
94
96
  };
95
97
  const callListeners = (idSetNode, ids, ...extraArgs) =>
96
98
  arrayForEach(getWildcardedLeaves(idSetNode, ids), (set) =>
97
99
  collForEach(set, (id) =>
98
- ifNotUndefined(mapGet(allListeners, id), ([listener]) =>
99
- listener(thing, ...(ids ?? []), ...extraArgs),
100
- ),
100
+ mapGet(allListeners, id)[0](thing, ...(ids ?? []), ...extraArgs),
101
101
  ),
102
102
  );
103
103
  const delListener = (id) =>
@@ -1,3 +1,6 @@
1
+ const EMPTY_STRING = '';
2
+
1
3
  const defaultSorter = (sortKey1, sortKey2) => (sortKey1 < sortKey2 ? -1 : 1);
4
+ const id = (key) => EMPTY_STRING + key;
2
5
 
3
- export {defaultSorter};
6
+ export {defaultSorter, id};
@@ -128,11 +128,13 @@ export type SliceRowIdsListener = (
128
128
  */
129
129
  export type IndexesListenerStats = {
130
130
  /**
131
- * The number of SlideIdsListeners registered with the Indexes object.
131
+ * The number of SlideIdsListener functions registered with the Indexes
132
+ * object.
132
133
  */
133
134
  sliceIds?: number;
134
135
  /**
135
- * The number of SliceRowIdsListeners registered with the Indexes object.
136
+ * The number of SliceRowIdsListener functions registered with the Indexes
137
+ * object.
136
138
  */
137
139
  sliceRowIds?: number;
138
140
  };
@@ -42,7 +42,7 @@ const mapSet = (map, key, value) =>
42
42
  isUndefined(value) ? (collDel(map, key), map) : map?.set(key, value);
43
43
  const mapEnsure = (map, key, getDefaultValue) => {
44
44
  if (!collHas(map, key)) {
45
- map.set(key, getDefaultValue());
45
+ mapSet(map, key, getDefaultValue());
46
46
  }
47
47
  return mapGet(map, key);
48
48
  };
@@ -225,32 +225,32 @@ const getCreateFunction = (getFunction) => {
225
225
  const defaultSorter = (sortKey1, sortKey2) => (sortKey1 < sortKey2 ? -1 : 1);
226
226
 
227
227
  const getWildcardedLeaves = (deepIdSet, path = [EMPTY_STRING]) => {
228
- const sets = [];
229
- const deep = (set, p) =>
228
+ const leaves = [];
229
+ const deep = (node, p) =>
230
230
  p == arrayLength(path)
231
- ? arrayPush(sets, set)
232
- : arrayForEach([path[p], null], (id) => deep(mapGet(set, id), p + 1));
231
+ ? arrayPush(leaves, node)
232
+ : path[p] === null
233
+ ? collForEach(node, (node2) => deep(node2, p + 1))
234
+ : arrayForEach([path[p], null], (id) => deep(mapGet(node, id), p + 1));
233
235
  deep(deepIdSet, 0);
234
- return sets;
236
+ return leaves;
235
237
  };
236
238
  const getListenerFunctions = (getThing) => {
237
239
  let thing;
238
240
  let nextId = 0;
239
241
  const listenerPool = [];
240
242
  const allListeners = mapNew();
241
- const addListener = (listener, idSetNode, idOrNulls) => {
243
+ const addListener = (listener, idSetNode, path) => {
242
244
  thing ??= getThing();
243
245
  const id = arrayPop(listenerPool) ?? EMPTY_STRING + nextId++;
244
- mapSet(allListeners, id, [listener, idSetNode, idOrNulls]);
245
- setAdd(visitTree(idSetNode, idOrNulls ?? [EMPTY_STRING], setNew), id);
246
+ mapSet(allListeners, id, [listener, idSetNode, path]);
247
+ setAdd(visitTree(idSetNode, path ?? [EMPTY_STRING], setNew), id);
246
248
  return id;
247
249
  };
248
250
  const callListeners = (idSetNode, ids, ...extraArgs) =>
249
251
  arrayForEach(getWildcardedLeaves(idSetNode, ids), (set) =>
250
252
  collForEach(set, (id) =>
251
- ifNotUndefined(mapGet(allListeners, id), ([listener]) =>
252
- listener(thing, ...(ids ?? []), ...extraArgs),
253
- ),
253
+ mapGet(allListeners, id)[0](thing, ...(ids ?? []), ...extraArgs),
254
254
  ),
255
255
  );
256
256
  const delListener = (id) =>
@@ -186,7 +186,7 @@ export type MetricListener = (
186
186
  */
187
187
  export type MetricsListenerStats = {
188
188
  /**
189
- * The number of MetricListeners registered with the Metrics object.
189
+ * The number of MetricListener functions registered with the Metrics object.
190
190
  */
191
191
  metric?: number;
192
192
  };
@@ -46,7 +46,7 @@ const mapSet = (map, key, value) =>
46
46
  isUndefined(value) ? (collDel(map, key), map) : map?.set(key, value);
47
47
  const mapEnsure = (map, key, getDefaultValue) => {
48
48
  if (!collHas(map, key)) {
49
- map.set(key, getDefaultValue());
49
+ mapSet(map, key, getDefaultValue());
50
50
  }
51
51
  return mapGet(map, key);
52
52
  };
@@ -296,32 +296,32 @@ const getCreateFunction = (getFunction) => {
296
296
  };
297
297
 
298
298
  const getWildcardedLeaves = (deepIdSet, path = [EMPTY_STRING]) => {
299
- const sets = [];
300
- const deep = (set, p) =>
299
+ const leaves = [];
300
+ const deep = (node, p) =>
301
301
  p == arrayLength(path)
302
- ? arrayPush(sets, set)
303
- : arrayForEach([path[p], null], (id) => deep(mapGet(set, id), p + 1));
302
+ ? arrayPush(leaves, node)
303
+ : path[p] === null
304
+ ? collForEach(node, (node2) => deep(node2, p + 1))
305
+ : arrayForEach([path[p], null], (id) => deep(mapGet(node, id), p + 1));
304
306
  deep(deepIdSet, 0);
305
- return sets;
307
+ return leaves;
306
308
  };
307
309
  const getListenerFunctions = (getThing) => {
308
310
  let thing;
309
311
  let nextId = 0;
310
312
  const listenerPool = [];
311
313
  const allListeners = mapNew();
312
- const addListener = (listener, idSetNode, idOrNulls) => {
314
+ const addListener = (listener, idSetNode, path) => {
313
315
  thing ??= getThing();
314
316
  const id = arrayPop(listenerPool) ?? EMPTY_STRING + nextId++;
315
- mapSet(allListeners, id, [listener, idSetNode, idOrNulls]);
316
- setAdd(visitTree(idSetNode, idOrNulls ?? [EMPTY_STRING], setNew), id);
317
+ mapSet(allListeners, id, [listener, idSetNode, path]);
318
+ setAdd(visitTree(idSetNode, path ?? [EMPTY_STRING], setNew), id);
317
319
  return id;
318
320
  };
319
321
  const callListeners = (idSetNode, ids, ...extraArgs) =>
320
322
  arrayForEach(getWildcardedLeaves(idSetNode, ids), (set) =>
321
323
  collForEach(set, (id) =>
322
- ifNotUndefined(mapGet(allListeners, id), ([listener]) =>
323
- listener(thing, ...(ids ?? []), ...extraArgs),
324
- ),
324
+ mapGet(allListeners, id)[0](thing, ...(ids ?? []), ...extraArgs),
325
325
  ),
326
326
  );
327
327
  const delListener = (id) =>
@@ -40,7 +40,13 @@ import {Callback} from './common.d';
40
40
  * @category Development
41
41
  */
42
42
  export type PersisterStats = {
43
+ /**
44
+ * The number of times data has been loaded.
45
+ */
43
46
  loads?: number;
47
+ /**
48
+ * The number of times data has been saved.
49
+ */
44
50
  saves?: number;
45
51
  };
46
52
 
@@ -194,6 +194,36 @@ export type ResultTableListener = (
194
194
  */
195
195
  export type ResultRowIdsListener = (queries: Queries, tableId: Id) => void;
196
196
 
197
+ /**
198
+ * The ResultSortedRowIdsListener type describes a function that is used to
199
+ * listen to changes to the sorted Row Ids in a query's result Table.
200
+ *
201
+ * A ResultSortedRowIdsListener is provided when using the
202
+ * addResultSortedRowIdsListener method. See that method for specific examples.
203
+ *
204
+ * When called, a ResultSortedRowIdsListener is given a reference to the Queries
205
+ * object, the Id of the Table whose Row Ids changed (which is the same as the
206
+ * query Id), the Cell Id being used to sort them, and and whether descending or
207
+ * not. It also receives the sorted array of Ids itself, so that you can use
208
+ * them in the listener without the additional cost of an explicit call to
209
+ * getResultSortedRowIds.
210
+ *
211
+ * @param queries A reference to the Queries object that changed.
212
+ * @param tableId The Id of the Table that changed, which is also the query Id.
213
+ * @param cellId The Id of the Cell whose values were used for the sorting.
214
+ * @param descending Whether the sorting was in descending order.
215
+ * @param sortedRowIds The sorted Row Ids themselves.
216
+ * @category Listener
217
+ * @since v2.0.0-beta
218
+ */
219
+ export type ResultSortedRowIdsListener = (
220
+ queries: Queries,
221
+ tableId: Id,
222
+ cellId: Id | undefined,
223
+ descending: boolean,
224
+ sortedRowIds: Ids,
225
+ ) => void;
226
+
197
227
  /**
198
228
  * The ResultRowListener type describes a function that is used to listen to
199
229
  * changes to a Row in a query's result Table.
@@ -290,23 +320,23 @@ export type ResultCellListener = (
290
320
  */
291
321
  export type QueriesListenerStats = {
292
322
  /**
293
- * The number of ResultTableListeners registered with the Store.
323
+ * The number of ResultTableListener functions registered with the Store.
294
324
  */
295
325
  table?: number;
296
326
  /**
297
- * The number of ResultRowIdsListeners registered with the Store.
327
+ * The number of ResultRowIdsListener functions registered with the Store.
298
328
  */
299
329
  rowIds?: number;
300
330
  /**
301
- * The number of ResultRowListeners registered with the Store.
331
+ * The number of ResultRowListener functions registered with the Store.
302
332
  */
303
333
  row?: number;
304
334
  /**
305
- * The number of ResultCellIdsListeners registered with the Store.
335
+ * The number of ResultCellIdsListener functions registered with the Store.
306
336
  */
307
337
  cellIds?: number;
308
338
  /**
309
- * The number of ResultCellListeners registered with the Store.
339
+ * The number of ResultCellListener functions registered with the Store.
310
340
  */
311
341
  cell?: number;
312
342
  };
@@ -1949,6 +1979,60 @@ export interface Queries {
1949
1979
  */
1950
1980
  getResultRowIds(queryId: Id): Ids;
1951
1981
 
1982
+ /**
1983
+ * The getResultSortedRowIds method returns the Ids of every Row in the result
1984
+ * Table of the given query, sorted according to the values in a specified
1985
+ * Cell.
1986
+ *
1987
+ * This has the same behavior as a Store's getSortedRowIds method. For
1988
+ * example, if the query Id is invalid, the method returns an empty array.
1989
+ * Similarly, the sorting of the rows is alphanumeric, and you can indicate
1990
+ * whether it should be in descending order.
1991
+ *
1992
+ * Note that every call to this method will perform the sorting afresh - there
1993
+ * is no caching of the results - and so you are advised to memoize the
1994
+ * results yourself, especially when the result Table is large. For a
1995
+ * performant approach to tracking the sorted Row Ids when they change, use
1996
+ * the addResultSortedRowIdsListener method.
1997
+ *
1998
+ * @param queryId The Id of a query.
1999
+ * @param cellId The Id of the Cell whose values are used for the sorting, or
2000
+ * `undefined` to by sort the Row Id itself.
2001
+ * @param descending Whether the sorting should be in descending order.
2002
+ * @returns An array of the sorted Ids of every Row in the result of the
2003
+ * query.
2004
+ * @example
2005
+ * This example creates a Queries object, a single query definition, and then
2006
+ * calls this method on it (as well as a non-existent definition) to get the
2007
+ * result Row Ids.
2008
+ *
2009
+ * ```js
2010
+ * const store = createStore().setTable('pets', {
2011
+ * fido: {species: 'dog', color: 'brown'},
2012
+ * felix: {species: 'cat', color: 'black'},
2013
+ * cujo: {species: 'dog', color: 'black'},
2014
+ * });
2015
+ *
2016
+ * const queries = createQueries(store).setQueryDefinition(
2017
+ * 'dogColors',
2018
+ * 'pets',
2019
+ * ({select, where}) => {
2020
+ * select('color');
2021
+ * where('species', 'dog');
2022
+ * },
2023
+ * );
2024
+ *
2025
+ * console.log(queries.getResultSortedRowIds('dogColors', 'color'));
2026
+ * // -> ['cujo', 'fido']
2027
+ *
2028
+ * console.log(queries.getResultSortedRowIds('catColors', 'color'));
2029
+ * // -> []
2030
+ * ```
2031
+ * @category Result
2032
+ * @since v2.0.0-beta
2033
+ */
2034
+ getResultSortedRowIds(queryId: Id, cellId?: Id, descending?: boolean): Ids;
2035
+
1952
2036
  /**
1953
2037
  * The getResultRow method returns an object containing the entire data of a
1954
2038
  * single Row in the result Table of the given query.
@@ -2120,8 +2204,8 @@ export interface Queries {
2120
2204
  hasResultTable(queryId: Id): boolean;
2121
2205
 
2122
2206
  /**
2123
- * The hasResultRow method returns a boolean indicating whether a given
2124
- * result Row exists.
2207
+ * The hasResultRow method returns a boolean indicating whether a given result
2208
+ * Row exists.
2125
2209
  *
2126
2210
  * @param queryId The Id of a possible query.
2127
2211
  * @param rowId The Id of a possible Row.
@@ -2471,7 +2555,7 @@ export interface Queries {
2471
2555
  *
2472
2556
  * const listenerId = queries.addResultRowIdsListener(
2473
2557
  * 'dogColors',
2474
- * (store, tableId) => {
2558
+ * (queries, tableId) => {
2475
2559
  * console.log(`Row Ids for dogColors result table changed`);
2476
2560
  * console.log(queries.getResultRowIds('dogColors'));
2477
2561
  * },
@@ -2511,7 +2595,7 @@ export interface Queries {
2511
2595
  *
2512
2596
  * const listenerId = queries.addResultRowIdsListener(
2513
2597
  * 'dogColors',
2514
- * (store, tableId) => {
2598
+ * (queries, tableId) => {
2515
2599
  * console.log(`Row Ids for dogColors result table changed`);
2516
2600
  * console.log(queries.getResultRowIds('dogColors'));
2517
2601
  * },
@@ -2571,6 +2655,119 @@ export interface Queries {
2571
2655
  trackReorder?: boolean,
2572
2656
  ): Id;
2573
2657
 
2658
+ /**
2659
+ * The addResultSortedRowIdsListener method registers a listener function with
2660
+ * the Queries object that will be called whenever sorted Row Ids in a result
2661
+ * Table change.
2662
+ *
2663
+ * The provided listener is a ResultSortedRowIdsListener function, and will be
2664
+ * called with a reference to the Queries object, the Id of the result Table
2665
+ * whose Row Ids sorting changed (which is also the query Id), the Cell Id
2666
+ * being used to sort them, and whether descending or not. It also receives
2667
+ * the sorted array of Ids itself, so that you can use them in the listener
2668
+ * without the additional cost of an explicit call to getResultSortedRowIds
2669
+ *
2670
+ * Such a listener is called when a Row is added or removed, but also when a
2671
+ * value in the specified Cell (somewhere in the result Table) has changed
2672
+ * enough to change the sorting of the Row Ids.
2673
+ *
2674
+ * Unlike most other listeners, you cannot provide wildcards (due to the cost
2675
+ * of detecting changes to the sorting). You can only listen to a single
2676
+ * specified result Table, sorted by a single specified Cell.
2677
+ *
2678
+ * @param queryId The Id of the query to listen to.
2679
+ * @param cellId The Id of the Cell whose values are used for the sorting, or
2680
+ * `undefined` to by sort the result Row Id itself.
2681
+ * @param descending Whether the sorting should be in descending order.
2682
+ * @param listener The function that will be called whenever the sorted Row
2683
+ * Ids in the result Table change.
2684
+ * @returns A unique Id for the listener that can later be used to remove it.
2685
+ * @example
2686
+ * This example registers a listener that responds to any change to the sorted
2687
+ * Row Ids of a specific result Table.
2688
+ *
2689
+ * ```js
2690
+ * const store = createStore().setTable('pets', {
2691
+ * fido: {species: 'dog', color: 'brown'},
2692
+ * felix: {species: 'cat', color: 'black'},
2693
+ * cujo: {species: 'dog', color: 'black'},
2694
+ * });
2695
+ *
2696
+ * const queries = createQueries(store).setQueryDefinition(
2697
+ * 'dogColors',
2698
+ * 'pets',
2699
+ * ({select, where}) => {
2700
+ * select('color');
2701
+ * where('species', 'dog');
2702
+ * },
2703
+ * );
2704
+ *
2705
+ * const listenerId = queries.addResultSortedRowIdsListener(
2706
+ * 'dogColors',
2707
+ * 'color',
2708
+ * false,
2709
+ * (queries, tableId, cellId, descending, sortedRowIds) => {
2710
+ * console.log(`Sorted Row Ids for dogColors result table changed`);
2711
+ * console.log(sortedRowIds);
2712
+ * // ^ cheaper than calling getResultSortedRowIds again
2713
+ * },
2714
+ * );
2715
+ *
2716
+ * store.setRow('pets', 'rex', {species: 'dog', color: 'tan'});
2717
+ * // -> 'Sorted Row Ids for dogColors result table changed'
2718
+ * // -> ['cujo', 'fido', 'rex']
2719
+ *
2720
+ * store.delListener(listenerId);
2721
+ * ```
2722
+ * @example
2723
+ * This example registers a listener that responds to any change to the sorted
2724
+ * Row Ids of a specific Table. The Row Ids are sorted by their own value,
2725
+ * since the `cellId` parameter is explicitly undefined.
2726
+ *
2727
+ * ```js
2728
+ * const store = createStore().setTable('pets', {
2729
+ * fido: {species: 'dog', color: 'brown'},
2730
+ * felix: {species: 'cat', color: 'black'},
2731
+ * cujo: {species: 'dog', color: 'black'},
2732
+ * });
2733
+ *
2734
+ * const queries = createQueries(store).setQueryDefinition(
2735
+ * 'dogColors',
2736
+ * 'pets',
2737
+ * ({select, where}) => {
2738
+ * select('color');
2739
+ * where('species', 'dog');
2740
+ * },
2741
+ * );
2742
+ * console.log(queries.getResultSortedRowIds('dogColors', undefined, false));
2743
+ * // -> ['cujo', 'fido']
2744
+ *
2745
+ * const listenerId = queries.addResultSortedRowIdsListener(
2746
+ * 'dogColors',
2747
+ * undefined,
2748
+ * false,
2749
+ * (queries, tableId, cellId, descending, sortedRowIds) => {
2750
+ * console.log(`Sorted Row Ids for dogColors result table changed`);
2751
+ * console.log(sortedRowIds);
2752
+ * // ^ cheaper than calling getSortedRowIds again
2753
+ * },
2754
+ * );
2755
+ *
2756
+ * store.setRow('pets', 'rex', {species: 'dog', color: 'tan'});
2757
+ * // -> 'Sorted Row Ids for dogColors result table changed'
2758
+ * // -> ['cujo', 'fido', 'rex']
2759
+ *
2760
+ * store.delListener(listenerId);
2761
+ * ```
2762
+ * @category Listener
2763
+ */
2764
+ addResultSortedRowIdsListener(
2765
+ queryId: Id,
2766
+ cellId: Id | undefined,
2767
+ descending: boolean,
2768
+ listener: ResultSortedRowIdsListener,
2769
+ ): Id;
2770
+
2574
2771
  /**
2575
2772
  * The addResultRowListener method registers a listener function with the
2576
2773
  * Queries object that will be called whenever data in a result Row changes.
@@ -50,7 +50,7 @@ const mapSet = (map, key, value) =>
50
50
  isUndefined(value) ? (collDel(map, key), map) : map?.set(key, value);
51
51
  const mapEnsure = (map, key, getDefaultValue) => {
52
52
  if (!collHas(map, key)) {
53
- map.set(key, getDefaultValue());
53
+ mapSet(map, key, getDefaultValue());
54
54
  }
55
55
  return mapGet(map, key);
56
56
  };
@@ -313,6 +313,7 @@ const objFreeze = object.freeze;
313
313
  const defaultSorter = (sortKey1, sortKey2) => (sortKey1 < sortKey2 ? -1 : 1);
314
314
 
315
315
  const createQueries = getCreateFunction((store) => {
316
+ const createStore = store.createStore;
316
317
  const [
317
318
  getStore,
318
319
  getQueryIds,
@@ -328,9 +329,9 @@ const createQueries = getCreateFunction((store) => {
328
329
  addStoreListeners,
329
330
  delStoreListeners,
330
331
  ] = getDefinableFunctions(store, () => true, getUndefined);
331
- const preStore1 = store.createStore();
332
- const preStore2 = store.createStore();
333
- const resultStore = store.createStore();
332
+ const preStore1 = createStore();
333
+ const preStore2 = createStore();
334
+ const resultStore = createStore();
334
335
  const preStoreListenerIds = mapNew();
335
336
  const addPreStoreListener = (preStore, queryId, ...listenerIds) =>
336
337
  arrayForEach(listenerIds, (listenerId) =>
@@ -343,10 +344,14 @@ const createQueries = getCreateFunction((store) => {
343
344
  listenerId,
344
345
  ),
345
346
  );
347
+ const cleanPreStores = (queryId) =>
348
+ arrayForEach([resultStore, preStore2, preStore1], (store2) =>
349
+ store2.delTable(queryId),
350
+ );
346
351
  const synchronizeTransactions = (queryId, fromStore, toStore) =>
347
- addStoreListeners(
352
+ addPreStoreListener(
353
+ fromStore,
348
354
  queryId,
349
- 0,
350
355
  fromStore.addWillFinishTransactionListener(toStore.startTransaction),
351
356
  fromStore.addDidFinishTransactionListener(() =>
352
357
  toStore.finishTransaction(),
@@ -354,9 +359,7 @@ const createQueries = getCreateFunction((store) => {
354
359
  );
355
360
  const setQueryDefinition = (queryId, tableId, build) => {
356
361
  setDefinition(queryId, tableId);
357
- preStore1.delTable(queryId);
358
- preStore2.delTable(queryId);
359
- resultStore.delTable(queryId);
362
+ cleanPreStores(queryId);
360
363
  let offsetLimit;
361
364
  const selectEntries = [];
362
365
  const joinEntries = [[null, [tableId, null, null, [], mapNew()]]];
@@ -791,11 +794,14 @@ const createQueries = getCreateFunction((store) => {
791
794
  preStore.delListener(listenerId),
792
795
  ),
793
796
  );
797
+ cleanPreStores(queryId);
794
798
  delDefinition(queryId);
795
799
  return queries;
796
800
  };
797
801
  const getResultTable = (queryId) => resultStore.getTable(queryId);
798
802
  const getResultRowIds = (queryId) => resultStore.getRowIds(queryId);
803
+ const getResultSortedRowIds = (queryId, cellId, descending) =>
804
+ resultStore.getSortedRowIds(queryId, cellId, descending);
799
805
  const getResultRow = (queryId, rowId) => resultStore.getRow(queryId, rowId);
800
806
  const getResultCellIds = (queryId, rowId) =>
801
807
  resultStore.getCellIds(queryId, rowId);
@@ -821,6 +827,18 @@ const createQueries = getCreateFunction((store) => {
821
827
  (_store, ...args) => listener(queries, ...args),
822
828
  trackReorder,
823
829
  );
830
+ const addResultSortedRowIdsListener = (
831
+ queryId,
832
+ cellId,
833
+ descending,
834
+ listener,
835
+ ) =>
836
+ resultStore.addSortedRowIdsListener(
837
+ queryId,
838
+ cellId,
839
+ descending,
840
+ (_store, ...args) => listener(queries, ...args),
841
+ );
824
842
  const addResultRowListener = (queryId, rowId, listener) =>
825
843
  resultStore.addRowListener(queryId, rowId, (_store, ...args) =>
826
844
  listener(queries, ...args),
@@ -856,6 +874,7 @@ const createQueries = getCreateFunction((store) => {
856
874
  getTableId,
857
875
  getResultTable,
858
876
  getResultRowIds,
877
+ getResultSortedRowIds,
859
878
  getResultRow,
860
879
  getResultCellIds,
861
880
  getResultCell,
@@ -867,6 +886,7 @@ const createQueries = getCreateFunction((store) => {
867
886
  forEachResultCell,
868
887
  addResultTableListener,
869
888
  addResultRowIdsListener,
889
+ addResultSortedRowIdsListener,
870
890
  addResultRowListener,
871
891
  addResultCellIdsListener,
872
892
  addResultCellListener,