tinybase 6.2.0 → 6.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. package/@types/omni/index.d.ts +1 -0
  2. package/@types/omni/with-schemas/index.d.ts +1 -0
  3. package/@types/persisters/index.d.ts +4 -2
  4. package/@types/persisters/persister-durable-object-sql-storage/index.d.ts +348 -0
  5. package/@types/persisters/persister-durable-object-sql-storage/with-schemas/index.d.ts +381 -0
  6. package/@types/persisters/with-schemas/index.d.ts +4 -2
  7. package/@types/queries/index.d.ts +1 -1
  8. package/@types/queries/with-schemas/index.d.ts +1 -1
  9. package/LICENSE +1 -1
  10. package/min/checkpoints/index.js +1 -1
  11. package/min/checkpoints/index.js.gz +0 -0
  12. package/min/checkpoints/with-schemas/index.js +1 -1
  13. package/min/checkpoints/with-schemas/index.js.gz +0 -0
  14. package/min/common/index.js +1 -1
  15. package/min/common/index.js.gz +0 -0
  16. package/min/common/with-schemas/index.js +1 -1
  17. package/min/common/with-schemas/index.js.gz +0 -0
  18. package/min/index.js +1 -1
  19. package/min/index.js.gz +0 -0
  20. package/min/indexes/index.js +1 -1
  21. package/min/indexes/index.js.gz +0 -0
  22. package/min/indexes/with-schemas/index.js +1 -1
  23. package/min/indexes/with-schemas/index.js.gz +0 -0
  24. package/min/mergeable-store/index.js +1 -1
  25. package/min/mergeable-store/index.js.gz +0 -0
  26. package/min/mergeable-store/with-schemas/index.js +1 -1
  27. package/min/mergeable-store/with-schemas/index.js.gz +0 -0
  28. package/min/metrics/index.js +1 -1
  29. package/min/metrics/index.js.gz +0 -0
  30. package/min/metrics/with-schemas/index.js +1 -1
  31. package/min/metrics/with-schemas/index.js.gz +0 -0
  32. package/min/omni/index.js +1 -1
  33. package/min/omni/index.js.gz +0 -0
  34. package/min/omni/with-schemas/index.js +1 -1
  35. package/min/omni/with-schemas/index.js.gz +0 -0
  36. package/min/persisters/index.js +1 -1
  37. package/min/persisters/index.js.gz +0 -0
  38. package/min/persisters/persister-automerge/index.js +1 -1
  39. package/min/persisters/persister-automerge/index.js.gz +0 -0
  40. package/min/persisters/persister-automerge/with-schemas/index.js +1 -1
  41. package/min/persisters/persister-automerge/with-schemas/index.js.gz +0 -0
  42. package/min/persisters/persister-browser/index.js +1 -1
  43. package/min/persisters/persister-browser/index.js.gz +0 -0
  44. package/min/persisters/persister-browser/with-schemas/index.js +1 -1
  45. package/min/persisters/persister-browser/with-schemas/index.js.gz +0 -0
  46. package/min/persisters/persister-cr-sqlite-wasm/index.js +1 -1
  47. package/min/persisters/persister-cr-sqlite-wasm/index.js.gz +0 -0
  48. package/min/persisters/persister-cr-sqlite-wasm/with-schemas/index.js +1 -1
  49. package/min/persisters/persister-cr-sqlite-wasm/with-schemas/index.js.gz +0 -0
  50. package/min/persisters/persister-durable-object-sql-storage/index.js +1 -0
  51. package/min/persisters/persister-durable-object-sql-storage/index.js.gz +0 -0
  52. package/min/persisters/persister-durable-object-sql-storage/with-schemas/index.js +1 -0
  53. package/min/persisters/persister-durable-object-sql-storage/with-schemas/index.js.gz +0 -0
  54. package/min/persisters/persister-durable-object-storage/index.js +1 -1
  55. package/min/persisters/persister-durable-object-storage/index.js.gz +0 -0
  56. package/min/persisters/persister-durable-object-storage/with-schemas/index.js +1 -1
  57. package/min/persisters/persister-durable-object-storage/with-schemas/index.js.gz +0 -0
  58. package/min/persisters/persister-electric-sql/index.js +1 -1
  59. package/min/persisters/persister-electric-sql/index.js.gz +0 -0
  60. package/min/persisters/persister-electric-sql/with-schemas/index.js +1 -1
  61. package/min/persisters/persister-electric-sql/with-schemas/index.js.gz +0 -0
  62. package/min/persisters/persister-expo-sqlite/index.js +1 -1
  63. package/min/persisters/persister-expo-sqlite/index.js.gz +0 -0
  64. package/min/persisters/persister-expo-sqlite/with-schemas/index.js +1 -1
  65. package/min/persisters/persister-expo-sqlite/with-schemas/index.js.gz +0 -0
  66. package/min/persisters/persister-file/index.js +1 -1
  67. package/min/persisters/persister-file/index.js.gz +0 -0
  68. package/min/persisters/persister-file/with-schemas/index.js +1 -1
  69. package/min/persisters/persister-file/with-schemas/index.js.gz +0 -0
  70. package/min/persisters/persister-indexed-db/index.js +1 -1
  71. package/min/persisters/persister-indexed-db/index.js.gz +0 -0
  72. package/min/persisters/persister-indexed-db/with-schemas/index.js +1 -1
  73. package/min/persisters/persister-indexed-db/with-schemas/index.js.gz +0 -0
  74. package/min/persisters/persister-libsql/index.js +1 -1
  75. package/min/persisters/persister-libsql/index.js.gz +0 -0
  76. package/min/persisters/persister-libsql/with-schemas/index.js +1 -1
  77. package/min/persisters/persister-libsql/with-schemas/index.js.gz +0 -0
  78. package/min/persisters/persister-partykit-client/index.js +1 -1
  79. package/min/persisters/persister-partykit-client/index.js.gz +0 -0
  80. package/min/persisters/persister-partykit-client/with-schemas/index.js +1 -1
  81. package/min/persisters/persister-partykit-client/with-schemas/index.js.gz +0 -0
  82. package/min/persisters/persister-partykit-server/index.js +1 -1
  83. package/min/persisters/persister-partykit-server/index.js.gz +0 -0
  84. package/min/persisters/persister-partykit-server/with-schemas/index.js +1 -1
  85. package/min/persisters/persister-partykit-server/with-schemas/index.js.gz +0 -0
  86. package/min/persisters/persister-pglite/index.js +1 -1
  87. package/min/persisters/persister-pglite/index.js.gz +0 -0
  88. package/min/persisters/persister-pglite/with-schemas/index.js +1 -1
  89. package/min/persisters/persister-pglite/with-schemas/index.js.gz +0 -0
  90. package/min/persisters/persister-postgres/index.js +1 -1
  91. package/min/persisters/persister-postgres/index.js.gz +0 -0
  92. package/min/persisters/persister-postgres/with-schemas/index.js +1 -1
  93. package/min/persisters/persister-postgres/with-schemas/index.js.gz +0 -0
  94. package/min/persisters/persister-powersync/index.js +1 -1
  95. package/min/persisters/persister-powersync/index.js.gz +0 -0
  96. package/min/persisters/persister-powersync/with-schemas/index.js +1 -1
  97. package/min/persisters/persister-powersync/with-schemas/index.js.gz +0 -0
  98. package/min/persisters/persister-remote/index.js +1 -1
  99. package/min/persisters/persister-remote/index.js.gz +0 -0
  100. package/min/persisters/persister-remote/with-schemas/index.js +1 -1
  101. package/min/persisters/persister-remote/with-schemas/index.js.gz +0 -0
  102. package/min/persisters/persister-sqlite-bun/index.js +1 -1
  103. package/min/persisters/persister-sqlite-bun/index.js.gz +0 -0
  104. package/min/persisters/persister-sqlite-bun/with-schemas/index.js +1 -1
  105. package/min/persisters/persister-sqlite-bun/with-schemas/index.js.gz +0 -0
  106. package/min/persisters/persister-sqlite-wasm/index.js +1 -1
  107. package/min/persisters/persister-sqlite-wasm/index.js.gz +0 -0
  108. package/min/persisters/persister-sqlite-wasm/with-schemas/index.js +1 -1
  109. package/min/persisters/persister-sqlite-wasm/with-schemas/index.js.gz +0 -0
  110. package/min/persisters/persister-sqlite3/index.js +1 -1
  111. package/min/persisters/persister-sqlite3/index.js.gz +0 -0
  112. package/min/persisters/persister-sqlite3/with-schemas/index.js +1 -1
  113. package/min/persisters/persister-sqlite3/with-schemas/index.js.gz +0 -0
  114. package/min/persisters/persister-yjs/index.js +1 -1
  115. package/min/persisters/persister-yjs/index.js.gz +0 -0
  116. package/min/persisters/persister-yjs/with-schemas/index.js +1 -1
  117. package/min/persisters/persister-yjs/with-schemas/index.js.gz +0 -0
  118. package/min/persisters/with-schemas/index.js +1 -1
  119. package/min/persisters/with-schemas/index.js.gz +0 -0
  120. package/min/queries/index.js +1 -1
  121. package/min/queries/index.js.gz +0 -0
  122. package/min/queries/with-schemas/index.js +1 -1
  123. package/min/queries/with-schemas/index.js.gz +0 -0
  124. package/min/relationships/index.js +1 -1
  125. package/min/relationships/index.js.gz +0 -0
  126. package/min/relationships/with-schemas/index.js +1 -1
  127. package/min/relationships/with-schemas/index.js.gz +0 -0
  128. package/min/store/index.js +1 -1
  129. package/min/store/index.js.gz +0 -0
  130. package/min/store/with-schemas/index.js +1 -1
  131. package/min/store/with-schemas/index.js.gz +0 -0
  132. package/min/synchronizers/index.js +1 -1
  133. package/min/synchronizers/index.js.gz +0 -0
  134. package/min/synchronizers/synchronizer-broadcast-channel/index.js +1 -1
  135. package/min/synchronizers/synchronizer-broadcast-channel/index.js.gz +0 -0
  136. package/min/synchronizers/synchronizer-broadcast-channel/with-schemas/index.js +1 -1
  137. package/min/synchronizers/synchronizer-broadcast-channel/with-schemas/index.js.gz +0 -0
  138. package/min/synchronizers/synchronizer-local/index.js +1 -1
  139. package/min/synchronizers/synchronizer-local/index.js.gz +0 -0
  140. package/min/synchronizers/synchronizer-local/with-schemas/index.js +1 -1
  141. package/min/synchronizers/synchronizer-local/with-schemas/index.js.gz +0 -0
  142. package/min/synchronizers/synchronizer-ws-client/index.js +1 -1
  143. package/min/synchronizers/synchronizer-ws-client/index.js.gz +0 -0
  144. package/min/synchronizers/synchronizer-ws-client/with-schemas/index.js +1 -1
  145. package/min/synchronizers/synchronizer-ws-client/with-schemas/index.js.gz +0 -0
  146. package/min/synchronizers/synchronizer-ws-server/index.js +1 -1
  147. package/min/synchronizers/synchronizer-ws-server/index.js.gz +0 -0
  148. package/min/synchronizers/synchronizer-ws-server/with-schemas/index.js +1 -1
  149. package/min/synchronizers/synchronizer-ws-server/with-schemas/index.js.gz +0 -0
  150. package/min/synchronizers/synchronizer-ws-server-durable-object/index.js +1 -1
  151. package/min/synchronizers/synchronizer-ws-server-durable-object/index.js.gz +0 -0
  152. package/min/synchronizers/synchronizer-ws-server-durable-object/with-schemas/index.js +1 -1
  153. package/min/synchronizers/synchronizer-ws-server-durable-object/with-schemas/index.js.gz +0 -0
  154. package/min/synchronizers/synchronizer-ws-server-simple/index.js +1 -1
  155. package/min/synchronizers/synchronizer-ws-server-simple/index.js.gz +0 -0
  156. package/min/synchronizers/synchronizer-ws-server-simple/with-schemas/index.js +1 -1
  157. package/min/synchronizers/synchronizer-ws-server-simple/with-schemas/index.js.gz +0 -0
  158. package/min/synchronizers/with-schemas/index.js +1 -1
  159. package/min/synchronizers/with-schemas/index.js.gz +0 -0
  160. package/min/ui-react/index.js +1 -1
  161. package/min/ui-react/index.js.gz +0 -0
  162. package/min/ui-react/with-schemas/index.js +1 -1
  163. package/min/ui-react/with-schemas/index.js.gz +0 -0
  164. package/min/ui-react-dom/index.js +1 -1
  165. package/min/ui-react-dom/index.js.gz +0 -0
  166. package/min/ui-react-dom/with-schemas/index.js +1 -1
  167. package/min/ui-react-dom/with-schemas/index.js.gz +0 -0
  168. package/min/ui-react-inspector/index.js +1 -1
  169. package/min/ui-react-inspector/index.js.gz +0 -0
  170. package/min/ui-react-inspector/with-schemas/index.js +1 -1
  171. package/min/ui-react-inspector/with-schemas/index.js.gz +0 -0
  172. package/min/with-schemas/index.js +1 -1
  173. package/min/with-schemas/index.js.gz +0 -0
  174. package/omni/index.js +4 -1
  175. package/omni/with-schemas/index.js +4 -1
  176. package/package.json +43 -7
  177. package/persisters/persister-browser/index.js +4 -1
  178. package/persisters/persister-browser/with-schemas/index.js +4 -1
  179. package/persisters/persister-durable-object-sql-storage/index.js +1391 -0
  180. package/persisters/persister-durable-object-sql-storage/with-schemas/index.js +1391 -0
  181. package/readme.md +13 -13
  182. package/releases.md +30 -30
  183. package/ui-react-inspector/index.js +4 -1
  184. package/ui-react-inspector/with-schemas/index.js +4 -1
