tinybase 2.0.0-beta.4 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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(),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=>{let r,i=0;const g=[],v=h();return[(t,n,o)=>{r??=e();const l=s(g)??""+i++;var c,a;return C(v,l,[t,n,o]),c=k(n,o??[""],f),a=l,c?.add(a),l},(e,s,...l)=>t(((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})(e,s),(e=>d(e,(e=>p(v,e)[0](r,...s??[],...l))))),e=>c(p(v,e),(([,t,r])=>(k(t,r??[""],void 0,(t=>(u(t,e),a(t)?1:0))),C(v,e),n(g)<1e3&&o(g,e),r))),(e,o,s)=>c(p(v,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()}))]},L=Object.freeze,w=(e=>{const t=new WeakMap;return n=>(t.has(n)||t.set(n,e(n)),t.get(n))})((u=>{let k,f,w,S=100,z=h(),E=1;const I=h(),M=h(),[b,j,x]=v((()=>Q)),y=h(),B=h(),F=[],O=[],T=(e,t)=>{E=0,u.transaction((()=>d(p(y,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))(u,n,r,o,t[e]))))))))),E=1},W=e=>{C(y,e),C(B,e),j(M,[e])},m=(e,r)=>t(((e,t)=>e.splice(0,t))(e,r??n(e)),W),q=()=>m(F,n(F)-S),A=u.addCellListener(null,null,null,((e,t,n,r,l,i)=>{if(E){c(k,(()=>{o(F,k),q(),m(O),k=void 0,w=1}));const e=g(z,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(z,t))&&(k=s(F),w=1),J()}})),D=(e="")=>(l(k)&&(k=""+f++,C(y,k,z),N(k,e),z=h(),w=1),k),G=()=>{r(F)||(O.unshift(D()),T(0,k),k=s(F),w=1)},H=()=>{r(O)||(o(F,k),k=O.shift(),T(1,k),w=1)},J=()=>{w&&(j(I),w=0)},K=e=>{const t=D(e);return J(),t},N=(e,t)=>(P(e)&&p(B,e)!==t&&(C(B,e,t),j(M,[e])),Q),P=e=>i(y,e),Q={setSize:e=>(S=e,q(),Q),addCheckpoint:K,setCheckpoint:N,getStore:()=>u,getCheckpointIds:()=>[[...F],k,[...O]],forEachCheckpoint:e=>{return t=e,d(B,((e,n)=>t(n,e)));var t},hasCheckpoint:P,getCheckpoint:e=>p(B,e),goBackward:()=>(G(),J(),Q),goForward:()=>(H(),J(),Q),goTo:t=>{const n=e(F,t)?G:e(O,t)?H:null;for(;!l(n)&&t!=k;)n();return J(),Q},addCheckpointIdsListener:e=>b(e,I),addCheckpointListener:(e,t)=>b(t,M,[e]),delListener:e=>(x(e),Q),clear:()=>(m(F),m(O),l(k)||W(k),k=void 0,f=0,K(),Q),destroy:()=>{u.delListener(A)},getListenerStats:()=>({})};return L(Q.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=>e.shift(),c=e=>null==e,i=(e,t,n)=>c(e)?n?.():t(e),d=(e,t)=>e?.has(t)??!1,a=e=>c(e)||0==(e=>e.size)(e),u=(e,t)=>e?.forEach(t),h=(e,t)=>e?.delete(t),p=e=>new Map(e),C=(e,t)=>e?.get(t),g=(e,t,n)=>c(n)?(h(e,t),e):e?.set(t,n),k=(e,t,n)=>(d(e,t)||g(e,t,n()),C(e,t)),f=(e,t,r,o,s=0)=>i((r?k:C)(e,t[s],s>n(t)-2?r:p),(l=>{if(s>n(t)-2)return o?.(l)&&g(e,t[s]),l;const c=f(l,t,r,o,s+1);return a(l)&&g(e,t[s]),c})),v=e=>new Set(e),L=/^\d+$/,w=e=>{let r;const[s,d]=(()=>{const e=[];let t=0;return[()=>l(e)??""+t++,t=>{L.test(t)&&n(e)<1e3&&o(e,t)}]})(),k=p();return[(t,n,o)=>{r??=e();const l=s();var c,i;return g(k,l,[t,n,o]),c=f(n,o??[""],v),i=l,c?.add(i),l},(e,s,...l)=>t(((e,r=[""])=>{const s=[],l=(e,c)=>c==n(r)?o(s,e):null===r[c]?u(e,(e=>l(e,c+1))):t([r[c],null],(t=>l(C(e,t),c+1)));return l(e,0),s})(e,s),(e=>u(e,(e=>C(k,e)[0](r,...s??[],...l))))),e=>i(C(k,e),(([,t,n])=>(f(t,n??[""],void 0,(t=>(h(t,e),a(t)?1:0))),g(k,e),d(e),n))),(e,o,s)=>i(C(k,e),(([e,,l=[]])=>{const i=(...d)=>{const a=n(d);a==n(l)?e(r,...d,...s(d)):c(l[a])?t(o[a](...d),(e=>i(...d,e))):i(...d,l[a])};i()}))]},S=Object.freeze,z=(e=>{const t=new WeakMap;return n=>(t.has(n)||t.set(n,e(n)),t.get(n))})((h=>{let f,v,L,z=100,E=p(),I=1;const M=p(),b=p(),[j,x,y]=w((()=>Q)),B=p(),F=p(),O=[],T=[],W=(e,t)=>{I=0,h.transaction((()=>u(C(B,t),((t,n)=>u(t,((t,r)=>u(t,((t,o)=>((e,t,n,r,o)=>c(o)?e.delCell(t,n,r,!0):e.setCell(t,n,r,o))(h,n,r,o,t[e]))))))))),I=1},$=e=>{g(B,e),g(F,e),x(b,[e])},m=(e,r)=>t(((e,t)=>e.splice(0,t))(e,r??n(e)),$),q=()=>m(O,n(O)-z),A=h.addCellListener(null,null,null,((e,t,n,r,l,c)=>{if(I){i(f,(()=>{o(O,f),q(),m(T),f=void 0,L=1}));const e=k(E,t,p),d=k(e,n,p),u=k(d,r,(()=>[c,void 0]));u[1]=l,u[0]===l&&a(g(d,r))&&a(g(e,n))&&a(g(E,t))&&(f=s(O),L=1),J()}})),D=(e="")=>(c(f)&&(f=""+v++,g(B,f,E),N(f,e),E=p(),L=1),f),G=()=>{r(O)||(T.unshift(D()),W(0,f),f=s(O),L=1)},H=()=>{r(T)||(o(O,f),f=l(T),W(1,f),L=1)},J=()=>{L&&(x(M),L=0)},K=e=>{const t=D(e);return J(),t},N=(e,t)=>(P(e)&&C(F,e)!==t&&(g(F,e,t),x(b,[e])),Q),P=e=>d(B,e),Q={setSize:e=>(z=e,q(),Q),addCheckpoint:K,setCheckpoint:N,getStore:()=>h,getCheckpointIds:()=>[[...O],f,[...T]],forEachCheckpoint:e=>{return t=e,u(F,((e,n)=>t(n,e)));var t},hasCheckpoint:P,getCheckpoint:e=>C(F,e),goBackward:()=>(G(),J(),Q),goForward:()=>(H(),J(),Q),goTo:t=>{const n=e(O,t)?G:e(T,t)?H:null;for(;!c(n)&&t!=f;)n();return J(),Q},addCheckpointIdsListener:e=>j(e,M),addCheckpointListener:(e,t)=>j(t,b,[e]),delListener:e=>(y(e),Q),clear:()=>(m(O),m(T),c(f)||$(f),f=void 0,v=0,K(),Q),destroy:()=>{h.delListener(A)},getListenerStats:()=>({})};return S(Q.clear())}));export{z as createCheckpoints};
Binary file
@@ -8,6 +8,7 @@ const arrayReduce = (array, cb, initial) => array.reduce(cb, initial);
8
8
  const arrayClear = (array, to) => array.splice(0, to);
9
9
  const arrayPush = (array, ...values) => array.push(...values);
10
10
  const arrayPop = (array) => array.pop();
11
+ const arrayShift = (array) => array.shift();
11
12
 
12
13
  const isUndefined = (thing) => thing == void 0;
13
14
  const ifNotUndefined = (value, then, otherwise) =>
@@ -70,6 +71,20 @@ const getCreateFunction = (getFunction) => {
70
71
  };
71
72
  };
