tinybase 1.0.3 → 1.1.0-beta.1

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 (42) hide show
  1. package/lib/checkpoints.d.ts +66 -0
  2. package/lib/checkpoints.js +1 -1
  3. package/lib/checkpoints.js.gz +0 -0
  4. package/lib/debug/checkpoints.d.ts +66 -0
  5. package/lib/debug/checkpoints.js +16 -10
  6. package/lib/debug/indexes.d.ts +167 -6
  7. package/lib/debug/indexes.js +35 -2
  8. package/lib/debug/metrics.d.ts +72 -0
  9. package/lib/debug/metrics.js +12 -2
  10. package/lib/debug/relationships.d.ts +86 -1
  11. package/lib/debug/relationships.js +18 -2
  12. package/lib/debug/store.d.ts +187 -5
  13. package/lib/debug/store.js +217 -164
  14. package/lib/debug/tinybase.js +272 -172
  15. package/lib/indexes.d.ts +167 -6
  16. package/lib/indexes.js +1 -1
  17. package/lib/indexes.js.gz +0 -0
  18. package/lib/metrics.d.ts +72 -0
  19. package/lib/metrics.js +1 -1
  20. package/lib/metrics.js.gz +0 -0
  21. package/lib/relationships.d.ts +86 -1
  22. package/lib/relationships.js +1 -1
  23. package/lib/relationships.js.gz +0 -0
  24. package/lib/store.d.ts +187 -5
  25. package/lib/store.js +1 -1
  26. package/lib/store.js.gz +0 -0
  27. package/lib/tinybase.js +1 -1
  28. package/lib/tinybase.js.gz +0 -0
  29. package/lib/umd/checkpoints.js +1 -1
  30. package/lib/umd/checkpoints.js.gz +0 -0
  31. package/lib/umd/indexes.js +1 -1
  32. package/lib/umd/indexes.js.gz +0 -0
  33. package/lib/umd/metrics.js +1 -1
  34. package/lib/umd/metrics.js.gz +0 -0
  35. package/lib/umd/relationships.js +1 -1
  36. package/lib/umd/relationships.js.gz +0 -0
  37. package/lib/umd/store.js +1 -1
  38. package/lib/umd/store.js.gz +0 -0
  39. package/lib/umd/tinybase.js +1 -1
  40. package/lib/umd/tinybase.js.gz +0 -0
  41. package/package.json +14 -14
  42. package/readme.md +2 -2
@@ -29,6 +29,8 @@ const arrayReduce = (array, cb, initial) => array.reduce(cb, initial);
29
29
  const arrayFilter = (array, cb) => array.filter(cb);
30
30
  const arrayFromSecond = (ids) => ids.slice(1);
31
31
  const arrayClear = (array, to) => array.splice(0, to);
32
+ const arrayPush = (array, value) => array.push(value);
33
+ const arrayPop = (array) => array.pop();
32
34
 
33
35
  const jsonString = (obj) =>
