tinybase 1.0.5 → 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.js +1 -1
- package/lib/checkpoints.js.gz +0 -0
- package/lib/debug/checkpoints.js +8 -6
- package/lib/debug/indexes.js +4 -2
- package/lib/debug/metrics.js +4 -2
- package/lib/debug/relationships.js +4 -2
- package/lib/debug/store.d.ts +159 -0
- package/lib/debug/store.js +87 -31
- package/lib/debug/tinybase.js +91 -35
- package/lib/store.d.ts +159 -0
- package/lib/store.js +1 -1
- package/lib/store.js.gz +0 -0
- package/lib/tinybase.js +1 -1
- package/lib/tinybase.js.gz +0 -0
- package/lib/umd/checkpoints.js +1 -1
- package/lib/umd/checkpoints.js.gz +0 -0
- package/lib/umd/store.js +1 -1
- package/lib/umd/store.js.gz +0 -0
- package/lib/umd/tinybase.js +1 -1
- package/lib/umd/tinybase.js.gz +0 -0
- package/package.json +12 -12
- package/readme.md +2 -2
package/lib/checkpoints.js
CHANGED
|
@@ -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,
|
|
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};
|
package/lib/checkpoints.js.gz
CHANGED
|
Binary file
|
package/lib/debug/checkpoints.js
CHANGED
|
@@ -5,6 +5,8 @@ const arrayIsEmpty = (array) => arrayLength(array) == 0;
|
|
|
5
5
|
const arrayReduce = (array, cb, initial) => array.reduce(cb, initial);
|
|
6
6
|
const arrayFromSecond = (ids) => ids.slice(1);
|
|
7
7
|
const arrayClear = (array, to) => array.splice(0, to);
|
|
8
|
+
const arrayPush = (array, value) => array.push(value);
|
|
9
|
+
const arrayPop = (array) => array.pop();
|
|
8
10
|
|
|
9
11
|
const isUndefined = (thing) => thing == void 0;
|
|
10
12
|
const ifNotUndefined = (value, then, otherwise) =>
|
|
@@ -76,7 +78,7 @@ const getListenerFunctions = (getThing) => {
|
|
|
76
78
|
const allListeners = mapNew();
|
|
77
79
|
const addListener = (listener, deepSet, idOrNulls = []) => {
|
|
78
80
|
thing ??= getThing();
|
|
79
|
-
const id = listenerPool
|
|
81
|
+
const id = arrayPop(listenerPool) ?? '' + nextId++;
|
|
80
82
|
mapSet(allListeners, id, [listener, deepSet, idOrNulls]);
|
|
81
83
|
addDeepSet(deepSet, id, idOrNulls);
|
|
82
84
|
return id;
|
|
@@ -97,7 +99,7 @@ const getListenerFunctions = (getThing) => {
|
|
|
97
99
|
forDeepSet(collDel)(deepSet, id, ...idOrNulls);
|
|
98
100
|
mapSet(allListeners, id);
|
|
99
101
|
if (arrayLength(listenerPool) < 1e3) {
|
|
100
|
-
listenerPool
|
|
102
|
+
arrayPush(listenerPool, id);
|
|
101
103
|
}
|
|
102
104
|
return idOrNulls;
|
|
103
105
|
},
|
|
@@ -173,7 +175,7 @@ const createCheckpoints = getCreateFunction((store) => {
|
|
|
173
175
|
(_store, tableId, rowId, cellId, newCell, oldCell) => {
|
|
174
176
|
if (listening) {
|
|
175
177
|
ifNotUndefined(currentId, () => {
|
|
176
|
-
backwardIds
|
|
178
|
+
arrayPush(backwardIds, currentId);
|
|
177
179
|
trimBackwardsIds();
|
|
178
180
|
clearCheckpointIds(forwardIds);
|
|
179
181
|
currentId = void 0;
|
|
@@ -192,7 +194,7 @@ const createCheckpoints = getCreateFunction((store) => {
|
|
|
192
194
|
if (collIsEmpty(mapSet(row, cellId))) {
|
|
193
195
|
if (collIsEmpty(mapSet(table, rowId))) {
|
|
194
196
|
if (collIsEmpty(mapSet(delta, tableId))) {
|
|
195
|
-
currentId = backwardIds
|
|
197
|
+
currentId = arrayPop(backwardIds);
|
|
196
198
|
checkpointsChanged = 1;
|
|
197
199
|
}
|
|
198
200
|
}
|
|
@@ -216,13 +218,13 @@ const createCheckpoints = getCreateFunction((store) => {
|
|
|
216
218
|
if (!arrayIsEmpty(backwardIds)) {
|
|
217
219
|
forwardIds.unshift(addCheckpointImpl());
|
|
218
220
|
updateStore(0, currentId);
|
|
219
|
-
currentId = backwardIds
|
|
221
|
+
currentId = arrayPop(backwardIds);
|
|
220
222
|
checkpointsChanged = 1;
|
|
221
223
|
}
|
|
222
224
|
};
|
|
223
225
|
const goForwardImpl = () => {
|
|
224
226
|
if (!arrayIsEmpty(forwardIds)) {
|
|
225
|
-
backwardIds
|
|
227
|
+
arrayPush(backwardIds, currentId);
|
|
226
228
|
currentId = forwardIds.shift();
|
|
227
229
|
updateStore(1, currentId);
|
|
228
230
|
checkpointsChanged = 1;
|
package/lib/debug/indexes.js
CHANGED
|
@@ -12,6 +12,8 @@ const arrayLength = (array) => array.length;
|
|
|
12
12
|
const arrayIsEmpty = (array) => arrayLength(array) == 0;
|
|
13
13
|
const arrayReduce = (array, cb, initial) => array.reduce(cb, initial);
|
|
14
14
|
const arrayFromSecond = (ids) => ids.slice(1);
|
|
15
|
+
const arrayPush = (array, value) => array.push(value);
|
|
16
|
+
const arrayPop = (array) => array.pop();
|
|
15
17
|
|
|
16
18
|
const isUndefined = (thing) => thing == void 0;
|
|
17
19
|
const ifNotUndefined = (value, then, otherwise) =>
|
|
@@ -203,7 +205,7 @@ const getListenerFunctions = (getThing) => {
|
|
|
203
205
|
const allListeners = mapNew();
|
|
204
206
|
const addListener = (listener, deepSet, idOrNulls = []) => {
|
|
205
207
|
thing ??= getThing();
|
|
206
|
-
const id = listenerPool
|
|
208
|
+
const id = arrayPop(listenerPool) ?? '' + nextId++;
|
|
207
209
|
mapSet(allListeners, id, [listener, deepSet, idOrNulls]);
|
|
208
210
|
addDeepSet(deepSet, id, idOrNulls);
|
|
209
211
|
return id;
|
|
@@ -224,7 +226,7 @@ const getListenerFunctions = (getThing) => {
|
|
|
224
226
|
forDeepSet(collDel)(deepSet, id, ...idOrNulls);
|
|
225
227
|
mapSet(allListeners, id);
|
|
226
228
|
if (arrayLength(listenerPool) < 1e3) {
|
|
227
|
-
listenerPool
|
|
229
|
+
arrayPush(listenerPool, id);
|
|
228
230
|
}
|
|
229
231
|
return idOrNulls;
|
|
230
232
|
},
|
package/lib/debug/metrics.js
CHANGED
|
@@ -13,6 +13,8 @@ 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 arrayFromSecond = (ids) => ids.slice(1);
|
|
16
|
+
const arrayPush = (array, value) => array.push(value);
|
|
17
|
+
const arrayPop = (array) => array.pop();
|
|
16
18
|
|
|
17
19
|
const mathMax = Math.max;
|
|
18
20
|
const mathMin = Math.min;
|
|
@@ -206,7 +208,7 @@ const getListenerFunctions = (getThing) => {
|
|
|
206
208
|
const allListeners = mapNew();
|
|
207
209
|
const addListener = (listener, deepSet, idOrNulls = []) => {
|
|
208
210
|
thing ??= getThing();
|
|
209
|
-
const id = listenerPool
|
|
211
|
+
const id = arrayPop(listenerPool) ?? '' + nextId++;
|
|
210
212
|
mapSet(allListeners, id, [listener, deepSet, idOrNulls]);
|
|
211
213
|
addDeepSet(deepSet, id, idOrNulls);
|
|
212
214
|
return id;
|
|
@@ -227,7 +229,7 @@ const getListenerFunctions = (getThing) => {
|
|
|
227
229
|
forDeepSet(collDel)(deepSet, id, ...idOrNulls);
|
|
228
230
|
mapSet(allListeners, id);
|
|
229
231
|
if (arrayLength(listenerPool) < 1e3) {
|
|
230
|
-
listenerPool
|
|
232
|
+
arrayPush(listenerPool, id);
|
|
231
233
|
}
|
|
232
234
|
return idOrNulls;
|
|
233
235
|
},
|
|
@@ -7,6 +7,8 @@ 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 arrayFromSecond = (ids) => ids.slice(1);
|
|
10
|
+
const arrayPush = (array, value) => array.push(value);
|
|
11
|
+
const arrayPop = (array) => array.pop();
|
|
10
12
|
|
|
11
13
|
const isUndefined = (thing) => thing == void 0;
|
|
12
14
|
const ifNotUndefined = (value, then, otherwise) =>
|
|
@@ -196,7 +198,7 @@ const getListenerFunctions = (getThing) => {
|
|
|
196
198
|
const allListeners = mapNew();
|
|
197
199
|
const addListener = (listener, deepSet, idOrNulls = []) => {
|
|
198
200
|
thing ??= getThing();
|
|
199
|
-
const id = listenerPool
|
|
201
|
+
const id = arrayPop(listenerPool) ?? '' + nextId++;
|
|
200
202
|
mapSet(allListeners, id, [listener, deepSet, idOrNulls]);
|
|
201
203
|
addDeepSet(deepSet, id, idOrNulls);
|
|
202
204
|
return id;
|
|
@@ -217,7 +219,7 @@ const getListenerFunctions = (getThing) => {
|
|
|
217
219
|
forDeepSet(collDel)(deepSet, id, ...idOrNulls);
|
|
218
220
|
mapSet(allListeners, id);
|
|
219
221
|
if (arrayLength(listenerPool) < 1e3) {
|
|
220
|
-
listenerPool
|
|
222
|
+
arrayPush(listenerPool, id);
|
|
221
223
|
}
|
|
222
224
|
return idOrNulls;
|
|
223
225
|
},
|
package/lib/debug/store.d.ts
CHANGED
|
@@ -332,6 +332,35 @@ export type CellListener = (
|
|
|
332
332
|
getCellChange: GetCellChange | undefined,
|
|
333
333
|
) => void;
|
|
334
334
|
|
|
335
|
+
/**
|
|
336
|
+
* The InvalidCellListener type describes a function that is used to listen to
|
|
337
|
+
* attempts to set invalid data to a Cell.
|
|
338
|
+
*
|
|
339
|
+
* A InvalidCellListener is provided when using the addInvalidCellListener
|
|
340
|
+
* method. See that method for specific examples.
|
|
341
|
+
*
|
|
342
|
+
* When called, a InvalidCellListener is given a reference to the Store, the Id
|
|
343
|
+
* of the Table, the Id of the Row, and the Id of Cell that were being attempted
|
|
344
|
+
* to be changed. It is also given the invalid value of the Cell, which could
|
|
345
|
+
* have been of absolutely any type. Since there could have been multiple failed
|
|
346
|
+
* attempts to set the Cell within a single transaction, this is an array
|
|
347
|
+
* containing each attempt, chronologically.
|
|
348
|
+
*
|
|
349
|
+
* @param store A reference to the Store that was being changed.
|
|
350
|
+
* @param tableId The Id of the Table that was being changed.
|
|
351
|
+
* @param rowId The Id of the Row that was being changed.
|
|
352
|
+
* @param cellId The Id of the Cell that was being changed.
|
|
353
|
+
* @param invalidCells An array of the values of the Cell that were invalid.
|
|
354
|
+
* @category Listener
|
|
355
|
+
*/
|
|
356
|
+
export type InvalidCellListener = (
|
|
357
|
+
store: Store,
|
|
358
|
+
tableId: Id,
|
|
359
|
+
rowId: Id,
|
|
360
|
+
cellId: Id,
|
|
361
|
+
invalidCells: any[],
|
|
362
|
+
) => void;
|
|
363
|
+
|
|
335
364
|
/**
|
|
336
365
|
* The GetCellChange type describes a function that returns information about
|
|
337
366
|
* any Cell's changes during a transaction.
|
|
@@ -545,6 +574,9 @@ export type StoreListenerStats = {
|
|
|
545
574
|
* unique Id. And the setPartialRow method lets you update multiple Cell values
|
|
546
575
|
* in a Row without affecting the others.
|
|
547
576
|
*
|
|
577
|
+
* You can listen to attempts to write invalid data to a Cell with the
|
|
578
|
+
* addInvalidCellListener method.
|
|
579
|
+
*
|
|
548
580
|
* The transaction method is used to wrap multiple changes to the Store so that
|
|
549
581
|
* the relevant listeners only fire once.
|
|
550
582
|
*
|
|
@@ -2371,6 +2403,133 @@ export interface Store {
|
|
|
2371
2403
|
mutator?: boolean,
|
|
2372
2404
|
): Id;
|
|
2373
2405
|
|
|
2406
|
+
/**
|
|
2407
|
+
* The addInvalidCellListener method registers a listener function with the
|
|
2408
|
+
* Store that will be called whenever invalid data was attempted to be written
|
|
2409
|
+
* to a Cell.
|
|
2410
|
+
*
|
|
2411
|
+
* You can either listen to a single Cell (by specifying the Table Id, Row Id,
|
|
2412
|
+
* and Cell Id as the method's first three parameters) or invalid attempts to
|
|
2413
|
+
* change any Cell (by providing `null` wildcards).
|
|
2414
|
+
*
|
|
2415
|
+
* All, some, or none of the `tableId`, `rowId`, and `cellId` parameters can
|
|
2416
|
+
* be wildcarded with `null`. You can listen to a specific Cell in a specific
|
|
2417
|
+
* Row in a specific Table, any Cell in any Row in any Table, for example - or
|
|
2418
|
+
* every other combination of wildcards.
|
|
2419
|
+
*
|
|
2420
|
+
* The provided listener is an InvalidCellListener function, and will be
|
|
2421
|
+
* called with a reference to the Store, the Id of the Table, the Id of the
|
|
2422
|
+
* Row, and the Id of Cell that were being attempted to be changed. It is also
|
|
2423
|
+
* given the invalid value of the Cell, which could have been of absolutely
|
|
2424
|
+
* any type. Since there could have been multiple failed attempts to set the
|
|
2425
|
+
* Cell within a single transaction, this is an array containing each attempt,
|
|
2426
|
+
* chronologically.
|
|
2427
|
+
*
|
|
2428
|
+
* Use the optional mutator parameter to indicate that there is code in the
|
|
2429
|
+
* listener that will mutate Store data. If set to `false` (or omitted), such
|
|
2430
|
+
* mutations will be silently ignored. All relevant mutator listeners (with
|
|
2431
|
+
* this flag set to `true`) are called _before_ any non-mutator listeners
|
|
2432
|
+
* (since the latter may become relevant due to changes made in the former).
|
|
2433
|
+
* The changes made by mutator listeners do not fire other mutating listeners,
|
|
2434
|
+
* though they will fire non-mutator listeners.
|
|
2435
|
+
*
|
|
2436
|
+
* @param tableId The Id of the Table to listen to, or `null` as a wildcard.
|
|
2437
|
+
* @param rowId The Id of the Row to listen to, or `null` as a wildcard.
|
|
2438
|
+
* @param cellId The Id of the Cell to listen to, or `null` as a wildcard.
|
|
2439
|
+
* @param listener The function that will be called whenever an attempt to
|
|
2440
|
+
* write invalid data to the matching Cell was made.
|
|
2441
|
+
* @param mutator An optional boolean that indicates that the listener mutates
|
|
2442
|
+
* Store data.
|
|
2443
|
+
* @returns A unique Id for the listener that can later be used to call it
|
|
2444
|
+
* explicitly, or to remove it.
|
|
2445
|
+
* @example
|
|
2446
|
+
* This example registers a listener that responds to any invalid changes to a
|
|
2447
|
+
* specific Cell.
|
|
2448
|
+
*
|
|
2449
|
+
* ```js
|
|
2450
|
+
* const store = createStore().setTables({
|
|
2451
|
+
* pets: {fido: {species: 'dog', color: 'brown'}},
|
|
2452
|
+
* });
|
|
2453
|
+
* const listenerId = store.addInvalidCellListener(
|
|
2454
|
+
* 'pets',
|
|
2455
|
+
* 'fido',
|
|
2456
|
+
* 'color',
|
|
2457
|
+
* (store, tableId, rowId, cellId, invalidCells) => {
|
|
2458
|
+
* console.log('Invalid color cell in fido row in pets table');
|
|
2459
|
+
* console.log(invalidCells);
|
|
2460
|
+
* },
|
|
2461
|
+
* );
|
|
2462
|
+
*
|
|
2463
|
+
* store.setCell('pets', 'fido', 'color', {r: '96', g: '4B', b: '00'});
|
|
2464
|
+
* // -> 'Invalid color cell in fido row in pets table'
|
|
2465
|
+
* // -> [{r: '96', g: '4B', b: '00'}]
|
|
2466
|
+
*
|
|
2467
|
+
* store.delListener(listenerId);
|
|
2468
|
+
* ```
|
|
2469
|
+
* @example
|
|
2470
|
+
* This example registers a listener that responds to any invalid changes to
|
|
2471
|
+
* any Cell.
|
|
2472
|
+
*
|
|
2473
|
+
* ```js
|
|
2474
|
+
* const store = createStore().setTables({
|
|
2475
|
+
* pets: {fido: {species: 'dog', color: 'brown'}},
|
|
2476
|
+
* });
|
|
2477
|
+
* const listenerId = store.addInvalidCellListener(
|
|
2478
|
+
* null,
|
|
2479
|
+
* null,
|
|
2480
|
+
* null,
|
|
2481
|
+
* (store, tableId, rowId, cellId) => {
|
|
2482
|
+
* console.log(
|
|
2483
|
+
* `Invalid ${cellId} cell in ${rowId} row in ${tableId} table`,
|
|
2484
|
+
* );
|
|
2485
|
+
* },
|
|
2486
|
+
* );
|
|
2487
|
+
*
|
|
2488
|
+
* store.setCell('pets', 'fido', 'color', {r: '96', g: '4B', b: '00'});
|
|
2489
|
+
* // -> 'Invalid color cell in fido row in pets table'
|
|
2490
|
+
* store.setTable('sales', {fido: {date: new Date()}});
|
|
2491
|
+
* // -> 'Invalid date cell in fido row in sales table'
|
|
2492
|
+
*
|
|
2493
|
+
* store.delListener(listenerId);
|
|
2494
|
+
* ```
|
|
2495
|
+
* @example
|
|
2496
|
+
* This example registers a listener that responds to any changes to a
|
|
2497
|
+
* specific Cell, and which also mutates the Store itself.
|
|
2498
|
+
*
|
|
2499
|
+
* ```js
|
|
2500
|
+
* const store = createStore().setTables({
|
|
2501
|
+
* pets: {fido: {species: 'dog', color: 'brown'}},
|
|
2502
|
+
* });
|
|
2503
|
+
* const listenerId = store.addInvalidCellListener(
|
|
2504
|
+
* 'pets',
|
|
2505
|
+
* 'fido',
|
|
2506
|
+
* 'color',
|
|
2507
|
+
* (store, tableId, rowId, cellId, invalidCells) =>
|
|
2508
|
+
* store.setCell(
|
|
2509
|
+
* 'meta',
|
|
2510
|
+
* 'invalid_updates',
|
|
2511
|
+
* `${tableId}_${rowId}_${cellId}`,
|
|
2512
|
+
* JSON.stringify(invalidCells[0]),
|
|
2513
|
+
* ),
|
|
2514
|
+
* true,
|
|
2515
|
+
* );
|
|
2516
|
+
*
|
|
2517
|
+
* store.setCell('pets', 'fido', 'color', {r: '96', g: '4B', b: '00'});
|
|
2518
|
+
* console.log(store.getRow('meta', 'invalid_updates'));
|
|
2519
|
+
* // -> {'pets_fido_color': '{"r":"96","g":"4B","b":"00"}'}
|
|
2520
|
+
*
|
|
2521
|
+
* store.delListener(listenerId);
|
|
2522
|
+
* ```
|
|
2523
|
+
* @category Listener
|
|
2524
|
+
*/
|
|
2525
|
+
addInvalidCellListener(
|
|
2526
|
+
tableId: IdOrNull,
|
|
2527
|
+
rowId: IdOrNull,
|
|
2528
|
+
cellId: IdOrNull,
|
|
2529
|
+
listener: InvalidCellListener,
|
|
2530
|
+
mutator?: boolean,
|
|
2531
|
+
): Id;
|
|
2532
|
+
|
|
2374
2533
|
/**
|
|
2375
2534
|
* The callListener method provides a way for you to manually provoke a
|
|
2376
2535
|
* listener to be called, even if the underlying data hasn't changed.
|
package/lib/debug/store.js
CHANGED
|
@@ -15,6 +15,8 @@ const arrayIsEmpty = (array) => arrayLength(array) == 0;
|
|
|
15
15
|
const arrayReduce = (array, cb, initial) => array.reduce(cb, initial);
|
|
16
16
|
const arrayFilter = (array, cb) => array.filter(cb);
|
|
17
17
|
const arrayFromSecond = (ids) => ids.slice(1);
|
|
18
|
+
const arrayPush = (array, value) => array.push(value);
|
|
19
|
+
const arrayPop = (array) => array.pop();
|
|
18
20
|
|
|
19
21
|
const jsonString = (obj) =>
|
|
20
22
|
JSON.stringify(obj, (_key, value) =>
|
|
@@ -125,7 +127,7 @@ const getListenerFunctions = (getThing) => {
|
|
|
125
127
|
const allListeners = mapNew();
|
|
126
128
|
const addListener = (listener, deepSet, idOrNulls = []) => {
|
|
127
129
|
thing ??= getThing();
|
|
128
|
-
const id = listenerPool
|
|
130
|
+
const id = arrayPop(listenerPool) ?? '' + nextId++;
|
|
129
131
|
mapSet(allListeners, id, [listener, deepSet, idOrNulls]);
|
|
130
132
|
addDeepSet(deepSet, id, idOrNulls);
|
|
131
133
|
return id;
|
|
@@ -146,7 +148,7 @@ const getListenerFunctions = (getThing) => {
|
|
|
146
148
|
forDeepSet(collDel)(deepSet, id, ...idOrNulls);
|
|
147
149
|
mapSet(allListeners, id);
|
|
148
150
|
if (arrayLength(listenerPool) < 1e3) {
|
|
149
|
-
listenerPool
|
|
151
|
+
arrayPush(listenerPool, id);
|
|
150
152
|
}
|
|
151
153
|
return idOrNulls;
|
|
152
154
|
},
|
|
@@ -207,6 +209,7 @@ const createStore = () => {
|
|
|
207
209
|
const changedRowIds = mapNew();
|
|
208
210
|
const changedCellIds = mapNew();
|
|
209
211
|
const changedCells = mapNew();
|
|
212
|
+
const invalidCells = mapNew();
|
|
210
213
|
const schemaMap = mapNew();
|
|
211
214
|
const schemaDefaultRows = mapNew();
|
|
212
215
|
const tablesMap = mapNew();
|
|
@@ -217,6 +220,7 @@ const createStore = () => {
|
|
|
217
220
|
const rowListeners = mapNewPair();
|
|
218
221
|
const cellIdsListeners = mapNewPair();
|
|
219
222
|
const cellListeners = mapNewPair();
|
|
223
|
+
const invalidCellListeners = mapNewPair();
|
|
220
224
|
const [addListener, callListeners, delListenerImpl, callListenerImpl] =
|
|
221
225
|
getListenerFunctions(() => store);
|
|
222
226
|
const validateSchema = (schema) =>
|
|
@@ -240,13 +244,13 @@ const createStore = () => {
|
|
|
240
244
|
const validateTables = (tables) => validate(tables, validateTable);
|
|
241
245
|
const validateTable = (table, tableId) =>
|
|
242
246
|
(!hasSchema || collHas(schemaMap, tableId)) &&
|
|
243
|
-
validate(table, (row) => validateRow(tableId, row));
|
|
244
|
-
const validateRow = (tableId, row, skipDefaults) =>
|
|
247
|
+
validate(table, (row, rowId) => validateRow(tableId, rowId, row));
|
|
248
|
+
const validateRow = (tableId, rowId, row, skipDefaults) =>
|
|
245
249
|
validate(
|
|
246
250
|
skipDefaults ? row : addDefaultsToRow(row, tableId),
|
|
247
251
|
(cell, cellId) =>
|
|
248
252
|
ifNotUndefined(
|
|
249
|
-
getValidatedCell(tableId, cellId, cell),
|
|
253
|
+
getValidatedCell(tableId, rowId, cellId, cell),
|
|
250
254
|
(validCell) => {
|
|
251
255
|
row[cellId] = validCell;
|
|
252
256
|
return true;
|
|
@@ -254,15 +258,17 @@ const createStore = () => {
|
|
|
254
258
|
() => false,
|
|
255
259
|
),
|
|
256
260
|
);
|
|
257
|
-
const getValidatedCell = (tableId, cellId, cell) =>
|
|
261
|
+
const getValidatedCell = (tableId, rowId, cellId, cell) =>
|
|
258
262
|
hasSchema
|
|
259
263
|
? ifNotUndefined(
|
|
260
264
|
mapGet(mapGet(schemaMap, tableId), cellId),
|
|
261
265
|
(cellSchema) =>
|
|
262
|
-
getCellType(cell) != cellSchema[TYPE]
|
|
266
|
+
getCellType(cell) != cellSchema[TYPE]
|
|
267
|
+
? cellInvalid(tableId, rowId, cellId, cell, cellSchema[DEFAULT])
|
|
268
|
+
: cell,
|
|
263
269
|
)
|
|
264
270
|
: isUndefined(getCellType(cell))
|
|
265
|
-
?
|
|
271
|
+
? cellInvalid(tableId, rowId, cellId, cell)
|
|
266
272
|
: cell;
|
|
267
273
|
const addDefaultsToRow = (row, tableId) => {
|
|
268
274
|
ifNotUndefined(mapGet(schemaDefaultRows, tableId), (defaultRow) =>
|
|
@@ -398,6 +404,17 @@ const createStore = () => {
|
|
|
398
404
|
cellId,
|
|
399
405
|
oldCell,
|
|
400
406
|
);
|
|
407
|
+
const cellInvalid = (tableId, rowId, cellId, invalidCell, defaultedCell) => {
|
|
408
|
+
arrayPush(
|
|
409
|
+
mapEnsure(
|
|
410
|
+
mapEnsure(mapEnsure(invalidCells, tableId, mapNew()), rowId, mapNew()),
|
|
411
|
+
cellId,
|
|
412
|
+
[],
|
|
413
|
+
),
|
|
414
|
+
invalidCell,
|
|
415
|
+
);
|
|
416
|
+
return defaultedCell;
|
|
417
|
+
};
|
|
401
418
|
const getCellChange = (tableId, rowId, cellId) => {
|
|
402
419
|
const changedRow = mapGet(mapGet(changedCells, tableId), rowId);
|
|
403
420
|
const newCell = getCell(tableId, rowId, cellId);
|
|
@@ -405,6 +422,25 @@ const createStore = () => {
|
|
|
405
422
|
? [true, mapGet(changedRow, cellId), newCell]
|
|
406
423
|
: [false, newCell, newCell];
|
|
407
424
|
};
|
|
425
|
+
const callInvalidCellListeners = (mutator) => {
|
|
426
|
+
if (!collIsEmpty(invalidCellListeners[mutator])) {
|
|
427
|
+
collForEach(
|
|
428
|
+
mutator
|
|
429
|
+
? mapClone(invalidCells, (table) => mapClone(table, mapClone))
|
|
430
|
+
: invalidCells,
|
|
431
|
+
(rows, tableId) =>
|
|
432
|
+
collForEach(rows, (cells, rowId) =>
|
|
433
|
+
collForEach(cells, (invalidCell, cellId) =>
|
|
434
|
+
callListeners(
|
|
435
|
+
invalidCellListeners[mutator],
|
|
436
|
+
[tableId, rowId, cellId],
|
|
437
|
+
invalidCell,
|
|
438
|
+
),
|
|
439
|
+
),
|
|
440
|
+
),
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
};
|
|
408
444
|
const callListenersForChanges = (mutator) => {
|
|
409
445
|
const emptyIdListeners =
|
|
410
446
|
collIsEmpty(cellIdsListeners[mutator]) &&
|
|
@@ -498,46 +534,50 @@ const createStore = () => {
|
|
|
498
534
|
const getJson = () => jsonString(tablesMap);
|
|
499
535
|
const getSchemaJson = () => jsonString(schemaMap);
|
|
500
536
|
const setTables = (tables) => {
|
|
501
|
-
|
|
502
|
-
transaction(() => setValidTables(tables))
|
|
503
|
-
|
|
537
|
+
validateTables(tables)
|
|
538
|
+
? transaction(() => setValidTables(tables))
|
|
539
|
+
: transaction();
|
|
504
540
|
return store;
|
|
505
541
|
};
|
|
506
542
|
const setTable = (tableId, table) => {
|
|
507
|
-
|
|
508
|
-
transaction(() => setValidTable(tableId, table))
|
|
509
|
-
|
|
543
|
+
validateTable(table, tableId)
|
|
544
|
+
? transaction(() => setValidTable(tableId, table))
|
|
545
|
+
: transaction();
|
|
510
546
|
return store;
|
|
511
547
|
};
|
|
512
548
|
const setRow = (tableId, rowId, row) => {
|
|
513
|
-
|
|
514
|
-
setValidRowTransaction(tableId, rowId, row)
|
|
515
|
-
|
|
549
|
+
validateRow(tableId, rowId, row)
|
|
550
|
+
? setValidRowTransaction(tableId, rowId, row)
|
|
551
|
+
: transaction();
|
|
516
552
|
return store;
|
|
517
553
|
};
|
|
518
554
|
const addRow = (tableId, row) => {
|
|
519
555
|
let rowId = void 0;
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
556
|
+
validateRow(tableId, rowId, row)
|
|
557
|
+
? setValidRowTransaction(
|
|
558
|
+
tableId,
|
|
559
|
+
(rowId = getNewRowId(mapGet(tablesMap, tableId))),
|
|
560
|
+
row,
|
|
561
|
+
)
|
|
562
|
+
: transaction();
|
|
524
563
|
return rowId;
|
|
525
564
|
};
|
|
526
565
|
const setPartialRow = (tableId, rowId, partialRow) => {
|
|
527
|
-
|
|
528
|
-
transaction(() => {
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
566
|
+
validateRow(tableId, rowId, partialRow, 1)
|
|
567
|
+
? transaction(() => {
|
|
568
|
+
const table = getOrCreateTable(tableId);
|
|
569
|
+
objForEach(partialRow, (cell, cellId) =>
|
|
570
|
+
setCellIntoDefaultRow(tableId, table, rowId, cellId, cell),
|
|
571
|
+
);
|
|
572
|
+
})
|
|
573
|
+
: transaction();
|
|
535
574
|
return store;
|
|
536
575
|
};
|
|
537
576
|
const setCell = (tableId, rowId, cellId, cell) => {
|
|
538
577
|
ifNotUndefined(
|
|
539
578
|
getValidatedCell(
|
|
540
579
|
tableId,
|
|
580
|
+
rowId,
|
|
541
581
|
cellId,
|
|
542
582
|
isFunction(cell) ? cell(getCell(tableId, rowId, cellId)) : cell,
|
|
543
583
|
),
|
|
@@ -551,6 +591,7 @@ const createStore = () => {
|
|
|
551
591
|
validCell,
|
|
552
592
|
),
|
|
553
593
|
),
|
|
594
|
+
transaction,
|
|
554
595
|
);
|
|
555
596
|
return store;
|
|
556
597
|
};
|
|
@@ -611,16 +652,24 @@ const createStore = () => {
|
|
|
611
652
|
return;
|
|
612
653
|
}
|
|
613
654
|
transactions++;
|
|
614
|
-
const result = actions();
|
|
655
|
+
const result = actions?.();
|
|
615
656
|
transactions--;
|
|
616
657
|
if (transactions == 0) {
|
|
617
658
|
transactions = 1;
|
|
659
|
+
callInvalidCellListeners(1);
|
|
618
660
|
callListenersForChanges(1);
|
|
619
661
|
transactions = -1;
|
|
662
|
+
callInvalidCellListeners(0);
|
|
620
663
|
callListenersForChanges(0);
|
|
621
664
|
transactions = 0;
|
|
622
665
|
arrayForEach(
|
|
623
|
-
[
|
|
666
|
+
[
|
|
667
|
+
changedCells,
|
|
668
|
+
invalidCells,
|
|
669
|
+
changedTableIds,
|
|
670
|
+
changedRowIds,
|
|
671
|
+
changedCellIds,
|
|
672
|
+
],
|
|
624
673
|
collClear,
|
|
625
674
|
);
|
|
626
675
|
}
|
|
@@ -660,6 +709,12 @@ const createStore = () => {
|
|
|
660
709
|
rowId,
|
|
661
710
|
cellId,
|
|
662
711
|
]);
|
|
712
|
+
const addInvalidCellListener = (tableId, rowId, cellId, listener, mutator) =>
|
|
713
|
+
addListener(listener, invalidCellListeners[mutator ? 1 : 0], [
|
|
714
|
+
tableId,
|
|
715
|
+
rowId,
|
|
716
|
+
cellId,
|
|
717
|
+
]);
|
|
663
718
|
const callListener = (listenerId) => {
|
|
664
719
|
callListenerImpl(listenerId, [getTableIds, getRowIds, getCellIds], (ids) =>
|
|
665
720
|
isUndefined(ids[2]) ? [] : Array(2).fill(getCell(...ids)),
|
|
@@ -717,6 +772,7 @@ const createStore = () => {
|
|
|
717
772
|
addRowListener,
|
|
718
773
|
addCellIdsListener,
|
|
719
774
|
addCellListener,
|
|
775
|
+
addInvalidCellListener,
|
|
720
776
|
callListener,
|
|
721
777
|
delListener,
|
|
722
778
|
getListenerStats,
|