72
73
 
74
+ const INTEGER = /^\d+$/;
75
+ const getPoolFunctions = () => {
76
+ const pool = [];
77
+ let nextId = 0;
78
+ return [
79
+ () => arrayShift(pool) ?? EMPTY_STRING + nextId++,
80
+ (id) => {
81
+ if (INTEGER.test(id) && arrayLength(pool) < 1e3) {
82
+ arrayPush(pool, id);
83
+ }
84
+ },
85
+ ];
86
+ };
87
+
73
88
  const getWildcardedLeaves = (deepIdSet, path = [EMPTY_STRING]) => {
74
89
  const leaves = [];
75
90
  const deep = (node, p) =>
@@ -83,12 +98,11 @@ const getWildcardedLeaves = (deepIdSet, path = [EMPTY_STRING]) => {
83
98
  };
84
99
  const getListenerFunctions = (getThing) => {
85
100
  let thing;
86
- let nextId = 0;
87
- const listenerPool = [];
101
+ const [getId, releaseId] = getPoolFunctions();
88
102
  const allListeners = mapNew();
89
103
  const addListener = (listener, idSetNode, path) => {
90
104
  thing ??= getThing();
91
- const id = arrayPop(listenerPool) ?? EMPTY_STRING + nextId++;
105
+ const id = getId();
92
106
  mapSet(allListeners, id, [listener, idSetNode, path]);
93
107
  setAdd(visitTree(idSetNode, path ?? [EMPTY_STRING], setNew), id);
94
108
  return id;
@@ -106,9 +120,7 @@ const getListenerFunctions = (getThing) => {
106
120
  return collIsEmpty(idSet) ? 1 : 0;
107
121
  });
108
122
  mapSet(allListeners, id);
109
- if (arrayLength(listenerPool) < 1e3) {
110
- arrayPush(listenerPool, id);
111
- }
123
+ releaseId(id);
112
124
  return idOrNulls;
113
125
  });
