tinybase 1.0.1 → 1.0.5

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.
@@ -33,6 +33,20 @@ import {Store} from './store.d';
33
33
  */
34
34
  export type CheckpointIds = [Ids, Id | undefined, Ids];
35
35
 
36
+ /**
37
+ * The CheckpointCallback type describes a function that takes a Checkpoint's
38
+ * Id.
39
+ *
40
+ * A CheckpointCallback is provided when using the forEachCheckpoint method,
41
+ * so that you can do something based on every Checkpoint in the Checkpoints
42
+ * object. See that method for specific examples.
43
+ *
44
+ * @param checkpointId The Id of the Checkpoint that the callback can operate
45
+ * on.
46
+ * @category Callback
47
+ */
48
+ export type CheckpointCallback = (checkpointId: Id, label?: string) => void;
49
+
36
50
  /**
37
51
  * The CheckpointIdsListener type describes a function that is used to listen to
38
52
  * changes to the checkpoint Ids in a Checkpoints object.
@@ -353,6 +367,58 @@ export interface Checkpoints {
353
367
  */
354
368
  getCheckpointIds(): CheckpointIds;
355
369
 
370
+ /**
371
+ * The forEachCheckpoint method takes a function that it will then call for
372
+ * each Checkpoint in a specified Checkpoints object.
373
+ *
374
+ * This method is useful for iterating over the structure of the Checkpoints
375
+ * object in a functional style. The `checkpointCallback` parameter is a
376
+ * CheckpointCallback function that will be called with the Id of each
377
+ * Checkpoint.
378
+ *
379
+ * @param checkpointCallback The function that should be called for every
380
+ * Checkpoint.
381
+ * @example
382
+ * This example iterates over each Checkpoint in a Checkpoints object.
383
+ *
384
+ * ```js
385
+ * const store = createStore().setTables({pets: {fido: {sold: false}}});
386
+ * const checkpoints = createCheckpoints(store);
387
+ * store.setCell('pets', 'fido', 'sold', true);
388
+ * checkpoints.addCheckpoint('sale');
389
+ *
390
+ * checkpoints.forEachCheckpoint((checkpointId, label) => {
391
+ * console.log(`${checkpointId}:${label}`);
392
+ * });
393
+ * // -> '0:'
394
+ * // -> '1:sale'
395
+ * ```
396
+ * @category Iterator
397
+ */
398
+ forEachCheckpoint(checkpointCallback: CheckpointCallback): void;
399
+
400
+ /**
401
+ * The hasCheckpoint method returns a boolean indicating whether a given
402
+ * Checkpoint exists in the Checkpoints object.
403
+ *
404
+ * @param checkpointId The Id of a possible Checkpoint in the Checkpoints
405
+ * object.
406
+ * @returns Whether a Checkpoint with that Id exists.
407
+ * @example
408
+ * This example shows two simple Checkpoint existence checks.
409
+ *
410
+ * ```js
411
+ * const store = createStore().setTables({pets: {fido: {sold: false}}});
412
+ * const checkpoints = createCheckpoints(store);
413
+ * console.log(checkpoints.hasCheckpoint('0'));
414
+ * // -> true
415
+ * console.log(checkpoints.hasCheckpoint('1'));
416
+ * // -> false
417
+ * ```
418
+ * @category Getter
419
+ */
420
+ hasCheckpoint(checkpointId: Id): boolean;
421
+
356
422
  /**
357
423
  * The getCheckpoint method fetches the label for a checkpoint, if it had been
358
424
  * provided at the time of the addCheckpoint method or set subsequently with
@@ -1 +1 @@
1
- const e=(e,t)=>e.includes(t),t=(e,t)=>e.forEach(t),n=e=>e.length,s=e=>0==n(e),o=e=>e.slice(1),r=e=>null==e,l=(e,t,n)=>r(e)?n?.():t(e),c=(e,t)=>e?.has(t)??!1,i=e=>r(e)||0==(e=>e.size)(e),d=(e,t)=>e?.forEach(t),a=(e,t)=>e?.delete(t),p=e=>new Map(e),u=(e,t)=>e?.get(t),h=(e,t,n)=>r(n)?(a(e,t),e):e?.set(t,n),g=(e,t,n,s)=>(c(e,t)||(s?.(n),e.set(t,n)),u(e,t)),C=e=>new Set(e),k=(e,t,r)=>n(r)<2?((e,t)=>e?.add(t))(s(r)?e:g(e,r[0],C()),t):k(g(e,r[0],p()),t,o(r)),f=e=>{const n=(r,c,...i)=>l(r,(r=>s(i)?e(r,c):t([i[0],null],(e=>n(u(r,e),c,...o(i))))));return n},L=Object.freeze,w=(e=>{const t=new WeakMap;return n=>(t.has(n)||t.set(n,e(n)),t.get(n))})((o=>{let w,v,S,z=100,E=p(),I=1;const M=C(),b=p(),[j,x,y]=(e=>{let s,o=0;const c=[],i=p();return[(t,n,r=[])=>{s??=e();const l=c.pop()??""+o++;return h(i,l,[t,n,r]),k(n,l,r),l},(e,t=[],...n)=>f(d)(e,(e=>l(u(i,e),(([e])=>e(s,...t,...n)))),...t),e=>l(u(i,e),(([,t,s])=>(f(a)(t,e,...s),h(i,e),n(c)<1e3&&c.push(e),s)),(()=>[])),(e,o,c)=>l(u(i,e),(([e,,l])=>{const i=(...d)=>{const a=n(d);a==n(l)?e(s,...d,...c(d)):r(l[a])?t(o[a](...d),(e=>i(...d,e))):i(...d,l[a])};i()}))]})((()=>Q)),B=p(),F=p(),O=[],T=[],W=(e,t)=>{I=0,o.transaction((()=>d(u(B,t),((t,n)=>d(t,((t,s)=>d(t,((t,l)=>r(t[e])?o.delCell(n,s,l,!0):o.setCell(n,s,l,t[e]))))))))),I=1},m=e=>{h(B,e),h(F,e),x(b,[e])},q=(e,s)=>t(((e,t)=>e.splice(0,t))(e,s??n(e)),m),A=()=>q(O,n(O)-z),D=o.addCellListener(null,null,null,((e,t,n,s,o,r)=>{if(I){l(w,(()=>{O.push(w),A(),q(T),w=void 0,S=1}));const e=g(E,t,p()),c=g(e,n,p()),d=g(c,s,[void 0,void 0],(e=>e[0]=r));d[1]=o,d[0]===d[1]&&i(h(c,s))&&i(h(e,n))&&i(h(E,t))&&(w=O.pop(),S=1),K()}})),G=(e="")=>(r(w)&&(w=""+v++,h(B,w,E),P(w,e),E=p(),S=1),w),H=()=>{s(O)||(T.unshift(G()),W(0,w),w=O.pop(),S=1)},J=()=>{s(T)||(O.push(w),w=T.shift(),W(1,w),S=1)},K=()=>{S&&(x(M),S=0)},N=e=>{const t=G(e);return K(),t},P=(e,t)=>(c(B,e)&&u(F,e)!==t&&(h(F,e,t),x(b,[e])),Q),Q={setSize:e=>(z=e,A(),Q),addCheckpoint:N,setCheckpoint:P,getStore:()=>o,getCheckpointIds:()=>[[...O],w,[...T]],getCheckpoint:e=>u(F,e),goBackward:()=>(H(),K(),Q),goForward:()=>(J(),K(),Q),goTo:t=>{const n=e(O,t)?H:e(T,t)?J:null;for(;!r(n)&&t!=w;)n();return K(),Q},addCheckpointIdsListener:e=>j(e,M),addCheckpointListener:(e,t)=>j(t,b,[e]),delListener:e=>(y(e),Q),clear:()=>(q(O),q(T),r(w)||m(w),w=void 0,v=0,N(),Q),destroy:()=>{o.delListener(D)},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,s=e=>0==n(e),o=e=>e.slice(1),r=e=>null==e,c=(e,t,n)=>r(e)?n?.():t(e),l=(e,t)=>e?.has(t)??!1,i=e=>r(e)||0==(e=>e.size)(e),d=(e,t)=>e?.forEach(t),a=(e,t)=>e?.delete(t),p=e=>new Map(e),h=(e,t)=>e?.get(t),u=(e,t,n)=>r(n)?(a(e,t),e):e?.set(t,n),C=(e,t,n,s)=>(l(e,t)||(s?.(n),e.set(t,n)),h(e,t)),g=e=>new Set(e),k=(e,t,r)=>n(r)<2?((e,t)=>e?.add(t))(s(r)?e:C(e,r[0],g()),t):k(C(e,r[0],p()),t,o(r)),f=e=>{const n=(r,l,...i)=>c(r,(r=>s(i)?e(r,l):t([i[0],null],(e=>n(h(r,e),l,...o(i))))));return n},L=Object.freeze,v=(e=>{const t=new WeakMap;return n=>(t.has(n)||t.set(n,e(n)),t.get(n))})((o=>{let v,w,S,z=100,E=p(),I=1;const M=g(),b=p(),[j,x,y]=(e=>{let s,o=0;const l=[],i=p();return[(t,n,r=[])=>{s??=e();const c=l.pop()??""+o++;return u(i,c,[t,n,r]),k(n,c,r),c},(e,t=[],...n)=>f(d)(e,(e=>c(h(i,e),(([e])=>e(s,...t,...n)))),...t),e=>c(h(i,e),(([,t,s])=>(f(a)(t,e,...s),u(i,e),n(l)<1e3&&l.push(e),s)),(()=>[])),(e,o,l)=>c(h(i,e),(([e,,c])=>{const i=(...d)=>{const a=n(d);a==n(c)?e(s,...d,...l(d)):r(c[a])?t(o[a](...d),(e=>i(...d,e))):i(...d,c[a])};i()}))]})((()=>R)),B=p(),F=p(),O=[],T=[],W=(e,t)=>{I=0,o.transaction((()=>d(h(B,t),((t,n)=>d(t,((t,s)=>d(t,((t,c)=>r(t[e])?o.delCell(n,s,c,!0):o.setCell(n,s,c,t[e]))))))))),I=1},m=e=>{u(B,e),u(F,e),x(b,[e])},q=(e,s)=>t(((e,t)=>e.splice(0,t))(e,s??n(e)),m),A=()=>q(O,n(O)-z),D=o.addCellListener(null,null,null,((e,t,n,s,o,r)=>{if(I){c(v,(()=>{O.push(v),A(),q(T),v=void 0,S=1}));const e=C(E,t,p()),l=C(e,n,p()),d=C(l,s,[void 0,void 0],(e=>e[0]=r));d[1]=o,d[0]===d[1]&&i(u(l,s))&&i(u(e,n))&&i(u(E,t))&&(v=O.pop(),S=1),K()}})),G=(e="")=>(r(v)&&(v=""+w++,u(B,v,E),P(v,e),E=p(),S=1),v),H=()=>{s(O)||(T.unshift(G()),W(0,v),v=O.pop(),S=1)},J=()=>{s(T)||(O.push(v),v=T.shift(),W(1,v),S=1)},K=()=>{S&&(x(M),S=0)},N=e=>{const t=G(e);return K(),t},P=(e,t)=>(Q(e)&&h(F,e)!==t&&(u(F,e,t),x(b,[e])),R),Q=e=>l(B,e),R={setSize:e=>(z=e,A(),R),addCheckpoint:N,setCheckpoint:P,getStore:()=>o,getCheckpointIds:()=>[[...O],v,[...T]],forEachCheckpoint:e=>{return t=e,d(F,((e,n)=>t(n,e)));var t},hasCheckpoint:Q,getCheckpoint:e=>h(F,e),goBackward:()=>(H(),K(),R),goForward:()=>(J(),K(),R),goTo:t=>{const n=e(O,t)?H:e(T,t)?J:null;for(;!r(n)&&t!=v;)n();return K(),R},addCheckpointIdsListener:e=>j(e,M),addCheckpointListener:(e,t)=>j(t,b,[e]),delListener:e=>(y(e),R),clear:()=>(q(O),q(T),r(v)||m(v),v=void 0,w=0,N(),R),destroy:()=>{o.delListener(D)},getListenerStats:()=>({})};return L(R.clear())}));export{v as createCheckpoints};
Binary file
@@ -33,6 +33,20 @@ import {Store} from './store.d';
33
33
  */
