tinybase 7.3.5 → 8.0.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 (59) hide show
  1. package/@types/index.d.ts +1 -0
  2. package/@types/middleware/index.d.ts +1064 -0
  3. package/@types/middleware/with-schemas/index.d.ts +1355 -0
  4. package/@types/omni/index.d.ts +1 -0
  5. package/@types/omni/with-schemas/index.d.ts +1 -0
  6. package/@types/store/index.d.ts +0 -1
  7. package/@types/with-schemas/index.d.ts +1 -0
  8. package/agents.md +33 -11
  9. package/checkpoints/index.js +8 -9
  10. package/checkpoints/with-schemas/index.js +8 -9
  11. package/index.js +484 -153
  12. package/mergeable-store/index.js +371 -135
  13. package/mergeable-store/with-schemas/index.js +371 -135
  14. package/middleware/index.js +130 -0
  15. package/middleware/with-schemas/index.js +130 -0
  16. package/min/checkpoints/index.js +1 -1
  17. package/min/checkpoints/index.js.gz +0 -0
  18. package/min/checkpoints/with-schemas/index.js +1 -1
  19. package/min/checkpoints/with-schemas/index.js.gz +0 -0
  20. package/min/index.js +1 -1
  21. package/min/index.js.gz +0 -0
  22. package/min/mergeable-store/index.js +1 -1
  23. package/min/mergeable-store/index.js.gz +0 -0
  24. package/min/mergeable-store/with-schemas/index.js +1 -1
  25. package/min/mergeable-store/with-schemas/index.js.gz +0 -0
  26. package/min/middleware/index.js +1 -0
  27. package/min/middleware/index.js.gz +0 -0
  28. package/min/middleware/with-schemas/index.js +1 -0
  29. package/min/middleware/with-schemas/index.js.gz +0 -0
  30. package/min/omni/index.js +1 -1
  31. package/min/omni/index.js.gz +0 -0
  32. package/min/omni/with-schemas/index.js +1 -1
  33. package/min/omni/with-schemas/index.js.gz +0 -0
  34. package/min/queries/index.js +1 -1
  35. package/min/queries/index.js.gz +0 -0
  36. package/min/queries/with-schemas/index.js +1 -1
  37. package/min/queries/with-schemas/index.js.gz +0 -0
  38. package/min/store/index.js +1 -1
  39. package/min/store/index.js.gz +0 -0
  40. package/min/store/with-schemas/index.js +1 -1
  41. package/min/store/with-schemas/index.js.gz +0 -0
  42. package/min/ui-react-inspector/index.js +1 -1
  43. package/min/ui-react-inspector/index.js.gz +0 -0
  44. package/min/ui-react-inspector/with-schemas/index.js +1 -1
  45. package/min/ui-react-inspector/with-schemas/index.js.gz +0 -0
  46. package/min/with-schemas/index.js +1 -1
  47. package/min/with-schemas/index.js.gz +0 -0
  48. package/omni/index.js +492 -161
  49. package/omni/with-schemas/index.js +492 -161
  50. package/package.json +37 -1
  51. package/queries/index.js +1 -6
  52. package/queries/with-schemas/index.js +1 -6
  53. package/readme.md +14 -14
  54. package/releases.md +41 -41
  55. package/store/index.js +370 -134
  56. package/store/with-schemas/index.js +370 -134
  57. package/ui-react-inspector/index.js +349 -134
  58. package/ui-react-inspector/with-schemas/index.js +349 -134
  59. package/with-schemas/index.js +484 -153
@@ -27,6 +27,7 @@ const id = (key) => EMPTY_STRING + key;
27
27
 
28
28
  const getIfNotFunction = (predicate) => (value, then, otherwise) =>
29
29
  predicate(value) ? otherwise?.() : then(value);
30
+ const GLOBAL = globalThis;
30
31
  const isFiniteNumber = isFinite;
31
32
  const isInstanceOf = (thing, cls) => thing instanceof cls;
32
33
  const isNullish = (thing) => thing == null;
@@ -40,6 +41,7 @@ const isArray = (thing) => Array.isArray(thing);
40
41
  const slice = (arrayOrString, start, end) => arrayOrString.slice(start, end);
41
42
  const size = (arrayOrString) => arrayOrString.length;
42
43
  const test = (regex, subject) => regex.test(subject);
