tinybase 4.0.0-beta.1 → 4.0.0-beta.2

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 (197) hide show
  1. package/bin/cli.js +1 -1
  2. package/lib/checkpoints.js +1 -1
  3. package/lib/checkpoints.js.gz +0 -0
  4. package/lib/cjs/checkpoints.cjs +1 -1
  5. package/lib/cjs/checkpoints.cjs.gz +0 -0
  6. package/lib/cjs/indexes.cjs +1 -1
  7. package/lib/cjs/indexes.cjs.gz +0 -0
  8. package/lib/cjs/metrics.cjs +1 -1
  9. package/lib/cjs/metrics.cjs.gz +0 -0
  10. package/lib/cjs/persister-browser.cjs +1 -1
  11. package/lib/cjs/persister-browser.cjs.gz +0 -0
  12. package/lib/cjs/persister-file.cjs +1 -1
  13. package/lib/cjs/persister-file.cjs.gz +0 -0
  14. package/lib/cjs/persister-remote.cjs +1 -1
  15. package/lib/cjs/persister-remote.cjs.gz +0 -0
  16. package/lib/cjs/persister-yjs.cjs +1 -1
  17. package/lib/cjs/persister-yjs.cjs.gz +0 -0
  18. package/lib/cjs/persisters.cjs +1 -1
  19. package/lib/cjs/persisters.cjs.gz +0 -0
  20. package/lib/cjs/queries.cjs +1 -1
  21. package/lib/cjs/queries.cjs.gz +0 -0
  22. package/lib/cjs/relationships.cjs +1 -1
  23. package/lib/cjs/relationships.cjs.gz +0 -0
  24. package/lib/cjs/store.cjs +1 -1
  25. package/lib/cjs/store.cjs.gz +0 -0
  26. package/lib/cjs/tinybase.cjs +1 -1
  27. package/lib/cjs/tinybase.cjs.gz +0 -0
  28. package/lib/cjs/tools.cjs +1 -1
  29. package/lib/cjs/tools.cjs.gz +0 -0
  30. package/lib/cjs/ui-react.cjs +1 -1
  31. package/lib/cjs/ui-react.cjs.gz +0 -0
  32. package/lib/cjs-es6/checkpoints.cjs +1 -1
  33. package/lib/cjs-es6/checkpoints.cjs.gz +0 -0
  34. package/lib/cjs-es6/indexes.cjs +1 -1
  35. package/lib/cjs-es6/indexes.cjs.gz +0 -0
  36. package/lib/cjs-es6/metrics.cjs +1 -1
  37. package/lib/cjs-es6/metrics.cjs.gz +0 -0
  38. package/lib/cjs-es6/persister-browser.cjs +1 -1
  39. package/lib/cjs-es6/persister-browser.cjs.gz +0 -0
  40. package/lib/cjs-es6/persister-file.cjs +1 -1
  41. package/lib/cjs-es6/persister-file.cjs.gz +0 -0
  42. package/lib/cjs-es6/persister-remote.cjs +1 -1
  43. package/lib/cjs-es6/persister-remote.cjs.gz +0 -0
  44. package/lib/cjs-es6/persister-yjs.cjs +1 -1
  45. package/lib/cjs-es6/persister-yjs.cjs.gz +0 -0
  46. package/lib/cjs-es6/persisters.cjs +1 -1
  47. package/lib/cjs-es6/persisters.cjs.gz +0 -0
  48. package/lib/cjs-es6/queries.cjs +1 -1
  49. package/lib/cjs-es6/queries.cjs.gz +0 -0
  50. package/lib/cjs-es6/relationships.cjs +1 -1
  51. package/lib/cjs-es6/relationships.cjs.gz +0 -0
  52. package/lib/cjs-es6/store.cjs +1 -1
  53. package/lib/cjs-es6/store.cjs.gz +0 -0
  54. package/lib/cjs-es6/tinybase.cjs +1 -1
  55. package/lib/cjs-es6/tinybase.cjs.gz +0 -0
  56. package/lib/cjs-es6/tools.cjs +1 -1
  57. package/lib/cjs-es6/tools.cjs.gz +0 -0
  58. package/lib/cjs-es6/ui-react.cjs +1 -1
  59. package/lib/cjs-es6/ui-react.cjs.gz +0 -0
  60. package/lib/debug/checkpoints.js +3 -3
  61. package/lib/debug/indexes.js +3 -3
  62. package/lib/debug/metrics.js +3 -3
  63. package/lib/debug/persister-browser.js +38 -39
  64. package/lib/debug/persister-file.js +37 -37
  65. package/lib/debug/persister-remote.js +37 -34
  66. package/lib/debug/persister-yjs.js +225 -40
  67. package/lib/debug/persisters.js +36 -33
  68. package/lib/debug/queries.js +5 -5
  69. package/lib/debug/relationships.js +3 -3
  70. package/lib/debug/store.js +129 -94
  71. package/lib/debug/tinybase.js +165 -125
  72. package/lib/debug/tools.js +9 -9
  73. package/lib/es6/checkpoints.js +1 -1
  74. package/lib/es6/checkpoints.js.gz +0 -0
  75. package/lib/es6/indexes.js +1 -1
  76. package/lib/es6/indexes.js.gz +0 -0
  77. package/lib/es6/metrics.js +1 -1
  78. package/lib/es6/metrics.js.gz +0 -0
  79. package/lib/es6/persister-browser.js +1 -1
  80. package/lib/es6/persister-browser.js.gz +0 -0
  81. package/lib/es6/persister-file.js +1 -1
  82. package/lib/es6/persister-file.js.gz +0 -0
  83. package/lib/es6/persister-remote.js +1 -1
  84. package/lib/es6/persister-remote.js.gz +0 -0
  85. package/lib/es6/persister-yjs.js +1 -1
  86. package/lib/es6/persister-yjs.js.gz +0 -0
  87. package/lib/es6/persisters.js +1 -1
  88. package/lib/es6/persisters.js.gz +0 -0
  89. package/lib/es6/queries.js +1 -1
  90. package/lib/es6/queries.js.gz +0 -0
  91. package/lib/es6/relationships.js +1 -1
  92. package/lib/es6/relationships.js.gz +0 -0
  93. package/lib/es6/store.js +1 -1
  94. package/lib/es6/store.js.gz +0 -0
  95. package/lib/es6/tinybase.js +1 -1
  96. package/lib/es6/tinybase.js.gz +0 -0
  97. package/lib/es6/tools.js +1 -1
  98. package/lib/es6/tools.js.gz +0 -0
  99. package/lib/es6/ui-react.js +1 -1
  100. package/lib/es6/ui-react.js.gz +0 -0
  101. package/lib/indexes.js +1 -1
  102. package/lib/indexes.js.gz +0 -0
  103. package/lib/metrics.js +1 -1
  104. package/lib/metrics.js.gz +0 -0
  105. package/lib/persister-browser.js +1 -1
  106. package/lib/persister-browser.js.gz +0 -0
  107. package/lib/persister-file.js +1 -1
  108. package/lib/persister-file.js.gz +0 -0
  109. package/lib/persister-remote.js +1 -1
  110. package/lib/persister-remote.js.gz +0 -0
  111. package/lib/persister-yjs.js +1 -1
  112. package/lib/persister-yjs.js.gz +0 -0
  113. package/lib/persisters.js +1 -1
  114. package/lib/persisters.js.gz +0 -0
  115. package/lib/queries.js +1 -1
  116. package/lib/queries.js.gz +0 -0
  117. package/lib/relationships.js +1 -1
  118. package/lib/relationships.js.gz +0 -0
  119. package/lib/store.js +1 -1
  120. package/lib/store.js.gz +0 -0
  121. package/lib/tinybase.js +1 -1
  122. package/lib/tinybase.js.gz +0 -0
  123. package/lib/tools.js +1 -1
  124. package/lib/tools.js.gz +0 -0
  125. package/lib/types/persister-yjs.d.ts +70 -8
  126. package/lib/types/persisters.d.ts +42 -17
  127. package/lib/types/queries.d.ts +52 -52
  128. package/lib/types/store.d.ts +399 -145
  129. package/lib/types/tools.d.ts +16 -16
  130. package/lib/types/ui-react.d.ts +56 -47
  131. package/lib/types/with-schemas/internal/ui-react.d.ts +2 -2
  132. package/lib/types/with-schemas/persister-yjs.d.ts +72 -9
  133. package/lib/types/with-schemas/persisters.d.ts +59 -18
  134. package/lib/types/with-schemas/queries.d.ts +52 -52
  135. package/lib/types/with-schemas/store.d.ts +480 -163
  136. package/lib/types/with-schemas/tools.d.ts +16 -16
  137. package/lib/types/with-schemas/ui-react.d.ts +54 -45
  138. package/lib/ui-react.js +1 -1
  139. package/lib/ui-react.js.gz +0 -0
  140. package/lib/umd/checkpoints.js +1 -1
  141. package/lib/umd/checkpoints.js.gz +0 -0
  142. package/lib/umd/indexes.js +1 -1
  143. package/lib/umd/indexes.js.gz +0 -0
  144. package/lib/umd/metrics.js +1 -1
  145. package/lib/umd/metrics.js.gz +0 -0
  146. package/lib/umd/persister-browser.js +1 -1
  147. package/lib/umd/persister-browser.js.gz +0 -0
  148. package/lib/umd/persister-file.js +1 -1
  149. package/lib/umd/persister-file.js.gz +0 -0
  150. package/lib/umd/persister-remote.js +1 -1
  151. package/lib/umd/persister-remote.js.gz +0 -0
  152. package/lib/umd/persister-yjs.js +1 -1
  153. package/lib/umd/persister-yjs.js.gz +0 -0
  154. package/lib/umd/persisters.js +1 -1
  155. package/lib/umd/persisters.js.gz +0 -0
  156. package/lib/umd/queries.js +1 -1
  157. package/lib/umd/queries.js.gz +0 -0
  158. package/lib/umd/relationships.js +1 -1
  159. package/lib/umd/relationships.js.gz +0 -0
  160. package/lib/umd/store.js +1 -1
  161. package/lib/umd/store.js.gz +0 -0
  162. package/lib/umd/tinybase.js +1 -1
  163. package/lib/umd/tinybase.js.gz +0 -0
  164. package/lib/umd/tools.js +1 -1
  165. package/lib/umd/tools.js.gz +0 -0
  166. package/lib/umd/ui-react.js +1 -1
  167. package/lib/umd/ui-react.js.gz +0 -0
  168. package/lib/umd-es6/checkpoints.js +1 -1
  169. package/lib/umd-es6/checkpoints.js.gz +0 -0
  170. package/lib/umd-es6/indexes.js +1 -1
  171. package/lib/umd-es6/indexes.js.gz +0 -0
  172. package/lib/umd-es6/metrics.js +1 -1
  173. package/lib/umd-es6/metrics.js.gz +0 -0
  174. package/lib/umd-es6/persister-browser.js +1 -1
  175. package/lib/umd-es6/persister-browser.js.gz +0 -0
  176. package/lib/umd-es6/persister-file.js +1 -1
  177. package/lib/umd-es6/persister-file.js.gz +0 -0
  178. package/lib/umd-es6/persister-remote.js +1 -1
  179. package/lib/umd-es6/persister-remote.js.gz +0 -0
  180. package/lib/umd-es6/persister-yjs.js +1 -1
  181. package/lib/umd-es6/persister-yjs.js.gz +0 -0
  182. package/lib/umd-es6/persisters.js +1 -1
  183. package/lib/umd-es6/persisters.js.gz +0 -0
  184. package/lib/umd-es6/queries.js +1 -1
  185. package/lib/umd-es6/queries.js.gz +0 -0
  186. package/lib/umd-es6/relationships.js +1 -1
  187. package/lib/umd-es6/relationships.js.gz +0 -0
  188. package/lib/umd-es6/store.js +1 -1
  189. package/lib/umd-es6/store.js.gz +0 -0
  190. package/lib/umd-es6/tinybase.js +1 -1
  191. package/lib/umd-es6/tinybase.js.gz +0 -0
  192. package/lib/umd-es6/tools.js +1 -1
  193. package/lib/umd-es6/tools.js.gz +0 -0
  194. package/lib/umd-es6/ui-react.js +1 -1
  195. package/lib/umd-es6/ui-react.js.gz +0 -0
  196. package/package.json +13 -13
  197. package/readme.md +2 -2