34
34
  export type CheckpointIds = [Ids, Id | undefined, Ids];
35
35
 
36
+ /**
37
+ * The CheckpointCallback type describes a function that takes a Checkpoint's
38
+ * Id.
39
+ *
40
+ * A CheckpointCallback is provided when using the forEachCheckpoint method,
41
+ * so that you can do something based on every Checkpoint in the Checkpoints
42
+ * object. See that method for specific examples.
43
+ *
44
+ * @param checkpointId The Id of the Checkpoint that the callback can operate
45
+ * on.
46
+ * @category Callback
47
+ */
48
+ export type CheckpointCallback = (checkpointId: Id, label?: string) => void;
49
+
36
50
  /**
37
51
  * The CheckpointIdsListener type describes a function that is used to listen to
38
52
  * changes to the checkpoint Ids in a Checkpoints object.
@@ -353,6 +367,58 @@ export interface Checkpoints {
353
367
  */
354
368
  getCheckpointIds(): CheckpointIds;
355
369
 
370
+ /**
371
+ * The forEachCheckpoint method takes a function that it will then call for
372
+ * each Checkpoint in a specified Checkpoints object.
373
+ *
374
+ * This method is useful for iterating over the structure of the Checkpoints
375
+ * object in a functional style. The `checkpointCallback` parameter is a
376
+ * CheckpointCallback function that will be called with the Id of each
377
+ * Checkpoint.
378
+ *
379
+ * @param checkpointCallback The function that should be called for every
380
+ * Checkpoint.
381
+ * @example
382
+ * This example iterates over each Checkpoint in a Checkpoints object.
383
+ *
384
+ * ```js
385
+ * const store = createStore().setTables({pets: {fido: {sold: false}}});
386
+ * const checkpoints = createCheckpoints(store);
387
+ * store.setCell('pets', 'fido', 'sold', true);
388
+ * checkpoints.addCheckpoint('sale');
389
+ *
390
+ * checkpoints.forEachCheckpoint((checkpointId, label) => {
391
+ * console.log(`${checkpointId}:${label}`);
392
+ * });
393
+ * // -> '0:'
394
+ * // -> '1:sale'
395
+ * ```
396
+ * @category Iterator
397
+ */
398
+ forEachCheckpoint(checkpointCallback: CheckpointCallback): void;
399
+
400
+ /**
401
+ * The hasCheckpoint method returns a boolean indicating whether a given
402
+ * Checkpoint exists in the Checkpoints object.
403
+ *
404
+ * @param checkpointId The Id of a possible Checkpoint in the Checkpoints
405
+ * object.
406
+ * @returns Whether a Checkpoint with that Id exists.
407
+ * @example
408
+ * This example shows two simple Checkpoint existence checks.
409
+ *
410
+ * ```js
411
+ * const store = createStore().setTables({pets: {fido: {sold: false}}});
412
+ * const checkpoints = createCheckpoints(store);
413
+ * console.log(checkpoints.hasCheckpoint('0'));
414
+ * // -> true
415
+ * console.log(checkpoints.hasCheckpoint('1'));
416
+ * // -> false
417
+ * ```
418
+ * @category Getter
419
+ */
420
+ hasCheckpoint(checkpointId: Id): boolean;
421
+
356
422
  /**
357
423
  * The getCheckpoint method fetches the label for a checkpoint, if it had been
358
424
  * provided at the time of the addCheckpoint method or set subsequently with
@@ -22,6 +22,8 @@ const collDel = (coll, keyOrValue) => coll?.delete(keyOrValue);
22
22
 
23
23
  const mapNew = (entries) => new Map(entries);
24
24
  const mapGet = (map, key) => map?.get(key);
25
+ const mapForEach = (map, cb) =>
26
+ collForEach(map, (value, key) => cb(key, value));
25
27
  const mapSet = (map, key, value) =>
26
28
  isUndefined(value) ? (collDel(map, key), map) : map?.set(key, value);
27
29
  const mapEnsure = (map, key, defaultValue, onWillAdd) => {
@@ -243,10 +245,7 @@ const createCheckpoints = getCreateFunction((store) => {
243
245
  return id;
244
246
  };
245
247
  const setCheckpoint = (checkpointId, label) => {
246
- if (
247
- collHas(deltas, checkpointId) &&
248
- mapGet(labels, checkpointId) !== label
249
- ) {
248
+ if (hasCheckpoint(checkpointId) && mapGet(labels, checkpointId) !== label) {
250
249
  mapSet(labels, checkpointId, label);
251
250
  callListeners(checkpointListeners, [checkpointId]);
252
251
  }
@@ -254,6 +253,9 @@ const createCheckpoints = getCreateFunction((store) => {
254
253
  };
255
254
  const getStore = () => store;
256
255
  const getCheckpointIds = () => [[...backwardIds], currentId, [...forwardIds]];
256
+ const forEachCheckpoint = (checkpointCallback) =>
257
+ mapForEach(labels, checkpointCallback);
258
+ const hasCheckpoint = (checkpointId) => collHas(deltas, checkpointId);
257
259
  const getCheckpoint = (checkpointId) => mapGet(labels, checkpointId);
258
260
  const goBackward = () => {
259
261
  goBackwardImpl();
@@ -309,6 +311,8 @@ const createCheckpoints = getCreateFunction((store) => {
309
311
  setCheckpoint,
310
312
  getStore,
311
313
  getCheckpointIds,
314
+ forEachCheckpoint,
315
+ hasCheckpoint,
312
316
  getCheckpoint,
313
317
  goBackward,
314
318
  goForward,
@@ -11,7 +11,7 @@
11
11
  * @module indexes
12
12
  */
