tinybase 0.0.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/LICENSE +21 -0
  2. package/lib/checkpoints.d.ts +861 -0
  3. package/lib/checkpoints.js +1 -0
  4. package/lib/checkpoints.js.gz +0 -0
  5. package/lib/common.d.ts +59 -0
  6. package/lib/debug/checkpoints.d.ts +861 -0
  7. package/lib/debug/checkpoints.js +326 -0
  8. package/lib/debug/common.d.ts +59 -0
  9. package/lib/debug/indexes.d.ts +815 -0
  10. package/lib/debug/indexes.js +390 -0
  11. package/lib/debug/metrics.d.ts +728 -0
  12. package/lib/debug/metrics.js +391 -0
  13. package/lib/debug/persisters.d.ts +521 -0
  14. package/lib/debug/persisters.js +191 -0
  15. package/lib/debug/react.d.ts +7077 -0
  16. package/lib/debug/react.js +1037 -0
  17. package/lib/debug/relationships.d.ts +1091 -0
  18. package/lib/debug/relationships.js +418 -0
  19. package/lib/debug/store.d.ts +2424 -0
  20. package/lib/debug/store.js +725 -0
  21. package/lib/debug/tinybase.d.ts +14 -0
  22. package/lib/debug/tinybase.js +2727 -0
  23. package/lib/indexes.d.ts +815 -0
  24. package/lib/indexes.js +1 -0
  25. package/lib/indexes.js.gz +0 -0
  26. package/lib/metrics.d.ts +728 -0
  27. package/lib/metrics.js +1 -0
  28. package/lib/metrics.js.gz +0 -0
  29. package/lib/persisters.d.ts +521 -0
  30. package/lib/persisters.js +1 -0
  31. package/lib/persisters.js.gz +0 -0
  32. package/lib/react.d.ts +7077 -0
  33. package/lib/react.js +1 -0
  34. package/lib/react.js.gz +0 -0
  35. package/lib/relationships.d.ts +1091 -0
  36. package/lib/relationships.js +1 -0
  37. package/lib/relationships.js.gz +0 -0
  38. package/lib/store.d.ts +2424 -0
  39. package/lib/store.js +1 -0
  40. package/lib/store.js.gz +0 -0
  41. package/lib/tinybase.d.ts +14 -0
  42. package/lib/tinybase.js +1 -0
  43. package/lib/tinybase.js.gz +0 -0
  44. package/lib/umd/checkpoints.js +1 -0
  45. package/lib/umd/checkpoints.js.gz +0 -0
  46. package/lib/umd/indexes.js +1 -0
  47. package/lib/umd/indexes.js.gz +0 -0
  48. package/lib/umd/metrics.js +1 -0
  49. package/lib/umd/metrics.js.gz +0 -0
  50. package/lib/umd/persisters.js +1 -0
  51. package/lib/umd/persisters.js.gz +0 -0
  52. package/lib/umd/react.js +1 -0
  53. package/lib/umd/react.js.gz +0 -0
  54. package/lib/umd/relationships.js +1 -0
  55. package/lib/umd/relationships.js.gz +0 -0
  56. package/lib/umd/store.js +1 -0
  57. package/lib/umd/store.js.gz +0 -0
  58. package/lib/umd/tinybase.js +1 -0
  59. package/lib/umd/tinybase.js.gz +0 -0
  60. package/package.json +93 -2
  61. package/readme.md +195 -0