@@ -1,7 +1,6 @@
1
1
  import {readFile, writeFile} from 'fs/promises';
2
2
  import {watch} from 'fs';
3
3
 
4
- const EMPTY_STRING = '';
5
4
  const UTF8 = 'utf8';
6
5
 
7
6
  const arrayReduce = (array, cb, initial) => array.reduce(cb, initial);
@@ -40,43 +39,45 @@ const createCustomPersister = (
40
39
  let saves = 0;
41
40
  let listening = false;
42
41
  let listeningHandle;
42
+ const loadLock = async (actions) => {
43
+ /* istanbul ignore else */
44
+ if (loadSave != 2) {
45
+ loadSave = 1;
46
+ {
47
+ loads++;
48
+ }
49
+ await actions();
50
+ loadSave = 0;
51
+ }
52
+ };
43
53
  const persister = {
44
54
  load: async (initialTables, initialValues) => {
45
- /* istanbul ignore else */
46
- if (loadSave != 2) {
47
- loadSave = 1;
48
- {
49
- loads++;
50
- }
51
- const body = await getPersisted();
52
- if (!isUndefined(body) && body != EMPTY_STRING) {
53
- store.setJson(body);
54
- } else {
55
- store.setContent([initialTables, initialValues]);
56
- }
57
- loadSave = 0;
58
- }
55
+ await loadLock(async () => {
56
+ let content;
57
+ try {
58
+ content = await getPersisted();
59
+ } catch {}
60
+ store.setContent(content ?? [initialTables, initialValues]);
61
+ });
59
62
  return persister;
60
63
  },
61
64
  startAutoLoad: async (initialTables, initialValues) => {
62
65
  persister.stopAutoLoad();
63
66
  await persister.load(initialTables, initialValues);
64
67
  listening = true;
65
- listeningHandle = addPersisterListener(async (content) => {
66
- if (isUndefined(content)) {
67
- await persister.load();
68
- } else {
69
- /* istanbul ignore else */
70
- if (loadSave != 2) {
71
- loadSave = 1;
72
- {
73
- loads++;
68
+ listeningHandle = addPersisterListener(
69
+ async (getContent, getTransactionChanges) => {
70
+ await loadLock(async () => {
71
+ if (getTransactionChanges) {
72
+ store.setTransactionChanges(getTransactionChanges());
73
+ } else {
74
+ try {
75
+ store.setContent(getContent?.() ?? (await getPersisted()));
76
+ } catch {}
74
77
  }
75
- store.setContent(content);
76
- loadSave = 0;
77
- }
78
- }
79
- });
78
+ });
79
+ },
80
+ );
80
81
  return persister;
81
82
  },
82
83
  stopAutoLoad: () => {
@@ -87,21 +88,24 @@ const createCustomPersister = (
87
88
  }
88
89
  return persister;
89
90
  },
90
- save: async () => {
91
+ save: async (getTransactionChanges) => {
91
92
  /* istanbul ignore else */
92
93
  if (loadSave != 1) {
93
94
  loadSave = 2;
94
95
  {
95
96
  saves++;
96
97
  }
97
- await setPersisted(store.getContent);
98
+ await setPersisted(store.getContent, getTransactionChanges);
98
99
  loadSave = 0;
99
100
  }
100
101
  return persister;
101
102
  },
102
103
  startAutoSave: async () => {
103
104
  await persister.stopAutoSave().save();
104
- listenerId = store.addDidFinishTransactionListener(persister.save);
105
+ listenerId = store.addDidFinishTransactionListener(
106
+ (_store, getTransactionChanges) =>
107
+ persister.save(getTransactionChanges),
108
+ );
105
109
  return persister;
106
110
  },
107
111
  stopAutoSave: () => {
@@ -116,11 +120,7 @@ const createCustomPersister = (
116
120
  };
117
121
 
118
122
  const createFilePersister = (store, filePath) => {
119
- const getPersisted = async () => {
120
- try {
121
- return await readFile(filePath, UTF8);
122
- } catch {}
123
- };
123
+ const getPersisted = async () => JSON.parse(await readFile(filePath, UTF8));
124
124
  const setPersisted = async (getContent) => {
125
125
  try {
126
126
  await writeFile(filePath, jsonString(getContent()), UTF8);
@@ -1,5 +1,3 @@
1
- const EMPTY_STRING = '';
2
-
3
1
  const arrayReduce = (array, cb, initial) => array.reduce(cb, initial);
4
2
 
5
3
  const jsonString = (obj) =>
@@ -36,43 +34,45 @@ const createCustomPersister = (
36
34
  let saves = 0;
37
35
  let listening = false;
38
36
  let listeningHandle;
37
+ const loadLock = async (actions) => {
38
+ /* istanbul ignore else */
39
+ if (loadSave != 2) {
40
+ loadSave = 1;
41
+ {
42
+ loads++;
43
+ }
44
+ await actions();
45
+ loadSave = 0;
46
+ }
47
+ };
39
48
  const persister = {
40
49
  load: async (initialTables, initialValues) => {
41
- /* istanbul ignore else */
42
- if (loadSave != 2) {
43
- loadSave = 1;
44
- {
45
- loads++;
46
- }
47
- const body = await getPersisted();
48
- if (!isUndefined(body) && body != EMPTY_STRING) {
49
- store.setJson(body);
50
- } else {
51
- store.setContent([initialTables, initialValues]);
52
- }
53
- loadSave = 0;
54
- }
50
+ await loadLock(async () => {
51
+ let content;
52
+ try {
53
+ content = await getPersisted();
54
+ } catch {}
55
+ store.setContent(content ?? [initialTables, initialValues]);
56
+ });
55
57
  return persister;
56
58
  },
57
59
  startAutoLoad: async (initialTables, initialValues) => {
58
60
  persister.stopAutoLoad();
59
61
  await persister.load(initialTables, initialValues);
60
62
  listening = true;
61
- listeningHandle = addPersisterListener(async (content) => {
62
- if (isUndefined(content)) {
63
- await persister.load();
64
- } else {
65
- /* istanbul ignore else */
66
- if (loadSave != 2) {
67
- loadSave = 1;
68
- {
69
- loads++;
63
+ listeningHandle = addPersisterListener(
64
+ async (getContent, getTransactionChanges) => {
65
+ await loadLock(async () => {
66
+ if (getTransactionChanges) {
67
+ store.setTransactionChanges(getTransactionChanges());
68
+ } else {
69
+ try {
70
+ store.setContent(getContent?.() ?? (await getPersisted()));
71
+ } catch {}
70
72
  }
71
- store.setContent(content);
72
- loadSave = 0;
73
- }
74
- }
75
- });
73
+ });
74
+ },
75
+ );
76
76
  return persister;
77
77
  },
78
78
  stopAutoLoad: () => {
@@ -83,21 +83,24 @@ const createCustomPersister = (
83
83
  }
84
84
  return persister;
85
85
  },
86
- save: async () => {
86
+ save: async (getTransactionChanges) => {
87
87
  /* istanbul ignore else */
88
88
  if (loadSave != 1) {
89
89
  loadSave = 2;
90
90
  {
91
91
  saves++;
92
92
  }
93
- await setPersisted(store.getContent);
93
+ await setPersisted(store.getContent, getTransactionChanges);
94
94
  loadSave = 0;
95
95
  }
96
96
  return persister;
97
97
  },
98
98
  startAutoSave: async () => {
99
99
  await persister.stopAutoSave().save();
100
- listenerId = store.addDidFinishTransactionListener(persister.save);
100
+ listenerId = store.addDidFinishTransactionListener(
101
+ (_store, getTransactionChanges) =>
102
+ persister.save(getTransactionChanges),
103
+ );
101
104
  return persister;
102
105
  },
103
106
  stopAutoSave: () => {
@@ -122,7 +125,7 @@ const createRemotePersister = (
122
125
  const getPersisted = async () => {
123
126
  const response = await fetch(loadUrl);
124
127
  lastEtag = getETag(response);
125
- return response.text();
128
+ return JSON.parse(await response.text());
126
129
  };
127
130
  const setPersisted = async (getContent) =>
128
131
  await fetch(saveUrl, {
@@ -1,4 +1,10 @@
1
- const EMPTY_STRING = '';
1
+ import {Map} from 'yjs';
2
+
3
+ const arrayForEach = (array, cb) => array.forEach(cb);
4
+ const arrayMap = (array, cb) => array.map(cb);
5
+ const arrayLength = (array) => array.length;
6
+ const arrayIsEmpty = (array) => arrayLength(array) == 0;
7
+ const arrayShift = (array) => array.shift();
2
8
 
3
9
  const isUndefined = (thing) => thing == void 0;
4
10
  const ifNotUndefined = (value, then, otherwise) =>
@@ -6,6 +12,17 @@ const ifNotUndefined = (value, then, otherwise) =>
6
12
 
7
13
  const object = Object;
8
14
  const objFreeze = object.freeze;
15
+ const objNew = () => ({});
16
+ const objGet = (obj, id) => ifNotUndefined(obj, (obj2) => obj2[id]);
17
+ const objHas = (obj, id) => !isUndefined(objGet(obj, id));
18
+ const objMap = (obj, cb) =>
19
+ arrayMap(object.entries(obj), ([id, value]) => cb(value, id));
20
+ const objEnsure = (obj, id, getDefaultValue) => {
21
+ if (!objHas(obj, id)) {
22
+ obj[id] = getDefaultValue();
23
+ }
24
+ return obj[id];
25
+ };
9
26
 
10
27
  const createCustomPersister = (
11
28
  store,
@@ -20,43 +37,45 @@ const createCustomPersister = (
20
37
  let saves = 0;
21
38
  let listening = false;
22
39
  let listeningHandle;
40
+ const loadLock = async (actions) => {
41
+ /* istanbul ignore else */
42
+ if (loadSave != 2) {
43
+ loadSave = 1;
44
+ {
45
+ loads++;
46
+ }
47
+ await actions();
48
+ loadSave = 0;
49
+ }
50
+ };
23
51
  const persister = {
24
52
  load: async (initialTables, initialValues) => {
25
- /* istanbul ignore else */
26
- if (loadSave != 2) {
27
- loadSave = 1;
28
- {
29
- loads++;
30
- }
31
- const body = await getPersisted();
32
- if (!isUndefined(body) && body != EMPTY_STRING) {
33
- store.setJson(body);
34
- } else {
35
- store.setContent([initialTables, initialValues]);
36
- }
37
- loadSave = 0;
38
- }
53
+ await loadLock(async () => {
54
+ let content;
55
+ try {
56
+ content = await getPersisted();
57
+ } catch {}
58
+ store.setContent(content ?? [initialTables, initialValues]);
59
+ });
39
60
  return persister;
40
61
  },
41
62
  startAutoLoad: async (initialTables, initialValues) => {
42
63
  persister.stopAutoLoad();
43
64
  await persister.load(initialTables, initialValues);
44
65
  listening = true;
45
- listeningHandle = addPersisterListener(async (content) => {
46
- if (isUndefined(content)) {
47
- await persister.load();
48
- } else {
49
- /* istanbul ignore else */
50
- if (loadSave != 2) {
51
- loadSave = 1;
52
- {
53
- loads++;
66
+ listeningHandle = addPersisterListener(
67
+ async (getContent, getTransactionChanges) => {
68
+ await loadLock(async () => {
69
+ if (getTransactionChanges) {
70
+ store.setTransactionChanges(getTransactionChanges());
71
+ } else {
72
+ try {
73
+ store.setContent(getContent?.() ?? (await getPersisted()));
74
+ } catch {}
54
75
  }
55
- store.setContent(content);
56
- loadSave = 0;
57
- }
58
- }
59
- });
76
+ });
77
+ },
78
+ );
60
79
  return persister;
61
80
  },
62
81
  stopAutoLoad: () => {
@@ -67,21 +86,24 @@ const createCustomPersister = (
67
86
  }
68
87
  return persister;
69
88
  },
70
- save: async () => {
89
+ save: async (getTransactionChanges) => {
71
90
  /* istanbul ignore else */
72
91
  if (loadSave != 1) {
73
92
  loadSave = 2;
74
93
  {
75
94
  saves++;
76
95
  }
77
- await setPersisted(store.getContent);
96
+ await setPersisted(store.getContent, getTransactionChanges);
78
97
  loadSave = 0;
79
98
  }
80
99
  return persister;
81
100
  },
82
101
  startAutoSave: async () => {
83
102
  await persister.stopAutoSave().save();
84
- listenerId = store.addDidFinishTransactionListener(persister.save);
103
+ listenerId = store.addDidFinishTransactionListener(
104
+ (_store, getTransactionChanges) =>
105
+ persister.save(getTransactionChanges),
106
+ );
85
107
  return persister;
86
108
  },
87
109
  stopAutoSave: () => {
@@ -95,18 +117,181 @@ const createCustomPersister = (
95
117
  return objFreeze(persister);
96
118
  };
97
119
 
98
- const createYjsPersister = (store, yDoc) => {
99
- const map = yDoc.getMap('tinybase/store');
100
- const getPersisted = async () => map.get('json');
101
- const setPersisted = async (getContent) => {
102
- map.set('json', JSON.stringify(getContent()));
120
+ const collForEach = (coll, cb) => coll?.forEach(cb);
121
+
122
+ const mapForEach = (map, cb) =>
123
+ collForEach(map, (value, key) => cb(key, value));
124
+
125
+ const tablesKey = 't';
126
+ const valuesKey = 'v';
127
+ const DELETE = 'delete';
128
+ const ensureYContent = (yContent) => {
129
+ if (!yContent.size) {
130
+ yContent.set(tablesKey, new Map());
131
+ yContent.set(valuesKey, new Map());
132
+ }
133
+ };
134
+ const getYContent = (yContent) => [
135
+ yContent.get(tablesKey),
136
+ yContent.get(valuesKey),
137
+ ];
138
+ const getTransactionChangesFromYDoc = (yContent, events) => {
139
+ if (arrayLength(events) == 1 && arrayIsEmpty(events[0].path)) {
140
+ return [yContent.get('t').toJSON(), yContent.get('v').toJSON()];
141
+ }
142
+ const [yTables, yValues] = getYContent(yContent);
143
+ const tables = {};
144
+ const values = {};
145
+ arrayForEach(events, ({path, changes: {keys}}) =>
146
+ arrayShift(path) == tablesKey
147
+ ? ifNotUndefined(
148
+ arrayShift(path),
149
+ (yTableId) => {
150
+ const table = objEnsure(tables, yTableId, objNew);
151
+ const yTable = yTables.get(yTableId);
152
+ ifNotUndefined(
153
+ arrayShift(path),
154
+ (yRowId) => {
155
+ const row = objEnsure(table, yRowId, objNew);
156
+ const yRow = yTable.get(yRowId);
157
+ mapForEach(
158
+ keys,
159
+ (cellId, {action}) =>
160
+ (row[cellId] = action == DELETE ? null : yRow.get(cellId)),
161
+ );
162
+ },
163
+ () =>
164
+ mapForEach(
165
+ keys,
166
+ (rowId, {action}) =>
167
+ (table[rowId] =
168
+ action == DELETE ? null : yTable.get(rowId)?.toJSON()),
169
+ ),
170
+ );
171
+ },
172
+ () =>
173
+ mapForEach(
174
+ keys,
175
+ (tableId, {action}) =>
176
+ (tables[tableId] =
177
+ action == DELETE ? null : yTables.get(tableId)?.toJSON()),
178
+ ),
179
+ )
180
+ : mapForEach(
181
+ keys,
182
+ (valueId, {action}) =>
183
+ (values[valueId] = action == DELETE ? null : yValues.get(valueId)),
184
+ ),
185
+ );
186
+ return [tables, values];
187
+ };
188
+ const setTransactionChangesToYDoc = (
189
+ yContent,
190
+ getContent,
191
+ getTransactionChanges,
192
+ ) => {
193
+ ensureYContent(yContent);
194
+ const [yTables, yValues] = getYContent(yContent);
195
+ const transactionChangesDidFail = () => {
196
+ transactionChangesFailed = 1;
103
197
  };
198
+ let transactionChangesFailed = 1;
199
+ ifNotUndefined(getTransactionChanges?.(), ([cellChanges, valueChanges]) => {
200
+ transactionChangesFailed = 0;
201
+ objMap(cellChanges, (table, tableId) =>
202
+ transactionChangesFailed
203
+ ? 0
204
+ : isUndefined(table)
205
+ ? yTables.delete(tableId)
206
+ : ifNotUndefined(
207
+ yTables.get(tableId),
208
+ (yTable) =>
209
+ objMap(table, (row, rowId) =>
210
+ transactionChangesFailed
211
+ ? 0
212
+ : isUndefined(row)
213
+ ? yTable.delete(rowId)
214
+ : ifNotUndefined(
215
+ yTable.get(rowId),
216
+ (yRow) =>
217
+ objMap(row, (cell, cellId) =>
218
+ isUndefined(cell)
219
+ ? yRow.delete(cellId)
220
+ : yRow.set(cellId, cell),
221
+ ),
222
+ transactionChangesDidFail,
223
+ ),
224
+ ),
225
+ transactionChangesDidFail,
226
+ ),
227
+ );
228
+ objMap(valueChanges, (value, valueId) =>
229
+ transactionChangesFailed
230
+ ? 0
231
+ : isUndefined(value)
232
+ ? yValues.delete(valueId)
233
+ : yValues.set(valueId, value),
234
+ );
235
+ });
236
+ if (transactionChangesFailed) {
237
+ const [tables, values] = getContent();
238
+ yMapMatch(yTables, void 0, tables, (_, tableId, table) =>
239
+ yMapMatch(yTables, tableId, table, (yTable, rowId, row) =>
240
+ yMapMatch(yTable, rowId, row, (yRow, cellId, cell) => {
241
+ if (yRow.get(cellId) !== cell) {
242
+ yRow.set(cellId, cell);
243
+ return 1;
244
+ }
245
+ }),
246
+ ),
247
+ );
248
+ yMapMatch(yValues, void 0, values, (_, valueId, value) => {
249
+ if (yValues.get(valueId) !== value) {
250
+ yValues.set(valueId, value);
251
+ }
252
+ });
253
+ }
254
+ };
255
+ const yMapMatch = (yMapOrParent, idInParent, obj, set) => {
256
+ const yMap = isUndefined(idInParent)
257
+ ? yMapOrParent
258
+ : yMapOrParent.get(idInParent) ?? yMapOrParent.set(idInParent, new Map());
259
+ let changed;
260
+ objMap(obj, (value, id) => {
261
+ if (set(yMap, id, value)) {
262
+ changed = 1;
263
+ }
264
+ });
265
+ yMap.forEach((_, id) => {
266
+ if (!objHas(obj, id)) {
267
+ yMap.delete(id);
268
+ changed = 1;
269
+ }
270
+ });
271
+ if (!isUndefined(idInParent) && !yMap.size) {
272
+ yMapOrParent.delete(idInParent);
273
+ }
274
+ return changed;
275
+ };
276
+ const createYjsPersister = (store, yDoc, yMapName = 'tinybase') => {
277
+ const yContent = yDoc.getMap(yMapName);
278
+ const getPersisted = async () =>
279
+ yContent.size
280
+ ? [yContent.get('t').toJSON(), yContent.get('v').toJSON()]
281
+ : void 0;
282
+ const setPersisted = async (getContent, getTransactionChanges) =>
283
+ yDoc.transact(() =>
284
+ setTransactionChangesToYDoc(yContent, getContent, getTransactionChanges),
285
+ );
104
286
  const addPersisterListener = (listener) => {
105
- const observer = () => listener();
106
- map.observe(observer);
287
+ const observer = (events) =>
288
+ listener(void 0, () => getTransactionChangesFromYDoc(yContent, events));
289
+ yContent.observeDeep(observer);
107
290
  return observer;
108
291
  };
109
- const delPersisterListener = (observer) => map.unobserve(observer);
292
+ const delPersisterListener = (observer) => {
293
+ yContent.unobserveDeep(observer);
294
+ };
110
295
  return createCustomPersister(
111
296
  store,
112
297
  getPersisted,
@@ -1,5 +1,3 @@
1
- const EMPTY_STRING = '';
2
-
3
1
  const isUndefined = (thing) => thing == void 0;
4
2
  const ifNotUndefined = (value, then, otherwise) =>
5
3
  isUndefined(value) ? otherwise?.() : then(value);
@@ -20,43 +18,45 @@ const createCustomPersister = (
20
18
  let saves = 0;
21
19
  let listening = false;
22
20
  let listeningHandle;
21
+ const loadLock = async (actions) => {
22
+ /* istanbul ignore else */
23
+ if (loadSave != 2) {
24
+ loadSave = 1;
25
+ {
26
+ loads++;
27
+ }
28
+ await actions();
29
+ loadSave = 0;
30
+ }
31
+ };
23
32
  const persister = {
24
33
  load: async (initialTables, initialValues) => {
25
- /* istanbul ignore else */
26
- if (loadSave != 2) {
27
- loadSave = 1;
28
- {
29
- loads++;
30
- }
31
- const body = await getPersisted();
32
- if (!isUndefined(body) && body != EMPTY_STRING) {
33
- store.setJson(body);
34
- } else {
35
- store.setContent([initialTables, initialValues]);
36
- }
37
- loadSave = 0;
38
- }
34
+ await loadLock(async () => {
35
+ let content;
36
+ try {
37
+ content = await getPersisted();
38
+ } catch {}
39
+ store.setContent(content ?? [initialTables, initialValues]);
40
+ });
39
41
  return persister;
40
42
  },
41
43
  startAutoLoad: async (initialTables, initialValues) => {
42
44
  persister.stopAutoLoad();
43
45
  await persister.load(initialTables, initialValues);
44
46
  listening = true;
45
- listeningHandle = addPersisterListener(async (content) => {
46
- if (isUndefined(content)) {
47
- await persister.load();
48
- } else {
49
- /* istanbul ignore else */
50
- if (loadSave != 2) {
51
- loadSave = 1;
52
- {
53
- loads++;
47
+ listeningHandle = addPersisterListener(
48
+ async (getContent, getTransactionChanges) => {
49
+ await loadLock(async () => {
50
+ if (getTransactionChanges) {
51
+ store.setTransactionChanges(getTransactionChanges());
52
+ } else {
53
+ try {
54
+ store.setContent(getContent?.() ?? (await getPersisted()));
55
+ } catch {}
54
56
  }
55
- store.setContent(content);
56
- loadSave = 0;
57
- }
58
- }
59
- });
57
+ });
58
+ },
59
+ );
60
60
  return persister;
61
61
  },
62
62
  stopAutoLoad: () => {
@@ -67,21 +67,24 @@ const createCustomPersister = (
67
67
  }
68
68
  return persister;
69
69
  },
70
- save: async () => {
70
+ save: async (getTransactionChanges) => {
71
71
  /* istanbul ignore else */
72
72
  if (loadSave != 1) {
73
73
  loadSave = 2;
74
74
  {
75
75
  saves++;
76
76
  }
77
- await setPersisted(store.getContent);
77
+ await setPersisted(store.getContent, getTransactionChanges);
78
78
  loadSave = 0;
79
79
  }
80
80
  return persister;
81
81
  },
82
82
  startAutoSave: async () => {
83
83
  await persister.stopAutoSave().save();
84
- listenerId = store.addDidFinishTransactionListener(persister.save);
84
+ listenerId = store.addDidFinishTransactionListener(
85
+ (_store, getTransactionChanges) =>
86
+ persister.save(getTransactionChanges),
87
+ );
85
88
  return persister;
86
89
  },
87
90
  stopAutoSave: () => {
@@ -44,6 +44,11 @@ const isFunction = (thing) => getTypeOf(thing) == FUNCTION;
44
44
  const isArray = (thing) => Array.isArray(thing);
45
45
  const getUndefined = () => void 0;
46
46
 
47
+ const object = Object;
48
+ const objFreeze = object.freeze;
49
+ const objMap = (obj, cb) =>
50
+ arrayMap(object.entries(obj), ([id, value]) => cb(value, id));
51
+
47
52
  const collSize = (coll) => coll.size;
48
53
  const collHas = (coll, keyOrValue) => coll?.has(keyOrValue) ?? false;
49
54
  const collIsEmpty = (coll) => isUndefined(coll) || collSize(coll) == 0;
@@ -329,11 +334,6 @@ const getCreateFunction = (getFunction) => {
329
334
  };
330
335
  };
331
336
 
332
- const object = Object;
333
- const objFreeze = object.freeze;
334
- const objMap = (obj, cb) =>
335
- arrayMap(object.entries(obj), ([id, value]) => cb(value, id));
336
-
337
337
  const createQueries = getCreateFunction((store) => {
338
338
  const createStore = store.createStore;
339
339
  const [