13
13
 
14
- import {GetCell, Store} from './store.d';
14
+ import {GetCell, RowCallback, Store} from './store.d';
15
15
  import {Id, IdOrNull, Ids, SortKey} from './common.d';
16
16
 
17
17
  /**
@@ -43,6 +43,42 @@ export type Index = {[sliceId: Id]: Slice};
43
43
  */
44
44
  export type Slice = Ids;
45
45
 
46
+ /**
47
+ * The IndexCallback type describes a function that takes an Index's Id and a
48
+ * callback to loop over each Slice within it.
49
+ *
50
+ * A IndexCallback is provided when using the forEachIndex method, so that you
51
+ * can do something based on every Index in the Indexes object. See that method
52
+ * for specific examples.
53
+ *
54
+ * @param indexId The Id of the Index that the callback can operate on.
55
+ * @param forEachRow A function that will let you iterate over the Slice objects
56
+ * in this Index.
57
+ * @category Callback
58
+ */
59
+ export type IndexCallback = (
60
+ indexId: Id,
61
+ forEachSlice: (sliceCallback: SliceCallback) => void,
62
+ ) => void;
63
+
64
+ /**
65
+ * The SliceCallback type describes a function that takes a Slice's Id and a
66
+ * callback to loop over each Row within it.
67
+ *
68
+ * A SliceCallback is provided when using the forEachSlice method, so that you
69
+ * can do something based on every Slice in an Index. See that method for
70
+ * specific examples.
71
+ *
72
+ * @param sliceId The Id of the Slice that the callback can operate on.
73
+ * @param forEachRow A function that will let you iterate over the Row objects
74
+ * in this Slice.
75
+ * @category Callback
76
+ */
77
+ export type SliceCallback = (
78
+ sliceId: Id,
79
+ forEachRow: (rowCallback: RowCallback) => void,
80
+ ) => void;
81
+
46
82
  /**
47
83
  * The SliceIdsListener type describes a function that is used to listen to
48
84
  * changes to the Slice Ids in an Index.
@@ -362,6 +398,131 @@ export interface Indexes {
362
398
  */