@@ -0,0 +1,728 @@
1
+ /**
2
+ * The metrics module of the TinyBase project provides the ability to create
3
+ * and track metrics and aggregates of the data in Store objects.
4
+ *
5
+ * The main entry point to this module is the createMetrics function, which
6
+ * returns a new Metrics object. From there, you can create new metric
7
+ * definitions, access the values of those metrics directly, and register
8
+ * listeners for when they change.
9
+ *
10
+ * @packageDocumentation
11
+ * @module metrics
12
+ */
13
+
14
+ import {GetCell, Store} from './store.d';
15
+ import {Id, IdOrNull, Ids} from './common.d';
16
+
17
+ /**
18
+ * The Metric type is simply an alias, but represents a number formed by
19
+ * aggregating multiple other numbers together.
20
+ *
21
+ * @category Metric
22
+ */
23
+ export type Metric = number;
24
+
25
+ /**
26
+ * The Aggregate type describes a custom function that takes an array or numbers
27
+ * and returns an aggregate that is used as a Metric.
28
+ *
29
+ * There are a number of common predefined aggregators, such as for counting,
30
+ * summing, and averaging values. This type is instead used for when you wish to
31
+ * use a more complex aggregation of your own devising. See the
32
+ * setMetricDefinition method for more examples.
33
+ *
34
+ * @category Aggregators
35
+ */
36
+ export type Aggregate = (numbers: number[], length: number) => Metric;
37
+
38
+ /**
39
+ * The AggregateAdd type describes a function that can be used to optimize a
40
+ * custom Aggregate by providing a shortcut for when a single value is added to
41
+ * the input values.
42
+ *
43
+ * Some aggregation functions do not need to recalculate the aggregation of the
44
+ * whole set when one value changes. For example, when adding a new number to a
45
+ * series, the new sum of the series is the new value added to the previous sum.
46
+ *
47
+ * If it is not possible to shortcut the aggregation based on just one value
48
+ * being added, return `undefined` and the Metric will be completely
49
+ * recalculated.
50
+ *
51
+ * Where possible, if you are providing a custom Aggregate, seek an
52
+ * implementation of an AggregateAdd function that can reduce the complexity
53
+ * cost of growing the input data set. See the setMetricDefinition method for
54
+ * more examples.
55
+ *
56
+ * @category Aggregators
57
+ */
58
+ export type AggregateAdd = (
59
+ metric: Metric,
60
+ add: number,
61
+ length: number,
62
+ ) => Metric | undefined;
63
+
64
+ /**
65
+ * The AggregateRemove type describes a function that can be used to optimize a
66
+ * custom Aggregate by providing a shortcut for when a single value is removed
67
+ * from the input values.
68
+ *
69
+ * Some aggregation functions do not need to recalculate the aggregation of the
70
+ * whole set when one value changes. For example, when removing a number from a
71
+ * series, the new sum of the series is the new value subtracted from the
72
+ * previous sum.
73
+ *
74
+ * If it is not possible to shortcut the aggregation based on just one value
75
+ * being removed, return `undefined` and the Metric will be completely
76
+ * recalculated. One example might be if you were taking the minimum of the
77
+ * values, and the previous minimum is being removed. The whole of the rest of
78
+ * the list will need to be re-scanned to find a new minimum.
79
+ *
80
+ * Where possible, if you are providing a custom Aggregate, seek an
81
+ * implementation of an AggregateRemove function that can reduce the complexity
82
+ * cost of shrinking the input data set. See the setMetricDefinition method for
83
+ * more examples.
84
+ *
85
+ * @category Aggregators
86
+ */
87
+ export type AggregateRemove = (
88
+ metric: Metric,
89
+ remove: number,
90
+ length: number,
91
+ ) => Metric | undefined;
92
+
93
+ /**
94
+ * The AggregateReplace type describes a function that can be used to optimize a
95
+ * custom Aggregate by providing a shortcut for when a single value in the input
96
+ * values is replaced with another.
97
+ *
98
+ * Some aggregation functions do not need to recalculate the aggregation of the
99
+ * whole set when one value changes. For example, when replacing a number in a
100
+ * series, the new sum of the series is the previous sum, plus the new value,
101
+ * minus the old value.
102
+ *
103
+ * If it is not possible to shortcut the aggregation based on just one value
104
+ * changing, return `undefined` and the Metric will be completely
105
+ * recalculated.
106
+ *
107
+ * Where possible, if you are providing a custom Aggregate, seek an
108
+ * implementation of an AggregateReplace function that can reduce the complexity
109
+ * cost of changing the input data set in place. See the setMetricDefinition
110
+ * method for more examples.
111
+ *
112
+ * @category Aggregators
113
+ */
114
+ export type AggregateReplace = (
115
+ metric: Metric,
116
+ add: number,
117
+ remove: number,
118
+ length: number,
119
+ ) => Metric | undefined;
120
+
121
+ /**
122
+ * The MetricListener type describes a function that is used to listen to
123
+ * changes to a Metric.
124
+ *
125
+ * A MetricListener is provided when using the addMetricListener method. See
126
+ * that method for specific examples.
127
+ *
128
+ * When called, a MetricListener is given a reference to the Metrics object, the
129
+ * Id of the Metric that changed, and the new and old values of the Metric.
130
+ *
131
+ * If this is the first time that a Metric has had a value (such as when a table
132
+ * has gained its first row), the old value will be `undefined`. If a Metric now
133
+ * no longer has a value, the new value will be `undefined`.
134
+ *
135
+ * @category Listener
136
+ */
137
+ export type MetricListener = (
138
+ metrics: Metrics,
139
+ metricId: Id,
140
+ newMetric: Metric | undefined,
141
+ oldMetric: Metric | undefined,
142
+ ) => void;
143
+
144
+ /**
145
+ * The MetricsListenerStats type describes the number of listeners registered
146
+ * with the Metrics object, and can be used for debugging purposes.
147
+ *
148
+ * A MetricsListenerStats object is returned from the getListenerStats method,
149
+ * and is only populated in a debug build.
150
+ *
151
+ * @category Development
152
+ */
153
+ export type MetricsListenerStats = {
154
+ metric?: number;
155
+ };
156
+
157
+ /**
158
+ * A Metrics object lets you define, query, and listen to, aggregations of Cell
159
+ * values within a Table in a Store.
160
+ *
161
+ * This is useful for counting the number of Row objects in a Table, averaging
162
+ * Cell values, or efficiently performing any arbitrary aggregations.
163
+ *
164
+ * Create a Metrics object easily with the createMetrics function. From there,
165
+ * you can add new Metric definitions (with the setMetricDefinition method),
166
+ * query their values (with the getMetric method), and add listeners for when
167
+ * they change (with the addMetricListener method).
168
+ *
169
+ * This module provides a number of predefined and self-explanatory aggregations
170
+ * ('sum', 'avg', 'min', and 'max'), and defaults to counting Row objects when
171
+ * using the setMetricDefinition method. However, far more complex aggregations
172
+ * can be configured with custom functions.
173
+ *
174
+ * @example
175
+ * This example shows a very simple lifecycle of a Metrics object: from
176
+ * creation, to adding a definition, getting an Metric, and then registering and
177
+ * removing a listener for it.
178
+ *
179
+ * ```tsx
180
+ * const store = createStore().setTable('species', {
181
+ * dog: {price: 5},
182
+ * cat: {price: 4},
183
+ * worm: {price: 1},
184
+ * });
185
+ *
186
+ * const metrics = createMetrics(store);
187
+ * metrics.setMetricDefinition(
188
+ * 'highestPrice', // metricId
189
+ * 'species', // tableId to aggregate
190
+ * 'max', // aggregation
191
+ * 'price', // cellId to aggregate
192
+ * );
193
+ *
194
+ * console.log(metrics.getMetric('highestPrice'));
195
+ * // -> 5
196
+ *
197
+ * const listenerId = metrics.addMetricListener('highestPrice', () => {
198
+ * console.log(metrics.getMetric('highestPrice'));
199
+ * });
200
+ * store.setCell('species', 'horse', 'price', 20);
201
+ * // -> 20
202
+ *
203
+ * metrics.delListener(listenerId);
204
+ * metrics.destroy();
205
+ * ```
206
+ */
207
+ export interface Metrics {
208
+ /**
209
+ * The setMetricDefinition method lets you set the definition of a Metric.
210
+ *
211
+ * Every Metric definition is identified by a unique Id, and if you re-use an
212
+ * existing Id with this method, the previous definition is overwritten.
213
+ *
214
+ * A Metric is an aggregation of numeric values produced from each Row within
215
+ * a single Table. Therefore the definition must specify the Table (by its Id)
216
+ * to be aggregated.
217
+ *
218
+ * Without the third `aggregate` parameter, the Metric will simply be a count
219
+ * of the number of Row objects in the Table. But often you will specify a
220
+ * more interesting aggregate - such as the four predefined aggregates, 'sum',
221
+ * 'avg', 'min', and 'max' - or a custom function that produces your own
222
+ * aggregation of an array of numbers.
223
+ *
224
+ * The fourth `getNumber` parameter specifies which Cell in each Row contains
225
+ * the numerical values to be used in the aggregation. Alternatively, a custom
226
+ * function can be provided that produces your own numeric value from the
227
+ * local Row as a whole.
228
+ *
229
+ * The final three parameters, `aggregateAdd`, `aggregateRemove`,
230
+ * `aggregateReplace` need only be provided when you are using your own custom
231
+ * `aggregate` function. These give you the opportunity to reduce your custom
232
+ * function's algorithmic complexity by providing shortcuts that can nudge an
233
+ * aggregation result when a single value is added, removed, or replaced in
234
+ * the input values.
235
+ *
236
+ * @param metricId The Id of the Metric to define.
237
+ * @param tableId The Id of the Table the Metric will be calculated from.
238
+ * @param aggregate Either a string representing one of a set of common
239
+ * aggregation techniques ('sum', 'avg', 'min', or 'max'), or a function that
240
+ * aggregates numeric values from each Row to create the Metric's overall
241
+ * value. Defaults to 'sum'.
242
+ * @param getNumber Either the Id of a Cell containing, or a function that
243
+ * produces, the numeric value that will be aggregated in the way specified by
244
+ * the `aggregate` parameter. Defaults to a function that returns `1` (meaning
245
+ * that if the `aggregate` and `getNumber` parameters are both omitted, the
246
+ * Metric will simply be a count of the Row objects in the Table).
247
+ * @param aggregateAdd A function that can be used to optimize a custom
248
+ * Aggregate by providing a shortcut for when a single value is added to the
249
+ * input values - for example, when a Row is added to the Table.
250
+ * @param aggregateRemove A function that can be used to optimize a custom
251
+ * Aggregate by providing a shortcut for when a single value is removed from
252
+ * the input values - for example ,when a Row is removed from the Table.
253
+ * @param aggregateReplace A function that can be used to optimize a custom
254
+ * Aggregate by providing a shortcut for when a single value in the input
255
+ * values is replaced with another - for example, when a Row is updated.
256
+ * @returns A reference to the Metrics object.
257
+ * @example
258
+ * This example creates a Store, creates a Metrics object, and defines a
259
+ * simple Metric to count the Row objects in the Table.
260
+ *
261
+ * ```tsx
262
+ * const store = createStore().setTable('species', {
263
+ * dog: {price: 5},
264
+ * cat: {price: 4},
265
+ * worm: {price: 1},
266
+ * });
267
+ *
268
+ * const metrics = createMetrics(store);
269
+ * metrics.setMetricDefinition('speciesCount', 'species');
270
+ *
271
+ * console.log(metrics.getMetric('speciesCount'));
272
+ * // -> 3
273
+ * ```
274
+ * @example
275
+ * This example creates a Store, creates a Metrics object, and defines a
276
+ * standard Metric to get the highest value of each `price` Cell in the Row
277
+ * objects in the Table.
278
+ *
279
+ * ```tsx
280
+ * const store = createStore().setTable('species', {
281
+ * dog: {price: 5},
282
+ * cat: {price: 4},
283
+ * worm: {price: 1},
284
+ * });
285
+ *
286
+ * const metrics = createMetrics(store);
287
+ * metrics.setMetricDefinition('highestPrice', 'species', 'max', 'price');
288
+ *
289
+ * console.log(metrics.getMetric('highestPrice'));
290
+ * // -> 5
291
+ * ```
292
+ * @example
293
+ * This example creates a Store, creates a Metrics object, and defines a
294
+ * custom Metric to get the lowest value of each `price` Cell, greater than 2.
295
+ *
296
+ * ```tsx
297
+ * const store = createStore().setTable('species', {
298
+ * dog: {price: 5},
299
+ * cat: {price: 4},
300
+ * worm: {price: 1},
301
+ * });
302
+ *
303
+ * const metrics = createMetrics(store);
304
+ * metrics.setMetricDefinition(
305
+ * 'lowestPriceOver2',
306
+ * 'species',
307
+ * (numbers) => Math.min(...numbers.filter((number) => number > 2)),
308
+ * 'price',
309
+ * );
310
+ *
311
+ * console.log(metrics.getMetric('lowestPriceOver2'));
312
+ * // -> 4
313
+ * ```
314
+ * @example
315
+ * This example also creates a Store, creates a Metrics object, and defines a
316
+ * custom Metric to get the lowest value of each `price` Cell, greater than 2.
317
+ * However, it also reduces algorithmic complexity with two shortcut
318
+ * functions.
319
+ *
320
+ * ```tsx
321
+ * const store = createStore().setTable('species', {
322
+ * dog: {price: 5},
323
+ * cat: {price: 4},
324
+ * worm: {price: 1},
325
+ * });
326
+ *
327
+ * const metrics = createMetrics(store);
328
+ * metrics.setMetricDefinition(
329
+ * 'lowestPriceOver2',
330
+ * 'species',
331
+ * (numbers) => Math.min(...numbers.filter((number) => number > 2)),
332
+ * 'price',
333
+ * (metric, add) => (add > 2 ? Math.min(metric, add) : metric),
334
+ * (metric, remove) => (remove == metric ? undefined : metric),
335
+ * (metric, add, remove) =>
336
+ * remove == metric
337
+ * ? undefined
338
+ * : add > 2
339
+ * ? Math.min(metric, add)
340
+ * : metric,
341
+ * );
342
+ *
343
+ * console.log(metrics.getMetric('lowestPriceOver2'));
344
+ * // -> 4
345
+ * store.setRow('species', 'fish', {price: 3});
346
+ * console.log(metrics.getMetric('lowestPriceOver2'));
347
+ * // -> 3
348
+ * ```
349
+ * @example
350
+ * This example creates a Store, creates a Metrics object, and defines a
351
+ * custom Metric to get the average value of a discounted price.
352
+ *
353
+ * ```tsx
354
+ * const store = createStore().setTable('species', {
355
+ * dog: {price: 5, discount: 0.3},
356
+ * cat: {price: 4, discount: 0.2},
357
+ * worm: {price: 1, discount: 0.2},
358
+ * });
359
+ *
360
+ * const metrics = createMetrics(store);
361
+ * metrics.setMetricDefinition(
362
+ * 'averageDiscountedPrice',
363
+ * 'species',
364
+ * 'avg',
365
+ * (getCell) => getCell('price') * (1 - getCell('discount')),
366
+ * );
367
+ *
368
+ * console.log(metrics.getMetric('averageDiscountedPrice'));
369
+ * // -> 2.5
370
+ * ```
371
+ * @category Configuration
372
+ */
373
+ setMetricDefinition(
374
+ metricId: Id,
375
+ tableId: Id,
376
+ aggregate?: 'sum' | 'avg' | 'min' | 'max' | Aggregate,
377
+ getNumber?: Id | ((getCell: GetCell, rowId: Id) => number),
378
+ aggregateAdd?: AggregateAdd,
379
+ aggregateRemove?: AggregateRemove,
380
+ aggregateReplace?: AggregateReplace,
381
+ ): Metrics;
382
+
383
+ /**
384
+ * The delMetricDefinition method removes an existing Metric definition.
385
+ *
386
+ * @param metricId The Id of the Metric to remove.
387
+ * @returns A reference to the Metrics object.
388
+ * @example
389
+ * This example creates a Store, creates a Metrics object, defines a simple
390
+ * Metric, and then removes it.
391
+ *
392
+ * ```tsx
393
+ * const store = createStore().setTable('species', {
394
+ * dog: {price: 5},
395
+ * cat: {price: 4},
396
+ * worm: {price: 1},
397
+ * });
398
+ *
399
+ * const metrics = createMetrics(store);
400
+ * metrics.setMetricDefinition('speciesCount', 'species');
401
+ * console.log(metrics.getMetricIds());
402
+ * // -> ['speciesCount']
403
+ *
404
+ * metrics.delMetricDefinition('speciesCount');
405
+ * console.log(metrics.getMetricIds());
406
+ * // -> []
407
+ * ```
408
+ * @category Configuration
409
+ */
410
+ delMetricDefinition(metricId: Id): Metrics;
411
+
412
+ /**
413
+ * The getStore method returns a reference to the underlying Store that is
414
+ * backing this Metrics object.
415
+ *
416
+ * @returns A reference to the Store.
417
+ * @example
418
+ * This example creates a Metrics object against a newly-created Store and
419
+ * then gets its reference in order to update its data.
420
+ *
421
+ * ```tsx
422
+ * const metrics = createMetrics(createStore());
423
+ * metrics.setMetricDefinition('speciesCount', 'species');
424
+ * metrics.getStore().setCell('species', 'dog', 'price', 5);
425
+ * console.log(metrics.getMetric('speciesCount'));
426
+ * // -> 1
427
+ * ```
428
+ * @category Getter
429
+ */
430
+ getStore(): Store;
431
+
432
+ /**
433
+ * The getMetricIds method returns an array of the Metric Ids registered with
434
+ * this Metrics object.
435
+ *
436
+ * @returns An array of Ids.
437
+ * @example
438
+ * This example creates a Metrics object with two definitions, and then gets
439
+ * the Ids of the definitions.
440
+ *
441
+ * ```tsx
442
+ * const metrics = createMetrics(createStore())
443
+ * .setMetricDefinition('speciesCount', 'species')
444
+ * .setMetricDefinition('petsCount', 'pets');
445
+ *
446
+ * console.log(metrics.getMetricIds());
447
+ * // -> ['speciesCount', 'petsCount']
448
+ * ```
449
+ * @category Getter
450
+ */
451
+ getMetricIds(): Ids;
452
+
453
+ /**
454
+ * The getTableId method returns the Id of the underlying Table that is
455
+ * backing a Metric.
456
+ *
457
+ * If the Metric Id is invalid, the method returns `undefined`.
458
+ *
459
+ * @param metricId The Id of a Metric.
460
+ * @returns The Id of the Table backing the Metric, or `undefined`.
461
+ * @example
462
+ * This example creates a Metrics object, a single Metric definition, and then
463
+ * queries it (and a non-existent definition) to get the underlying Table Id.
464
+ *
465
+ * ```tsx
466
+ * const metrics = createMetrics(createStore());
467
+ * metrics.setMetricDefinition('speciesCount', 'species');
468
+ *
469
+ * console.log(metrics.getTableId('speciesCount'));
470
+ * // -> 'species'
471
+ * console.log(metrics.getTableId('petsCount'));
472
+ * // -> undefined
473
+ * ```
474
+ * @category Getter
475
+ */
476
+ getTableId(metricId: Id): Id | undefined;
477
+
478
+ /**
479
+ * The getMetric method gets the current value of a Metric.
480
+ *
481
+ * If the identified Metric does not exist (or if the definition references a
482
+ * Table or Cell value that does not exist) then `undefined` is returned.
483
+ *
484
+ * @param metricId The Id of the Metric.
485
+ * @returns The numeric value of the Metric, or `undefined`.
486
+ * @example
487
+ * This example creates a Store, creates a Metrics object, and defines a
488
+ * simple Metric to average the price values in the Table. It then uses
489
+ * getMetric to access its value (and also the value of a Metric that has not
490
+ * been defined).
491
+ *
492
+ * ```tsx
493
+ * const store = createStore().setTable('species', {
494
+ * dog: {price: 5},
495
+ * cat: {price: 4},
496
+ * worm: {price: 1},
497
+ * });
498
+ *
499
+ * const metrics = createMetrics(store);
500
+ * metrics.setMetricDefinition('highestPrice', 'species', 'max', 'price');
501
+ *
502
+ * console.log(metrics.getMetric('highestPrice'));
503
+ * // -> 5
504
+ * console.log(metrics.getMetric('lowestPrice'));
505
+ * // -> undefined
506
+ * ```
507
+ * @category Getter
508
+ */
509
+ getMetric(metricId: Id): Metric | undefined;
510
+
511
+ /**
512
+ * The addMetricListener method registers a listener function with the Metrics
513
+ * object that will be called whenever the value of a specified Metric
514
+ * changes.
515
+ *
516
+ * You can either listen to a single Metric (by specifying the Metric Id as
517
+ * the method's first parameter), or changes to any Metric (by providing a
518
+ * `null` wildcard).
519
+ *
520
+ * The provided listener is a MetricListener function, and will be called with
521
+ * a reference to the Metrics object, the Id of the Metric that changed, the
522
+ * new Metric value, and the old Metric value.
523
+ *
524
+ * @param metricId The Id of the Metric to listen to, or `null` as a wildcard.
525
+ * @param listener The function that will be called whenever the Metric
526
+ * changes.
527
+ * @returns A unique Id for the listener that can later be used to remove it.
528
+ * @example
529
+ * This example creates a Store, a Metrics object, and then registers a
530
+ * listener that responds to any changes to a specific Metric.
531
+ *
532
+ * ```tsx
533
+ * const store = createStore().setTable('species', {
534
+ * dog: {price: 5},
535
+ * cat: {price: 4},
536
+ * worm: {price: 1},
537
+ * });
538
+ *
539
+ * const metrics = createMetrics(store);
540
+ * metrics.setMetricDefinition('highestPrice', 'species', 'max', 'price');
541
+ *
542
+ * const listenerId = metrics.addMetricListener(
543
+ * 'highestPrice',
544
+ * (metrics, metricId, newMetric, oldMetric) => {
545
+ * console.log('highestPrice metric changed');
546
+ * console.log([oldMetric, newMetric]);
547
+ * },
548
+ * );
549
+ *
550
+ * store.setCell('species', 'horse', 'price', 20);
551
+ * // -> 'highestPrice metric changed'
552
+ * // -> [5, 20]
553
+ *
554
+ * metrics.delListener(listenerId);
555
+ * ```
556
+ * @example
557
+ * This example creates a Store, a Metrics object, and then registers a
558
+ * listener that responds to any changes to any Metric.
559
+ *
560
+ * ```tsx
561
+ * const store = createStore().setTable('species', {
562
+ * dog: {price: 5},
563
+ * cat: {price: 4},
564
+ * worm: {price: 1},
565
+ * });
566
+ *
567
+ * const metrics = createMetrics(store)
568
+ * .setMetricDefinition('highestPrice', 'species', 'max', 'price')
569
+ * .setMetricDefinition('speciesCount', 'species');
570
+ *
571
+ * const listenerId = metrics.addMetricListener(
572
+ * null,
573
+ * (metrics, metricId, newMetric, oldMetric) => {
574
+ * console.log(`${metricId} metric changed`);
575
+ * console.log([oldMetric, newMetric]);
576
+ * },
577
+ * );
578
+ *
579
+ * store.setCell('species', 'horse', 'price', 20);
580
+ * // -> 'highestPrice metric changed'
581
+ * // -> [5, 20]
582
+ * // -> 'speciesCount metric changed'
583
+ * // -> [3, 4]
584
+ *
585
+ * metrics.delListener(listenerId);
586
+ * ```
587
+ * @category Listener
588
+ */
589
+ addMetricListener(metricId: IdOrNull, listener: MetricListener): Id;
590
+
591
+ /**
592
+ * The delListener method removes a listener that was previously added to the
593
+ * Metrics object.
594
+ *
595
+ * Use the Id returned by the addMetricListener method. Note that the Metrics
596
+ * object may re-use this Id for future listeners added to it.
597
+ *
598
+ * @param listenerId The Id of the listener to remove.
599
+ * @returns A reference to the Metrics object.
600
+ * @example
601
+ * This example creates a Store, a Metrics object, registers a listener, and
602
+ * then removes it.
603
+ *
604
+ * ```tsx
605
+ * const store = createStore().setTable('species', {
606
+ * dog: {price: 5},
607
+ * cat: {price: 4},
608
+ * worm: {price: 1},
609
+ * });
610
+ *
611
+ * const metrics = createMetrics(store);
612
+ * metrics.setMetricDefinition('highestPrice', 'species', 'max', 'price');
613
+ *
614
+ * const listenerId = metrics.addMetricListener(
615
+ * 'highestPrice',
616
+ * (metrics, metricId, newMetric, oldMetric) => {
617
+ * console.log('highestPrice metric changed');
618
+ * },
619
+ * );
620
+ *
621
+ * store.setCell('species', 'horse', 'price', 20);
622
+ * // -> 'highestPrice metric changed'
623
+ *
624
+ * metrics.delListener(listenerId);
625
+ *
626
+ * store.setCell('species', 'giraffe', 'price', 50);
627
+ * // -> undefined
628
+ * // The listener is not called.
629
+ * ```
630
+ * @category Listener
631
+ */
632
+ delListener(listenerId: Id): Metrics;
633
+
634
+ /**
635
+ * The destroy method should be called when this Metrics object is no longer
636
+ * used.
637
+ *
638
+ * This guarantees that all of the listeners that the object registered with
639
+ * the underlying Store are removed and it can be correctly garbage collected.
640
+ *
641
+ * @example
642
+ * This example creates a Store, adds a Metrics object with a definition (that
643
+ * registers a RowListener with the underlying Store), and then destroys it
644
+ * again, removing the listener.
645
+ *
646
+ * ```tsx
647
+ * const store = createStore().setTable('species', {
648
+ * dog: {price: 5},
649
+ * cat: {price: 4},
650
+ * worm: {price: 1},
651
+ * });
652
+ *
653
+ * const metrics = createMetrics(store);
654
+ * metrics.setMetricDefinition('speciesCount', 'species');
655
+ * console.log(store.getListenerStats().row);
656
+ * // -> 1
657
+ *
658
+ * metrics.destroy();
659
+ *
660
+ * console.log(store.getListenerStats().row);
661
+ * // -> 0
662
+ * ```
663
+ * @category Lifecycle
664
+ */
665
+ destroy(): void;
666
+
667
+ /**
668
+ * The getListenerStats method provides a set of statistics about the
669
+ * listeners registered with the Metrics object, and is used for debugging
670
+ * purposes.
671
+ *
672
+ * The statistics are only populated in a debug build: production builds
673
+ * return an empty object. The method is intended to be used during
674
+ * development to ensure your application is not leaking listener
675
+ * registrations, for example.
676
+ *
677
+ * @returns A MetricsListenerStats object containing Metrics listener
678
+ * statistics.
679
+ * @example
680
+ * This example gets the listener statistics of a Metrics object.
681
+ *
682
+ * ```tsx
683
+ * const store = createStore();
684
+ * const metrics = createMetrics(store);
685
+ * metrics.addMetricListener(null, () => console.log('Metric changed'));
686
+ *
687
+ * console.log(metrics.getListenerStats());
688
+ * // -> {metric: 1}
689
+ * ```
690
+ * @category Development
691
+ */
692
+ getListenerStats(): MetricsListenerStats;
693
+ }
694
+
695
+ /**
696
+ * The createMetrics function creates a Metrics object, and is the main entry
697
+ * point into the metrics module.
698
+ *
699
+ * It is trivially simple.
700
+ *
701
+ * A given Store can only have one Metrics object associated with it. If you
702
+ * call this function twice on the same Store, your second call will return a
703
+ * reference to the Metrics object created by the first.
704
+ *
705
+ * @param store The Store for which to register Metric definitions.
706
+ * @returns A reference to the new Metrics object.
707
+ * @example
708
+ * This example creates a Metrics object.
709
+ *
710
+ * ```tsx
711
+ * const store = createStore();
712
+ * const metrics = createMetrics(store);
713
+ * console.log(metrics.getMetricIds());
714
+ * // -> []
715
+ * ```
716
+ * @example
717
+ * This example creates a Metrics object, and calls the method a second time
718
+ * for the same Store to return the same object.
719
+ *
720
+ * ```tsx
721
+ * const store = createStore();
722
+ * const metrics1 = createMetrics(store);
723
+ * const metrics2 = createMetrics(store);
724
+ * console.log(metrics1 === metrics2);
725
+ * // -> true
726
+ * ```
727
+ */
728
+ export function createMetrics(store: Store): Metrics;