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
@@ -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.pop() ?? '' + nextId++;
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.push(id);
151
+ arrayPush(listenerPool, id);
150
152
  }
151
153
  return idOrNulls;
152
154
  },
@@ -186,8 +188,9 @@ const getCellType = (cell) => {
186
188
  ? type
187
189
  : void 0;
188
190
  };
189
- const validate = (obj, validateChild) => {
190
- if (isUndefined(obj) || !isObject(obj) || objFrozen(obj)) {
191
+ const validate = (obj, validateChild, onInvalidObj) => {
192
+ if (isUndefined(obj) || !isObject(obj) || objIsEmpty(obj) || objFrozen(obj)) {
193
+ onInvalidObj?.();
191
194
  return false;
192
195
  }
193
196
  objForEach(obj, (child, id) => {
@@ -207,6 +210,7 @@ const createStore = () => {
207
210
  const changedRowIds = mapNew();
208
211
  const changedCellIds = mapNew();
209
212
  const changedCells = mapNew();
213
+ const invalidCells = mapNew();
210
214
  const schemaMap = mapNew();
211
215
  const schemaDefaultRows = mapNew();
212
216
  const tablesMap = mapNew();
@@ -217,6 +221,7 @@ const createStore = () => {
217
221
  const rowListeners = mapNewPair();
218
222
  const cellIdsListeners = mapNewPair();
219
223
  const cellListeners = mapNewPair();
224
+ const invalidCellListeners = mapNewPair();
220
225
  const [addListener, callListeners, delListenerImpl, callListenerImpl] =
221
226
  getListenerFunctions(() => store);
222
227
  const validateSchema = (schema) =>
@@ -237,32 +242,41 @@ const createStore = () => {
237
242
  return true;
238
243
  }),
239
244
  );
240
- const validateTables = (tables) => validate(tables, validateTable);
245
+ const validateTables = (tables) =>
246
+ validate(tables, validateTable, cellInvalid);
241
247
  const validateTable = (table, tableId) =>
242
- (!hasSchema || collHas(schemaMap, tableId)) &&
243
- validate(table, (row) => validateRow(tableId, row));
244
- const validateRow = (tableId, row, skipDefaults) =>
248
+ (!hasSchema || collHas(schemaMap, tableId) || cellInvalid(tableId)) &&
249
+ validate(
250
+ table,
251
+ (row, rowId) => validateRow(tableId, rowId, row),
252
+ () => cellInvalid(tableId),
253
+ );
254
+ const validateRow = (tableId, rowId, row, skipDefaults) =>
245
255
  validate(
246
256
  skipDefaults ? row : addDefaultsToRow(row, tableId),
247
257
  (cell, cellId) =>
248
258
  ifNotUndefined(
249
- getValidatedCell(tableId, cellId, cell),
259
+ getValidatedCell(tableId, rowId, cellId, cell),
250
260
  (validCell) => {
251
261
  row[cellId] = validCell;
252
262
  return true;
253
263
  },
254
264
  () => false,
255
265
  ),
266
+ () => cellInvalid(tableId, rowId),
256
267
  );
257
- const getValidatedCell = (tableId, cellId, cell) =>
268
+ const getValidatedCell = (tableId, rowId, cellId, cell) =>
258
269
  hasSchema
259
270
  ? ifNotUndefined(
260
271
  mapGet(mapGet(schemaMap, tableId), cellId),
261
272
  (cellSchema) =>
262
- getCellType(cell) != cellSchema[TYPE] ? cellSchema[DEFAULT] : cell,
273
+ getCellType(cell) != cellSchema[TYPE]
274
+ ? cellInvalid(tableId, rowId, cellId, cell, cellSchema[DEFAULT])
275
+ : cell,
276
+ () => cellInvalid(tableId, rowId, cellId, cell),
263
277
  )
264
278
  : isUndefined(getCellType(cell))
265
- ? void 0
279
+ ? cellInvalid(tableId, rowId, cellId, cell)
266
280
  : cell;
267
281
  const addDefaultsToRow = (row, tableId) => {
268
282
  ifNotUndefined(mapGet(schemaDefaultRows, tableId), (defaultRow) =>
@@ -335,10 +349,6 @@ const createStore = () => {
335
349
  mapSet(rowMap, cellId, newCell);
336
350
  }
337
351
  };
338
- const setValidRowTransaction = (tableId, rowId, row) =>
339
- transaction(() =>
340
- setValidRow(tableId, getOrCreateTable(tableId), rowId, row),
341
- );
342
352
  const setCellIntoDefaultRow = (tableId, tableMap, rowId, cellId, validCell) =>
343
353
  ifNotUndefined(
344
354
  mapGet(tableMap, rowId),
@@ -398,6 +408,17 @@ const createStore = () => {
398
408
  cellId,
399
409
  oldCell,
400
410
  );
411
+ const cellInvalid = (tableId, rowId, cellId, invalidCell, defaultedCell) => {
412
+ arrayPush(
413
+ mapEnsure(
414
+ mapEnsure(mapEnsure(invalidCells, tableId, mapNew()), rowId, mapNew()),
415
+ cellId,
416
+ [],
417
+ ),
418
+ invalidCell,
419
+ );
420
+ return defaultedCell;
421
+ };
401
422
  const getCellChange = (tableId, rowId, cellId) => {
402
423
  const changedRow = mapGet(mapGet(changedCells, tableId), rowId);
403
424
  const newCell = getCell(tableId, rowId, cellId);
@@ -405,80 +426,106 @@ const createStore = () => {
405
426
  ? [true, mapGet(changedRow, cellId), newCell]
406
427
  : [false, newCell, newCell];
407
428
  };
429
+ const callInvalidCellListeners = (mutator) =>
430
+ !collIsEmpty(invalidCells) && !collIsEmpty(invalidCellListeners[mutator])
431
+ ? collForEach(
432
+ mutator
433
+ ? mapClone(invalidCells, (table) => mapClone(table, mapClone))
434
+ : invalidCells,
435
+ (rows, tableId) =>
436
+ collForEach(rows, (cells, rowId) =>
437
+ collForEach(cells, (invalidCell, cellId) =>
438
+ callListeners(
439
+ invalidCellListeners[mutator],
440
+ [tableId, rowId, cellId],
441
+ invalidCell,
442
+ ),
443
+ ),
444
+ ),
445
+ )
446
+ : 0;
408
447
  const callListenersForChanges = (mutator) => {
409
- const emptyIdListeners =
410
- collIsEmpty(cellIdsListeners[mutator]) &&
411
- collIsEmpty(rowIdsListeners[mutator]) &&
412
- collIsEmpty(tableIdsListeners[mutator]);
413
- const emptyOtherListeners =
414
- collIsEmpty(cellListeners[mutator]) &&
415
- collIsEmpty(rowListeners[mutator]) &&
416
- collIsEmpty(tableListeners[mutator]) &&
417
- collIsEmpty(tablesListeners[mutator]);
418
- if (emptyIdListeners && emptyOtherListeners) {
419
- return;
420
- }
421
- const changes = mutator
422
- ? [
423
- mapClone(changedTableIds),
424
- mapClone(changedRowIds, mapClone),
425
- mapClone(changedCellIds, (table) => mapClone(table, mapClone)),
426
- mapClone(changedCells, (table) => mapClone(table, mapClone)),
427
- ]
428
- : [changedTableIds, changedRowIds, changedCellIds, changedCells];
429
- if (!emptyIdListeners) {
430
- collForEach(changes[2], (rowCellIds, tableId) =>
431
- collForEach(rowCellIds, (changedIds, rowId) => {
448
+ let emptyIdListeners, emptyOtherListeners;
449
+ if (
450
+ !collIsEmpty(changedCells) &&
451
+ !(
452
+ (emptyIdListeners =
453
+ collIsEmpty(cellIdsListeners[mutator]) &&
454
+ collIsEmpty(rowIdsListeners[mutator]) &&
455
+ collIsEmpty(tableIdsListeners[mutator])) &&
456
+ (emptyOtherListeners =
457
+ collIsEmpty(cellListeners[mutator]) &&
458
+ collIsEmpty(rowListeners[mutator]) &&
459
+ collIsEmpty(tableListeners[mutator]) &&
460
+ collIsEmpty(tablesListeners[mutator]))
461
+ )
462
+ ) {
463
+ const changes = mutator
464
+ ? [
465
+ mapClone(changedTableIds),
466
+ mapClone(changedRowIds, mapClone),
467
+ mapClone(changedCellIds, (table) => mapClone(table, mapClone)),
468
+ mapClone(changedCells, (table) => mapClone(table, mapClone)),
469
+ ]
470
+ : [changedTableIds, changedRowIds, changedCellIds, changedCells];
471
+ if (!emptyIdListeners) {
472
+ collForEach(changes[2], (rowCellIds, tableId) =>
473
+ collForEach(rowCellIds, (changedIds, rowId) => {
474
+ if (!collIsEmpty(changedIds)) {
475
+ callListeners(cellIdsListeners[mutator], [tableId, rowId]);
476
+ }
477
+ }),
478
+ );
479
+ collForEach(changes[1], (changedIds, tableId) => {
432
480
  if (!collIsEmpty(changedIds)) {
433
- callListeners(cellIdsListeners[mutator], [tableId, rowId]);
481
+ callListeners(rowIdsListeners[mutator], [tableId]);
434
482
  }
435
- }),
436
- );
437
- collForEach(changes[1], (changedIds, tableId) => {
438
- if (!collIsEmpty(changedIds)) {
439
- callListeners(rowIdsListeners[mutator], [tableId]);
483
+ });
484
+ if (!collIsEmpty(changes[0])) {
485
+ callListeners(tableIdsListeners[mutator]);
440
486
  }
441
- });
442
- if (!collIsEmpty(changes[0])) {
443
- callListeners(tableIdsListeners[mutator]);
444
487
  }
445
- }
446
- if (!emptyOtherListeners) {
447
- let tablesChanged;
448
- collForEach(changes[3], (rows, tableId) => {
449
- let tableChanged;
450
- collForEach(rows, (cells, rowId) => {
451
- let rowChanged;
452
- collForEach(cells, (oldCell, cellId) => {
453
- const newCell = getCell(tableId, rowId, cellId);
454
- if (newCell !== oldCell) {
488
+ if (!emptyOtherListeners) {
489
+ let tablesChanged;
490
+ collForEach(changes[3], (rows, tableId) => {
491
+ let tableChanged;
492
+ collForEach(rows, (cells, rowId) => {
493
+ let rowChanged;
494
+ collForEach(cells, (oldCell, cellId) => {
495
+ const newCell = getCell(tableId, rowId, cellId);
496
+ if (newCell !== oldCell) {
497
+ callListeners(
498
+ cellListeners[mutator],
499
+ [tableId, rowId, cellId],
500
+ newCell,
501
+ oldCell,
502
+ getCellChange,
503
+ );
504
+ tablesChanged = tableChanged = rowChanged = 1;
505
+ }
506
+ });
507
+ if (rowChanged) {
455
508
  callListeners(
456
- cellListeners[mutator],
457
- [tableId, rowId, cellId],
458
- newCell,
459
- oldCell,
509
+ rowListeners[mutator],
510
+ [tableId, rowId],
460
511
  getCellChange,
461
512
  );
462
- tablesChanged = tableChanged = rowChanged = 1;
463
513
  }
464
514
  });
465
- if (rowChanged) {
466
- callListeners(
467
- rowListeners[mutator],
468
- [tableId, rowId],
469
- getCellChange,
470
- );
515
+ if (tableChanged) {
516
+ callListeners(tableListeners[mutator], [tableId], getCellChange);
471
517
  }
472
518
  });
473
- if (tableChanged) {
474
- callListeners(tableListeners[mutator], [tableId], getCellChange);
519
+ if (tablesChanged) {
520
+ callListeners(tablesListeners[mutator], [], getCellChange);
475
521
  }
476
- });
477
- if (tablesChanged) {
478
- callListeners(tablesListeners[mutator], [], getCellChange);
479
522
  }
480
523
  }
481
524
  };
525
+ const fluentTransaction = (actions) => {
526
+ transaction(actions);
527
+ return store;
528
+ };
482
529
  const getTables = () =>
483
530
  mapToObj(tablesMap, (tableMap) => mapToObj(tableMap, mapToObj));
484
531
  const getTableIds = () => mapKeys(tablesMap);
@@ -490,58 +537,59 @@ const createStore = () => {
490
537
  mapKeys(mapGet(mapGet(tablesMap, tableId), rowId));
491
538
  const getCell = (tableId, rowId, cellId) =>
492
539
  mapGet(mapGet(mapGet(tablesMap, tableId), rowId), cellId);
540
+ const hasTables = () => !collIsEmpty(tablesMap);
493
541
  const hasTable = (tableId) => collHas(tablesMap, tableId);
494
542
  const hasRow = (tableId, rowId) => collHas(mapGet(tablesMap, tableId), rowId);
495
543
  const hasCell = (tableId, rowId, cellId) =>
496
544
  collHas(mapGet(mapGet(tablesMap, tableId), rowId), cellId);
497
545
  const getJson = () => jsonString(tablesMap);
498
546
  const getSchemaJson = () => jsonString(schemaMap);
499
- const setTables = (tables) => {
500
- if (validateTables(tables)) {
501
- transaction(() => setValidTables(tables));
502
- }
503
- return store;
504
- };
505
- const setTable = (tableId, table) => {
506
- if (validateTable(table, tableId)) {
507
- transaction(() => setValidTable(tableId, table));
508
- }
509
- return store;
510
- };
511
- const setRow = (tableId, rowId, row) => {
512
- if (validateRow(tableId, row)) {
513
- setValidRowTransaction(tableId, rowId, row);
514
- }
515
- return store;
516
- };
517
- const addRow = (tableId, row) => {
518
- let rowId = void 0;
519
- if (validateRow(tableId, row)) {
520
- rowId = getNewRowId(mapGet(tablesMap, tableId));
521
- setValidRowTransaction(tableId, rowId, row);
522
- }
523
- return rowId;
524
- };
525
- const setPartialRow = (tableId, rowId, partialRow) => {
526
- if (validateRow(tableId, partialRow, 1)) {
527
- transaction(() => {
547
+ const setTables = (tables) =>
548
+ fluentTransaction(() =>
549
+ validateTables(tables) ? setValidTables(tables) : 0,
550
+ );
551
+ const setTable = (tableId, table) =>
552
+ fluentTransaction(() =>
553
+ validateTable(table, tableId) ? setValidTable(tableId, table) : 0,
554
+ );
555
+ const setRow = (tableId, rowId, row) =>
556
+ fluentTransaction(() =>
557
+ validateRow(tableId, rowId, row)
558
+ ? setValidRow(tableId, getOrCreateTable(tableId), rowId, row)
559
+ : 0,
560
+ );
561
+ const addRow = (tableId, row) =>
562
+ transaction(() => {
563
+ let rowId = void 0;
564
+ if (validateRow(tableId, rowId, row)) {
565
+ setValidRow(
566
+ tableId,
567
+ getOrCreateTable(tableId),
568
+ (rowId = getNewRowId(mapGet(tablesMap, tableId))),
569
+ row,
570
+ );
571
+ }
572
+ return rowId;
573
+ });
574
+ const setPartialRow = (tableId, rowId, partialRow) =>
575
+ fluentTransaction(() => {
576
+ if (validateRow(tableId, rowId, partialRow, 1)) {
528
577
  const table = getOrCreateTable(tableId);
529
578
  objForEach(partialRow, (cell, cellId) =>
530
579
  setCellIntoDefaultRow(tableId, table, rowId, cellId, cell),
531
580
  );
532
- });
533
- }
534
- return store;
535
- };
536
- const setCell = (tableId, rowId, cellId, cell) => {
537
- ifNotUndefined(
538
- getValidatedCell(
539
- tableId,
540
- cellId,
541
- isFunction(cell) ? cell(getCell(tableId, rowId, cellId)) : cell,
542
- ),
543
- (validCell) =>
544
- transaction(() =>
581
+ }
582
+ });
583
+ const setCell = (tableId, rowId, cellId, cell) =>
584
+ fluentTransaction(() =>
585
+ ifNotUndefined(
586
+ getValidatedCell(
587
+ tableId,
588
+ rowId,
589
+ cellId,
590
+ isFunction(cell) ? cell(getCell(tableId, rowId, cellId)) : cell,
591
+ ),
592
+ (validCell) =>
545
593
  setCellIntoDefaultRow(
546
594
  tableId,
547
595
  getOrCreateTable(tableId),
@@ -549,77 +597,74 @@ const createStore = () => {
549
597
  cellId,
550
598
  validCell,
551
599
  ),
552
- ),
600
+ ),
553
601
  );
554
- return store;
555
- };
556
602
  const setJson = (json) => {
557
603
  try {
558
604
  json === EMPTY_OBJECT ? delTables() : setTables(jsonParse(json));
559
605
  } catch {}
560
606
  return store;
561
607
  };
562
- const setSchema = (schema) => {
563
- if ((hasSchema = validateSchema(schema))) {
564
- setValidSchema(schema);
565
- if (!collIsEmpty(tablesMap)) {
566
- const tables = getTables();
567
- delTables();
568
- setTables(tables);
569
- }
570
- }
571
- return store;
572
- };
573
- const delTables = () => {
574
- transaction(() => setValidTables({}));
575
- return store;
576
- };
577
- const delTable = (tableId) => {
578
- if (collHas(tablesMap, tableId)) {
579
- transaction(() => delValidTable(tableId));
580
- }
581
- return store;
582
- };
583
- const delRow = (tableId, rowId) => {
584
- ifNotUndefined(mapGet(tablesMap, tableId), (tableMap) => {
585
- if (collHas(tableMap, rowId)) {
586
- transaction(() => delValidRow(tableId, tableMap, rowId));
608
+ const setSchema = (schema) =>
609
+ fluentTransaction(() => {
610
+ if ((hasSchema = validateSchema(schema))) {
611
+ setValidSchema(schema);
612
+ if (!collIsEmpty(tablesMap)) {
613
+ const tables = getTables();
614
+ delTables();
615
+ setTables(tables);
616
+ }
587
617
  }
588
618
  });
589
- return store;
590
- };
591
- const delCell = (tableId, rowId, cellId, forceDel) => {
592
- ifNotUndefined(mapGet(tablesMap, tableId), (tableMap) =>
593
- ifNotUndefined(mapGet(tableMap, rowId), (rowMap) => {
594
- if (collHas(rowMap, cellId)) {
595
- transaction(() =>
596
- delValidCell(tableId, tableMap, rowId, rowMap, cellId, forceDel),
597
- );
598
- }
599
- }),
619
+ const delTables = () => fluentTransaction(() => setValidTables({}));
620
+ const delTable = (tableId) =>
621
+ fluentTransaction(() =>
622
+ collHas(tablesMap, tableId) ? delValidTable(tableId) : 0,
600
623
  );
601
- return store;
602
- };
603
- const delSchema = () => {
604
- setValidSchema({});
605
- hasSchema = false;
606
- return store;
607
- };
624
+ const delRow = (tableId, rowId) =>
625
+ fluentTransaction(() =>
626
+ ifNotUndefined(mapGet(tablesMap, tableId), (tableMap) =>
627
+ collHas(tableMap, rowId) ? delValidRow(tableId, tableMap, rowId) : 0,
628
+ ),
629
+ );
630
+ const delCell = (tableId, rowId, cellId, forceDel) =>
631
+ fluentTransaction(() =>
632
+ ifNotUndefined(mapGet(tablesMap, tableId), (tableMap) =>
633
+ ifNotUndefined(mapGet(tableMap, rowId), (rowMap) =>
634
+ collHas(rowMap, cellId)
635
+ ? delValidCell(tableId, tableMap, rowId, rowMap, cellId, forceDel)
636
+ : 0,
637
+ ),
638
+ ),
639
+ );
640
+ const delSchema = () =>
641
+ fluentTransaction(() => {
642
+ setValidSchema({});
643
+ hasSchema = false;
644
+ });
608
645
  const transaction = (actions) => {
609
646
  if (transactions == -1) {
610
647
  return;
611
648
  }
612
649
  transactions++;
613
- const result = actions();
650
+ const result = actions?.();
614
651
  transactions--;
615
652
  if (transactions == 0) {
616
653
  transactions = 1;
654
+ callInvalidCellListeners(1);
617
655
  callListenersForChanges(1);
618
656
  transactions = -1;
657
+ callInvalidCellListeners(0);
619
658
  callListenersForChanges(0);
620
659
  transactions = 0;
621
660
  arrayForEach(
622
- [changedCells, changedTableIds, changedRowIds, changedCellIds],
661
+ [
662
+ changedCells,
663
+ invalidCells,
664
+ changedTableIds,
665
+ changedRowIds,
666
+ changedCellIds,
667
+ ],
623
668
  collClear,
624
669
  );
625
670
  }
@@ -659,6 +704,12 @@ const createStore = () => {
659
704
  rowId,
660
705
  cellId,
661
706
  ]);
707
+ const addInvalidCellListener = (tableId, rowId, cellId, listener, mutator) =>
708
+ addListener(listener, invalidCellListeners[mutator ? 1 : 0], [
709
+ tableId,
710
+ rowId,
711
+ cellId,
712
+ ]);
662
713
  const callListener = (listenerId) => {
663
714
  callListenerImpl(listenerId, [getTableIds, getRowIds, getCellIds], (ids) =>
664
715
  isUndefined(ids[2]) ? [] : Array(2).fill(getCell(...ids)),
@@ -686,6 +737,7 @@ const createStore = () => {
686
737
  getRow,
687
738
  getCellIds,
688
739
  getCell,
740
+ hasTables,
689
741
  hasTable,
690
742
  hasRow,
691
743
  hasCell,
@@ -715,6 +767,7 @@ const createStore = () => {
715
767
  addRowListener,
716
768
  addCellIdsListener,
717
769
  addCellListener,
770
+ addInvalidCellListener,
718
771
  callListener,
719
772
  delListener,
720
773
  getListenerStats,