363
399
  getIndexIds(): Ids;
364
400
 
401
+ /**
402
+ * The forEachIndex method takes a function that it will then call for each
403
+ * Index in a specified Indexes object.
404
+ *
405
+ * This method is useful for iterating over the structure of the Indexes
406
+ * object in a functional style. The `indexCallback` parameter is a
407
+ * IndexCallback function that will be called with the Id of each Index, and
408
+ * with a function that can then be used to iterate over each Slice of the
409
+ * Index, should you wish.
410
+ *
411
+ * @param indexCallback The function that should be called for every Index.
412
+ * @example
413
+ * This example iterates over each Index in an Indexes object, and lists each
414
+ * Slice Id within them.
415
+ *
416
+ * ```js
417
+ * const store = createStore().setTable('pets', {
418
+ * fido: {species: 'dog', color: 'brown'},
419
+ * felix: {species: 'cat', color: 'black'},
420
+ * cujo: {species: 'dog', color: 'black'},
421
+ * });
422
+ * const indexes = createIndexes(store)
423
+ * .setIndexDefinition('bySpecies', 'pets', 'species')
424
+ * .setIndexDefinition('byColor', 'pets', 'color');
425
+ *
426
+ * indexes.forEachIndex((indexId, forEachSlice) => {
427
+ * console.log(indexId);
428
+ * forEachSlice((sliceId) => console.log(`- ${sliceId}`));
429
+ * });
430
+ * // -> 'bySpecies'
431
+ * // -> '- dog'
432
+ * // -> '- cat'
433
+ * // -> 'byColor'
434
+ * // -> '- brown'
435
+ * // -> '- black'
436
+ * ```
437
+ * @category Iterator
438
+ */
439
+ forEachIndex(indexCallback: IndexCallback): void;
440
+
441
+ /**
442
+ * The forEachSlice method takes a function that it will then call for each
443
+ * Slice in a specified Index.
444
+ *
445
+ * This method is useful for iterating over the Slice structure of the Index
446
+ * in a functional style. The `rowCallback` parameter is a RowCallback
447
+ * function that will be called with the Id and value of each Row in the
448
+ * Slice.
449
+ *
450
+ * @param indexId The Id of the Index to iterate over.
451
+ * @param sliceCallback The function that should be called for every Slice.
452
+ * @example
453
+ * This example iterates over each Row in a Slice, and lists its Id.
454
+ *
455
+ * ```js
456
+ * const store = createStore().setTable('pets', {
457
+ * fido: {species: 'dog'},
458
+ * felix: {species: 'cat'},
459
+ * cujo: {species: 'dog'},
460
+ * });
461
+ * const indexes = createIndexes(store);
462
+ * indexes.setIndexDefinition('bySpecies', 'pets', 'species');
463
+ *
464
+ * indexes.forEachSlice('bySpecies', (sliceId, forEachRow) => {
465
+ * console.log(sliceId);
466
+ * forEachRow((rowId) => console.log(`- ${rowId}`));
467
+ * });
468
+ * // -> 'dog'
469
+ * // -> '- fido'
470
+ * // -> '- cujo'
471
+ * // -> 'cat'
472
+ * // -> '- felix'
473
+ * ```
474
+ * @category Iterator
475
+ */
476
+ forEachSlice(indexId: Id, sliceCallback: SliceCallback): void;
477
+
478
+ /**
479
+ * The hasIndex method returns a boolean indicating whether a given Index
480
+ * exists in the Indexes object.
481
+ *
482
+ * @param indexId The Id of a possible Index in the Indexes object.
483
+ * @returns Whether an Index with that Id exists.
484
+ * @example
485
+ * This example shows two simple Index existence checks.
486
+ *
487
+ * ```js
488
+ * const indexes = createIndexes(createStore());
489
+ * indexes.setIndexDefinition('bySpecies', 'pets', 'species');
490
+ * console.log(indexes.hasIndex('bySpecies'));
491
+ * // -> true
492
+ * console.log(indexes.hasIndex('byColor'));
493
+ * // -> false
494
+ * ```
495
+ * @category Getter
496
+ */
497
+ hasIndex(indexId: Id): boolean;
498
+
499
+ /**
500
+ * The hasSlice method returns a boolean indicating whether a given Slice
501
+ * exists in the Indexes object.
502
+ *
503
+ * @param indexId The Id of a possible Index in the Indexes object.
504
+ * @param sliceId The Id of a possible Slice in the Index.
505
+ * @returns Whether a Slice with that Id exists.
506
+ * @example
507
+ * This example shows two simple Index existence checks.
508
+ *
509
+ * ```js
510
+ * const store = createStore().setTable('pets', {
511
+ * fido: {species: 'dog'},
512
+ * felix: {species: 'cat'},
513
+ * cujo: {species: 'dog'},
514
+ * });
515
+ * const indexes = createIndexes(store);
516
+ * indexes.setIndexDefinition('bySpecies', 'pets', 'species');
517
+ * console.log(indexes.hasSlice('bySpecies', 'dog'));
518
+ * // -> true
519
+ * console.log(indexes.hasSlice('bySpecies', 'worm'));
520
+ * // -> false
521
+ * ```
522
+ * @category Getter
523
+ */
524
+ hasSlice(indexId: Id, sliceId: Id): boolean;
525
+
365
526
  /**
366
527
  * The getTableId method returns the Id of the underlying Table that is
367
528
  * backing an Index.
@@ -473,7 +634,7 @@ export interface Indexes {
473
634
  * the Index change.
474
635
  * @returns A unique Id for the listener that can later be used to remove it.
475
636
  * @example
476
- * This example creates a Store, a Indexes object, and then registers a
637
+ * This example creates a Store, an Indexes object, and then registers a
477
638
  * listener that responds to any changes to a specific Index.
478
639
  *
479
640
  * ```js
@@ -501,7 +662,7 @@ export interface Indexes {
501
662
  * indexes.delListener(listenerId);
502
663
  * ```
503
664
  * @example
504
- * This example creates a Store, a Indexes object, and then registers a
665
+ * This example creates a Store, an Indexes object, and then registers a
505
666
  * listener that responds to any changes to any Index.
506
667
  *
507
668
  * ```js
@@ -558,7 +719,7 @@ export interface Indexes {
558
719
  * the Slice change.
559
720
  * @returns A unique Id for the listener that can later be used to remove it.
560
721
  * @example
561
- * This example creates a Store, a Indexes object, and then registers a
722
+ * This example creates a Store, an Indexes object, and then registers a
562
723
  * listener that responds to any changes to a specific Slice.
563
724
  *
564
725
  * ```js
@@ -587,7 +748,7 @@ export interface Indexes {
587
748
  * indexes.delListener(listenerId);
588
749
  * ```
589
750
  * @example
590
- * This example creates a Store, a Indexes object, and then registers a
751
+ * This example creates a Store, an Indexes object, and then registers a
591
752
  * listener that responds to any changes to any Slice.
592
753
  *
593
754
  * ```js
@@ -639,7 +800,7 @@ export interface Indexes {
639
800
  * @param listenerId The Id of the listener to remove.
640
801
  * @returns A reference to the Indexes object.
641
802
  * @example
642
- * This example creates a Store, a Indexes object, registers a listener, and
803
+ * This example creates a Store, an Indexes object, registers a listener, and
643
804
  * then removes it.
644
805
  *
645
806
  * ```js
@@ -57,6 +57,8 @@ const getDefinableFunctions = (store, getDefaultThing, validateRowValue) => {
57
57
  const storeListenerIds = mapNew();
58
58
  const getStore = () => store;
59
59
  const getThingIds = () => mapKeys(tableIds);
60
+ const forEachThing = (cb) => mapForEach(things, cb);
61
+ const hasThing = (id) => collHas(things, id);
60
62
  const getTableId = (id) => mapGet(tableIds, id);
61
63
  const getThing = (id) => mapGet(things, id);
62
64
  const setThing = (id, thing) => mapSet(things, id, thing);
@@ -146,6 +148,8 @@ const getDefinableFunctions = (store, getDefaultThing, validateRowValue) => {
146
148
  return [
147
149
  getStore,
148
150
  getThingIds,
151
+ forEachThing,
152
+ hasThing,
149
153
  getTableId,
150
154
  getThing,
151
155
  setThing,
@@ -252,6 +256,8 @@ const createIndexes = getCreateFunction((store) => {
252
256
  const [
253
257
  getStore,
254
258
  getIndexIds,
259
+ forEachIndexImpl,
260
+ hasIndex,
255
261
  getTableId,
256
262
  getIndex,
257
263
  setIndex,
@@ -264,6 +270,7 @@ const createIndexes = getCreateFunction((store) => {
264
270
  const [addListener, callListeners, delListenerImpl] = getListenerFunctions(
265
271
  () => indexes,
266
272
  );
273
+ const hasSlice = (indexId, sliceId) => collHas(getIndex(indexId), sliceId);
267
274
  const setIndexDefinition = (
268
275
  indexId,
269
276
  tableId,
@@ -359,6 +366,26 @@ const createIndexes = getCreateFunction((store) => {
359
366
  );
360
367
  return indexes;
361
368
  };
369
+ const forEachIndex = (indexCallback) =>
370
+ forEachIndexImpl((indexId, slices) =>
371
+ indexCallback(indexId, (sliceCallback) =>
372
+ forEachSliceImpl(indexId, sliceCallback, slices),
373
+ ),
374
+ );
375
+ const forEachSlice = (indexId, sliceCallback) =>
376
+ forEachSliceImpl(indexId, sliceCallback, getIndex(indexId));
377
+ const forEachSliceImpl = (indexId, sliceCallback, slices) => {
378
+ const tableId = getTableId(indexId);
379
+ collForEach(slices, (rowIds, sliceId) =>
380
+ sliceCallback(sliceId, (rowCallback) =>
381
+ collForEach(rowIds, (rowId) =>
382
+ rowCallback(rowId, (cellCallback) =>
383
+ store.forEachCell(tableId, rowId, cellCallback),
384
+ ),
385
+ ),
386
+ ),
387
+ );
388
+ };
362
389
  const delIndexDefinition = (indexId) => {
363
390
  delDefinition(indexId);
364
391
  return indexes;
@@ -383,6 +410,10 @@ const createIndexes = getCreateFunction((store) => {
383
410
  delIndexDefinition,
384
411
  getStore,
385
412
  getIndexIds,
413
+ forEachIndex,
414
+ forEachSlice,
415
+ hasIndex,
416
+ hasSlice,
386
417
  getTableId,
387
418
  getSliceIds,
388
419
  getSliceRowIds,
@@ -22,6 +22,20 @@ import {Id, IdOrNull, Ids} from './common.d';
22
22
  */