@@ -0,0 +1,1391 @@
1
+ const getTypeOf = (thing) => typeof thing;
2
+ const TINYBASE = 'tinybase';
3
+ const EMPTY_STRING = '';
4
+ const COMMA = ',';
5
+ const DOT = '.';
6
+ const STRING = getTypeOf(EMPTY_STRING);
7
+ const TRUE = 'true';
8
+ const T = 't';
9
+ const UNDEFINED = '\uFFFC';
10
+ const strSplit = (str, separator = EMPTY_STRING, limit) =>
11
+ str.split(separator, limit);
12
+ const strReplace = (str, searchValue, replaceValue) =>
13
+ str.replace(searchValue, replaceValue);
14
+
15
+ const promise = Promise;
16
+ const THOUSAND = 1e3;
17
+ const startInterval = (callback, sec, immediate) => {
18
+ return setInterval(callback, sec * THOUSAND);
19
+ };
20
+ const stopInterval = clearInterval;
21
+ const isUndefined = (thing) => thing == void 0;
22
+ const ifNotUndefined = (value, then, otherwise) =>
23
+ isUndefined(value) ? otherwise?.() : then(value);
24
+ const isString = (thing) => getTypeOf(thing) == STRING;
25
+ const isArray = (thing) => Array.isArray(thing);
26
+ const slice = (arrayOrString, start, end) => arrayOrString.slice(start, end);
27
+ const size = (arrayOrString) => arrayOrString.length;
28
+ const test = (regex, subject) => regex.test(subject);
29
+ const noop = () => {};
30
+ const promiseAll = async (promises) => promise.all(promises);
31
+ const errorNew = (message) => {
32
+ throw new Error(message);
33
+ };
34
+ const tryCatch = async (action, then1, then2) => {
35
+ try {
36
+ return await action();
37
+ } catch (error) {
38
+ /* istanbul ignore next */
39
+ then1?.(error);
40
+ }
41
+ };
42
+
43
+ const arrayForEach = (array, cb) => array.forEach(cb);
44
+ const arrayJoin = (array, sep = EMPTY_STRING) => array.join(sep);
45
+ const arrayMap = (array, cb) => array.map(cb);
46
+ const arrayIsEmpty = (array) => size(array) == 0;
47
+ const arrayFilter = (array, cb) => array.filter(cb);
48
+ const arrayClear = (array, to) => array.splice(0, to);
49
+ const arrayPush = (array, ...values) => array.push(...values);
50
+ const arrayShift = (array) => array.shift();
51
+
52
+ const object = Object;
53
+ const getPrototypeOf = (obj) => object.getPrototypeOf(obj);
54
+ const objEntries = object.entries;
55
+ const isObject = (obj) =>
56
+ !isUndefined(obj) &&
57
+ ifNotUndefined(
58
+ getPrototypeOf(obj),
59
+ (objPrototype) =>
60
+ objPrototype == object.prototype ||
61
+ isUndefined(getPrototypeOf(objPrototype)),
62
+
63
+ /* istanbul ignore next */
64
+ () => true,
65
+ );
66
+ const objIds = object.keys;
67
+ const objFreeze = object.freeze;
68
+ const objNew = (entries = []) => object.fromEntries(entries);
69
+ const objMerge = (...objs) => object.assign({}, ...objs);
70
+ const objHas = (obj, id) => id in obj;
71
+ const objDel = (obj, id) => {
72
+ delete obj[id];
73
+ return obj;
74
+ };
75
+ const objForEach = (obj, cb) =>
76
+ arrayForEach(objEntries(obj), ([id, value]) => cb(value, id));
77
+ const objToArray = (obj, cb) =>
78
+ arrayMap(objEntries(obj), ([id, value]) => cb(value, id));
79
+ const objMap = (obj, cb) =>
80
+ objNew(objToArray(obj, (value, id) => [id, cb(value, id)]));
81
+ const objValues = (obj) => object.values(obj);
82
+ const objSize = (obj) => size(objIds(obj));
83
+ const objIsEmpty = (obj) => isObject(obj) && objSize(obj) == 0;
84
+ const objEnsure = (obj, id, getDefaultValue) => {
85
+ if (!objHas(obj, id)) {
86
+ obj[id] = getDefaultValue();
87
+ }
88
+ return obj[id];
89
+ };
90
+
91
+ const jsonString = JSON.stringify;
92
+ const jsonParse = JSON.parse;
93
+ const jsonStringWithUndefined = (obj) =>
94
+ jsonString(obj, (_key, value) => (value === void 0 ? UNDEFINED : value));
95
+ const jsonParseWithUndefined = (str) =>
96
+ jsonParse(str, (_key, value) => (value === UNDEFINED ? void 0 : value));
97
+
98
+ const collSize = (coll) => coll?.size ?? 0;
99
+ const collHas = (coll, keyOrValue) => coll?.has(keyOrValue) ?? false;
100
+ const collIsEmpty = (coll) => isUndefined(coll) || collSize(coll) == 0;
101
+ const collValues = (coll) => [...(coll?.values() ?? [])];
102
+ const collClear = (coll) => coll.clear();
103
+ const collForEach = (coll, cb) => coll?.forEach(cb);
104
+ const collDel = (coll, keyOrValue) => coll?.delete(keyOrValue);
105
+
106
+ const mapNew = (entries) => new Map(entries);
107
+ const mapGet = (map, key) => map?.get(key);
108
+ const mapForEach = (map, cb) =>
109
+ collForEach(map, (value, key) => cb(key, value));
110
+ const mapMap = (coll, cb) =>
111
+ arrayMap([...(coll?.entries() ?? [])], ([key, value]) => cb(value, key));
112
+ const mapSet = (map, key, value) =>
113
+ isUndefined(value) ? (collDel(map, key), map) : map?.set(key, value);
114
+ const mapEnsure = (map, key, getDefaultValue, hadExistingValue) => {
115
+ if (!collHas(map, key)) {
116
+ mapSet(map, key, getDefaultValue());
117
+ } else {
118
+ hadExistingValue?.(mapGet(map, key));
119
+ }
120
+ return mapGet(map, key);
121
+ };
122
+ const visitTree = (node, path, ensureLeaf, pruneLeaf, p = 0) =>
123
+ ifNotUndefined(
124
+ (ensureLeaf ? mapEnsure : mapGet)(
125
+ node,
126
+ path[p],
127
+ p > size(path) - 2 ? ensureLeaf : mapNew,
128
+ ),
129
+ (nodeOrLeaf) => {
130
+ if (p > size(path) - 2) {
131
+ if (pruneLeaf?.(nodeOrLeaf)) {
132
+ mapSet(node, path[p]);
133
+ }
134
+ return nodeOrLeaf;
135
+ }
136
+ const leaf = visitTree(nodeOrLeaf, path, ensureLeaf, pruneLeaf, p + 1);
137
+ if (collIsEmpty(nodeOrLeaf)) {
138
+ mapSet(node, path[p]);
139
+ }
140
+ return leaf;
141
+ },
142
+ );
143
+
144
+ const stampNewWithHash = (value, hlc, hash) => [value, hlc, hash];
145
+ const stampUpdate = (stamp, hlc, hash) => {
146
+ if (hlc > stamp[1]) {
147
+ stamp[1] = hlc;
148
+ }
149
+ stamp[2] = hash >>> 0;
150
+ };
151
+
152
+ const INTEGER = /^\d+$/;
153
+ const getPoolFunctions = () => {
154
+ const pool = [];
155
+ let nextId = 0;
156
+ return [
157
+ (reuse) => (reuse ? arrayShift(pool) : null) ?? EMPTY_STRING + nextId++,
158
+ (id) => {
159
+ if (test(INTEGER, id) && size(pool) < 1e3) {
160
+ arrayPush(pool, id);
161
+ }
162
+ },
163
+ ];
164
+ };
165
+
166
+ const setNew = (entryOrEntries) =>
167
+ new Set(
168
+ isArray(entryOrEntries) || isUndefined(entryOrEntries)
169
+ ? entryOrEntries
170
+ : [entryOrEntries],
171
+ );
172
+ const setAdd = (set, value) => set?.add(value);
173
+
174
+ const getWildcardedLeaves = (deepIdSet, path = [EMPTY_STRING]) => {
175
+ const leaves = [];
176
+ const deep = (node, p) =>
177
+ p == size(path)
178
+ ? arrayPush(leaves, node)
179
+ : path[p] === null
180
+ ? collForEach(node, (node2) => deep(node2, p + 1))
181
+ : arrayForEach([path[p], null], (id) => deep(mapGet(node, id), p + 1));
182
+ deep(deepIdSet, 0);
183
+ return leaves;
184
+ };
185
+ const getListenerFunctions = (getThing) => {
186
+ let thing;
187
+ const [getId, releaseId] = getPoolFunctions();
188
+ const allListeners = mapNew();
189
+ const addListener = (
190
+ listener,
191
+ idSetNode,
192
+ path,
193
+ pathGetters = [],
194
+ extraArgsGetter = () => [],
195
+ ) => {
196
+ thing ??= getThing();
197
+ const id = getId(1);
198
+ mapSet(allListeners, id, [
199
+ listener,
200
+ idSetNode,
201
+ path,
202
+ pathGetters,
203
+ extraArgsGetter,
204
+ ]);
205
+ setAdd(visitTree(idSetNode, path ?? [EMPTY_STRING], setNew), id);
206
+ return id;
207
+ };
208
+ const callListeners = (idSetNode, ids, ...extraArgs) =>
209
+ arrayForEach(getWildcardedLeaves(idSetNode, ids), (set) =>
210
+ collForEach(set, (id) =>
211
+ mapGet(allListeners, id)[0](thing, ...(ids ?? []), ...extraArgs),
212
+ ),
213
+ );
214
+ const delListener = (id) =>
215
+ ifNotUndefined(mapGet(allListeners, id), ([, idSetNode, idOrNulls]) => {
216
+ visitTree(idSetNode, idOrNulls ?? [EMPTY_STRING], void 0, (idSet) => {
217
+ collDel(idSet, id);
218
+ return collIsEmpty(idSet) ? 1 : 0;
219
+ });
220
+ mapSet(allListeners, id);
221
+ releaseId(id);
222
+ return idOrNulls;
223
+ });
224
+ const callListener = (id) =>
225
+ ifNotUndefined(
226
+ mapGet(allListeners, id),
227
+ ([listener, , path = [], pathGetters, extraArgsGetter]) => {
228
+ const callWithIds = (...ids) => {
229
+ const index = size(ids);
230
+ if (index == size(path)) {
231
+ listener(thing, ...ids, ...extraArgsGetter(ids));
232
+ } else if (isUndefined(path[index])) {
233
+ arrayForEach(pathGetters[index]?.(...ids) ?? [], (id2) =>
234
+ callWithIds(...ids, id2),
235
+ );
236
+ } else {
237
+ callWithIds(...ids, path[index]);
238
+ }
239
+ };
240
+ callWithIds();
241
+ },
242
+ );
243
+ return [addListener, callListeners, delListener, callListener];
244
+ };
245
+
246
+ const scheduleRunning = mapNew();
247
+ const scheduleActions = mapNew();
248
+ const getStoreFunctions = (
249
+ persist = 1 /* StoreOnly */,
250
+ store,
251
+ isSynchronizer,
252
+ ) =>
253
+ persist != 1 /* StoreOnly */ && store.isMergeable()
254
+ ? [
255
+ 1,
256
+ store.getMergeableContent,
257
+ () => store.getTransactionMergeableChanges(!isSynchronizer),
258
+ ([[changedTables], [changedValues]]) =>
259
+ !objIsEmpty(changedTables) || !objIsEmpty(changedValues),
260
+ store.setDefaultContent,
261
+ ]
262
+ : persist != 2 /* MergeableStoreOnly */
263
+ ? [
264
+ 0,
265
+ store.getContent,
266
+ store.getTransactionChanges,
267
+ ([changedTables, changedValues]) =>
268
+ !objIsEmpty(changedTables) || !objIsEmpty(changedValues),
269
+ store.setContent,
270
+ ]
271
+ : errorNew('Store type not supported by this Persister');
272
+ const createCustomPersister = (
273
+ store,
274
+ getPersisted,
275
+ setPersisted,
276
+ addPersisterListener,
277
+ delPersisterListener,
278
+ onIgnoredError,
279
+ persist,
280
+ extra = {},
281
+ isSynchronizer = 0,
282
+ scheduleId = [],
283
+ ) => {
284
+ let status = 0; /* Idle */
285
+ let loads = 0;
286
+ let saves = 0;
287
+ let action;
288
+ let autoLoadHandle;
289
+ let autoSaveListenerId;
290
+ mapEnsure(scheduleRunning, scheduleId, () => 0);
291
+ mapEnsure(scheduleActions, scheduleId, () => []);
292
+ const statusListeners = mapNew();
293
+ const [
294
+ isMergeableStore,
295
+ getContent,
296
+ getChanges,
297
+ hasChanges,
298
+ setDefaultContent,
299
+ ] = getStoreFunctions(persist, store, isSynchronizer);
300
+ const [addListener, callListeners, delListenerImpl] = getListenerFunctions(
301
+ () => persister,
302
+ );
303
+ const setStatus = (newStatus) => {
304
+ if (newStatus != status) {
305
+ status = newStatus;
306
+ callListeners(statusListeners, void 0, status);
307
+ }
308
+ };
309
+ const run = async () => {
310
+ /* istanbul ignore else */
311
+ if (!mapGet(scheduleRunning, scheduleId)) {
312
+ mapSet(scheduleRunning, scheduleId, 1);
313
+ while (
314
+ !isUndefined((action = arrayShift(mapGet(scheduleActions, scheduleId))))
315
+ ) {
316
+ await tryCatch(action, onIgnoredError);
317
+ }
318
+ mapSet(scheduleRunning, scheduleId, 0);
319
+ }
320
+ };
321
+ const setContentOrChanges = (contentOrChanges) => {
322
+ (isMergeableStore && isArray(contentOrChanges?.[0])
323
+ ? contentOrChanges?.[2] === 1
324
+ ? store.applyMergeableChanges
325
+ : store.setMergeableContent
326
+ : contentOrChanges?.[2] === 1
327
+ ? store.applyChanges
328
+ : store.setContent)(contentOrChanges);
329
+ };
330
+ const load = async (initialContent) => {
331
+ /* istanbul ignore else */
332
+ if (status != 2 /* Saving */) {
333
+ setStatus(1 /* Loading */);
334
+ loads++;
335
+ await schedule(async () => {
336
+ await tryCatch(
337
+ async () => {
338
+ const content = await getPersisted();
339
+ if (isArray(content)) {
340
+ setContentOrChanges(content);
341
+ } else if (initialContent) {
342
+ setDefaultContent(initialContent);
343
+ } else {
344
+ errorNew(`Content is not an array: ${content}`);
345
+ }
346
+ },
347
+ () => {
348
+ if (initialContent) {
349
+ setDefaultContent(initialContent);
350
+ }
351
+ },
352
+ );
353
+ setStatus(0 /* Idle */);
354
+ });
355
+ }
356
+ return persister;
357
+ };
358
+ const startAutoLoad = async (initialContent) => {
359
+ stopAutoLoad();
360
+ await load(initialContent);
361
+ await tryCatch(
362
+ async () =>
363
+ (autoLoadHandle = await addPersisterListener(
364
+ async (content, changes) => {
365
+ if (changes || content) {
366
+ /* istanbul ignore else */
367
+ if (status != 2 /* Saving */) {
368
+ setStatus(1 /* Loading */);
369
+ loads++;
370
+ setContentOrChanges(changes ?? content);
371
+ setStatus(0 /* Idle */);
372
+ }
373
+ } else {
374
+ await load();
375
+ }
376
+ },
377
+ )),
378
+ onIgnoredError,
379
+ );
380
+ return persister;
381
+ };
382
+ const stopAutoLoad = async () => {
383
+ if (autoLoadHandle) {
384
+ await tryCatch(
385
+ () => delPersisterListener(autoLoadHandle),
386
+ onIgnoredError,
387
+ );
388
+ autoLoadHandle = void 0;
389
+ }
390
+ return persister;
391
+ };
392
+ const isAutoLoading = () => !isUndefined(autoLoadHandle);
393
+ const save = async (changes) => {
394
+ /* istanbul ignore else */
395
+ if (status != 1 /* Loading */) {
396
+ setStatus(2 /* Saving */);
397
+ saves++;
398
+ await schedule(async () => {
399
+ await tryCatch(() => setPersisted(getContent, changes), onIgnoredError);
400
+ setStatus(0 /* Idle */);
401
+ });
402
+ }
403
+ return persister;
404
+ };
405
+ const startAutoSave = async () => {
406
+ stopAutoSave();
407
+ await save();
408
+ autoSaveListenerId = store.addDidFinishTransactionListener(() => {
409
+ const changes = getChanges();
410
+ if (hasChanges(changes)) {
411
+ save(changes);
412
+ }
413
+ });
414
+ return persister;
415
+ };
416
+ const stopAutoSave = async () => {
417
+ if (autoSaveListenerId) {
418
+ store.delListener(autoSaveListenerId);
419
+ autoSaveListenerId = void 0;
420
+ }
421
+ return persister;
422
+ };
423
+ const isAutoSaving = () => !isUndefined(autoSaveListenerId);
424
+ const startAutoPersisting = async (
425
+ initialContent,
426
+ startSaveFirst = false,
427
+ ) => {
428
+ const [call1, call2] = startSaveFirst
429
+ ? [startAutoSave, startAutoLoad]
430
+ : [startAutoLoad, startAutoSave];
431
+ await call1(initialContent);
432
+ await call2(initialContent);
433
+ return persister;
434
+ };
435
+ const stopAutoPersisting = async (stopSaveFirst = false) => {
436
+ const [call1, call2] = stopSaveFirst
437
+ ? [stopAutoSave, stopAutoLoad]
438
+ : [stopAutoLoad, stopAutoSave];
439
+ await call1();
440
+ await call2();
441
+ return persister;
442
+ };
443
+ const getStatus = () => status;
444
+ const addStatusListener = (listener) =>
445
+ addListener(listener, statusListeners);
446
+ const delListener = (listenerId) => {
447
+ delListenerImpl(listenerId);
448
+ return store;
449
+ };
450
+ const schedule = async (...actions) => {
451
+ arrayPush(mapGet(scheduleActions, scheduleId), ...actions);
452
+ await run();
453
+ return persister;
454
+ };
455
+ const getStore = () => store;
456
+ const destroy = () => {
457
+ arrayClear(mapGet(scheduleActions, scheduleId));
458
+ return stopAutoPersisting();
459
+ };
460
+ const getStats = () => ({loads, saves});
461
+ const persister = {
462
+ load,
463
+ startAutoLoad,
464
+ stopAutoLoad,
465
+ isAutoLoading,
466
+ save,
467
+ startAutoSave,
468
+ stopAutoSave,
469
+ isAutoSaving,
470
+ startAutoPersisting,
471
+ stopAutoPersisting,
472
+ getStatus,
473
+ addStatusListener,
474
+ delListener,
475
+ schedule,
476
+ getStore,
477
+ destroy,
478
+ getStats,
479
+ ...extra,
480
+ };
481
+ return objFreeze(persister);
482
+ };
483
+
484
+ const SINGLE_ROW_ID = '_';
485
+ const DEFAULT_ROW_ID_COLUMN_NAME = '_id';
486
+ const SELECT = 'SELECT';
487
+ const WHERE = 'WHERE';
488
+ const TABLE = 'TABLE';
489
+ const INSERT = 'INSERT';
490
+ const DELETE = 'DELETE';
491
+ const UPDATE = 'UPDATE';
492
+ const ALTER_TABLE = 'ALTER ' + TABLE;
493
+ const FROM = 'FROM';
494
+ const DELETE_FROM = DELETE + ' ' + FROM;
495
+ const SELECT_STAR_FROM = SELECT + '*' + FROM;
496
+ const PRAGMA = 'pragma_';
497
+ const DATA_VERSION = 'data_version';
498
+ const SCHEMA_VERSION = 'schema_version';
499
+ const PRAGMA_TABLE = 'pragma_table_';
500
+ const CREATE = 'CREATE ';
501
+ const CREATE_TABLE = CREATE + TABLE;
502
+ const TABLE_NAME_PLACEHOLDER = '$tableName';
503
+ const getWrappedCommand = (executeCommand, onSqlCommand) =>
504
+ onSqlCommand
505
+ ? async (sql, params) => {
506
+ onSqlCommand(sql, params);
507
+ return await executeCommand(sql, params);
508
+ }
509
+ : executeCommand;
510
+ const escapeId = (str) =>
511
+ arrayJoin(
512
+ arrayMap(strSplit(str, DOT), (part) => `"${strReplace(part, /"/g, '""')}"`),
513
+ DOT,
514
+ );
515
+ const escapeColumnNames = (...columnNames) =>
516
+ arrayJoin(arrayMap(columnNames, escapeId), COMMA);
517
+ const getPlaceholders = (array, offset = [1]) =>
518
+ arrayJoin(
519
+ arrayMap(array, () => '$' + offset[0]++),
520
+ COMMA,
521
+ );
522
+ const getWhereCondition = (tableName, condition = TRUE) =>
523
+ WHERE +
524
+ `(${strReplace(condition, TABLE_NAME_PLACEHOLDER, escapeId(tableName))})`;
525
+
526
+ const COLUMN_NAME = 'ColumnName';
527
+ const STORE = 'store';
528
+ const JSON$1 = 'json';
529
+ const STORE_TABLE_NAME = STORE + 'TableName';
530
+ const STORE_ID_COLUMN_NAME = STORE + 'Id' + COLUMN_NAME;
531
+ const STORE_COLUMN_NAME = STORE + COLUMN_NAME;
532
+ const AUTO_LOAD_INTERVAL_SECONDS = 'autoLoadIntervalSeconds';
533
+ const ROW_ID_COLUMN_NAME = 'rowId' + COLUMN_NAME;
534
+ const TABLE_ID = 'tableId';
535
+ const TABLE_NAME = 'tableName';
536
+ const DELETE_EMPTY_COLUMNS = 'deleteEmptyColumns';
537
+ const DELETE_EMPTY_TABLE = 'deleteEmptyTable';
538
+ const CONDITION = 'condition';
539
+ const DEFAULT_CONFIG = {
540
+ mode: JSON$1,
541
+ [AUTO_LOAD_INTERVAL_SECONDS]: 1,
542
+ };
543
+ const DEFAULT_TABULAR_VALUES_CONFIG = {
544
+ load: 0,
545
+ save: 0,
546
+ [TABLE_NAME]: TINYBASE + '_values',
547
+ };
548
+ const getDefaultedConfig = (configOrStoreTableName) =>
549
+ objMerge(
550
+ DEFAULT_CONFIG,
551
+ isString(configOrStoreTableName)
552
+ ? {[STORE_TABLE_NAME]: configOrStoreTableName}
553
+ : (configOrStoreTableName ?? {}),
554
+ );
555
+ const getDefaultedTabularConfigMap = (
556
+ configsObj,
557
+ defaultObj,
558
+ tableField,
559
+ exclude,
560
+ then,
561
+ ) => {
562
+ const configMap = mapNew();
563
+ objMap(configsObj, (configObj, id) => {
564
+ const defaultedConfig = slice(
565
+ objValues(
566
+ objMerge(
567
+ defaultObj,
568
+ isString(configObj) ? {[tableField]: configObj} : configObj,
569
+ ),
570
+ ),
571
+ 0,
572
+ objSize(defaultObj),
573
+ );
574
+ if (!isUndefined(defaultedConfig[0]) && !exclude(id, defaultedConfig[0])) {
575
+ then(id, defaultedConfig[0]);
576
+ mapSet(configMap, id, defaultedConfig);
577
+ }
578
+ });
579
+ return configMap;
580
+ };
581
+ const getConfigStructures = (configOrStoreTableName) => {
582
+ const config = getDefaultedConfig(configOrStoreTableName);
583
+ const autoLoadIntervalSeconds = config[AUTO_LOAD_INTERVAL_SECONDS];
584
+ if (config.mode == JSON$1) {
585
+ const storeTableName = config[STORE_TABLE_NAME] ?? TINYBASE;
586
+ return [
587
+ 1,
588
+ autoLoadIntervalSeconds,
589
+ [
590
+ storeTableName,
591
+ config[STORE_ID_COLUMN_NAME] ?? DEFAULT_ROW_ID_COLUMN_NAME,
592
+ config[STORE_COLUMN_NAME] ?? STORE,
593
+ ],
594
+ setNew(storeTableName),
595
+ ];
596
+ }
597
+ const {tables: {load = {}, save = {}} = {}, values = {}} = config;
598
+ const valuesConfig = slice(
599
+ objValues(objMerge(DEFAULT_TABULAR_VALUES_CONFIG, values)),
600
+ 0,
601
+ objSize(DEFAULT_TABULAR_VALUES_CONFIG),
602
+ );
603
+ const valuesTable = valuesConfig[2];
604
+ const managedTableNames = setNew(valuesTable);
605
+ const excludedTableNames = setNew(valuesTable);
606
+ const tablesLoadConfig = getDefaultedTabularConfigMap(
607
+ load,
608
+ {
609
+ [TABLE_ID]: null,
610
+ [ROW_ID_COLUMN_NAME]: DEFAULT_ROW_ID_COLUMN_NAME,
611
+ [CONDITION]: TRUE,
612
+ },
613
+ TABLE_ID,
614
+ (tableName) => collHas(excludedTableNames, tableName),
615
+ (tableName) => setAdd(managedTableNames, tableName),
616
+ );
617
+ const tablesSaveConfig = getDefaultedTabularConfigMap(
618
+ save,
619
+ {
620
+ [TABLE_NAME]: null,
621
+ [ROW_ID_COLUMN_NAME]: DEFAULT_ROW_ID_COLUMN_NAME,
622
+ [DELETE_EMPTY_COLUMNS]: 0,
623
+ [DELETE_EMPTY_TABLE]: 0,
624
+ [CONDITION]: null,
625
+ },
626
+ TABLE_NAME,
627
+ (_, tableName) => collHas(excludedTableNames, tableName),
628
+ (_, tableName) => setAdd(managedTableNames, tableName),
629
+ );
630
+ mapForEach(
631
+ tablesSaveConfig,
632
+ (_, tableSaveConfig) =>
633
+ (tableSaveConfig[4] ??=
634
+ mapGet(tablesLoadConfig, tableSaveConfig[0])?.[2] ?? TRUE),
635
+ );
636
+ return [
637
+ 0,
638
+ autoLoadIntervalSeconds,
639
+ [tablesLoadConfig, tablesSaveConfig, valuesConfig],
640
+ managedTableNames,
641
+ ];
642
+ };
643
+
644
+ const getCommandFunctions = (
645
+ databaseExecuteCommand,
646
+ managedTableNames,
647
+ querySchema,
648
+ onIgnoredError,
649
+ columnType,
650
+ upsert = defaultUpsert,
651
+ encode,
652
+ decode,
653
+ ) => {
654
+ const schemaMap = mapNew();
655
+ const canSelect = (tableName, rowIdColumnName) =>
656
+ collHas(mapGet(schemaMap, tableName), rowIdColumnName);
657
+ const refreshSchema = async () => {
658
+ collClear(schemaMap);
659
+ arrayMap(
660
+ await querySchema(databaseExecuteCommand, managedTableNames),
661
+ ({tn, cn}) => setAdd(mapEnsure(schemaMap, tn, setNew), cn),
662
+ );
663
+ };
664
+ const loadTable = async (tableName, rowIdColumnName, condition) =>
665
+ canSelect(tableName, rowIdColumnName)
666
+ ? objNew(
667
+ arrayFilter(
668
+ arrayMap(
669
+ await databaseExecuteCommand(
670
+ SELECT_STAR_FROM +
671
+ escapeId(tableName) +
672
+ getWhereCondition(tableName, condition),
673
+ ),
674
+ (row) => [
675
+ row[rowIdColumnName],
676
+ decode
677
+ ? objMap(objDel(row, rowIdColumnName), decode)
678
+ : objDel(row, rowIdColumnName),
679
+ ],
680
+ ),
681
+ ([rowId, row]) => !isUndefined(rowId) && !objIsEmpty(row),
682
+ ),
683
+ )
684
+ : {};
685
+ const saveTable = async (
686
+ tableName,
687
+ rowIdColumnName,
688
+ content,
689
+ deleteEmptyColumns,
690
+ deleteEmptyTable,
691
+ partial = false,
692
+ condition = TRUE,
693
+ ) => {
694
+ const settingColumnNameSet = setNew();
695
+ objMap(content ?? {}, (contentRow) =>
696
+ arrayMap(objIds(contentRow ?? {}), (cellOrValueId) =>
697
+ setAdd(settingColumnNameSet, cellOrValueId),
698
+ ),
699
+ );
700
+ const settingColumnNames = collValues(settingColumnNameSet);
701
+ if (
702
+ !partial &&
703
+ deleteEmptyTable &&
704
+ condition == TRUE &&
705
+ arrayIsEmpty(settingColumnNames) &&
706
+ collHas(schemaMap, tableName)
707
+ ) {
708
+ await databaseExecuteCommand('DROP ' + TABLE + escapeId(tableName));
709
+ mapSet(schemaMap, tableName);
710
+ return;
711
+ }
712
+ const currentColumnNames = mapGet(schemaMap, tableName);
713
+ const unaccountedColumnNames = setNew(collValues(currentColumnNames));
714
+ if (!arrayIsEmpty(settingColumnNames)) {
715
+ if (!collHas(schemaMap, tableName)) {
716
+ await databaseExecuteCommand(
717
+ CREATE_TABLE +
718
+ escapeId(tableName) +
719
+ `(${escapeId(rowIdColumnName)}${columnType} PRIMARY KEY${arrayJoin(
720
+ arrayMap(
721
+ settingColumnNames,
722
+ (settingColumnName) =>
723
+ COMMA + escapeId(settingColumnName) + columnType,
724
+ ),
725
+ )});`,
726
+ );
727
+ mapSet(
728
+ schemaMap,
729
+ tableName,
730
+ setNew([rowIdColumnName, ...settingColumnNames]),
731
+ );
732
+ } else {
733
+ await promiseAll(
734
+ arrayMap(
735
+ [rowIdColumnName, ...settingColumnNames],
736
+ async (settingColumnName, index) => {
737
+ if (!collDel(unaccountedColumnNames, settingColumnName)) {
738
+ await databaseExecuteCommand(
739
+ ALTER_TABLE +
740
+ escapeId(tableName) +
741
+ 'ADD' +
742
+ escapeId(settingColumnName) +
743
+ columnType,
744
+ );
745
+ if (index == 0) {
746
+ await databaseExecuteCommand(
747
+ 'CREATE UNIQUE INDEX pk ON ' +
748
+ escapeId(tableName) +
749
+ `(${escapeId(rowIdColumnName)})`,
750
+ );
751
+ }
752
+ setAdd(currentColumnNames, settingColumnName);
753
+ }
754
+ },
755
+ ),
756
+ );
757
+ }
758
+ }
759
+ await promiseAll([
760
+ ...(!partial && deleteEmptyColumns
761
+ ? arrayMap(
762
+ collValues(unaccountedColumnNames),
763
+ async (unaccountedColumnName) => {
764
+ if (unaccountedColumnName != rowIdColumnName) {
765
+ await databaseExecuteCommand(
766
+ ALTER_TABLE +
767
+ escapeId(tableName) +
768
+ 'DROP' +
769
+ escapeId(unaccountedColumnName),
770
+ );
771
+ collDel(currentColumnNames, unaccountedColumnName);
772
+ }
773
+ },
774
+ )
775
+ : []),
776
+ ]);
777
+ if (partial) {
778
+ if (isUndefined(content)) {
779
+ await databaseExecuteCommand(
780
+ DELETE_FROM +
781
+ escapeId(tableName) +
782
+ getWhereCondition(tableName, condition),
783
+ );
784
+ } else {
785
+ await promiseAll(
786
+ objToArray(content, async (row, rowId) => {
787
+ if (isUndefined(row)) {
788
+ await databaseExecuteCommand(
789
+ DELETE_FROM +
790
+ escapeId(tableName) +
791
+ getWhereCondition(tableName, condition) +
792
+ `AND(${escapeId(rowIdColumnName)}=$1)`,
793
+ [rowId],
794
+ );
795
+ } else if (!arrayIsEmpty(settingColumnNames)) {
796
+ await upsert(
797
+ databaseExecuteCommand,
798
+ tableName,
799
+ rowIdColumnName,
800
+ objIds(row),
801
+ {
802
+ [rowId]: encode
803
+ ? arrayMap(objValues(row), encode)
804
+ : objValues(row),
805
+ },
806
+ currentColumnNames,
807
+ );
808
+ }
809
+ }),
810
+ );
811
+ }
812
+ } else {
813
+ if (!arrayIsEmpty(settingColumnNames)) {
814
+ const changingColumnNames = arrayFilter(
815
+ collValues(mapGet(schemaMap, tableName)),
816
+ (changingColumnName) => changingColumnName != rowIdColumnName,
817
+ );
818
+ const rows = {};
819
+ const deleteRowIds = [];
820
+ objMap(content ?? {}, (row, rowId) => {
821
+ rows[rowId] = arrayMap(changingColumnNames, (cellId) =>
822
+ encode ? encode(row?.[cellId]) : row?.[cellId],
823
+ );
824
+ arrayPush(deleteRowIds, rowId);
825
+ });
826
+ await upsert(
827
+ databaseExecuteCommand,
828
+ tableName,
829
+ rowIdColumnName,
830
+ changingColumnNames,
831
+ rows,
832
+ );
833
+ await databaseExecuteCommand(
834
+ DELETE_FROM +
835
+ escapeId(tableName) +
836
+ getWhereCondition(tableName, condition) + // eslint-disable-next-line max-len
837
+ `AND${escapeId(rowIdColumnName)}NOT IN(${getPlaceholders(deleteRowIds)})`,
838
+ deleteRowIds,
839
+ );
840
+ } else if (collHas(schemaMap, tableName)) {
841
+ await databaseExecuteCommand(
842
+ DELETE_FROM +
843
+ escapeId(tableName) +
844
+ getWhereCondition(tableName, condition),
845
+ );
846
+ }
847
+ }
848
+ };
849
+ const transaction = async (actions) => {
850
+ let result;
851
+ await databaseExecuteCommand('BEGIN');
852
+ await tryCatch(async () => (result = await actions()), onIgnoredError);
853
+ await databaseExecuteCommand('END');
854
+ return result;
855
+ };
856
+ return [refreshSchema, loadTable, saveTable, transaction];
857
+ };
858
+ const defaultUpsert = async (
859
+ executeCommand,
860
+ tableName,
861
+ rowIdColumnName,
862
+ changingColumnNames,
863
+ rows,
864
+ ) => {
865
+ const offset = [1];
866
+ await executeCommand(
867
+ INSERT +
868
+ ' INTO' +
869
+ escapeId(tableName) +
870
+ '(' +
871
+ escapeColumnNames(rowIdColumnName, ...changingColumnNames) +
872
+ ')VALUES' +
873
+ arrayJoin(
874
+ objToArray(
875
+ rows,
876
+ (row) =>
877
+ '($' + offset[0]++ + ',' + getPlaceholders(row, offset) + ')',
878
+ ),
879
+ COMMA,
880
+ ) +
881
+ 'ON CONFLICT(' +
882
+ escapeId(rowIdColumnName) +
883
+ `)DO ${UPDATE} SET` +
884
+ arrayJoin(
885
+ arrayMap(
886
+ changingColumnNames,
887
+ (columnName) =>
888
+ escapeId(columnName) + '=excluded.' + escapeId(columnName),
889
+ ),
890
+ COMMA,
891
+ ),
892
+ objToArray(rows, (row, id) => [
893
+ id,
894
+ ...arrayMap(row, (value) => value ?? null),
895
+ ]).flat(),
896
+ );
897
+ };
898
+
899
+ const createJsonPersister = (
900
+ store,
901
+ executeCommand,
902
+ addPersisterListener,
903
+ delPersisterListener,
904
+ onIgnoredError,
905
+ extraDestroy,
906
+ persist,
907
+ [storeTableName, storeIdColumnName, storeColumnName],
908
+ managedTableNames,
909
+ querySchema,
910
+ thing,
911
+ getThing,
912
+ columnType,
913
+ upsert,
914
+ ) => {
915
+ const [refreshSchema, loadTable, saveTable, transaction] =
916
+ getCommandFunctions(
917
+ executeCommand,
918
+ managedTableNames,
919
+ querySchema,
920
+ onIgnoredError,
921
+ columnType,
922
+ upsert,
923
+ );
924
+ const getPersisted = () =>
925
+ transaction(async () => {
926
+ await refreshSchema();
927
+ return jsonParseWithUndefined(
928
+ (await loadTable(storeTableName, storeIdColumnName))[SINGLE_ROW_ID]?.[
929
+ storeColumnName
930
+ ] ?? 'null',
931
+ );
932
+ });
933
+ const setPersisted = (getContent) =>
934
+ transaction(async () => {
935
+ await refreshSchema();
936
+ await saveTable(
937
+ storeTableName,
938
+ storeIdColumnName,
939
+ {
940
+ [SINGLE_ROW_ID]: {
941
+ [storeColumnName]: jsonStringWithUndefined(getContent() ?? null),
942
+ },
943
+ },
944
+ true,
945
+ true,
946
+ );
947
+ });
948
+ const destroy = async () => {
949
+ await persister.stopAutoPersisting();
950
+ extraDestroy();
951
+ return persister;
952
+ };
953
+ const persister = createCustomPersister(
954
+ store,
955
+ getPersisted,
956
+ setPersisted,
957
+ addPersisterListener,
958
+ delPersisterListener,
959
+ onIgnoredError,
960
+ persist,
961
+ {[getThing]: () => thing, destroy},
962
+ 0,
963
+ thing,
964
+ );
965
+ return persister;
966
+ };
967
+
968
+ const createTabularPersister = (
969
+ store,
970
+ executeCommand,
971
+ addPersisterListener,
972
+ delPersisterListener,
973
+ onIgnoredError,
974
+ extraDestroy,
975
+ persist,
976
+ [
977
+ tablesLoadConfig,
978
+ tablesSaveConfig,
979
+ [valuesLoad, valuesSave, valuesTableName],
980
+ ],
981
+ managedTableNames,
982
+ querySchema,
983
+ thing,
984
+ getThing,
985
+ columnType,
986
+ upsert,
987
+ encode,
988
+ decode,
989
+ ) => {
990
+ const [refreshSchema, loadTable, saveTable, transaction] =
991
+ getCommandFunctions(
992
+ executeCommand,
993
+ managedTableNames,
994
+ querySchema,
995
+ onIgnoredError,
996
+ columnType,
997
+ upsert,
998
+ encode,
999
+ decode,
1000
+ );
1001
+ const saveTables = (tables, partial) =>
1002
+ promiseAll(
1003
+ mapMap(
1004
+ tablesSaveConfig,
1005
+ async (
1006
+ [
1007
+ tableName,
1008
+ rowIdColumnName,
1009
+ deleteEmptyColumns,
1010
+ deleteEmptyTable,
1011
+ condition,
1012
+ ],
1013
+ tableId,
1014
+ ) => {
1015
+ if (!partial || objHas(tables, tableId)) {
1016
+ await saveTable(
1017
+ tableName,
1018
+ rowIdColumnName,
1019
+ tables[tableId],
1020
+ deleteEmptyColumns,
1021
+ deleteEmptyTable,
1022
+ partial,
1023
+ condition,
1024
+ );
1025
+ }
1026
+ },
1027
+ ),
1028
+ );
1029
+ const saveValues = async (values, partial) =>
1030
+ valuesSave
1031
+ ? await saveTable(
1032
+ valuesTableName,
1033
+ DEFAULT_ROW_ID_COLUMN_NAME,
1034
+ {[SINGLE_ROW_ID]: values},
1035
+ true,
1036
+ true,
1037
+ partial,
1038
+ )
1039
+ : null;
1040
+ const loadTables = async () =>
1041
+ objNew(
1042
+ arrayFilter(
1043
+ await promiseAll(
1044
+ mapMap(
1045
+ tablesLoadConfig,
1046
+ async ([tableId, rowIdColumnName, condition], tableName) => [
1047
+ tableId,
1048
+ await loadTable(tableName, rowIdColumnName, condition),
1049
+ ],
1050
+ ),
1051
+ ),
1052
+ (pair) => !objIsEmpty(pair[1]),
1053
+ ),
1054
+ );
1055
+ const loadValues = async () =>
1056
+ valuesLoad
1057
+ ? (await loadTable(valuesTableName, DEFAULT_ROW_ID_COLUMN_NAME))[
1058
+ SINGLE_ROW_ID
1059
+ ]
1060
+ : {};
1061
+ const getPersisted = () =>
1062
+ transaction(async () => {
1063
+ await refreshSchema();
1064
+ const tables = await loadTables();
1065
+ const values = await loadValues();
1066
+ return !objIsEmpty(tables) || !isUndefined(values)
1067
+ ? [tables, values]
1068
+ : void 0;
1069
+ });
1070
+ const setPersisted = (getContent, changes) =>
1071
+ transaction(async () => {
1072
+ await refreshSchema();
1073
+ if (!isUndefined(changes)) {
1074
+ await saveTables(changes[0], true);
1075
+ await saveValues(changes[1], true);
1076
+ } else {
1077
+ const [tables, values] = getContent();
1078
+ await saveTables(tables);
1079
+ await saveValues(values);
1080
+ }
1081
+ });
1082
+ const destroy = async () => {
1083
+ await persister.stopAutoPersisting();
1084
+ extraDestroy();
1085
+ return persister;
1086
+ };
1087
+ const persister = createCustomPersister(
1088
+ store,
1089
+ getPersisted,
1090
+ setPersisted,
1091
+ addPersisterListener,
1092
+ delPersisterListener,
1093
+ onIgnoredError,
1094
+ persist,
1095
+ {[getThing]: () => thing, destroy},
1096
+ 0,
1097
+ thing,
1098
+ );
1099
+ return persister;
1100
+ };
1101
+
1102
+ const createCustomSqlitePersister = (
1103
+ store,
1104
+ configOrStoreTableName,
1105
+ rawExecuteCommand,
1106
+ addChangeListener,
1107
+ delChangeListener,
1108
+ onSqlCommand,
1109
+ onIgnoredError,
1110
+ destroy,
1111
+ persist,
1112
+ thing,
1113
+ getThing = 'getDb',
1114
+ upsert,
1115
+ ) => {
1116
+ let dataVersion;
1117
+ let schemaVersion;
1118
+ let totalChanges;
1119
+ const executeCommand = getWrappedCommand(rawExecuteCommand, onSqlCommand);
1120
+ const [
1121
+ isJson,
1122
+ autoLoadIntervalSeconds,
1123
+ defaultedConfig,
1124
+ managedTableNamesSet,
1125
+ ] = getConfigStructures(configOrStoreTableName);
1126
+ const addPersisterListener = (listener) => {
1127
+ let interval;
1128
+ const startPolling = () =>
1129
+ (interval = startInterval(
1130
+ () =>
1131
+ tryCatch(async () => {
1132
+ const [{d, s, c}] = await executeCommand(
1133
+ SELECT + // eslint-disable-next-line max-len
1134
+ ` ${DATA_VERSION} d,${SCHEMA_VERSION} s,TOTAL_CHANGES() c FROM ${PRAGMA}${DATA_VERSION} JOIN ${PRAGMA}${SCHEMA_VERSION}`,
1135
+ );
1136
+ if (d != dataVersion || s != schemaVersion || c != totalChanges) {
1137
+ if (dataVersion != null) {
1138
+ listener();
1139
+ }
1140
+ dataVersion = d;
1141
+ schemaVersion = s;
1142
+ totalChanges = c;
1143
+ }
1144
+ }),
1145
+ autoLoadIntervalSeconds,
1146
+ ));
1147
+ const stopPolling = () => {
1148
+ dataVersion = schemaVersion = totalChanges = null;
1149
+ stopInterval(interval);
1150
+ };
1151
+ const listeningHandle = addChangeListener((tableName) => {
1152
+ if (managedTableNamesSet.has(tableName)) {
1153
+ stopPolling();
1154
+ listener();
1155
+ startPolling();
1156
+ }
1157
+ });
1158
+ startPolling();
1159
+ return () => {
1160
+ stopPolling();
1161
+ delChangeListener(listeningHandle);
1162
+ };
1163
+ };
1164
+ const delPersisterListener = (stopPollingAndDelUpdateListener) =>
1165
+ stopPollingAndDelUpdateListener();
1166
+ return (isJson ? createJsonPersister : createTabularPersister)(
1167
+ store,
1168
+ executeCommand,
1169
+ addPersisterListener,
1170
+ delPersisterListener,
1171
+ onIgnoredError,
1172
+ destroy,
1173
+ persist,
1174
+ defaultedConfig,
1175
+ collValues(managedTableNamesSet),
1176
+ async (executeCommand2, managedTableNames) =>
1177
+ await executeCommand2(
1178
+ SELECT + // eslint-disable-next-line max-len
1179
+ ` t.name tn,c.name cn FROM ${PRAGMA_TABLE}list()t,${PRAGMA_TABLE}info(t.name)c ${WHERE} t.schema='main'AND t.type IN('table','view')AND t.name IN(${getPlaceholders(managedTableNames)})ORDER BY t.name,c.name`,
1180
+ managedTableNames,
1181
+ ),
1182
+ thing,
1183
+ getThing,
1184
+ EMPTY_STRING,
1185
+ upsert,
1186
+ (cellOrValue) =>
1187
+ cellOrValue === true ? 1 : cellOrValue === false ? 0 : cellOrValue,
1188
+ void 0,
1189
+ );
1190
+ };
1191
+
1192
+ const createDurableObjectSqlStoragePersister = (
1193
+ store,
1194
+ sqlStorage,
1195
+ configOrStoreTableName,
1196
+ onSqlCommand,
1197
+ onIgnoredError,
1198
+ ) => {
1199
+ if (
1200
+ typeof configOrStoreTableName === 'object' &&
1201
+ configOrStoreTableName.mode === 'fragmented'
1202
+ ) {
1203
+ return createDurableObjectFragmentedSqlStoragePersister(
1204
+ store,
1205
+ sqlStorage,
1206
+ configOrStoreTableName?.storagePrefix ?? EMPTY_STRING,
1207
+ onIgnoredError,
1208
+ );
1209
+ }
1210
+ return createCustomSqlitePersister(
1211
+ store,
1212
+ configOrStoreTableName,
1213
+ async (sql, params = []) => {
1214
+ if (!['BEGIN', 'END'].includes(sql)) {
1215
+ sql = sql.replace(/\$\d+/g, '?');
1216
+ return sqlStorage.exec(sql, ...params).toArray();
1217
+ }
1218
+ return [];
1219
+ },
1220
+ () => noop,
1221
+ (unsubscribeFunction) => unsubscribeFunction(),
1222
+ onSqlCommand,
1223
+ onIgnoredError,
1224
+ noop,
1225
+ 2,
1226
+ // MergeableStoreOnly,
1227
+ sqlStorage,
1228
+ 'getSqlStorage',
1229
+ );
1230
+ };
1231
+ const stampNewObjectWithHash = () => stampNewWithHash({}, EMPTY_STRING, 0);
1232
+ const createDurableObjectFragmentedSqlStoragePersister = (
1233
+ store,
1234
+ sqlStorage,
1235
+ storagePrefix = EMPTY_STRING,
1236
+ onIgnoredError,
1237
+ ) => {
1238
+ const tablePrefix = storagePrefix.replace(/[^a-zA-Z0-9_]/g, '_');
1239
+ const tablesTable = `${tablePrefix}tinybase_tables`;
1240
+ const valuesTable = `${tablePrefix}tinybase_values`;
1241
+ const initializeTables = () => {
1242
+ sqlStorage.exec(`
1243
+ CREATE TABLE IF NOT EXISTS ${tablesTable} (
1244
+ type TEXT NOT NULL,
1245
+ table_id TEXT,
1246
+ row_id TEXT,
1247
+ cell_id TEXT,
1248
+ value_data TEXT NOT NULL,
1249
+ timestamp TEXT NOT NULL,
1250
+ hash INTEGER NOT NULL,
1251
+ PRIMARY KEY (type, table_id, row_id, cell_id)
1252
+ );
1253
+
1254
+ CREATE TABLE IF NOT EXISTS ${valuesTable} (
1255
+ value_id TEXT,
1256
+ value_data TEXT NOT NULL,
1257
+ timestamp TEXT NOT NULL,
1258
+ hash INTEGER NOT NULL
1259
+ );
1260
+ `);
1261
+ };
1262
+ initializeTables();
1263
+ const getPersisted = async () => {
1264
+ const tables = stampNewObjectWithHash();
1265
+ const values = stampNewObjectWithHash();
1266
+ const tablesResult = sqlStorage.exec(`SELECT * FROM ${tablesTable}`);
1267
+ for (const row of tablesResult.toArray()) {
1268
+ const type = String(row.type);
1269
+ const table_id = row.table_id ? String(row.table_id) : null;
1270
+ const row_id = row.row_id ? String(row.row_id) : null;
1271
+ const cell_id = row.cell_id ? String(row.cell_id) : null;
1272
+ const value_data = String(row.value_data);
1273
+ const timestamp = String(row.timestamp);
1274
+ const hash = Number(row.hash);
1275
+ const [zeroOrCellOrValue] = JSON.parse(value_data);
1276
+ if (type === T) {
1277
+ if (table_id && row_id && cell_id) {
1278
+ const table = objEnsure(tables[0], table_id, stampNewObjectWithHash);
1279
+ const tableRow = objEnsure(table[0], row_id, stampNewObjectWithHash);
1280
+ tableRow[0][cell_id] = [zeroOrCellOrValue, timestamp, hash];
1281
+ } else if (table_id && row_id) {
1282
+ const table = objEnsure(tables[0], table_id, stampNewObjectWithHash);
1283
+ const tableRow = objEnsure(table[0], row_id, stampNewObjectWithHash);
1284
+ stampUpdate(tableRow, timestamp, hash);
1285
+ } else if (table_id) {
1286
+ const table = objEnsure(tables[0], table_id, stampNewObjectWithHash);
1287
+ stampUpdate(table, timestamp, hash);
1288
+ } else {
1289
+ stampUpdate(tables, timestamp, hash);
1290
+ }
1291
+ }
1292
+ }
1293
+ const valuesResult = sqlStorage.exec(`SELECT * FROM ${valuesTable}`);
1294
+ for (const row of valuesResult.toArray()) {
1295
+ const value_id = row.value_id ? String(row.value_id) : null;
1296
+ const value_data = String(row.value_data);
1297
+ const timestamp = String(row.timestamp);
1298
+ const hash = Number(row.hash);
1299
+ const [zeroOrCellOrValue] = JSON.parse(value_data);
1300
+ if (value_id) {
1301
+ values[0][value_id] = [zeroOrCellOrValue, timestamp, hash];
1302
+ } else {
1303
+ stampUpdate(values, timestamp, hash);
1304
+ }
1305
+ }
1306
+ return [tables, values];
1307
+ };
1308
+ const setPersisted = async (
1309
+ getContent,
1310
+ [
1311
+ [tablesObj, tablesTime, tablesHash],
1312
+ [valuesObj, valuesTime, valuesHash],
1313
+ ] = getContent(),
1314
+ ) => {
1315
+ sqlStorage.exec(
1316
+ `INSERT OR REPLACE INTO ${tablesTable} (type, table_id, row_id, cell_id, value_data, timestamp, hash) VALUES (?, ?, ?, ?, ?, ?, ?)`,
1317
+ T,
1318
+ null,
1319
+ null,
1320
+ null,
1321
+ JSON.stringify([0]),
1322
+ tablesTime,
1323
+ tablesHash,
1324
+ );
1325
+ objForEach(tablesObj, ([tableObj, tableTime, tableHash], tableId) => {
1326
+ sqlStorage.exec(
1327
+ `INSERT OR REPLACE INTO ${tablesTable} (type, table_id, row_id, cell_id, value_data, timestamp, hash) VALUES (?, ?, ?, ?, ?, ?, ?)`,
1328
+ T,
1329
+ tableId,
1330
+ null,
1331
+ null,
1332
+ JSON.stringify([0]),
1333
+ tableTime,
1334
+ tableHash,
1335
+ );
1336
+ objForEach(tableObj, ([rowObj, rowTime, rowHash], rowId) => {
1337
+ sqlStorage.exec(
1338
+ `INSERT OR REPLACE INTO ${tablesTable} (type, table_id, row_id, cell_id, value_data, timestamp, hash) VALUES (?, ?, ?, ?, ?, ?, ?)`,
1339
+ T,
1340
+ tableId,
1341
+ rowId,
1342
+ null,
1343
+ JSON.stringify([0]),
1344
+ rowTime,
1345
+ rowHash,
1346
+ );
1347
+ objForEach(rowObj, (cellStamp, cellId) => {
1348
+ sqlStorage.exec(
1349
+ `INSERT OR REPLACE INTO ${tablesTable} (type, table_id, row_id, cell_id, value_data, timestamp, hash) VALUES (?, ?, ?, ?, ?, ?, ?)`,
1350
+ T,
1351
+ tableId,
1352
+ rowId,
1353
+ cellId,
1354
+ JSON.stringify([cellStamp[0]]),
1355
+ cellStamp[1],
1356
+ cellStamp[2],
1357
+ );
1358
+ });
1359
+ });
1360
+ });
1361
+ sqlStorage.exec(
1362
+ `INSERT OR REPLACE INTO ${valuesTable} (value_id, value_data, timestamp, hash) VALUES (?, ?, ?, ?)`,
1363
+ null,
1364
+ JSON.stringify([0]),
1365
+ valuesTime,
1366
+ valuesHash,
1367
+ );
1368
+ objForEach(valuesObj, (valueStamp, valueId) => {
1369
+ sqlStorage.exec(
1370
+ `INSERT OR REPLACE INTO ${valuesTable} (value_id, value_data, timestamp, hash) VALUES (?, ?, ?, ?)`,
1371
+ valueId,
1372
+ JSON.stringify([valueStamp[0]]),
1373
+ valueStamp[1],
1374
+ valueStamp[2],
1375
+ );
1376
+ });
1377
+ };
1378
+ return createCustomPersister(
1379
+ store,
1380
+ getPersisted,
1381
+ setPersisted,
1382
+ noop,
1383
+ noop,
1384
+ onIgnoredError,
1385
+ 2,
1386
+ // MergeableStoreOnly,
1387
+ {getSqlStorage: () => sqlStorage},
1388
+ );
1389
+ };
1390
+
1391
+ export {createDurableObjectSqlStoragePersister};