44
+ const structuredClone = GLOBAL.structuredClone;
43
45
  const tryCatch = async (action, then1, then2) => {
44
46
  try {
45
47
  return await action();
@@ -71,12 +73,6 @@ const getCellOrValueType = (cellOrValue) => {
71
73
  ? type
72
74
  : void 0;
73
75
  };
74
- const setOrDelCell = (store, tableId, rowId, cellId, cell) =>
75
- isUndefined(cell)
76
- ? store.delCell(tableId, rowId, cellId, true)
77
- : store.setCell(tableId, rowId, cellId, cell);
78
- const setOrDelValue = (store, valueId, value) =>
79
- isUndefined(value) ? store.delValue(valueId) : store.setValue(valueId, value);
80
76
 
81
77
  const collSizeN = (collSizer) => (coll) =>
82
78
  arrayReduce(collValues(coll), (total, coll2) => total + collSizer(coll2), 0);
@@ -122,6 +118,26 @@ const objMap = (obj, cb) =>
122
118
  objNew(objToArray(obj, (value, id) => [id, cb(value, id)]));
123
119
  const objSize = (obj) => size(objIds(obj));
124
120
  const objIsEmpty = (obj) => isObject(obj) && objSize(obj) == 0;
121
+
122
+ /* istanbul ignore next */
123
+ const objIsEqual = (
124
+ obj1,
125
+ obj2,
126
+ isEqual = (value1, value2) => value1 === value2,
127
+ ) => {
128
+ const entries1 = objEntries(obj1);
129
+ return (
130
+ size(entries1) === objSize(obj2) &&
131
+ arrayEvery(entries1, ([index, value1]) =>
132
+ isObject(value1)
133
+ ? /* istanbul ignore next */
134
+ isObject(obj2[index])
135
+ ? objIsEqual(obj2[index], value1)
136
+ : false
137
+ : isEqual(value1, obj2[index]),
138
+ )
139
+ );
140
+ };
125
141
  const objValidate = (obj, validateChild, onInvalidObj, emptyIsValid = 0) => {
126
142
  if (
127
143
  isNullish(obj) ||
@@ -334,12 +350,15 @@ const idsChanged = (changedIds, id2, addedOrRemoved) =>
334
350
  id2,
335
351
  mapGet(changedIds, id2) == -addedOrRemoved ? void 0 : addedOrRemoved,
336
352
  );
353
+ const contentOrChangesIsEqual = ([tables1, values1], [tables2, values2]) =>
354
+ objIsEqual(tables1, tables2) && objIsEqual(values1, values2);
337
355
  const createStore = () => {
338
356
  let hasTablesSchema;
339
357
  let hasValuesSchema;
340
358
  let hadTables = false;
341
359
  let hadValues = false;
342
360
  let transactions = 0;
361
+ let middleware = [];
343
362
  let internalListeners = [];
344
363
  let mutating = 0;
345
364
  const changedTableIds = mapNew();
@@ -387,6 +406,19 @@ const createStore = () => {
387
406
  const finishTransactionListeners = pairNewMap();
388
407
  const [addListener, callListeners, delListenerImpl, callListenerImpl] =
389
408
  getListenerFunctions(() => store);
409
+ const whileMutating = (action) => {
410
+ const wasMutating = mutating;
411
+ mutating = 1;
412
+ const result = action();
413
+ mutating = wasMutating;
414
+ return result;
415
+ };
416
+ const ifTransformed = (snapshot, getResult, then, isEqual = Object.is) =>
417
+ ifNotUndefined(getResult(), (result) =>
418
+ snapshot === result || isEqual(snapshot, result)
419
+ ? then(result)
420
+ : whileMutating(() => then(result)),
421
+ );
390
422
  const validateTablesSchema = (tableSchema) =>
391
423
  objValidate(tableSchema, (tableSchema2) =>
392
424
  objValidate(tableSchema2, validateCellOrValueSchema),
@@ -574,82 +606,194 @@ const createStore = () => {
574
606
  );
575
607
  const setOrDelTables = (tables) =>
576
608
  objIsEmpty(tables) ? delTables() : setTables(tables);
577
- const setValidContent = ([tables, values]) => {
578
- (objIsEmpty(tables) ? delTables : setTables)(tables);
579
- (objIsEmpty(values) ? delValues : setValues)(values);
580
- };
581
- const setValidTables = (tables) =>
582
- mapMatch(
583
- tablesMap,
609
+ const setOrDelCell = (tableId, rowId, cellId, cell, skipMiddleware) =>
610
+ isUndefined(cell)
611
+ ? delCell(tableId, rowId, cellId, true, skipMiddleware)
612
+ : setCell(tableId, rowId, cellId, cell, skipMiddleware);
613
+ const setOrDelValues = (values) =>
614
+ objIsEmpty(values) ? delValues() : setValues(values);
615
+ const setOrDelValue = (valueId, value, skipMiddleware) =>
616
+ isUndefined(value)
617
+ ? delValue(valueId, skipMiddleware)
618
+ : setValue(valueId, value, skipMiddleware);
619
+ const setValidContent = (content) =>
620
+ ifTransformed(
621
+ content,
622
+ () =>
623
+ ifNotUndefined(
624
+ middleware[0],
625
+ (willSetContent) =>
626
+ whileMutating(() => willSetContent(structuredClone(content))),
627
+ () => content,
628
+ ),
629
+ ([tables, values]) => {
630
+ (objIsEmpty(tables) ? delTables : setTables)(tables);
631
+ (objIsEmpty(values) ? delValues : setValues)(values);
632
+ },
633
+ contentOrChangesIsEqual,
634
+ );
635
+ const setValidTables = (tables, forceDel) =>
636
+ ifTransformed(
584
637
  tables,
585
- (_tables, tableId, table) => setValidTable(tableId, table),
586
- (_tables, tableId) => delValidTable(tableId),
638
+ () =>
639
+ forceDel
640
+ ? tables
641
+ : ifNotUndefined(
642
+ middleware[1],
643
+ (willSetTables) =>
644
+ whileMutating(() => willSetTables(structuredClone(tables))),
645
+ () => tables,
646
+ ),
647
+ (validTables) =>
648
+ mapMatch(
649
+ tablesMap,
650
+ validTables,
651
+ (_tables, tableId, table) => setValidTable(tableId, table),
652
+ (_tables, tableId) => delValidTable(tableId),
653
+ ),
654
+ objIsEqual,
587
655
  );
588
- const setValidTable = (tableId, table) =>
589
- mapMatch(
590
- mapEnsure(tablesMap, tableId, () => {
591
- tableIdsChanged(tableId, 1);
592
- mapSet(tablePoolFunctions, tableId, getPoolFunctions());
593
- mapSet(tableCellIds, tableId, mapNew());
594
- return mapNew();
595
- }),
656
+ const setValidTable = (tableId, table, forceDel) =>
657
+ ifTransformed(
596
658
  table,
597
- (tableMap, rowId, row) => setValidRow(tableId, tableMap, rowId, row),
598
- (tableMap, rowId) => delValidRow(tableId, tableMap, rowId),
659
+ () =>
660
+ forceDel
661
+ ? table
662
+ : ifNotUndefined(
663
+ middleware[2],
664
+ (willSetTable) =>
665
+ whileMutating(() =>
666
+ willSetTable(tableId, structuredClone(table)),
667
+ ),
668
+ () => table,
669
+ ),
670
+ (validTable) =>
671
+ mapMatch(
672
+ mapEnsure(tablesMap, tableId, () => {
673
+ tableIdsChanged(tableId, 1);
674
+ mapSet(tablePoolFunctions, tableId, getPoolFunctions());
675
+ mapSet(tableCellIds, tableId, mapNew());
676
+ return mapNew();
677
+ }),
678
+ validTable,
679
+ (tableMap, rowId, row) => setValidRow(tableId, tableMap, rowId, row),
680
+ (tableMap, rowId) => delValidRow(tableId, tableMap, rowId),
681
+ ),
682
+ objIsEqual,
599
683
  );
600
684
  const setValidRow = (tableId, tableMap, rowId, row, forceDel) =>
601
- mapMatch(
602
- mapEnsure(tableMap, rowId, () => {
603
- rowIdsChanged(tableId, rowId, 1);
604
- return mapNew();
605
- }),
685
+ ifTransformed(
606
686
  row,
607
- (rowMap, cellId, cell) =>
608
- setValidCell(tableId, rowId, rowMap, cellId, cell),
609
- (rowMap, cellId) =>
610
- delValidCell(tableId, tableMap, rowId, rowMap, cellId, forceDel),
687
+ () =>
688
+ forceDel
689
+ ? row
690
+ : ifNotUndefined(
691
+ middleware[3],
692
+ (willSetRow) =>
693
+ whileMutating(() =>
694
+ willSetRow(tableId, rowId, structuredClone(row)),
695
+ ),
696
+ () => row,
697
+ ),
698
+ (validRow) =>
699
+ mapMatch(
700
+ mapEnsure(tableMap, rowId, () => {
701
+ rowIdsChanged(tableId, rowId, 1);
702
+ return mapNew();
703
+ }),
704
+ validRow,
705
+ (rowMap, cellId, cell) =>
706
+ setValidCell(tableId, rowId, rowMap, cellId, cell),
707
+ (rowMap, cellId) =>
708
+ delValidCell(tableId, tableMap, rowId, rowMap, cellId, forceDel),
709
+ ),
710
+ objIsEqual,
611
711
  );
612
- const setValidCell = (tableId, rowId, rowMap, cellId, cell) => {
613
- if (!collHas(rowMap, cellId)) {
614
- cellIdsChanged(tableId, rowId, cellId, 1);
615
- }
616
- const oldCell = mapGet(rowMap, cellId);
617
- if (cell !== oldCell) {
618
- cellChanged(tableId, rowId, cellId, oldCell, cell);
619
- mapSet(rowMap, cellId, cell);
620
- }
621
- };
622
- const setCellIntoDefaultRow = (tableId, tableMap, rowId, cellId, validCell) =>
712
+ const setValidCell = (tableId, rowId, rowMap, cellId, cell, skipMiddleware) =>
713
+ ifTransformed(
714
+ cell,
715
+ () =>
716
+ ifNotUndefined(
717
+ skipMiddleware ? void 0 : middleware[4],
718
+ (willSetCell) =>
719
+ whileMutating(() => willSetCell(tableId, rowId, cellId, cell)),
720
+ () => cell,
721
+ ),
722
+ (cell2) => {
723
+ if (!collHas(rowMap, cellId)) {
724
+ cellIdsChanged(tableId, rowId, cellId, 1);
725
+ }
726
+ const oldCell = mapGet(rowMap, cellId);
727
+ if (cell2 !== oldCell) {
728
+ cellChanged(tableId, rowId, cellId, oldCell, cell2);
729
+ mapSet(rowMap, cellId, cell2);
730
+ }
731
+ },
732
+ );
733
+ const setCellIntoNewRow = (
734
+ tableId,
735
+ tableMap,
736
+ rowId,
737
+ cellId,
738
+ validCell,
739
+ skipMiddleware,
740
+ ) =>
623
741
  ifNotUndefined(
624
742
  mapGet(tableMap, rowId),
625
- (rowMap) => setValidCell(tableId, rowId, rowMap, cellId, validCell),
626
- () =>
627
- setValidRow(
628
- tableId,
629
- tableMap,
630
- rowId,
743
+ (rowMap) =>
744
+ setValidCell(tableId, rowId, rowMap, cellId, validCell, skipMiddleware),
745
+ () => {
746
+ const rowMap = mapNew();
747
+ mapSet(tableMap, rowId, rowMap);
748
+ rowIdsChanged(tableId, rowId, 1);
749
+ objMap(
631
750
  addDefaultsToRow({[cellId]: validCell}, tableId, rowId),
632
- ),
751
+ (cell, cellId2) =>
752
+ setValidCell(tableId, rowId, rowMap, cellId2, cell, skipMiddleware),
753
+ );
754
+ },
633
755
  );
634
- const setOrDelValues = (values) =>
635
- objIsEmpty(values) ? delValues() : setValues(values);
636
- const setValidValues = (values) =>
637
- mapMatch(
638
- valuesMap,
756
+ const setValidValues = (values, forceDel) =>
757
+ ifTransformed(
639
758
  values,
640
- (_valuesMap, valueId, value) => setValidValue(valueId, value),
641
- (_valuesMap, valueId) => delValidValue(valueId),
759
+ () =>
760
+ forceDel
761
+ ? values
762
+ : ifNotUndefined(
763
+ middleware[5],
764
+ (willSetValues) =>
765
+ whileMutating(() => willSetValues(structuredClone(values))),
766
+ () => values,
767
+ ),
768
+ (validValues) =>
769
+ mapMatch(
770
+ valuesMap,
771
+ validValues,
772
+ (_valuesMap, valueId, value) => setValidValue(valueId, value),
773
+ (_valuesMap, valueId) => delValidValue(valueId),
774
+ ),
775
+ objIsEqual,
776
+ );
777
+ const setValidValue = (valueId, value, skipMiddleware) =>
778
+ ifTransformed(
779
+ value,
780
+ () =>
781
+ ifNotUndefined(
782
+ skipMiddleware ? void 0 : middleware[6],
783
+ (willSetValue) => whileMutating(() => willSetValue(valueId, value)),
784
+ () => value,
785
+ ),
786
+ (value2) => {
787
+ if (!collHas(valuesMap, valueId)) {
788
+ valueIdsChanged(valueId, 1);
789
+ }
790
+ const oldValue = mapGet(valuesMap, valueId);
791
+ if (value2 !== oldValue) {
792
+ valueChanged(valueId, oldValue, value2);
793
+ mapSet(valuesMap, valueId, value2);
794
+ }
795
+ },
642
796
  );
643
- const setValidValue = (valueId, value) => {
644
- if (!collHas(valuesMap, valueId)) {
645
- valueIdsChanged(valueId, 1);
646
- }
647
- const oldValue = mapGet(valuesMap, valueId);
648
- if (value !== oldValue) {
649
- valueChanged(valueId, oldValue, value);
650
- mapSet(valuesMap, valueId, value);
651
- }
652
- };
653
797
  const getNewRowId = (tableId, reuse) => {
654
798
  const [getId] = mapGet(tablePoolFunctions, tableId);
655
799
  let rowId;
@@ -659,14 +803,34 @@ const createStore = () => {
659
803
  return rowId;
660
804
  };
661
805
  const getOrCreateTable = (tableId) =>
662
- mapGet(tablesMap, tableId) ?? setValidTable(tableId, {});
663
- const delValidTable = (tableId) => setValidTable(tableId, {});
806
+ mapEnsure(tablesMap, tableId, () => {
807
+ tableIdsChanged(tableId, 1);
808
+ mapSet(tablePoolFunctions, tableId, getPoolFunctions());
809
+ mapSet(tableCellIds, tableId, mapNew());
810
+ return mapNew();
811
+ });
812
+ const delValidTable = (tableId) => {
813
+ if (whileMutating(() => middleware[8]?.(tableId)) ?? true) {
814
+ return setValidTable(tableId, {}, true);
815
+ }
816
+ return mapGet(tablesMap, tableId);
817
+ };
664
818
  const delValidRow = (tableId, tableMap, rowId) => {
665
- const [, releaseId] = mapGet(tablePoolFunctions, tableId);
666
- releaseId(rowId);
667
- setValidRow(tableId, tableMap, rowId, {}, true);
819
+ if (whileMutating(() => middleware[9]?.(tableId, rowId)) ?? true) {
820
+ const [, releaseId] = mapGet(tablePoolFunctions, tableId);
821
+ releaseId(rowId);
822
+ setValidRow(tableId, tableMap, rowId, {}, true);
823
+ }
668
824
  };
669
- const delValidCell = (tableId, table, rowId, row, cellId, forceDel) => {
825
+ const delValidCell = (
826
+ tableId,
827
+ table,
828
+ rowId,
829
+ row,
830
+ cellId,
831
+ forceDel,
832
+ skipMiddleware,
833
+ ) => {
670
834
  const defaultCell = mapGet(
671
835
  mapGet(tablesSchemaRowCache, tableId)?.[0],
672
836
  cellId,
@@ -674,34 +838,44 @@ const createStore = () => {
674
838
  if (!isUndefined(defaultCell) && !forceDel) {
675
839
  return setValidCell(tableId, rowId, row, cellId, defaultCell);
676
840
  }
677
- const delCell2 = (cellId2) => {
678
- cellChanged(tableId, rowId, cellId2, mapGet(row, cellId2));
679
- cellIdsChanged(tableId, rowId, cellId2, -1);
680
- mapSet(row, cellId2);
681
- };
682
- if (isUndefined(defaultCell)) {
683
- delCell2(cellId);
684
- } else {
685
- mapForEach(row, delCell2);
686
- }
687
- if (collIsEmpty(row)) {
688
- rowIdsChanged(tableId, rowId, -1);
689
- if (collIsEmpty(mapSet(table, rowId))) {
690
- tableIdsChanged(tableId, -1);
691
- mapSet(tablesMap, tableId);
692
- mapSet(tablePoolFunctions, tableId);
693
- mapSet(tableCellIds, tableId);
841
+ if (
842
+ skipMiddleware ||
843
+ (whileMutating(() => middleware[10]?.(tableId, rowId, cellId)) ?? true)
844
+ ) {
845
+ const delCell2 = (cellId2) => {
846
+ cellChanged(tableId, rowId, cellId2, mapGet(row, cellId2));
847
+ cellIdsChanged(tableId, rowId, cellId2, -1);
848
+ mapSet(row, cellId2);
849
+ };
850
+ if (isUndefined(defaultCell)) {
851
+ delCell2(cellId);
852
+ } else {
853
+ mapForEach(row, delCell2);
854
+ }
855
+ if (collIsEmpty(row)) {
856
+ rowIdsChanged(tableId, rowId, -1);
857
+ if (collIsEmpty(mapSet(table, rowId))) {
858
+ tableIdsChanged(tableId, -1);
859
+ mapSet(tablesMap, tableId);
860
+ mapSet(tablePoolFunctions, tableId);
861
+ mapSet(tableCellIds, tableId);
862
+ }
694
863
  }
695
864
  }
696
865
  };
697
- const delValidValue = (valueId) => {
866
+ const delValidValue = (valueId, skipMiddleware) => {
698
867
  const defaultValue = mapGet(valuesDefaulted, valueId);
699
868
  if (!isUndefined(defaultValue)) {
700
869
  return setValidValue(valueId, defaultValue);
701
870
  }
702
- valueChanged(valueId, mapGet(valuesMap, valueId));
703
- valueIdsChanged(valueId, -1);
704
- mapSet(valuesMap, valueId);
871
+ if (
872
+ skipMiddleware ||
873
+ (whileMutating(() => middleware[12]?.(valueId)) ?? true)
874
+ ) {
875
+ valueChanged(valueId, mapGet(valuesMap, valueId));
876
+ valueIdsChanged(valueId, -1);
877
+ mapSet(valuesMap, valueId);
878
+ }
705
879
  };
706
880
  const tableIdsChanged = (tableId, addedOrRemoved) =>
707
881
  idsChanged(changedTableIds, tableId, addedOrRemoved);
@@ -1160,14 +1334,14 @@ const createStore = () => {
1160
1334
  if (validateRow(tableId2, rowId2, partialRow, 1)) {
1161
1335
  const table = getOrCreateTable(tableId2);
1162
1336
  objMap(partialRow, (cell, cellId) =>
1163
- setCellIntoDefaultRow(tableId2, table, rowId2, cellId, cell),
1337
+ setCellIntoNewRow(tableId2, table, rowId2, cellId, cell),
1164
1338
  );
1165
1339
  }
1166
1340
  },
1167
1341
  tableId,
1168
1342
  rowId,
1169
1343
  );
1170
- const setCell = (tableId, rowId, cellId, cell) =>
1344
+ const setCell = (tableId, rowId, cellId, cell, skipMiddleware) =>
1171
1345
  fluentTransaction(
1172
1346
  (tableId2, rowId2, cellId2) =>
1173
1347
  ifNotUndefined(
@@ -1178,12 +1352,13 @@ const createStore = () => {
1178
1352
  isFunction(cell) ? cell(getCell(tableId2, rowId2, cellId2)) : cell,
1179
1353
  ),
1180
1354
  (validCell) =>
1181
- setCellIntoDefaultRow(
1355
+ setCellIntoNewRow(
1182
1356
  tableId2,
1183
1357
  getOrCreateTable(tableId2),
1184
1358
  rowId2,
1185
1359
  cellId2,
1186
1360
  validCell,
1361
+ skipMiddleware,
1187
1362
  ),
1188
1363
  ),
1189
1364
  tableId,
@@ -1202,7 +1377,7 @@ const createStore = () => {
1202
1377
  )
1203
1378
  : 0,
1204
1379
  );
1205
- const setValue = (valueId, value) =>
1380
+ const setValue = (valueId, value, skipMiddleware) =>
1206
1381
  fluentTransaction(
1207
1382
  (valueId2) =>
1208
1383
  ifNotUndefined(
@@ -1210,27 +1385,40 @@ const createStore = () => {
1210
1385
  valueId2,
1211
1386
  isFunction(value) ? value(getValue(valueId2)) : value,
1212
1387
  ),
1213
- (validValue) => setValidValue(valueId2, validValue),
1388
+ (validValue) => setValidValue(valueId2, validValue, skipMiddleware),
1214
1389
  ),
1215
1390
  valueId,
1216
1391
  );
1217
1392
  const applyChanges = (changes) =>
1218
- fluentTransaction(() => {
1219
- objMap(changes[0], (table, tableId) =>
1220
- isUndefined(table)
1221
- ? delTable(tableId)
1222
- : objMap(table, (row, rowId) =>
1223
- isUndefined(row)
1224
- ? delRow(tableId, rowId)
1225
- : objMap(row, (cell, cellId) =>
1226
- setOrDelCell(store, tableId, rowId, cellId, cell),
1227
- ),
1228
- ),
1229
- );
1230
- objMap(changes[1], (value, valueId) =>
1231
- setOrDelValue(store, valueId, value),
1232
- );
1233
- });
1393
+ fluentTransaction(() =>
1394
+ ifTransformed(
1395
+ changes,
1396
+ () =>
1397
+ ifNotUndefined(
1398
+ middleware[13],
1399
+ (willApplyChanges) =>
1400
+ whileMutating(() => willApplyChanges(structuredClone(changes))),
1401
+ () => changes,
1402
+ ),
1403
+ (changes2) => {
1404
+ objMap(changes2[0], (table, tableId) =>
1405
+ isUndefined(table)
1406
+ ? delTable(tableId)
1407
+ : objMap(table, (row, rowId) =>
1408
+ isUndefined(row)
1409
+ ? delRow(tableId, rowId)
1410
+ : objMap(row, (cell, cellId) =>
1411
+ setOrDelCell(tableId, rowId, cellId, cell),
1412
+ ),
1413
+ ),
1414
+ );
1415
+ objMap(changes2[1], (value, valueId) =>
1416
+ setOrDelValue(valueId, value),
1417
+ );
1418
+ },
1419
+ contentOrChangesIsEqual,
1420
+ ),
1421
+ );
1234
1422
  const setTablesJson = (tablesJson) => {
1235
1423
  tryCatch(() => setOrDelTables(jsonParse(tablesJson)));
1236
1424
  return store;
@@ -1277,7 +1465,12 @@ const createStore = () => {
1277
1465
  setTablesSchema(tablesSchema);
1278
1466
  setValuesSchema(valuesSchema);
1279
1467
  });
1280
- const delTables = () => fluentTransaction(() => setValidTables({}));
1468
+ const delTables = () =>
1469
+ fluentTransaction(() =>
1470
+ (whileMutating(() => middleware[7]?.()) ?? true)
1471
+ ? setValidTables({}, true)
1472
+ : 0,
1473
+ );
1281
1474
  const delTable = (tableId) =>
1282
1475
  fluentTransaction(
1283
1476
  (tableId2) =>
@@ -1295,7 +1488,7 @@ const createStore = () => {
1295
1488
  tableId,
1296
1489
  rowId,
1297
1490
  );
1298
- const delCell = (tableId, rowId, cellId, forceDel) =>
1491
+ const delCell = (tableId, rowId, cellId, forceDel, skipMiddleware) =>
1299
1492
  fluentTransaction(
1300
1493
  (tableId2, rowId2, cellId2) =>
1301
1494
  ifNotUndefined(mapGet(tablesMap, tableId2), (tableMap) =>
@@ -1308,6 +1501,7 @@ const createStore = () => {
1308
1501
  rowMap,
1309
1502
  cellId2,
1310
1503
  forceDel,
1504
+ skipMiddleware,
1311
1505
  )
1312
1506
  : 0,
1313
1507
  ),
@@ -1316,11 +1510,18 @@ const createStore = () => {
1316
1510
  rowId,
1317
1511
  cellId,
1318
1512
  );
1319
- const delValues = () => fluentTransaction(() => setValidValues({}));
1320
- const delValue = (valueId) =>
1513
+ const delValues = () =>
1514
+ fluentTransaction(() =>
1515
+ (whileMutating(() => middleware[11]?.()) ?? true)
1516
+ ? setValidValues({}, true)
1517
+ : 0,
1518
+ );
1519
+ const delValue = (valueId, skipMiddleware) =>
1321
1520
  fluentTransaction(
1322
1521
  (valueId2) =>
1323
- collHas(valuesMap, valueId2) ? delValidValue(valueId2) : 0,
1522
+ collHas(valuesMap, valueId2)
1523
+ ? delValidValue(valueId2, skipMiddleware)
1524
+ : 0,
1324
1525
  valueId,
1325
1526
  );
1326
1527
  const delTablesSchema = () =>
@@ -1402,27 +1603,27 @@ const createStore = () => {
1402
1603
  transactions--;
1403
1604
  if (transactions == 0) {
1404
1605
  transactions = 1;
1405
- mutating = 1;
1406
- callInvalidCellListeners(1);
1407
- if (!collIsEmpty(changedCells)) {
1408
- callTabularListenersForChanges(1);
1409
- }
1410
- callInvalidValueListeners(1);
1411
- if (!collIsEmpty(changedValues)) {
1412
- callValuesListenersForChanges(1);
1413
- }
1414
- mutating = 0;
1606
+ whileMutating(() => {
1607
+ callInvalidCellListeners(1);
1608
+ if (!collIsEmpty(changedCells)) {
1609
+ callTabularListenersForChanges(1);
1610
+ }
1611
+ callInvalidValueListeners(1);
1612
+ if (!collIsEmpty(changedValues)) {
1613
+ callValuesListenersForChanges(1);
1614
+ }
1615
+ });
1415
1616
  if (doRollback?.(store)) {
1416
1617
  collForEach(changedCells, (table, tableId) =>
1417
1618
  collForEach(table, (row, rowId) =>
1418
1619
  collForEach(row, ([oldCell], cellId) =>
1419
- setOrDelCell(store, tableId, rowId, cellId, oldCell),
1620
+ setOrDelCell(tableId, rowId, cellId, oldCell, true),
1420
1621
  ),
1421
1622
  ),
1422
1623
  );
1423
1624
  collClear(changedCells);
1424
1625
  collForEach(changedValues, ([oldValue], valueId) =>
1425
- setOrDelValue(store, valueId, oldValue),
1626
+ setOrDelValue(valueId, oldValue, true),
1426
1627
  );
1427
1628
  collClear(changedValues);
1428
1629
  }
@@ -1549,6 +1750,38 @@ const createStore = () => {
1549
1750
  collSize2(startTransactionListeners) +
1550
1751
  pairCollSize2(finishTransactionListeners),
1551
1752
  });
1753
+ const setMiddleware = (
1754
+ willSetContent,
1755
+ willSetTables,
1756
+ willSetTable,
1757
+ willSetRow,
1758
+ willSetCell,
1759
+ willSetValues,
1760
+ willSetValue,
1761
+ willDelTables,
1762
+ willDelTable,
1763
+ willDelRow,
1764
+ willDelCell,
1765
+ willDelValues,
1766
+ willDelValue,
1767
+ willApplyChanges,
1768
+ ) =>
1769
+ (middleware = [
1770
+ willSetContent,
1771
+ willSetTables,
1772
+ willSetTable,
1773
+ willSetRow,
1774
+ willSetCell,
1775
+ willSetValues,
1776
+ willSetValue,
1777
+ willDelTables,
1778
+ willDelTable,
1779
+ willDelRow,
1780
+ willDelCell,
1781
+ willDelValues,
1782
+ willDelValue,
1783
+ willApplyChanges,
1784
+ ]);
1552
1785
  const setInternalListeners = (
1553
1786
  preStartTransaction,
1554
1787
  preFinishTransaction,
@@ -1642,6 +1875,9 @@ const createStore = () => {
1642
1875
  addListener,
1643
1876
  callListeners,
1644
1877
  setInternalListeners,
1878
+ setMiddleware,
1879
+ setOrDelCell,
1880
+ setOrDelValue,
1645
1881
  };
1646
1882
  objMap(
1647
1883
  {