34
36
  JSON.stringify(obj, (_key, value) =>
@@ -110,6 +112,8 @@ const getDefinableFunctions = (store, getDefaultThing, validateRowValue) => {
110
112
  const storeListenerIds = mapNew();
111
113
  const getStore = () => store;
112
114
  const getThingIds = () => mapKeys(tableIds);
115
+ const forEachThing = (cb) => mapForEach(things, cb);
116
+ const hasThing = (id) => collHas(things, id);
113
117
  const getTableId = (id) => mapGet(tableIds, id);
114
118
  const getThing = (id) => mapGet(things, id);
115
119
  const setThing = (id, thing) => mapSet(things, id, thing);
@@ -199,6 +203,8 @@ const getDefinableFunctions = (store, getDefaultThing, validateRowValue) => {
199
203
  return [
200
204
  getStore,
201
205
  getThingIds,
206
+ forEachThing,
207
+ hasThing,
202
208
  getTableId,
203
209
  getThing,
204
210
  setThing,
@@ -250,7 +256,7 @@ const getListenerFunctions = (getThing) => {
250
256
  const allListeners = mapNew();
251
257
  const addListener = (listener, deepSet, idOrNulls = []) => {
252
258
  thing ??= getThing();
253
- const id = listenerPool.pop() ?? '' + nextId++;
259
+ const id = arrayPop(listenerPool) ?? '' + nextId++;
254
260
  mapSet(allListeners, id, [listener, deepSet, idOrNulls]);
255
261
  addDeepSet(deepSet, id, idOrNulls);
256
262
  return id;
@@ -271,7 +277,7 @@ const getListenerFunctions = (getThing) => {
271
277
  forDeepSet(collDel)(deepSet, id, ...idOrNulls);
272
278
  mapSet(allListeners, id);
273
279
  if (arrayLength(listenerPool) < 1e3) {
274
- listenerPool.push(id);
280
+ arrayPush(listenerPool, id);
275
281
  }
276
282
  return idOrNulls;
277
283
  },
@@ -357,7 +363,7 @@ const createCheckpoints = getCreateFunction((store) => {
357
363
  (_store, tableId, rowId, cellId, newCell, oldCell) => {
358
364
  if (listening) {
359
365
  ifNotUndefined(currentId, () => {
360
- backwardIds.push(currentId);
366
+ arrayPush(backwardIds, currentId);
361
367
  trimBackwardsIds();
362
368
  clearCheckpointIds(forwardIds);
363
369
  currentId = void 0;
@@ -376,7 +382,7 @@ const createCheckpoints = getCreateFunction((store) => {
376
382
  if (collIsEmpty(mapSet(row, cellId))) {
377
383
  if (collIsEmpty(mapSet(table, rowId))) {
378
384
  if (collIsEmpty(mapSet(delta, tableId))) {
379
- currentId = backwardIds.pop();
385
+ currentId = arrayPop(backwardIds);
380
386
  checkpointsChanged = 1;
381
387
  }
382
388
  }
@@ -400,13 +406,13 @@ const createCheckpoints = getCreateFunction((store) => {
400
406
  if (!arrayIsEmpty(backwardIds)) {
401
407
  forwardIds.unshift(addCheckpointImpl());
402
408
  updateStore(0, currentId);
403
- currentId = backwardIds.pop();
409
+ currentId = arrayPop(backwardIds);
404
410
  checkpointsChanged = 1;
405
411
  }
406
412
  };
407
413
  const goForwardImpl = () => {
408
414
  if (!arrayIsEmpty(forwardIds)) {
409
- backwardIds.push(currentId);
415
+ arrayPush(backwardIds, currentId);
410
416
  currentId = forwardIds.shift();
411
417
  updateStore(1, currentId);
412
418
  checkpointsChanged = 1;
@@ -429,10 +435,7 @@ const createCheckpoints = getCreateFunction((store) => {
429
435
  return id;
430
436
  };
431
437
  const setCheckpoint = (checkpointId, label) => {
432
- if (
433
- collHas(deltas, checkpointId) &&
434
- mapGet(labels, checkpointId) !== label
435
- ) {
438
+ if (hasCheckpoint(checkpointId) && mapGet(labels, checkpointId) !== label) {
436
439
  mapSet(labels, checkpointId, label);
437
440
  callListeners(checkpointListeners, [checkpointId]);
438
441
  }
@@ -440,6 +443,9 @@ const createCheckpoints = getCreateFunction((store) => {
440
443
  };
441
444
  const getStore = () => store;
442
445
  const getCheckpointIds = () => [[...backwardIds], currentId, [...forwardIds]];
446
+ const forEachCheckpoint = (checkpointCallback) =>
447
+ mapForEach(labels, checkpointCallback);
448
+ const hasCheckpoint = (checkpointId) => collHas(deltas, checkpointId);
443
449
  const getCheckpoint = (checkpointId) => mapGet(labels, checkpointId);
444
450
  const goBackward = () => {
445
451
  goBackwardImpl();
@@ -495,6 +501,8 @@ const createCheckpoints = getCreateFunction((store) => {
495
501
  setCheckpoint,
496
502
  getStore,
497
503
  getCheckpointIds,
504
+ forEachCheckpoint,
505
+ hasCheckpoint,
498
506
  getCheckpoint,
499
507
  goBackward,
500
508
  goForward,
@@ -517,6 +525,8 @@ const createIndexes = getCreateFunction((store) => {
517
525
  const [
518
526
  getStore,
519
527
  getIndexIds,
528
+ forEachIndexImpl,
529
+ hasIndex,
520
530
  getTableId,
521
531
  getIndex,
522
532
  setIndex,
@@ -529,6 +539,7 @@ const createIndexes = getCreateFunction((store) => {
529
539
  const [addListener, callListeners, delListenerImpl] = getListenerFunctions(
530
540
  () => indexes,
531
541
  );
542
+ const hasSlice = (indexId, sliceId) => collHas(getIndex(indexId), sliceId);
532
543
  const setIndexDefinition = (
533
544
  indexId,
534
545
  tableId,
@@ -624,6 +635,26 @@ const createIndexes = getCreateFunction((store) => {
624
635
  );
625
636
  return indexes;
626
637
  };
638
+ const forEachIndex = (indexCallback) =>
639
+ forEachIndexImpl((indexId, slices) =>
640
+ indexCallback(indexId, (sliceCallback) =>
641
+ forEachSliceImpl(indexId, sliceCallback, slices),
642
+ ),
643
+ );
644
+ const forEachSlice = (indexId, sliceCallback) =>
645
+ forEachSliceImpl(indexId, sliceCallback, getIndex(indexId));
646
+ const forEachSliceImpl = (indexId, sliceCallback, slices) => {
647
+ const tableId = getTableId(indexId);
648
+ collForEach(slices, (rowIds, sliceId) =>
649
+ sliceCallback(sliceId, (rowCallback) =>
650
+ collForEach(rowIds, (rowId) =>
651
+ rowCallback(rowId, (cellCallback) =>
652
+ store.forEachCell(tableId, rowId, cellCallback),
653
+ ),
654
+ ),
655
+ ),
656
+ );
657
+ };
627
658
  const delIndexDefinition = (indexId) => {
628
659
  delDefinition(indexId);
629
660
  return indexes;
@@ -648,6 +679,10 @@ const createIndexes = getCreateFunction((store) => {
648
679
  delIndexDefinition,
649
680
  getStore,
650
681
  getIndexIds,
682
+ forEachIndex,
683
+ forEachSlice,
684
+ hasIndex,
685
+ hasSlice,
651
686
  getTableId,
652
687
  getSliceIds,
653
688
  getSliceRowIds,
@@ -705,6 +740,8 @@ const createMetrics = getCreateFunction((store) => {
705
740
  const [
706
741
  getStore,
707
742
  getMetricIds,
743
+ forEachMetric,
744
+ hasMetric,
708
745
  getTableId,
709
746
  getMetric,
710
747
  setMetric,
@@ -789,6 +826,8 @@ const createMetrics = getCreateFunction((store) => {
789
826
  delMetricDefinition,
790
827
  getStore,
791
828
  getMetricIds,
829
+ forEachMetric,
830
+ hasMetric,
792
831
  getTableId,
793
832
  getMetric,
794
833
  addMetricListener,
@@ -980,6 +1019,8 @@ const createRelationships = getCreateFunction((store) => {
980
1019
  const [
981
1020
  getStore,
982
1021
  getRelationshipIds,
1022
+ forEachRelationshipImpl,
1023
+ hasRelationship,
983
1024
  getLocalTableId,
984
1025
  getRelationship,
985
1026
  _setRelationship,
@@ -1093,6 +1134,12 @@ const createRelationships = getCreateFunction((store) => {
1093
1134
  );
1094
1135
  return relationships;
1095
1136
  };
1137
+ const forEachRelationship = (relationshipCallback) =>
1138
+ forEachRelationshipImpl((relationshipId) =>
1139
+ relationshipCallback(relationshipId, (rowCallback) =>
1140
+ store.forEachRow(getLocalTableId(relationshipId), rowCallback),
1141
+ ),
1142
+ );
1096
1143
  const delRelationshipDefinition = (relationshipId) => {
1097
1144
  mapSet(remoteTableIds, relationshipId);
1098
1145
  delDefinition(relationshipId);
@@ -1133,6 +1180,8 @@ const createRelationships = getCreateFunction((store) => {
1133
1180
  delRelationshipDefinition,
1134
1181
  getStore,
1135
1182
  getRelationshipIds,
1183
+ forEachRelationship,
1184
+ hasRelationship,
1136
1185
  getLocalTableId,
1137
1186
  getRemoteTableId,
1138
1187
  getRemoteRowId,
@@ -1165,8 +1214,9 @@ const getCellType = (cell) => {
1165
1214
  ? type
1166
1215
  : void 0;
1167
1216
  };
1168
- const validate = (obj, validateChild) => {
1169
- if (isUndefined(obj) || !isObject(obj) || objFrozen(obj)) {
1217
+ const validate = (obj, validateChild, onInvalidObj) => {
1218
+ if (isUndefined(obj) || !isObject(obj) || objIsEmpty(obj) || objFrozen(obj)) {
1219
+ onInvalidObj?.();
1170
1220
  return false;
1171
1221
  }
1172
1222
  objForEach(obj, (child, id) => {
@@ -1186,6 +1236,7 @@ const createStore = () => {
1186
1236
  const changedRowIds = mapNew();
1187
1237
  const changedCellIds = mapNew();
1188
1238
  const changedCells = mapNew();
1239
+ const invalidCells = mapNew();
1189
1240
  const schemaMap = mapNew();
1190
1241
  const schemaDefaultRows = mapNew();
1191
1242
  const tablesMap = mapNew();
@@ -1196,6 +1247,7 @@ const createStore = () => {
1196
1247
  const rowListeners = mapNewPair();
1197
1248
  const cellIdsListeners = mapNewPair();
1198
1249
  const cellListeners = mapNewPair();
1250
+ const invalidCellListeners = mapNewPair();
1199
1251
  const [addListener, callListeners, delListenerImpl, callListenerImpl] =
1200
1252
  getListenerFunctions(() => store);
1201
1253
  const validateSchema = (schema) =>
@@ -1216,32 +1268,41 @@ const createStore = () => {
1216
1268
  return true;
1217
1269
  }),
1218
1270
  );
1219
- const validateTables = (tables) => validate(tables, validateTable);
1271
+ const validateTables = (tables) =>
1272
+ validate(tables, validateTable, cellInvalid);
1220
1273
  const validateTable = (table, tableId) =>
1221
- (!hasSchema || collHas(schemaMap, tableId)) &&
1222
- validate(table, (row) => validateRow(tableId, row));
1223
- const validateRow = (tableId, row, skipDefaults) =>
1274
+ (!hasSchema || collHas(schemaMap, tableId) || cellInvalid(tableId)) &&
1275
+ validate(
1276
+ table,
1277
+ (row, rowId) => validateRow(tableId, rowId, row),
1278
+ () => cellInvalid(tableId),
1279
+ );
1280
+ const validateRow = (tableId, rowId, row, skipDefaults) =>
1224
1281
  validate(
1225
1282
  skipDefaults ? row : addDefaultsToRow(row, tableId),
1226
1283
  (cell, cellId) =>
1227
1284
  ifNotUndefined(
1228
- getValidatedCell(tableId, cellId, cell),
1285
+ getValidatedCell(tableId, rowId, cellId, cell),
1229
1286
  (validCell) => {
1230
1287
  row[cellId] = validCell;
1231
1288
  return true;
1232
1289
  },
1233
1290
  () => false,
1234
1291
  ),
1292
+ () => cellInvalid(tableId, rowId),
1235
1293
  );
1236
- const getValidatedCell = (tableId, cellId, cell) =>
1294
+ const getValidatedCell = (tableId, rowId, cellId, cell) =>
1237
1295
  hasSchema
1238
1296
  ? ifNotUndefined(
1239
1297
  mapGet(mapGet(schemaMap, tableId), cellId),
1240
1298
  (cellSchema) =>
1241
- getCellType(cell) != cellSchema[TYPE] ? cellSchema[DEFAULT] : cell,
1299
+ getCellType(cell) != cellSchema[TYPE]
1300
+ ? cellInvalid(tableId, rowId, cellId, cell, cellSchema[DEFAULT])
1301
+ : cell,
1302
+ () => cellInvalid(tableId, rowId, cellId, cell),
1242
1303
  )
1243
1304
  : isUndefined(getCellType(cell))
1244
- ? void 0
1305
+ ? cellInvalid(tableId, rowId, cellId, cell)
1245
1306
  : cell;
1246
1307
  const addDefaultsToRow = (row, tableId) => {
1247
1308
  ifNotUndefined(mapGet(schemaDefaultRows, tableId), (defaultRow) =>
@@ -1314,10 +1375,6 @@ const createStore = () => {
1314
1375
  mapSet(rowMap, cellId, newCell);
1315
1376
  }
1316
1377
  };
1317
- const setValidRowTransaction = (tableId, rowId, row) =>
1318
- transaction(() =>
1319
- setValidRow(tableId, getOrCreateTable(tableId), rowId, row),
1320
- );
1321
1378
  const setCellIntoDefaultRow = (tableId, tableMap, rowId, cellId, validCell) =>
1322
1379
  ifNotUndefined(
1323
1380
  mapGet(tableMap, rowId),
@@ -1377,6 +1434,17 @@ const createStore = () => {
1377
1434
  cellId,
1378
1435
  oldCell,
1379
1436
  );
1437
+ const cellInvalid = (tableId, rowId, cellId, invalidCell, defaultedCell) => {
1438
+ arrayPush(
1439
+ mapEnsure(
1440
+ mapEnsure(mapEnsure(invalidCells, tableId, mapNew()), rowId, mapNew()),
1441
+ cellId,
1442
+ [],
1443
+ ),
1444
+ invalidCell,
1445
+ );
1446
+ return defaultedCell;
1447
+ };
1380
1448
  const getCellChange = (tableId, rowId, cellId) => {
1381
1449
  const changedRow = mapGet(mapGet(changedCells, tableId), rowId);
1382
1450
  const newCell = getCell(tableId, rowId, cellId);
@@ -1384,80 +1452,106 @@ const createStore = () => {
1384
1452
  ? [true, mapGet(changedRow, cellId), newCell]
1385
1453
  : [false, newCell, newCell];
1386
1454
  };
1455
+ const callInvalidCellListeners = (mutator) =>
1456
+ !collIsEmpty(invalidCells) && !collIsEmpty(invalidCellListeners[mutator])
1457
+ ? collForEach(
1458
+ mutator
1459
+ ? mapClone(invalidCells, (table) => mapClone(table, mapClone))
1460
+ : invalidCells,
1461
+ (rows, tableId) =>
1462
+ collForEach(rows, (cells, rowId) =>
1463
+ collForEach(cells, (invalidCell, cellId) =>
1464
+ callListeners(
1465
+ invalidCellListeners[mutator],
1466
+ [tableId, rowId, cellId],
1467
+ invalidCell,
1468
+ ),
1469
+ ),
1470
+ ),
1471
+ )
1472
+ : 0;
1387
1473
  const callListenersForChanges = (mutator) => {
1388
- const emptyIdListeners =
1389
- collIsEmpty(cellIdsListeners[mutator]) &&
1390
- collIsEmpty(rowIdsListeners[mutator]) &&
1391
- collIsEmpty(tableIdsListeners[mutator]);
1392
- const emptyOtherListeners =
1393
- collIsEmpty(cellListeners[mutator]) &&
1394
- collIsEmpty(rowListeners[mutator]) &&
1395
- collIsEmpty(tableListeners[mutator]) &&
1396
- collIsEmpty(tablesListeners[mutator]);
1397
- if (emptyIdListeners && emptyOtherListeners) {
1398
- return;
1399
- }
1400
- const changes = mutator
1401
- ? [
1402
- mapClone(changedTableIds),
1403
- mapClone(changedRowIds, mapClone),
1404
- mapClone(changedCellIds, (table) => mapClone(table, mapClone)),
1405
- mapClone(changedCells, (table) => mapClone(table, mapClone)),
1406
- ]
1407
- : [changedTableIds, changedRowIds, changedCellIds, changedCells];
1408
- if (!emptyIdListeners) {
1409
- collForEach(changes[2], (rowCellIds, tableId) =>
1410
- collForEach(rowCellIds, (changedIds, rowId) => {
1474
+ let emptyIdListeners, emptyOtherListeners;
1475
+ if (
1476
+ !collIsEmpty(changedCells) &&
1477
+ !(
1478
+ (emptyIdListeners =
1479
+ collIsEmpty(cellIdsListeners[mutator]) &&
1480
+ collIsEmpty(rowIdsListeners[mutator]) &&
1481
+ collIsEmpty(tableIdsListeners[mutator])) &&
1482
+ (emptyOtherListeners =
1483
+ collIsEmpty(cellListeners[mutator]) &&
1484
+ collIsEmpty(rowListeners[mutator]) &&
1485
+ collIsEmpty(tableListeners[mutator]) &&
1486
+ collIsEmpty(tablesListeners[mutator]))
1487
+ )
1488
+ ) {
1489
+ const changes = mutator
1490
+ ? [
1491
+ mapClone(changedTableIds),
1492
+ mapClone(changedRowIds, mapClone),
1493
+ mapClone(changedCellIds, (table) => mapClone(table, mapClone)),
1494
+ mapClone(changedCells, (table) => mapClone(table, mapClone)),
1495
+ ]
1496
+ : [changedTableIds, changedRowIds, changedCellIds, changedCells];
1497
+ if (!emptyIdListeners) {
1498
+ collForEach(changes[2], (rowCellIds, tableId) =>
1499
+ collForEach(rowCellIds, (changedIds, rowId) => {
1500
+ if (!collIsEmpty(changedIds)) {
1501
+ callListeners(cellIdsListeners[mutator], [tableId, rowId]);
1502
+ }
1503
+ }),
1504
+ );
1505
+ collForEach(changes[1], (changedIds, tableId) => {
1411
1506
  if (!collIsEmpty(changedIds)) {
1412
- callListeners(cellIdsListeners[mutator], [tableId, rowId]);
1507
+ callListeners(rowIdsListeners[mutator], [tableId]);
1413
1508
  }
1414
- }),
1415
- );
1416
- collForEach(changes[1], (changedIds, tableId) => {
1417
- if (!collIsEmpty(changedIds)) {
1418
- callListeners(rowIdsListeners[mutator], [tableId]);
1509
+ });
1510
+ if (!collIsEmpty(changes[0])) {
1511
+ callListeners(tableIdsListeners[mutator]);
1419
1512
  }
1420
- });
1421
- if (!collIsEmpty(changes[0])) {
1422
- callListeners(tableIdsListeners[mutator]);
1423
1513
  }
1424
- }
1425
- if (!emptyOtherListeners) {
1426
- let tablesChanged;
1427
- collForEach(changes[3], (rows, tableId) => {
1428
- let tableChanged;
1429
- collForEach(rows, (cells, rowId) => {
1430
- let rowChanged;
1431
- collForEach(cells, (oldCell, cellId) => {
1432
- const newCell = getCell(tableId, rowId, cellId);
1433
- if (newCell !== oldCell) {
1514
+ if (!emptyOtherListeners) {
1515
+ let tablesChanged;
1516
+ collForEach(changes[3], (rows, tableId) => {
1517
+ let tableChanged;
1518
+ collForEach(rows, (cells, rowId) => {
1519
+ let rowChanged;
1520
+ collForEach(cells, (oldCell, cellId) => {
1521
+ const newCell = getCell(tableId, rowId, cellId);
1522
+ if (newCell !== oldCell) {
1523
+ callListeners(
1524
+ cellListeners[mutator],
1525
+ [tableId, rowId, cellId],
1526
+ newCell,
1527
+ oldCell,
1528
+ getCellChange,
1529
+ );
1530
+ tablesChanged = tableChanged = rowChanged = 1;
1531
+ }
1532
+ });
1533
+ if (rowChanged) {
1434
1534
  callListeners(
1435
- cellListeners[mutator],
1436
- [tableId, rowId, cellId],
1437
- newCell,
1438
- oldCell,
1535
+ rowListeners[mutator],
1536
+ [tableId, rowId],
1439
1537
  getCellChange,
1440
1538
  );
1441
- tablesChanged = tableChanged = rowChanged = 1;
1442
1539
  }
1443
1540
  });
1444
- if (rowChanged) {
1445
- callListeners(
1446
- rowListeners[mutator],
1447
- [tableId, rowId],
1448
- getCellChange,
1449
- );
1541
+ if (tableChanged) {
1542
+ callListeners(tableListeners[mutator], [tableId], getCellChange);
1450
1543
  }
1451
1544
  });
1452
- if (tableChanged) {
1453
- callListeners(tableListeners[mutator], [tableId], getCellChange);
1545
+ if (tablesChanged) {
1546
+ callListeners(tablesListeners[mutator], [], getCellChange);
1454
1547
  }
1455
- });
1456
- if (tablesChanged) {
1457
- callListeners(tablesListeners[mutator], [], getCellChange);
1458
1548
  }
1459
1549
  }
1460
1550
  };
1551
+ const fluentTransaction = (actions) => {
1552
+ transaction(actions);
1553
+ return store;
1554
+ };
1461
1555
  const getTables = () =>
1462
1556
  mapToObj(tablesMap, (tableMap) => mapToObj(tableMap, mapToObj));
1463
1557
  const getTableIds = () => mapKeys(tablesMap);
@@ -1469,58 +1563,59 @@ const createStore = () => {
1469
1563
  mapKeys(mapGet(mapGet(tablesMap, tableId), rowId));
1470
1564
  const getCell = (tableId, rowId, cellId) =>
1471
1565
  mapGet(mapGet(mapGet(tablesMap, tableId), rowId), cellId);
1566
+ const hasTables = () => !collIsEmpty(tablesMap);
1472
1567
  const hasTable = (tableId) => collHas(tablesMap, tableId);
1473
1568
  const hasRow = (tableId, rowId) => collHas(mapGet(tablesMap, tableId), rowId);
1474
1569
  const hasCell = (tableId, rowId, cellId) =>
1475
1570
  collHas(mapGet(mapGet(tablesMap, tableId), rowId), cellId);
1476
1571
  const getJson = () => jsonString(tablesMap);
1477
1572
  const getSchemaJson = () => jsonString(schemaMap);
1478
- const setTables = (tables) => {
1479
- if (validateTables(tables)) {
1480
- transaction(() => setValidTables(tables));
1481
- }
1482
- return store;
1483
- };
1484
- const setTable = (tableId, table) => {
1485
- if (validateTable(table, tableId)) {
1486
- transaction(() => setValidTable(tableId, table));
1487
- }
1488
- return store;
1489
- };
1490
- const setRow = (tableId, rowId, row) => {
1491
- if (validateRow(tableId, row)) {
1492
- setValidRowTransaction(tableId, rowId, row);
1493
- }
1494
- return store;
1495
- };
1496
- const addRow = (tableId, row) => {
1497
- let rowId = void 0;
1498
- if (validateRow(tableId, row)) {
1499
- rowId = getNewRowId(mapGet(tablesMap, tableId));
1500
- setValidRowTransaction(tableId, rowId, row);
1501
- }
1502
- return rowId;
1503
- };
1504
- const setPartialRow = (tableId, rowId, partialRow) => {
1505
- if (validateRow(tableId, partialRow, 1)) {
1506
- transaction(() => {
1573
+ const setTables = (tables) =>
1574
+ fluentTransaction(() =>
1575
+ validateTables(tables) ? setValidTables(tables) : 0,
1576
+ );
1577
+ const setTable = (tableId, table) =>
1578
+ fluentTransaction(() =>
1579
+ validateTable(table, tableId) ? setValidTable(tableId, table) : 0,
1580
+ );
1581
+ const setRow = (tableId, rowId, row) =>
1582
+ fluentTransaction(() =>
1583
+ validateRow(tableId, rowId, row)
1584
+ ? setValidRow(tableId, getOrCreateTable(tableId), rowId, row)
1585
+ : 0,
1586
+ );
1587
+ const addRow = (tableId, row) =>
1588
+ transaction(() => {
1589
+ let rowId = void 0;
1590
+ if (validateRow(tableId, rowId, row)) {
1591
+ setValidRow(
1592
+ tableId,
1593
+ getOrCreateTable(tableId),
1594
+ (rowId = getNewRowId(mapGet(tablesMap, tableId))),
1595
+ row,
1596
+ );
1597
+ }
1598
+ return rowId;
1599
+ });
1600
+ const setPartialRow = (tableId, rowId, partialRow) =>
1601
+ fluentTransaction(() => {
1602
+ if (validateRow(tableId, rowId, partialRow, 1)) {
1507
1603
  const table = getOrCreateTable(tableId);
1508
1604
  objForEach(partialRow, (cell, cellId) =>
1509
1605
  setCellIntoDefaultRow(tableId, table, rowId, cellId, cell),
1510
1606
  );
1511
- });
1512
- }
1513
- return store;
1514
- };
1515
- const setCell = (tableId, rowId, cellId, cell) => {
1516
- ifNotUndefined(
1517
- getValidatedCell(
1518
- tableId,
1519
- cellId,
1520
- isFunction(cell) ? cell(getCell(tableId, rowId, cellId)) : cell,
1521
- ),
1522
- (validCell) =>
1523
- transaction(() =>
1607
+ }
1608
+ });
1609
+ const setCell = (tableId, rowId, cellId, cell) =>
1610
+ fluentTransaction(() =>
1611
+ ifNotUndefined(
1612
+ getValidatedCell(
1613
+ tableId,
1614
+ rowId,
1615
+ cellId,
1616
+ isFunction(cell) ? cell(getCell(tableId, rowId, cellId)) : cell,
1617
+ ),
1618
+ (validCell) =>
1524
1619
  setCellIntoDefaultRow(
1525
1620
  tableId,
1526
1621
  getOrCreateTable(tableId),
@@ -1528,77 +1623,74 @@ const createStore = () => {
1528
1623
  cellId,
1529
1624
  validCell,
1530
1625
  ),
1531
- ),
1626
+ ),
1532
1627
  );
1533
- return store;
1534
- };
1535
1628
  const setJson = (json) => {
1536
1629
  try {
1537
1630
  json === EMPTY_OBJECT ? delTables() : setTables(jsonParse(json));
1538
1631
  } catch {}
1539
1632
  return store;
1540
1633
  };
1541
- const setSchema = (schema) => {
1542
- if ((hasSchema = validateSchema(schema))) {
1543
- setValidSchema(schema);
1544
- if (!collIsEmpty(tablesMap)) {
1545
- const tables = getTables();
1546
- delTables();
1547
- setTables(tables);
1548
- }
1549
- }
1550
- return store;
1551
- };
1552
- const delTables = () => {
1553
- transaction(() => setValidTables({}));
1554
- return store;
1555
- };
1556
- const delTable = (tableId) => {
1557
- if (collHas(tablesMap, tableId)) {
1558
- transaction(() => delValidTable(tableId));
1559
- }
1560
- return store;
1561
- };
1562
- const delRow = (tableId, rowId) => {
1563
- ifNotUndefined(mapGet(tablesMap, tableId), (tableMap) => {
1564
- if (collHas(tableMap, rowId)) {
1565
- transaction(() => delValidRow(tableId, tableMap, rowId));
1634
+ const setSchema = (schema) =>
1635
+ fluentTransaction(() => {
1636
+ if ((hasSchema = validateSchema(schema))) {
1637
+ setValidSchema(schema);
1638
+ if (!collIsEmpty(tablesMap)) {
1639
+ const tables = getTables();
1640
+ delTables();
1641
+ setTables(tables);
1642
+ }
1566
1643
  }
1567
1644
  });
1568
- return store;
1569
- };
1570
- const delCell = (tableId, rowId, cellId, forceDel) => {
1571
- ifNotUndefined(mapGet(tablesMap, tableId), (tableMap) =>
1572
- ifNotUndefined(mapGet(tableMap, rowId), (rowMap) => {
1573
- if (collHas(rowMap, cellId)) {
1574
- transaction(() =>
1575
- delValidCell(tableId, tableMap, rowId, rowMap, cellId, forceDel),
1576
- );
1577
- }
1578
- }),
1645
+ const delTables = () => fluentTransaction(() => setValidTables({}));
1646
+ const delTable = (tableId) =>
1647
+ fluentTransaction(() =>
1648
+ collHas(tablesMap, tableId) ? delValidTable(tableId) : 0,
1579
1649
  );
1580
- return store;
1581
- };
1582
- const delSchema = () => {
1583
- setValidSchema({});
1584
- hasSchema = false;
1585
- return store;
1586
- };
1650
+ const delRow = (tableId, rowId) =>
1651
+ fluentTransaction(() =>
1652
+ ifNotUndefined(mapGet(tablesMap, tableId), (tableMap) =>
1653
+ collHas(tableMap, rowId) ? delValidRow(tableId, tableMap, rowId) : 0,
1654
+ ),
1655
+ );
1656
+ const delCell = (tableId, rowId, cellId, forceDel) =>
1657
+ fluentTransaction(() =>
1658
+ ifNotUndefined(mapGet(tablesMap, tableId), (tableMap) =>
1659
+ ifNotUndefined(mapGet(tableMap, rowId), (rowMap) =>
1660
+ collHas(rowMap, cellId)
1661
+ ? delValidCell(tableId, tableMap, rowId, rowMap, cellId, forceDel)
1662
+ : 0,
1663
+ ),
1664
+ ),
1665
+ );
1666
+ const delSchema = () =>
1667
+ fluentTransaction(() => {
1668
+ setValidSchema({});
1669
+ hasSchema = false;
1670
+ });
1587
1671
  const transaction = (actions) => {
1588
1672
  if (transactions == -1) {
1589
1673
  return;
1590
1674
  }
1591
1675
  transactions++;
1592
- const result = actions();
1676
+ const result = actions?.();
1593
1677
  transactions--;
1594
1678
  if (transactions == 0) {
1595
1679
  transactions = 1;
1680
+ callInvalidCellListeners(1);
1596
1681
  callListenersForChanges(1);
1597
1682
  transactions = -1;
1683
+ callInvalidCellListeners(0);
1598
1684
  callListenersForChanges(0);
1599
1685
  transactions = 0;
1600
1686
  arrayForEach(
1601
- [changedCells, changedTableIds, changedRowIds, changedCellIds],
1687
+ [
1688
+ changedCells,
1689
+ invalidCells,
1690
+ changedTableIds,
1691
+ changedRowIds,
1692
+ changedCellIds,
1693
+ ],
1602
1694
  collClear,
1603
1695
  );
1604
1696
  }
@@ -1638,6 +1730,12 @@ const createStore = () => {
1638
1730
  rowId,
1639
1731
  cellId,
1640
1732
  ]);
1733
+ const addInvalidCellListener = (tableId, rowId, cellId, listener, mutator) =>
1734
+ addListener(listener, invalidCellListeners[mutator ? 1 : 0], [
1735
+ tableId,
1736
+ rowId,
1737
+ cellId,
1738
+ ]);
1641
1739
  const callListener = (listenerId) => {
1642
1740
  callListenerImpl(listenerId, [getTableIds, getRowIds, getCellIds], (ids) =>
1643
1741
  isUndefined(ids[2]) ? [] : Array(2).fill(getCell(...ids)),
@@ -1665,6 +1763,7 @@ const createStore = () => {
1665
1763
  getRow,
1666
1764
  getCellIds,
1667
1765
  getCell,
1766
+ hasTables,
1668
1767
  hasTable,
1669
1768
  hasRow,
1670
1769
  hasCell,
@@ -1694,6 +1793,7 @@ const createStore = () => {
1694
1793
  addRowListener,
1695
1794
  addCellIdsListener,
1696
1795
  addCellListener,
1796
+ addInvalidCellListener,
1697
1797
  callListener,
1698
1798
  delListener,
1699
1799
  getListenerStats,