tinybase 1.0.2 → 1.1.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/checkpoints.d.ts +945 -0
- package/lib/checkpoints.js +1 -0
- package/lib/checkpoints.js.gz +0 -0
- package/lib/common.d.ts +115 -0
- package/lib/common.js +1 -0
- package/lib/common.js.gz +0 -0
- package/lib/debug/checkpoints.d.ts +66 -0
- package/lib/debug/checkpoints.js +332 -0
- package/lib/debug/common.js +3 -0
- package/lib/debug/indexes.d.ts +167 -6
- package/lib/debug/indexes.js +431 -0
- package/lib/debug/metrics.d.ts +72 -0
- package/lib/debug/metrics.js +401 -0
- package/lib/debug/persisters.js +191 -0
- package/lib/debug/relationships.d.ts +86 -1
- package/lib/debug/relationships.js +434 -0
- package/lib/debug/store.d.ts +187 -5
- package/lib/debug/store.js +783 -0
- package/lib/debug/tinybase.js +144 -39
- package/lib/indexes.d.ts +939 -0
- package/lib/indexes.js +1 -0
- package/lib/indexes.js.gz +0 -0
- package/lib/metrics.d.ts +829 -0
- package/lib/metrics.js +1 -0
- package/lib/metrics.js.gz +0 -0
- package/lib/persisters.d.ts +711 -0
- package/lib/persisters.js +1 -0
- package/lib/persisters.js.gz +0 -0
- package/lib/relationships.d.ts +1201 -0
- package/lib/relationships.js +1 -0
- package/lib/relationships.js.gz +0 -0
- package/lib/store.d.ts +2688 -0
- package/lib/store.js +1 -0
- package/lib/store.js.gz +0 -0
- package/lib/tinybase.d.ts +13 -0
- package/lib/tinybase.js +1 -0
- package/lib/tinybase.js.gz +0 -0
- package/lib/ui-react.d.ts +7185 -0
- package/lib/ui-react.js +1 -0
- package/lib/ui-react.js.gz +0 -0
- package/lib/umd/checkpoints.js +1 -0
- package/lib/umd/checkpoints.js.gz +0 -0
- package/lib/umd/common.js +1 -0
- package/lib/umd/common.js.gz +0 -0
- package/lib/umd/indexes.js +1 -0
- package/lib/umd/indexes.js.gz +0 -0
- package/lib/umd/metrics.js +1 -0
- package/lib/umd/metrics.js.gz +0 -0
- package/lib/umd/persisters.js +1 -0
- package/lib/umd/persisters.js.gz +0 -0
- package/lib/umd/relationships.js +1 -0
- package/lib/umd/relationships.js.gz +0 -0
- package/lib/umd/store.js +1 -0
- package/lib/umd/store.js.gz +0 -0
- package/lib/umd/tinybase.js +1 -0
- package/lib/umd/tinybase.js.gz +0 -0
- package/lib/umd/ui-react.js +1 -0
- package/lib/umd/ui-react.js.gz +0 -0
- package/package.json +14 -14
- package/readme.md +2 -2
|
@@ -0,0 +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,t)=>e.push(t),c=e=>e.pop(),l=e=>null==e,i=(e,t,n)=>l(e)?n?.():t(e),d=(e,t)=>e?.has(t)??!1,a=e=>l(e)||0==(e=>e.size)(e),h=(e,t)=>e?.forEach(t),u=(e,t)=>e?.delete(t),p=e=>new Map(e),C=(e,t)=>e?.get(t),g=(e,t,n)=>l(n)?(u(e,t),e):e?.set(t,n),k=(e,t,n,s)=>(d(e,t)||(s?.(n),e.set(t,n)),C(e,t)),f=e=>new Set(e),L=(e,t,r)=>n(r)<2?((e,t)=>e?.add(t))(s(r)?e:k(e,r[0],f()),t):L(k(e,r[0],p()),t,o(r)),v=e=>{const n=(r,c,...l)=>i(r,(r=>s(l)?e(r,c):t([l[0],null],(e=>n(C(r,e),c,...o(l))))));return n},w=Object.freeze,S=(e=>{const t=new WeakMap;return n=>(t.has(n)||t.set(n,e(n)),t.get(n))})((o=>{let S,z,E,I=100,M=p(),b=1;const j=f(),x=p(),[y,B,F]=(e=>{let s,o=0;const d=[],a=p();return[(t,n,r=[])=>{s??=e();const l=c(d)??""+o++;return g(a,l,[t,n,r]),L(n,l,r),l},(e,t=[],...n)=>v(h)(e,(e=>i(C(a,e),(([e])=>e(s,...t,...n)))),...t),e=>i(C(a,e),(([,t,s])=>(v(u)(t,e,...s),g(a,e),n(d)<1e3&&r(d,e),s)),(()=>[])),(e,o,r)=>i(C(a,e),(([e,,c])=>{const i=(...d)=>{const a=n(d);a==n(c)?e(s,...d,...r(d)):l(c[a])?t(o[a](...d),(e=>i(...d,e))):i(...d,c[a])};i()}))]})((()=>V)),O=p(),T=p(),W=[],m=[],q=(e,t)=>{b=0,o.transaction((()=>h(C(O,t),((t,n)=>h(t,((t,s)=>h(t,((t,r)=>l(t[e])?o.delCell(n,s,r,!0):o.setCell(n,s,r,t[e]))))))))),b=1},A=e=>{g(O,e),g(T,e),B(x,[e])},D=(e,s)=>t(((e,t)=>e.splice(0,t))(e,s??n(e)),A),G=()=>D(W,n(W)-I),H=o.addCellListener(null,null,null,((e,t,n,s,o,l)=>{if(b){i(S,(()=>{r(W,S),G(),D(m),S=void 0,E=1}));const e=k(M,t,p()),d=k(e,n,p()),h=k(d,s,[void 0,void 0],(e=>e[0]=l));h[1]=o,h[0]===h[1]&&a(g(d,s))&&a(g(e,n))&&a(g(M,t))&&(S=c(W),E=1),P()}})),J=(e="")=>(l(S)&&(S=""+z++,g(O,S,M),R(S,e),M=p(),E=1),S),K=()=>{s(W)||(m.unshift(J()),q(0,S),S=c(W),E=1)},N=()=>{s(m)||(r(W,S),S=m.shift(),q(1,S),E=1)},P=()=>{E&&(B(j),E=0)},Q=e=>{const t=J(e);return P(),t},R=(e,t)=>(U(e)&&C(T,e)!==t&&(g(T,e,t),B(x,[e])),V),U=e=>d(O,e),V={setSize:e=>(I=e,G(),V),addCheckpoint:Q,setCheckpoint:R,getStore:()=>o,getCheckpointIds:()=>[[...W],S,[...m]],forEachCheckpoint:e=>{return t=e,h(T,((e,n)=>t(n,e)));var t},hasCheckpoint:U,getCheckpoint:e=>C(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=>y(e,j),addCheckpointListener:(e,t)=>y(t,x,[e]),delListener:e=>(F(e),V),clear:()=>(D(W),D(m),l(S)||A(S),S=void 0,z=0,Q(),V),destroy:()=>{o.delListener(H)},getListenerStats:()=>({})};return w(V.clear())}));export{S as createCheckpoints};
|
|
Binary file
|
package/lib/common.d.ts
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The common module of the TinyBase project provides a small collection of
|
|
3
|
+
* common types used across other modules.
|
|
4
|
+
*
|
|
5
|
+
* @packageDocumentation
|
|
6
|
+
* @module common
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The Json type is a simple alias for a string, but is used to indicate that
|
|
11
|
+
* the string should be considered to be a JSON serialization of an object.
|
|
12
|
+
*
|
|
13
|
+
* @category General
|
|
14
|
+
*/
|
|
15
|
+
export type Json = string;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The Ids type is a simple alias for an array of strings, but is used to
|
|
19
|
+
* indicate that the strings should be considered to be the keys of objects
|
|
20
|
+
* (such as the Row Id strings used in a Table).
|
|
21
|
+
*
|
|
22
|
+
* @category Identity
|
|
23
|
+
*/
|
|
24
|
+
export type Ids = Id[];
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The Id type is a simple alias for a string, but is used to indicate that the
|
|
28
|
+
* string should be considered to be the key of an object (such as a Row Id
|
|
29
|
+
* string used in a Table).
|
|
30
|
+
*
|
|
31
|
+
* @category Identity
|
|
32
|
+
*/
|
|
33
|
+
export type Id = string;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* The Id type is a simple alias for the union of a string or `null` value,
|
|
37
|
+
* where the string should be considered to be the key of an objects (such as a
|
|
38
|
+
* Row Id string used in a Table), and typically `null` indicates a wildcard -
|
|
39
|
+
* such as when used in the Store addRowListener method.
|
|
40
|
+
*
|
|
41
|
+
* @category Identity
|
|
42
|
+
*/
|
|
43
|
+
export type IdOrNull = Id | null;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The ParameterizedCallback type represents a generic function that will take
|
|
47
|
+
* an optional parameter - such as the handler of a DOM event.
|
|
48
|
+
*
|
|
49
|
+
* @category Callback
|
|
50
|
+
*/
|
|
51
|
+
export type ParameterizedCallback<Parameter> = (parameter?: Parameter) => void;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* The Callback type represents a function that is used as a callback and which
|
|
55
|
+
* does not take a parameter.
|
|
56
|
+
*
|
|
57
|
+
* @category Callback
|
|
58
|
+
*/
|
|
59
|
+
export type Callback = () => void;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* The SortKey type represents a value that can be used by a sort function.
|
|
63
|
+
*
|
|
64
|
+
* @category Parameter
|
|
65
|
+
*/
|
|
66
|
+
export type SortKey = string | number | boolean;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* The defaultSorter function is provided as a convenience to sort keys
|
|
70
|
+
* alphanumerically, and can be provided to the `sliceIdSorter` and
|
|
71
|
+
* `rowIdSorter` parameters of the setIndexDefinition method in the indexes
|
|
72
|
+
* module, for example.
|
|
73
|
+
*
|
|
74
|
+
* @param sortKey1 The first item of the pair to compare.
|
|
75
|
+
* @param sortKey2 The second item of the pair to compare.
|
|
76
|
+
* @returns A number indicating how to sort the pair.
|
|
77
|
+
* @example
|
|
78
|
+
* This example creates an Indexes object.
|
|
79
|
+
*
|
|
80
|
+
* ```js
|
|
81
|
+
* const store = createStore();
|
|
82
|
+
* const indexes = createIndexes(store);
|
|
83
|
+
* console.log(indexes.getIndexIds());
|
|
84
|
+
* // -> []
|
|
85
|
+
* ```
|
|
86
|
+
* @example
|
|
87
|
+
* This example creates a Store, creates an Indexes object, and defines an
|
|
88
|
+
* Index based on the first letter of the pets' names. The Slice Ids (and Row
|
|
89
|
+
* Ids within them) are alphabetically sorted using the defaultSorter function.
|
|
90
|
+
*
|
|
91
|
+
* ```js
|
|
92
|
+
* const store = createStore().setTable('pets', {
|
|
93
|
+
* fido: {species: 'dog'},
|
|
94
|
+
* felix: {species: 'cat'},
|
|
95
|
+
* cujo: {species: 'dog'},
|
|
96
|
+
* });
|
|
97
|
+
*
|
|
98
|
+
* const indexes = createIndexes(store);
|
|
99
|
+
* indexes.setIndexDefinition(
|
|
100
|
+
* 'byFirst', // indexId
|
|
101
|
+
* 'pets', // tableId
|
|
102
|
+
* (_, rowId) => rowId[0], // each Row's Slice Id
|
|
103
|
+
* (_, rowId) => rowId, // each Row's sort key
|
|
104
|
+
* defaultSorter, // sort Slice Ids
|
|
105
|
+
* defaultSorter, // sort Row Ids by sort key
|
|
106
|
+
* );
|
|
107
|
+
*
|
|
108
|
+
* console.log(indexes.getSliceIds('byFirst'));
|
|
109
|
+
* // -> ['c', 'f']
|
|
110
|
+
* console.log(indexes.getSliceRowIds('byFirst', 'f'));
|
|
111
|
+
* // -> ['felix', 'fido']
|
|
112
|
+
* ```
|
|
113
|
+
* @category Convenience
|
|
114
|
+
*/
|
|
115
|
+
export function defaultSorter(sortKey1: SortKey, sortKey2: SortKey): number;
|
package/lib/common.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const o=(o,t)=>o<t?-1:1;export{o as defaultSorter};
|
package/lib/common.js.gz
ADDED
|
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
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
const arrayHas = (array, value) => array.includes(value);
|
|
2
|
+
const arrayForEach = (array, cb) => array.forEach(cb);
|
|
3
|
+
const arrayLength = (array) => array.length;
|
|
4
|
+
const arrayIsEmpty = (array) => arrayLength(array) == 0;
|
|
5
|
+
const arrayReduce = (array, cb, initial) => array.reduce(cb, initial);
|
|
6
|
+
const arrayFromSecond = (ids) => ids.slice(1);
|
|
7
|
+
const arrayClear = (array, to) => array.splice(0, to);
|
|
8
|
+
const arrayPush = (array, value) => array.push(value);
|
|
9
|
+
const arrayPop = (array) => array.pop();
|
|
10
|
+
|
|
11
|
+
const isUndefined = (thing) => thing == void 0;
|
|
12
|
+
const ifNotUndefined = (value, then, otherwise) =>
|
|
13
|
+
isUndefined(value) ? otherwise?.() : then(value);
|
|
14
|
+
|
|
15
|
+
const collSizeN = (collSizer) => (coll) =>
|
|
16
|
+
arrayReduce(collValues(coll), (total, coll2) => total + collSizer(coll2), 0);
|
|
17
|
+
const collSize = (coll) => coll.size;
|
|
18
|
+
const collSize2 = collSizeN(collSize);
|
|
19
|
+
const collHas = (coll, keyOrValue) => coll?.has(keyOrValue) ?? false;
|
|
20
|
+
const collIsEmpty = (coll) => isUndefined(coll) || collSize(coll) == 0;
|
|
21
|
+
const collValues = (coll) => [...(coll?.values() ?? [])];
|
|
22
|
+
const collForEach = (coll, cb) => coll?.forEach(cb);
|
|
23
|
+
const collDel = (coll, keyOrValue) => coll?.delete(keyOrValue);
|
|
24
|
+
|
|
25
|
+
const mapNew = (entries) => new Map(entries);
|
|
26
|
+
const mapGet = (map, key) => map?.get(key);
|
|
27
|
+
const mapForEach = (map, cb) =>
|
|
28
|
+
collForEach(map, (value, key) => cb(key, value));
|
|
29
|
+
const mapSet = (map, key, value) =>
|
|
30
|
+
isUndefined(value) ? (collDel(map, key), map) : map?.set(key, value);
|
|
31
|
+
const mapEnsure = (map, key, defaultValue, onWillAdd) => {
|
|
32
|
+
if (!collHas(map, key)) {
|
|
33
|
+
onWillAdd?.(defaultValue);
|
|
34
|
+
map.set(key, defaultValue);
|
|
35
|
+
}
|
|
36
|
+
return mapGet(map, key);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const setNew = (entries) => new Set(entries);
|
|
40
|
+
const setAdd = (set, value) => set?.add(value);
|
|
41
|
+
|
|
42
|
+
const getCreateFunction = (getFunction) => {
|
|
43
|
+
const getFunctionsByStore = /* @__PURE__ */ new WeakMap();
|
|
44
|
+
return (store) => {
|
|
45
|
+
if (!getFunctionsByStore.has(store)) {
|
|
46
|
+
getFunctionsByStore.set(store, getFunction(store));
|
|
47
|
+
}
|
|
48
|
+
return getFunctionsByStore.get(store);
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const addDeepSet = (deepSet, value, ids) =>
|
|
53
|
+
arrayLength(ids) < 2
|
|
54
|
+
? setAdd(
|
|
55
|
+
arrayIsEmpty(ids) ? deepSet : mapEnsure(deepSet, ids[0], setNew()),
|
|
56
|
+
value,
|
|
57
|
+
)
|
|
58
|
+
: addDeepSet(
|
|
59
|
+
mapEnsure(deepSet, ids[0], mapNew()),
|
|
60
|
+
value,
|
|
61
|
+
arrayFromSecond(ids),
|
|
62
|
+
);
|
|
63
|
+
const forDeepSet = (valueDo) => {
|
|
64
|
+
const deep = (deepIdSet, arg, ...ids) =>
|
|
65
|
+
ifNotUndefined(deepIdSet, (deepIdSet2) =>
|
|
66
|
+
arrayIsEmpty(ids)
|
|
67
|
+
? valueDo(deepIdSet2, arg)
|
|
68
|
+
: arrayForEach([ids[0], null], (id) =>
|
|
69
|
+
deep(mapGet(deepIdSet2, id), arg, ...arrayFromSecond(ids)),
|
|
70
|
+
),
|
|
71
|
+
);
|
|
72
|
+
return deep;
|
|
73
|
+
};
|
|
74
|
+
const getListenerFunctions = (getThing) => {
|
|
75
|
+
let thing;
|
|
76
|
+
let nextId = 0;
|
|
77
|
+
const listenerPool = [];
|
|
78
|
+
const allListeners = mapNew();
|
|
79
|
+
const addListener = (listener, deepSet, idOrNulls = []) => {
|
|
80
|
+
thing ??= getThing();
|
|
81
|
+
const id = arrayPop(listenerPool) ?? '' + nextId++;
|
|
82
|
+
mapSet(allListeners, id, [listener, deepSet, idOrNulls]);
|
|
83
|
+
addDeepSet(deepSet, id, idOrNulls);
|
|
84
|
+
return id;
|
|
85
|
+
};
|
|
86
|
+
const callListeners = (deepSet, ids = [], ...extraArgs) =>
|
|
87
|
+
forDeepSet(collForEach)(
|
|
88
|
+
deepSet,
|
|
89
|
+
(id) =>
|
|
90
|
+
ifNotUndefined(mapGet(allListeners, id), ([listener]) =>
|
|
91
|
+
listener(thing, ...ids, ...extraArgs),
|
|
92
|
+
),
|
|
93
|
+
...ids,
|
|
94
|
+
);
|
|
95
|
+
const delListener = (id) =>
|
|
96
|
+
ifNotUndefined(
|
|
97
|
+
mapGet(allListeners, id),
|
|
98
|
+
([, deepSet, idOrNulls]) => {
|
|
99
|
+
forDeepSet(collDel)(deepSet, id, ...idOrNulls);
|
|
100
|
+
mapSet(allListeners, id);
|
|
101
|
+
if (arrayLength(listenerPool) < 1e3) {
|
|
102
|
+
arrayPush(listenerPool, id);
|
|
103
|
+
}
|
|
104
|
+
return idOrNulls;
|
|
105
|
+
},
|
|
106
|
+
() => [],
|
|
107
|
+
);
|
|
108
|
+
const callListener = (id, idNullGetters, extraArgsGetter) =>
|
|
109
|
+
ifNotUndefined(mapGet(allListeners, id), ([listener, , idOrNulls]) => {
|
|
110
|
+
const callWithIds = (...ids) => {
|
|
111
|
+
const index = arrayLength(ids);
|
|
112
|
+
index == arrayLength(idOrNulls)
|
|
113
|
+
? listener(thing, ...ids, ...extraArgsGetter(ids))
|
|
114
|
+
: isUndefined(idOrNulls[index])
|
|
115
|
+
? arrayForEach(idNullGetters[index](...ids), (id2) =>
|
|
116
|
+
callWithIds(...ids, id2),
|
|
117
|
+
)
|
|
118
|
+
: callWithIds(...ids, idOrNulls[index]);
|
|
119
|
+
};
|
|
120
|
+
callWithIds();
|
|
121
|
+
});
|
|
122
|
+
return [addListener, callListeners, delListener, callListener];
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const object = Object;
|
|
126
|
+
const objFreeze = object.freeze;
|
|
127
|
+
|
|
128
|
+
const createCheckpoints = getCreateFunction((store) => {
|
|
129
|
+
let backwardIdsSize = 100;
|
|
130
|
+
let currentId;
|
|
131
|
+
let delta = mapNew();
|
|
132
|
+
let listening = 1;
|
|
133
|
+
let nextCheckpointId;
|
|
134
|
+
let checkpointsChanged;
|
|
135
|
+
const checkpointIdsListeners = setNew();
|
|
136
|
+
const checkpointListeners = mapNew();
|
|
137
|
+
const [addListener, callListeners, delListenerImpl] = getListenerFunctions(
|
|
138
|
+
() => checkpoints,
|
|
139
|
+
);
|
|
140
|
+
const deltas = mapNew();
|
|
141
|
+
const labels = mapNew();
|
|
142
|
+
const backwardIds = [];
|
|
143
|
+
const forwardIds = [];
|
|
144
|
+
const updateStore = (oldOrNew, checkpointId) => {
|
|
145
|
+
listening = 0;
|
|
146
|
+
store.transaction(() =>
|
|
147
|
+
collForEach(mapGet(deltas, checkpointId), (table, tableId) =>
|
|
148
|
+
collForEach(table, (row, rowId) =>
|
|
149
|
+
collForEach(row, (oldNew, cellId) =>
|
|
150
|
+
isUndefined(oldNew[oldOrNew])
|
|
151
|
+
? store.delCell(tableId, rowId, cellId, true)
|
|
152
|
+
: store.setCell(tableId, rowId, cellId, oldNew[oldOrNew]),
|
|
153
|
+
),
|
|
154
|
+
),
|
|
155
|
+
),
|
|
156
|
+
);
|
|
157
|
+
listening = 1;
|
|
158
|
+
};
|
|
159
|
+
const clearCheckpointId = (checkpointId) => {
|
|
160
|
+
mapSet(deltas, checkpointId);
|
|
161
|
+
mapSet(labels, checkpointId);
|
|
162
|
+
callListeners(checkpointListeners, [checkpointId]);
|
|
163
|
+
};
|
|
164
|
+
const clearCheckpointIds = (checkpointIds, to) =>
|
|
165
|
+
arrayForEach(
|
|
166
|
+
arrayClear(checkpointIds, to ?? arrayLength(checkpointIds)),
|
|
167
|
+
clearCheckpointId,
|
|
168
|
+
);
|
|
169
|
+
const trimBackwardsIds = () =>
|
|
170
|
+
clearCheckpointIds(backwardIds, arrayLength(backwardIds) - backwardIdsSize);
|
|
171
|
+
const listenerId = store.addCellListener(
|
|
172
|
+
null,
|
|
173
|
+
null,
|
|
174
|
+
null,
|
|
175
|
+
(_store, tableId, rowId, cellId, newCell, oldCell) => {
|
|
176
|
+
if (listening) {
|
|
177
|
+
ifNotUndefined(currentId, () => {
|
|
178
|
+
arrayPush(backwardIds, currentId);
|
|
179
|
+
trimBackwardsIds();
|
|
180
|
+
clearCheckpointIds(forwardIds);
|
|
181
|
+
currentId = void 0;
|
|
182
|
+
checkpointsChanged = 1;
|
|
183
|
+
});
|
|
184
|
+
const table = mapEnsure(delta, tableId, mapNew());
|
|
185
|
+
const row = mapEnsure(table, rowId, mapNew());
|
|
186
|
+
const oldNew = mapEnsure(
|
|
187
|
+
row,
|
|
188
|
+
cellId,
|
|
189
|
+
[void 0, void 0],
|
|
190
|
+
(addOldNew) => (addOldNew[0] = oldCell),
|
|
191
|
+
);
|
|
192
|
+
oldNew[1] = newCell;
|
|
193
|
+
if (oldNew[0] === oldNew[1]) {
|
|
194
|
+
if (collIsEmpty(mapSet(row, cellId))) {
|
|
195
|
+
if (collIsEmpty(mapSet(table, rowId))) {
|
|
196
|
+
if (collIsEmpty(mapSet(delta, tableId))) {
|
|
197
|
+
currentId = arrayPop(backwardIds);
|
|
198
|
+
checkpointsChanged = 1;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
callListenersIfChanged();
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
);
|
|
207
|
+
const addCheckpointImpl = (label = '') => {
|
|
208
|
+
if (isUndefined(currentId)) {
|
|
209
|
+
currentId = '' + nextCheckpointId++;
|
|
210
|
+
mapSet(deltas, currentId, delta);
|
|
211
|
+
setCheckpoint(currentId, label);
|
|
212
|
+
delta = mapNew();
|
|
213
|
+
checkpointsChanged = 1;
|
|
214
|
+
}
|
|
215
|
+
return currentId;
|
|
216
|
+
};
|
|
217
|
+
const goBackwardImpl = () => {
|
|
218
|
+
if (!arrayIsEmpty(backwardIds)) {
|
|
219
|
+
forwardIds.unshift(addCheckpointImpl());
|
|
220
|
+
updateStore(0, currentId);
|
|
221
|
+
currentId = arrayPop(backwardIds);
|
|
222
|
+
checkpointsChanged = 1;
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
const goForwardImpl = () => {
|
|
226
|
+
if (!arrayIsEmpty(forwardIds)) {
|
|
227
|
+
arrayPush(backwardIds, currentId);
|
|
228
|
+
currentId = forwardIds.shift();
|
|
229
|
+
updateStore(1, currentId);
|
|
230
|
+
checkpointsChanged = 1;
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
const callListenersIfChanged = () => {
|
|
234
|
+
if (checkpointsChanged) {
|
|
235
|
+
callListeners(checkpointIdsListeners);
|
|
236
|
+
checkpointsChanged = 0;
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
const setSize = (size) => {
|
|
240
|
+
backwardIdsSize = size;
|
|
241
|
+
trimBackwardsIds();
|
|
242
|
+
return checkpoints;
|
|
243
|
+
};
|
|
244
|
+
const addCheckpoint = (label) => {
|
|
245
|
+
const id = addCheckpointImpl(label);
|
|
246
|
+
callListenersIfChanged();
|
|
247
|
+
return id;
|
|
248
|
+
};
|
|
249
|
+
const setCheckpoint = (checkpointId, label) => {
|
|
250
|
+
if (hasCheckpoint(checkpointId) && mapGet(labels, checkpointId) !== label) {
|
|
251
|
+
mapSet(labels, checkpointId, label);
|
|
252
|
+
callListeners(checkpointListeners, [checkpointId]);
|
|
253
|
+
}
|
|
254
|
+
return checkpoints;
|
|
255
|
+
};
|
|
256
|
+
const getStore = () => store;
|
|
257
|
+
const getCheckpointIds = () => [[...backwardIds], currentId, [...forwardIds]];
|
|
258
|
+
const forEachCheckpoint = (checkpointCallback) =>
|
|
259
|
+
mapForEach(labels, checkpointCallback);
|
|
260
|
+
const hasCheckpoint = (checkpointId) => collHas(deltas, checkpointId);
|
|
261
|
+
const getCheckpoint = (checkpointId) => mapGet(labels, checkpointId);
|
|
262
|
+
const goBackward = () => {
|
|
263
|
+
goBackwardImpl();
|
|
264
|
+
callListenersIfChanged();
|
|
265
|
+
return checkpoints;
|
|
266
|
+
};
|
|
267
|
+
const goForward = () => {
|
|
268
|
+
goForwardImpl();
|
|
269
|
+
callListenersIfChanged();
|
|
270
|
+
return checkpoints;
|
|
271
|
+
};
|
|
272
|
+
const goTo = (checkpointId) => {
|
|
273
|
+
const action = arrayHas(backwardIds, checkpointId)
|
|
274
|
+
? goBackwardImpl
|
|
275
|
+
: arrayHas(forwardIds, checkpointId)
|
|
276
|
+
? goForwardImpl
|
|
277
|
+
: null;
|
|
278
|
+
while (!isUndefined(action) && checkpointId != currentId) {
|
|
279
|
+
action();
|
|
280
|
+
}
|
|
281
|
+
callListenersIfChanged();
|
|
282
|
+
return checkpoints;
|
|
283
|
+
};
|
|
284
|
+
const addCheckpointIdsListener = (listener) =>
|
|
285
|
+
addListener(listener, checkpointIdsListeners);
|
|
286
|
+
const addCheckpointListener = (checkpointId, listener) =>
|
|
287
|
+
addListener(listener, checkpointListeners, [checkpointId]);
|
|
288
|
+
const delListener = (listenerId2) => {
|
|
289
|
+
delListenerImpl(listenerId2);
|
|
290
|
+
return checkpoints;
|
|
291
|
+
};
|
|
292
|
+
const clear = () => {
|
|
293
|
+
clearCheckpointIds(backwardIds);
|
|
294
|
+
clearCheckpointIds(forwardIds);
|
|
295
|
+
if (!isUndefined(currentId)) {
|
|
296
|
+
clearCheckpointId(currentId);
|
|
297
|
+
}
|
|
298
|
+
currentId = void 0;
|
|
299
|
+
nextCheckpointId = 0;
|
|
300
|
+
addCheckpoint();
|
|
301
|
+
return checkpoints;
|
|
302
|
+
};
|
|
303
|
+
const destroy = () => {
|
|
304
|
+
store.delListener(listenerId);
|
|
305
|
+
};
|
|
306
|
+
const getListenerStats = () => ({
|
|
307
|
+
checkpointIds: collSize(checkpointIdsListeners),
|
|
308
|
+
checkpoint: collSize2(checkpointListeners),
|
|
309
|
+
});
|
|
310
|
+
const checkpoints = {
|
|
311
|
+
setSize,
|
|
312
|
+
addCheckpoint,
|
|
313
|
+
setCheckpoint,
|
|
314
|
+
getStore,
|
|
315
|
+
getCheckpointIds,
|
|
316
|
+
forEachCheckpoint,
|
|
317
|
+
hasCheckpoint,
|
|
318
|
+
getCheckpoint,
|
|
319
|
+
goBackward,
|
|
320
|
+
goForward,
|
|
321
|
+
goTo,
|
|
322
|
+
addCheckpointIdsListener,
|
|
323
|
+
addCheckpointListener,
|
|
324
|
+
delListener,
|
|
325
|
+
clear,
|
|
326
|
+
destroy,
|
|
327
|
+
getListenerStats,
|
|
328
|
+
};
|
|
329
|
+
return objFreeze(checkpoints.clear());
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
export {createCheckpoints};
|