114
126
  const callListener = (id, idNullGetters, extraArgsGetter) =>
@@ -229,7 +241,7 @@ const createCheckpoints = getCreateFunction((store) => {
229
241
  const goForwardImpl = () => {
230
242
  if (!arrayIsEmpty(forwardIds)) {
231
243
  arrayPush(backwardIds, currentId);
232
- currentId = forwardIds.shift();
244
+ currentId = arrayShift(forwardIds);
233
245
  updateStore(1, currentId);
234
246
  checkpointsChanged = 1;
235
247
  }
@@ -14,7 +14,7 @@ const arrayLength = (array) => array.length;
14
14
  const arrayIsEmpty = (array) => arrayLength(array) == 0;
15
15
  const arrayReduce = (array, cb, initial) => array.reduce(cb, initial);
16
16
  const arrayPush = (array, ...values) => array.push(...values);
17
- const arrayPop = (array) => array.pop();
17
+ const arrayShift = (array) => array.shift();
18
18
 
19
19
  const isUndefined = (thing) => thing == void 0;
20
20
  const ifNotUndefined = (value, then, otherwise) =>
@@ -224,6 +224,20 @@ const getCreateFunction = (getFunction) => {
224
224
 
225
225
  const defaultSorter = (sortKey1, sortKey2) => (sortKey1 < sortKey2 ? -1 : 1);
226
226
 
227
+ const INTEGER = /^\d+$/;
228
+ const getPoolFunctions = () => {
229
+ const pool = [];
230
+ let nextId = 0;
231
+ return [
232
+ () => arrayShift(pool) ?? EMPTY_STRING + nextId++,
233
+ (id) => {
234
+ if (INTEGER.test(id) && arrayLength(pool) < 1e3) {
235
+ arrayPush(pool, id);
236
+ }
237
+ },
238
+ ];
239
+ };
240
+
227
241
  const getWildcardedLeaves = (deepIdSet, path = [EMPTY_STRING]) => {
228
242
  const leaves = [];
229
243
  const deep = (node, p) =>
@@ -237,12 +251,11 @@ const getWildcardedLeaves = (deepIdSet, path = [EMPTY_STRING]) => {
237
251
  };
238
252
  const getListenerFunctions = (getThing) => {
239
253
  let thing;
240
- let nextId = 0;
241
- const listenerPool = [];
254
+ const [getId, releaseId] = getPoolFunctions();
242
255
  const allListeners = mapNew();
243
256
  const addListener = (listener, idSetNode, path) => {
244
257
  thing ??= getThing();
245
- const id = arrayPop(listenerPool) ?? EMPTY_STRING + nextId++;
258
+ const id = getId();
246
259
  mapSet(allListeners, id, [listener, idSetNode, path]);
247
260
  setAdd(visitTree(idSetNode, path ?? [EMPTY_STRING], setNew), id);
248
261
  return id;
@@ -260,9 +273,7 @@ const getListenerFunctions = (getThing) => {
260
273
  return collIsEmpty(idSet) ? 1 : 0;
261
274
  });
262
275
  mapSet(allListeners, id);
263
- if (arrayLength(listenerPool) < 1e3) {
264
- arrayPush(listenerPool, id);
265
- }
276
+ releaseId(id);
266
277
  return idOrNulls;
267
278
  });
268
279
  const callListener = (id, idNullGetters, extraArgsGetter) =>
@@ -13,7 +13,7 @@ const arrayLength = (array) => array.length;
13
13
  const arrayIsEmpty = (array) => arrayLength(array) == 0;
14
14
  const arrayReduce = (array, cb, initial) => array.reduce(cb, initial);
15
15
  const arrayPush = (array, ...values) => array.push(...values);
16
- const arrayPop = (array) => array.pop();
16
+ const arrayShift = (array) => array.shift();
17
17
 
18
18
  const mathMax = Math.max;
19
19
  const mathMin = Math.min;
@@ -294,6 +294,20 @@ const getCreateFunction = (getFunction) => {
294
294
  };
295
295
  };
296
296
 
297
+ const INTEGER = /^\d+$/;
298
+ const getPoolFunctions = () => {
299
+ const pool = [];
300
+ let nextId = 0;
301
+ return [
302
+ () => arrayShift(pool) ?? EMPTY_STRING + nextId++,
303
+ (id) => {
304
+ if (INTEGER.test(id) && arrayLength(pool) < 1e3) {
305
+ arrayPush(pool, id);
306
+ }
307
+ },
308
+ ];
309
+ };
310
+
297
311
  const getWildcardedLeaves = (deepIdSet, path = [EMPTY_STRING]) => {
298
312
  const leaves = [];
299
313
  const deep = (node, p) =>
@@ -307,12 +321,11 @@ const getWildcardedLeaves = (deepIdSet, path = [EMPTY_STRING]) => {
307
321
  };
308
322
  const getListenerFunctions = (getThing) => {
309
323
  let thing;
310
- let nextId = 0;
311
- const listenerPool = [];
324
+ const [getId, releaseId] = getPoolFunctions();
312
325
  const allListeners = mapNew();
313
326
  const addListener = (listener, idSetNode, path) => {
314
327
  thing ??= getThing();
315
- const id = arrayPop(listenerPool) ?? EMPTY_STRING + nextId++;
328
+ const id = getId();
316
329
  mapSet(allListeners, id, [listener, idSetNode, path]);
317
330
  setAdd(visitTree(idSetNode, path ?? [EMPTY_STRING], setNew), id);
318
331
  return id;
@@ -330,9 +343,7 @@ const getListenerFunctions = (getThing) => {
330
343
  return collIsEmpty(idSet) ? 1 : 0;
331
344
  });
332
345
  mapSet(allListeners, id);
333
- if (arrayLength(listenerPool) < 1e3) {
334
- arrayPush(listenerPool, id);
335
- }
346
+ releaseId(id);
336
347
  return idOrNulls;
337
348
  });
338
349
  const callListener = (id, idNullGetters, extraArgsGetter) =>
@@ -2,10 +2,10 @@
2
2
  * The queries module of the TinyBase project provides the ability to create and
3
3
  * track queries of the data in Store objects.
4
4
  *
5
- * The main entry point to this module is the createQueries function, which
6
- * returns a new Queries object. From there, you can create new query
7
- * definitions, access the results of those directly, and register listeners for
8
- * when they change.
5
+ * The main entry point to using the queries module is the createQueries
6
+ * function, which returns a new Queries object. That object in turn has methods
7
+ * that let you create new query definitions, access their results directly, and
8
+ * register listeners for when those results change.
9
9
  *
10
10
  * @packageDocumentation
11
11
  * @module queries
@@ -385,18 +385,18 @@ export type GetTableCell = {
385
385
  * The Select type describes a function that lets you specify a Cell or
386
386
  * calculated value for including into the query's result.
387
387
  *
388
- * The Select function is provided as an parameter in the `build` parameter of
389
- * the setQueryDefinition method. A query definition must call the Select
390
- * function at least once, otherwise it will be meaningless and return no data.
388
+ * The Select function is provided to the third `query` parameter of the
389
+ * setQueryDefinition method. A query definition must call the Select function
390
+ * at least once, otherwise it will be meaningless and return no data.
391
391
  *
392
392
  * @example
393
393
  * This example shows a query that selects two Cells from the main query Table.
394
394
  *
395
395
  * ```js
396
396
  * const store = createStore().setTable('pets', {
397
- * fido: {species: 'dog', color: 'brown'},
398
- * felix: {species: 'cat', color: 'black'},
399
- * cujo: {species: 'dog', color: 'black'},
397
+ * fido: {species: 'dog', color: 'brown', legs: 4},
398
+ * felix: {species: 'cat', color: 'black', legs: 4},
399
+ * cujo: {species: 'dog', color: 'black', legs: 4},
400
400
  * });
401
401
  *
402
402
  * const queries = createQueries(store);
@@ -572,9 +572,9 @@ export type SelectedAs = {
572
572
 
573
573
  /**
574
574
  * The Join type describes a function that lets you specify a Cell or calculated
575
- * value to join the main query Table to others, by Row Id.
575
+ * value to join the main query Table to other Tables, by their Row Id.
576
576
  *
577
- * The Join function is provided as an parameter in the `build` parameter of the
577
+ * The Join function is provided to the third `query` parameter of the
578
578
  * setQueryDefinition method.
579
579
  *
580
580
  * You can join zero, one, or many Tables. You can join the same underlying
@@ -856,8 +856,8 @@ export type JoinedAs = {as: (joinedTableId: Id) => void};
856
856
  * The Where type describes a function that lets you specify conditions to
857
857
  * filter results, based on the underlying Cells of the main or joined Tables.
858
858
  *
859
- * The Where function is provided as an parameter in the `build` parameter of
860
- * the setQueryDefinition method.
859
+ * The Where function is provided to the third `query` parameter of the
860
+ * setQueryDefinition method.
861
861
  *
862
862
  * If you do not specify a Where clause, you should expect every non-empty Row
863
863
  * of the main Table to appear in the query's results.
@@ -867,10 +867,10 @@ export type JoinedAs = {as: (joinedTableId: Id) => void};
867
867
  * wish to create an 'or' expression, use the single parameter version of the
868
868
  * type that allows arbitrary programmatic conditions.
869
869
  *
870
- * The Where clause differs from the Having clause in that the former describes
871
- * conditions that should be met by underlying Cell values (whether selected or
872
- * not), and the latter describes conditions based on calculated and aggregated
873
- * values - after Group clauses have been applied.
870
+ * The Where keyword differs from the Having keyword in that the former
871
+ * describes conditions that should be met by underlying Cell values (whether
872
+ * selected or not), and the latter describes conditions based on calculated and
873
+ * aggregated values - after Group clauses have been applied.
874
874
  *
875
875
  * @example
876
876
  * This example shows a query that filters the results from a single Table by
@@ -1006,8 +1006,8 @@ export type Where = {
1006
1006
  * The Group type describes a function that lets you specify that the values of
1007
1007
  * a Cell in multiple result Rows should be aggregated together.
1008
1008
  *
1009
- * The Group function is provided as an parameter in the `build` parameter of
1010
- * the setQueryDefinition method. When called, it should refer to a Cell Id (or
1009
+ * The Group function is provided to the third `query` parameter of the
1010
+ * setQueryDefinition method. When called, it should refer to a Cell Id (or
1011
1011
  * aliased Id) specified in one of the Select functions, and indicate how the
1012
1012
  * values should be aggregated.
1013
1013
  *
@@ -1225,18 +1225,18 @@ export type GroupedAs = {as: (groupedCellId: Id) => void};
1225
1225
  * The Having type describes a function that lets you specify conditions to
1226
1226
  * filter results, based on the grouped Cells resulting from a Group clause.
1227
1227
  *
1228
- * The Having function is provided as an parameter in the `build` parameter of
1229
- * the setQueryDefinition method.
1228
+ * The Having function is provided to the third `query` parameter of the
1229
+ * setQueryDefinition method.
1230
1230
  *
1231
1231
  * A Having condition has to be true for a Row to be included in the results.
1232
1232
  * Each Having class is additive, as though combined with a logical 'and'. If
1233
1233
  * you wish to create an 'or' expression, use the single parameter version of
1234
1234
  * the type that allows arbitrary programmatic conditions.
1235
1235
  *
1236
- * The Where clause differs from the Having clause in that the former describes
1237
- * conditions that should be met by underlying Cell values (whether selected or
1238
- * not), and the latter describes conditions based on calculated and aggregated
1239
- * values - after Group clauses have been applied.
1236
+ * The Where keyword differs from the Having keyword in that the former
1237
+ * describes conditions that should be met by underlying Cell values (whether
1238
+ * selected or not), and the latter describes conditions based on calculated and
1239
+ * aggregated values - after Group clauses have been applied.
1240
1240
  *
1241
1241
  * Whilst it is technically possible to use a Having clause even if the results
1242
1242
  * have not been grouped with a Group clause, you should expect it to be less
@@ -1269,8 +1269,8 @@ export type GroupedAs = {as: (groupedCellId: Id) => void};
1269
1269
  * queries.forEachResultRow('query', (rowId) => {
1270
1270
  * console.log({[rowId]: queries.getResultRow('query', rowId)});
1271
1271
  * });
1272
- * // -> {1: {species: 'cat', minPrice: 3, maxPrice: 4}}
1273
- * // -> {2: {species: 'parrot', minPrice: 3, maxPrice: 3}}
1272
+ * // -> {0: {species: 'cat', minPrice: 3, maxPrice: 4}}
1273
+ * // -> {1: {species: 'parrot', minPrice: 3, maxPrice: 3}}
1274
1274
  * ```
1275
1275
  * @example
1276
1276
  * This example shows a query that filters the results from a grouped Table with
@@ -1421,7 +1421,9 @@ export type Having = {
1421
1421
  * queries.delListener(listenerId);
1422
1422
  * queries.destroy();
1423
1423
  * ```
1424
- * @see Queries guides
1424
+ * @see Making Queries guides
1425
+ * @see Car Analysis demo
1426
+ * @see Movie Database demo
1425
1427
  * @category Queries
1426
1428
  * @since v2.0.0
1427
1429
  */
@@ -1436,11 +1438,11 @@ export interface Queries {
1436
1438
  * The definition must specify this 'main' Table (by its Id) to be aggregated.
1437
1439
  * Other Tables can be joined to that using Join clauses.
1438
1440
  *
1439
- * The third `build` parameter is a callback that you provide to define the
1440
- * query. That callback is provided with a `builders` object that contains the
1441
- * named 'keywords' for the query, like `select`, `join`, and so on. You can
1442
- * see how that is used in the simple example below. The following five
1443
- * clause types are supported:
1441
+ * The third `query` parameter is a callback that you provide to define the
1442
+ * query. That callback is provided with a `keywords` object that contains the
1443
+ * functions you use to define the query, like `select`, `join`, and so on.
1444
+ * You can see how that is used in the simple example below. The following
1445
+ * five clause types are supported:
1444
1446
  *
1445
1447
  * - The Select type describes a function that lets you specify a Cell or
1446
1448
  * calculated value for including into the query's result.
@@ -1456,14 +1458,14 @@ export interface Queries {
1456
1458
  *
1457
1459
  * Full documentation and examples are provided in the sections for each of
1458
1460
  * those clause types.
1459
- *
1461
+ *
1460
1462
  * Additionally, you can use the getResultSortedRowIds method and
1461
1463
  * addResultSortedRowIdsListener method to sort and paginate the results.
1462
1464
  *
1463
1465
  * @param queryId The Id of the query to define.
1464
1466
  * @param tableId The Id of the main Table the query will be based on.
1465
- * @param build A callback which can take a `builders` object and which uses
1466
- the clause functions it contains to define the query.
1467
+ * @param query A callback which can take a `keywords` object and which uses
1468
+ the functions it contains to define the query.
1467
1469
  * @returns A reference to the Queries object.
1468
1470
  * @example
1469
1471
  * This example creates a Store, creates a Queries object, and defines a
@@ -1492,7 +1494,7 @@ export interface Queries {
1492
1494
  setQueryDefinition(
1493
1495
  queryId: Id,
1494
1496
  tableId: Id,
1495
- build: (builders: {
1497
+ query: (keywords: {
1496
1498
  select: Select;
1497
1499
  join: Join;
1498
1500
  where: Where;
@@ -348,10 +348,16 @@ const createQueries = getCreateFunction((store) => {
348
348
  ),
349
349
  );
350
350
  const resetPreStores = (queryId) => {
351
- mapForEach(mapGet(preStoreListenerIds, queryId), (preStore2, listenerIds) =>
352
- collForEach(listenerIds, (listenerId) =>
353
- preStore2.delListener(listenerId),
354
- ),
351
+ ifNotUndefined(
352
+ mapGet(preStoreListenerIds, queryId),
353
+ (queryPreStoreListenerIds) => {
354
+ mapForEach(queryPreStoreListenerIds, (preStore2, listenerIds) =>
355
+ collForEach(listenerIds, (listenerId) =>
356
+ preStore2.delListener(listenerId),
357
+ ),
358
+ );
359
+ collClear(queryPreStoreListenerIds);
360
+ },
355
361
  );
356
362
  arrayForEach([resultStore, preStore], (store2) => store2.delTable(queryId));
357
363
  };
@@ -511,12 +517,18 @@ const createQueries = getCreateFunction((store) => {
511
517
  }
512
518
  },
513
519
  );
514
- (collIsEmpty(selectedRowIds) ||
520
+ if (
521
+ collIsEmpty(selectedRowIds) ||
515
522
  !arrayEvery(havings, (having2) =>
516
523
  having2((cellId) => groupRow[cellId]),
517
524
  )
518
- ? resultStore.delRow
519
- : resultStore.setRow)(queryId, groupRowId, groupRow);
525
+ ) {
526
+ resultStore.delRow(queryId, groupRowId);
527
+ } else if (isUndefined(groupRowId)) {
528
+ leaf[2] = resultStore.addRow(queryId, groupRow);
529
+ } else {
530
+ resultStore.setRow(queryId, groupRowId, groupRow);
531
+ }
520
532
  },
521
533
  );
522
534
  addPreStoreListener(
@@ -556,18 +568,10 @@ const createQueries = getCreateFunction((store) => {
556
568
  });
557
569
  if (changedLeaf) {
558
570
  writeGroupRow(
559
- visitTree(
560
- tree,
561
- oldPath,
562
- void 0,
563
- ([, selectedRowIds, groupRowId]) => {
564
- collDel(selectedRowIds, selectedRowId);
565
- if (collIsEmpty(selectedRowIds)) {
566
- resultStore.delRow(queryId, groupRowId);
567
- return 1;
568
- }
569
- },
570
- ),
571
+ visitTree(tree, oldPath, void 0, ([, selectedRowIds]) => {
572
+ collDel(selectedRowIds, selectedRowId);
573
+ return collIsEmpty(selectedRowIds);
574
+ }),
571
575
  changedGroupedSelectedCells,
572
576
  selectedRowId,
573
577
  1,
@@ -590,12 +594,7 @@ const createQueries = getCreateFunction((store) => {
590
594
  selectedCellId,
591
595
  )),
592
596
  );
593
- return [
594
- mapNew(),
595
- setNew(),
596
- resultStore.addRow(queryId, groupRow, 1),
597
- groupRow,
598
- ];
597
+ return [mapNew(), setNew(), void 0, groupRow];
599
598
  },
600
599
  ([, selectedRowIds]) => {
601
600
  setAdd(selectedRowIds, selectedRowId);
@@ -7,7 +7,7 @@ const arrayLength = (array) => array.length;
7
7
  const arrayIsEmpty = (array) => arrayLength(array) == 0;
8
8
  const arrayReduce = (array, cb, initial) => array.reduce(cb, initial);
9
9
  const arrayPush = (array, ...values) => array.push(...values);
10
- const arrayPop = (array) => array.pop();
10
+ const arrayShift = (array) => array.shift();
11
11
 
12
12
  const isUndefined = (thing) => thing == void 0;
13
13
  const ifNotUndefined = (value, then, otherwise) =>
@@ -215,6 +215,20 @@ const getCreateFunction = (getFunction) => {
215
215
  };
216
216
  };
217
217
 
218
+ const INTEGER = /^\d+$/;
219
+ const getPoolFunctions = () => {
220
+ const pool = [];
221
+ let nextId = 0;
222
+ return [
223
+ () => arrayShift(pool) ?? EMPTY_STRING + nextId++,
224
+ (id) => {
225
+ if (INTEGER.test(id) && arrayLength(pool) < 1e3) {
226
+ arrayPush(pool, id);
227
+ }
228
+ },
229
+ ];
230
+ };
231
+
218
232
  const getWildcardedLeaves = (deepIdSet, path = [EMPTY_STRING]) => {
219
233
  const leaves = [];
220
234
  const deep = (node, p) =>
@@ -228,12 +242,11 @@ const getWildcardedLeaves = (deepIdSet, path = [EMPTY_STRING]) => {
228
242
  };
229
243
  const getListenerFunctions = (getThing) => {
230
244
  let thing;
231
- let nextId = 0;
232
- const listenerPool = [];
245
+ const [getId, releaseId] = getPoolFunctions();
233
246
  const allListeners = mapNew();
234
247
  const addListener = (listener, idSetNode, path) => {
235
248
  thing ??= getThing();
236
- const id = arrayPop(listenerPool) ?? EMPTY_STRING + nextId++;
249
+ const id = getId();
237
250
  mapSet(allListeners, id, [listener, idSetNode, path]);
238
251
  setAdd(visitTree(idSetNode, path ?? [EMPTY_STRING], setNew), id);
239
252
  return id;
@@ -251,9 +264,7 @@ const getListenerFunctions = (getThing) => {
251
264
  return collIsEmpty(idSet) ? 1 : 0;
252
265
  });
253
266
  mapSet(allListeners, id);
254
- if (arrayLength(listenerPool) < 1e3) {
255
- arrayPush(listenerPool, id);
256
- }
267
+ releaseId(id);
257
268
  return idOrNulls;
258
269
  });
259
270
  const callListener = (id, idNullGetters, extraArgsGetter) =>
@@ -31,7 +31,7 @@ const arrayReduce = (array, cb, initial) => array.reduce(cb, initial);
31
31
  const arrayFilter = (array, cb) => array.filter(cb);
32
32
  const arraySlice = (array, start, end) => array.slice(start, end);
33
33
  const arrayPush = (array, ...values) => array.push(...values);
34
- const arrayPop = (array) => array.pop();
34
+ const arrayShift = (array) => array.shift();
35
35
 
36
36
  const jsonString = (obj) =>
37
37
  JSON.stringify(obj, (_key, value) =>
@@ -136,6 +136,20 @@ const objIsEmpty = (obj) => arrayIsEmpty(objIds(obj));
136
136
  const setNew = (entries) => new Set(entries);
137
137
  const setAdd = (set, value) => set?.add(value);
138
138
 
139
+ const INTEGER = /^\d+$/;
140
+ const getPoolFunctions = () => {
141
+ const pool = [];
142
+ let nextId = 0;
143
+ return [
144
+ () => arrayShift(pool) ?? EMPTY_STRING + nextId++,
145
+ (id) => {
146
+ if (INTEGER.test(id) && arrayLength(pool) < 1e3) {
147
+ arrayPush(pool, id);
148
+ }
149
+ },
150
+ ];
151
+ };
152
+
139
153
  const getWildcardedLeaves = (deepIdSet, path = [EMPTY_STRING]) => {
140
154
  const leaves = [];
141
155
  const deep = (node, p) =>
@@ -149,12 +163,11 @@ const getWildcardedLeaves = (deepIdSet, path = [EMPTY_STRING]) => {
149
163
  };
150
164
  const getListenerFunctions = (getThing) => {
151
165
  let thing;
152
- let nextId = 0;
153
- const listenerPool = [];
166
+ const [getId, releaseId] = getPoolFunctions();
154
167
  const allListeners = mapNew();
155
168
  const addListener = (listener, idSetNode, path) => {
156
169
  thing ??= getThing();
157
- const id = arrayPop(listenerPool) ?? EMPTY_STRING + nextId++;
170
+ const id = getId();
158
171
  mapSet(allListeners, id, [listener, idSetNode, path]);
159
172
  setAdd(visitTree(idSetNode, path ?? [EMPTY_STRING], setNew), id);
160
173
  return id;
@@ -172,9 +185,7 @@ const getListenerFunctions = (getThing) => {
172
185
  return collIsEmpty(idSet) ? 1 : 0;
173
186
  });
174
187
  mapSet(allListeners, id);
175
- if (arrayLength(listenerPool) < 1e3) {
176
- arrayPush(listenerPool, id);
177
- }
188
+ releaseId(id);
178
189
  return idOrNulls;
179
190
  });
180
191
  const callListener = (id, idNullGetters, extraArgsGetter) =>
@@ -240,7 +251,6 @@ const idsChanged = (changedIds, id2, added) =>
240
251
  const createStore = () => {
241
252
  let hasSchema;
242
253
  let cellsTouched;
243
- let nextRowId = 0;
244
254
  let transactions = 0;
245
255
  const changedTableIds = mapNew();
246
256
  const changedRowIds = mapNew();
@@ -249,6 +259,7 @@ const createStore = () => {
249
259
  const invalidCells = mapNew();
250
260
  const schemaMap = mapNew();
251
261
  const schemaRowCache = mapNew();
262
+ const tablePoolFunctions = mapNew();
252
263
  const tablesMap = mapNew();
253
264
  const tablesListeners = pairNewMap();
254
265
  const tableIdsListeners = pairNewMap();
@@ -411,18 +422,26 @@ const createStore = () => {
411
422
  addDefaultsToRow({[cellId]: validCell}, tableId, rowId),
412
423
  ),
413
424
  );
414
- const getNewRowId = (tableMap) => {
415
- const rowId = EMPTY_STRING + nextRowId++;
416
- if (!collHas(tableMap, rowId)) {
425
+ const getNewRowId = (tableId) => {
426
+ const [getId] = mapEnsure(tablePoolFunctions, tableId, getPoolFunctions);
427
+ const rowId = getId();
428
+ if (!collHas(mapGet(tablesMap, tableId), rowId)) {
417
429
  return rowId;
418
430
  }
419
- return getNewRowId(tableMap);
431
+ return getNewRowId(tableId);
420
432
  };
421
433
  const getOrCreateTable = (tableId) =>
422
434
  mapGet(tablesMap, tableId) ?? setValidTable(tableId, {});
423
435
  const delValidTable = (tableId) => setValidTable(tableId, {});
424
- const delValidRow = (tableId, tableMap, rowId) =>
436
+ const delValidRow = (tableId, tableMap, rowId) => {
437
+ const [, releaseId] = mapEnsure(
438
+ tablePoolFunctions,
439
+ tableId,
440
+ getPoolFunctions,
441
+ );
442
+ releaseId(rowId);
425
443
  setValidRow(tableId, tableMap, rowId, {}, true);
444
+ };
426
445
  const delValidCell = (tableId, table, rowId, row, cellId, forceDel) => {
427
446
  const defaultCell = mapGet(mapGet(schemaRowCache, tableId)?.[0], cellId);
428
447
  if (!isUndefined(defaultCell) && !forceDel) {
@@ -668,16 +687,17 @@ const createStore = () => {
668
687
  tableId,
669
688
  rowId,
670
689
  );
671
- const addRow = (tableId, row, forceId) =>
690
+ const addRow = (tableId, row) =>
672
691
  transaction(() => {
673
- tableId = id(tableId);
674
- const isValidRow = validateRow(tableId, void 0, row);
675
- const rowId =
676
- isValidRow || forceId
677
- ? getNewRowId(mapGet(tablesMap, tableId))
678
- : void 0;
679
- if (isValidRow) {
680
- setValidRow(tableId, getOrCreateTable(tableId), rowId, row);
692
+ let rowId = void 0;
693
+ if (validateRow(tableId, rowId, row)) {
694
+ tableId = id(tableId);
695
+ setValidRow(
696
+ tableId,
697
+ getOrCreateTable(tableId),
698
+ (rowId = getNewRowId(tableId)),
699
+ row,
700
+ );
681
701
  }
682
702
  return rowId;
683
703
  });