23
23
  export type Metric = number;
24
24
 
25
+ /**
26
+ * The MetricCallback type describes a function that takes a Metric's Id and a
27
+ * callback to loop over each Row within it.
28
+ *
29
+ * A MetricCallback is provided when using the forEachMetric method, so that you
30
+ * can do something based on every Metric in the Metrics object. See that method
31
+ * for specific examples.
32
+ *
33
+ * @param metricId The Id of the Metric that the callback can operate on.
34
+ * @param metric The value of the Metric.
35
+ * @category Callback
36
+ */
37
+ export type MetricCallback = (metricId: Id, metric?: Metric) => void;
38
+
25
39
  /**
26
40
  * The Aggregate type describes a custom function that takes an array or numbers
27
41
  * and returns an aggregate that is used as a Metric.
@@ -478,6 +492,64 @@ export interface Metrics {
478
492
  */
479
493
  getMetricIds(): Ids;
480
494
 
495
+ /**
496
+ * The forEachMetric method takes a function that it will then call for each
497
+ * Metric in the Metrics object.
498
+ *
499
+ * This method is useful for iterating over all the Metrics in a functional
500
+ * style. The `metricCallback` parameter is a MetricCallback function that
501
+ * will be called with the Id of each Metric and its value.
502
+ *
503
+ * @param metricCallback The function that should be called for every Metric.
504
+ * @example
505
+ * This example iterates over each Metric in a Metrics object.
506
+ *
507
+ * ```js
508
+ * const store = createStore().setTable('species', {
509
+ * dog: {price: 5},
510
+ * cat: {price: 4},
511
+ * worm: {price: 1},
512
+ * });
513
+ * const metrics = createMetrics(store)
514
+ * .setMetricDefinition('highestPrice', 'species', 'max', 'price')
515
+ * .setMetricDefinition('lowestPrice', 'species', 'min', 'price');
516
+ *
517
+ * metrics.forEachMetric((metricId, metric) => {
518
+ * console.log([metricId, metric]);
519
+ * });
520
+ * // -> ['highestPrice', 5]
521
+ * // -> ['lowestPrice', 1]
522
+ * ```
523
+ * @category Iterator
524
+ */
525
+ forEachMetric(metricCallback: MetricCallback): void;
526
+
527
+ /**
528
+ * The hasMetric method returns a boolean indicating whether a given Metric
529
+ * exists in the Metrics object, and has a value.
530
+ *
531
+ * @param metricId The Id of a possible Metric in the Metrics object.
532
+ * @returns Whether a Metric with that Id exists.
533
+ * @example
534
+ * This example shows two simple Metric existence checks.
535
+ *
536
+ * ```js
537
+ * const store = createStore();
538
+ * const metrics = createMetrics(store);
539
+ * metrics.setMetricDefinition('highestPrice', 'species', 'max', 'price');
540
+ *
541
+ * console.log(metrics.hasMetric('lowestPrice'));
542
+ * // -> false
543
+ * console.log(metrics.hasMetric('highestPrice'));
544
+ * // -> false
545
+ * store.setTable('species', {dog: {price: 5}, cat: {price: 4}});
546
+ * console.log(metrics.hasMetric('highestPrice'));
547
+ * // -> true
548
+ * ```
549
+ * @category Getter
550
+ */
551
+ hasMetric(metricId: Id): boolean;
552
+
481
553
  /**
482
554
  * The getTableId method returns the Id of the underlying Table that is
483
555
  * backing a Metric.
@@ -62,6 +62,8 @@ const getDefinableFunctions = (store, getDefaultThing, validateRowValue) => {
62
62
  const storeListenerIds = mapNew();
63
63
  const getStore = () => store;
64
64
  const getThingIds = () => mapKeys(tableIds);
65
+ const forEachThing = (cb) => mapForEach(things, cb);
66
+ const hasThing = (id) => collHas(things, id);
65
67
  const getTableId = (id) => mapGet(tableIds, id);
66
68
  const getThing = (id) => mapGet(things, id);
67
69
  const setThing = (id, thing) => mapSet(things, id, thing);
@@ -151,6 +153,8 @@ const getDefinableFunctions = (store, getDefaultThing, validateRowValue) => {
151
153
  return [
152
154
  getStore,
153
155
  getThingIds,
156
+ forEachThing,
157
+ hasThing,
154
158
  getTableId,
155
159
  getThing,
156
160
  setThing,
@@ -294,6 +298,8 @@ const createMetrics = getCreateFunction((store) => {
294
298
  const [
295
299
  getStore,
296
300
  getMetricIds,
301
+ forEachMetric,
302
+ hasMetric,
297
303
  getTableId,
298
304
  getMetric,
299
305
  setMetric,
@@ -378,6 +384,8 @@ const createMetrics = getCreateFunction((store) => {
378
384
  delMetricDefinition,
379
385
  getStore,
380
386
  getMetricIds,
387
+ forEachMetric,
388
+ hasMetric,
381
389
  getTableId,
382
390
  getMetric,
383
391
  addMetricListener,