tinybase 4.6.14 → 4.7.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 (97) hide show
  1. package/lib/cjs/persisters/persister-cr-sqlite-wasm.cjs +1 -1
  2. package/lib/cjs/persisters/persister-cr-sqlite-wasm.cjs.gz +0 -0
  3. package/lib/cjs/persisters/persister-electric-sql.cjs +1 -1
  4. package/lib/cjs/persisters/persister-electric-sql.cjs.gz +0 -0
  5. package/lib/cjs/persisters/persister-expo-sqlite-next.cjs +1 -1
  6. package/lib/cjs/persisters/persister-expo-sqlite-next.cjs.gz +0 -0
  7. package/lib/cjs/persisters/persister-expo-sqlite.cjs +1 -1
  8. package/lib/cjs/persisters/persister-expo-sqlite.cjs.gz +0 -0
  9. package/lib/cjs/persisters/persister-libsql.cjs +1 -0
  10. package/lib/cjs/persisters/persister-libsql.cjs.gz +0 -0
  11. package/lib/cjs/persisters/persister-sqlite-wasm.cjs +1 -1
  12. package/lib/cjs/persisters/persister-sqlite-wasm.cjs.gz +0 -0
  13. package/lib/cjs/persisters/persister-sqlite3.cjs +1 -1
  14. package/lib/cjs/persisters/persister-sqlite3.cjs.gz +0 -0
  15. package/lib/cjs-es6/persisters/persister-cr-sqlite-wasm.cjs +1 -1
  16. package/lib/cjs-es6/persisters/persister-cr-sqlite-wasm.cjs.gz +0 -0
  17. package/lib/cjs-es6/persisters/persister-electric-sql.cjs +1 -1
  18. package/lib/cjs-es6/persisters/persister-electric-sql.cjs.gz +0 -0
  19. package/lib/cjs-es6/persisters/persister-expo-sqlite-next.cjs +1 -1
  20. package/lib/cjs-es6/persisters/persister-expo-sqlite-next.cjs.gz +0 -0
  21. package/lib/cjs-es6/persisters/persister-expo-sqlite.cjs +1 -1
  22. package/lib/cjs-es6/persisters/persister-expo-sqlite.cjs.gz +0 -0
  23. package/lib/cjs-es6/persisters/persister-libsql.cjs +1 -0
  24. package/lib/cjs-es6/persisters/persister-libsql.cjs.gz +0 -0
  25. package/lib/cjs-es6/persisters/persister-sqlite-wasm.cjs +1 -1
  26. package/lib/cjs-es6/persisters/persister-sqlite-wasm.cjs.gz +0 -0
  27. package/lib/cjs-es6/persisters/persister-sqlite3.cjs +1 -1
  28. package/lib/cjs-es6/persisters/persister-sqlite3.cjs.gz +0 -0
  29. package/lib/debug/persisters/persister-cr-sqlite-wasm.js +1 -1
  30. package/lib/debug/persisters/persister-electric-sql.js +1 -1
  31. package/lib/debug/persisters/persister-expo-sqlite-next.js +1 -1
  32. package/lib/debug/persisters/persister-expo-sqlite.js +1 -1
  33. package/lib/debug/persisters/persister-libsql.js +866 -0
  34. package/lib/debug/persisters/persister-sqlite-wasm.js +1 -1
  35. package/lib/debug/persisters/persister-sqlite3.js +1 -1
  36. package/lib/es6/persisters/persister-cr-sqlite-wasm.js +1 -1
  37. package/lib/es6/persisters/persister-cr-sqlite-wasm.js.gz +0 -0
  38. package/lib/es6/persisters/persister-electric-sql.js +1 -1
  39. package/lib/es6/persisters/persister-electric-sql.js.gz +0 -0
  40. package/lib/es6/persisters/persister-expo-sqlite-next.js +1 -1
  41. package/lib/es6/persisters/persister-expo-sqlite-next.js.gz +0 -0
  42. package/lib/es6/persisters/persister-expo-sqlite.js +1 -1
  43. package/lib/es6/persisters/persister-expo-sqlite.js.gz +0 -0
  44. package/lib/es6/persisters/persister-libsql.js +1 -0
  45. package/lib/es6/persisters/persister-libsql.js.gz +0 -0
  46. package/lib/es6/persisters/persister-sqlite-wasm.js +1 -1
  47. package/lib/es6/persisters/persister-sqlite-wasm.js.gz +0 -0
  48. package/lib/es6/persisters/persister-sqlite3.js +1 -1
  49. package/lib/es6/persisters/persister-sqlite3.js.gz +0 -0
  50. package/lib/persisters/persister-cr-sqlite-wasm.js +1 -1
  51. package/lib/persisters/persister-cr-sqlite-wasm.js.gz +0 -0
  52. package/lib/persisters/persister-electric-sql.js +1 -1
  53. package/lib/persisters/persister-electric-sql.js.gz +0 -0
  54. package/lib/persisters/persister-expo-sqlite-next.js +1 -1
  55. package/lib/persisters/persister-expo-sqlite-next.js.gz +0 -0
  56. package/lib/persisters/persister-expo-sqlite.js +1 -1
  57. package/lib/persisters/persister-expo-sqlite.js.gz +0 -0
  58. package/lib/persisters/persister-libsql.js +1 -0
  59. package/lib/persisters/persister-libsql.js.gz +0 -0
  60. package/lib/persisters/persister-sqlite-wasm.js +1 -1
  61. package/lib/persisters/persister-sqlite-wasm.js.gz +0 -0
  62. package/lib/persisters/persister-sqlite3.js +1 -1
  63. package/lib/persisters/persister-sqlite3.js.gz +0 -0
  64. package/lib/types/persisters/persister-libsql.d.ts +139 -0
  65. package/lib/types/persisters.d.ts +1 -0
  66. package/lib/types/with-schemas/persisters/persister-libsql.d.ts +152 -0
  67. package/lib/types/with-schemas/persisters.d.ts +1 -0
  68. package/lib/umd/persisters/persister-cr-sqlite-wasm.js +1 -1
  69. package/lib/umd/persisters/persister-cr-sqlite-wasm.js.gz +0 -0
  70. package/lib/umd/persisters/persister-electric-sql.js +1 -1
  71. package/lib/umd/persisters/persister-electric-sql.js.gz +0 -0
  72. package/lib/umd/persisters/persister-expo-sqlite-next.js +1 -1
  73. package/lib/umd/persisters/persister-expo-sqlite-next.js.gz +0 -0
  74. package/lib/umd/persisters/persister-expo-sqlite.js +1 -1
  75. package/lib/umd/persisters/persister-expo-sqlite.js.gz +0 -0
  76. package/lib/umd/persisters/persister-libsql.js +1 -0
  77. package/lib/umd/persisters/persister-libsql.js.gz +0 -0
  78. package/lib/umd/persisters/persister-sqlite-wasm.js +1 -1
  79. package/lib/umd/persisters/persister-sqlite-wasm.js.gz +0 -0
  80. package/lib/umd/persisters/persister-sqlite3.js +1 -1
  81. package/lib/umd/persisters/persister-sqlite3.js.gz +0 -0
  82. package/lib/umd-es6/persisters/persister-cr-sqlite-wasm.js +1 -1
  83. package/lib/umd-es6/persisters/persister-cr-sqlite-wasm.js.gz +0 -0
  84. package/lib/umd-es6/persisters/persister-electric-sql.js +1 -1
  85. package/lib/umd-es6/persisters/persister-electric-sql.js.gz +0 -0
  86. package/lib/umd-es6/persisters/persister-expo-sqlite-next.js +1 -1
  87. package/lib/umd-es6/persisters/persister-expo-sqlite-next.js.gz +0 -0
  88. package/lib/umd-es6/persisters/persister-expo-sqlite.js +1 -1
  89. package/lib/umd-es6/persisters/persister-expo-sqlite.js.gz +0 -0
  90. package/lib/umd-es6/persisters/persister-libsql.js +1 -0
  91. package/lib/umd-es6/persisters/persister-libsql.js.gz +0 -0
  92. package/lib/umd-es6/persisters/persister-sqlite-wasm.js +1 -1
  93. package/lib/umd-es6/persisters/persister-sqlite-wasm.js.gz +0 -0
  94. package/lib/umd-es6/persisters/persister-sqlite3.js +1 -1
  95. package/lib/umd-es6/persisters/persister-sqlite3.js.gz +0 -0
  96. package/package.json +6 -1
  97. package/readme.md +2 -2
@@ -0,0 +1,866 @@
1
+ const getTypeOf = (thing) => typeof thing;
2
+ const TINYBASE = 'tinybase';
3
+ const EMPTY_STRING = '';
4
+ const COMMA = ',';
5
+ const STRING = getTypeOf(EMPTY_STRING);
6
+ const strRepeat = (str, count) => str.repeat(count);
7
+
8
+ const promise = Promise;
9
+ const startInterval = (callback, sec, immediate) => {
10
+ immediate && callback();
11
+ return setInterval(callback, sec * 1e3);
12
+ };
13
+ const stopInterval = clearInterval;
14
+ const isInstanceOf = (thing, cls) => thing instanceof cls;
15
+ const isUndefined = (thing) => thing == void 0;
16
+ const ifNotUndefined = (value, then, otherwise) =>
17
+ isUndefined(value) ? otherwise?.() : then(value);
18
+ const isString = (thing) => getTypeOf(thing) == STRING;
19
+ const isArray = (thing) => Array.isArray(thing);
20
+ const slice = (arrayOrString, start, end) => arrayOrString.slice(start, end);
21
+ const size = (arrayOrString) => arrayOrString.length;
22
+ const promiseAll = async (promises) => promise.all(promises);
23
+
24
+ const SINGLE_ROW_ID = '_';
25
+ const DEFAULT_ROW_ID_COLUMN_NAME = '_id';
26
+ const escapeId = (str) => `"${str.replace(/"/g, '""')}"`;
27
+ const SELECT = 'SELECT';
28
+
29
+ const arrayJoin = (array, sep = EMPTY_STRING) => array.join(sep);
30
+ const arrayMap = (array, cb) => array.map(cb);
31
+ const arrayIsEmpty = (array) => size(array) == 0;
32
+ const arrayFilter = (array, cb) => array.filter(cb);
33
+ const arrayPush = (array, ...values) => array.push(...values);
34
+ const arrayShift = (array) => array.shift();
35
+
36
+ const collHas = (coll, keyOrValue) => coll?.has(keyOrValue) ?? false;
37
+ const collValues = (coll) => [...(coll?.values() ?? [])];
38
+ const collForEach = (coll, cb) => coll?.forEach(cb);
39
+ const collDel = (coll, keyOrValue) => coll?.delete(keyOrValue);
40
+
41
+ const object = Object;
42
+ const getPrototypeOf = (obj) => object.getPrototypeOf(obj);
43
+ const objIds = object.keys;
44
+ const objFreeze = object.freeze;
45
+ const isObject = (obj) =>
46
+ !isUndefined(obj) &&
47
+ ifNotUndefined(
48
+ getPrototypeOf(obj),
49
+ (objPrototype) =>
50
+ objPrototype == object.prototype ||
51
+ isUndefined(getPrototypeOf(objPrototype)),
52
+ () => true,
53
+ );
54
+ const objNew = (entries = []) => object.fromEntries(entries);
55
+ const objMerge = (...objs) => object.assign({}, ...objs);
56
+ const objGet = (obj, id) => ifNotUndefined(obj, (obj2) => obj2[id]);
57
+ const objHas = (obj, id) => !isUndefined(objGet(obj, id));
58
+ const objDel = (obj, id) => {
59
+ delete obj[id];
60
+ return obj;
61
+ };
62
+ const objMap = (obj, cb) =>
63
+ arrayMap(object.entries(obj), ([id, value]) => cb(value, id));
64
+ const objValues = (obj) => object.values(obj);
65
+ const objSize = (obj) => size(objIds(obj));
66
+ const objIsEmpty = (obj) => isObject(obj) && objSize(obj) == 0;
67
+
68
+ const mapNew = (entries) => new Map(entries);
69
+ const mapKeys = (map) => [...(map?.keys() ?? [])];
70
+ const mapGet = (map, key) => map?.get(key);
71
+ const mapForEach = (map, cb) =>
72
+ collForEach(map, (value, key) => cb(key, value));
73
+ const mapMap = (coll, cb) =>
74
+ arrayMap([...(coll?.entries() ?? [])], ([key, value]) => cb(value, key));
75
+ const mapSet = (map, key, value) =>
76
+ isUndefined(value) ? (collDel(map, key), map) : map?.set(key, value);
77
+ const mapEnsure = (map, key, getDefaultValue) => {
78
+ if (!collHas(map, key)) {
79
+ mapSet(map, key, getDefaultValue());
80
+ }
81
+ return mapGet(map, key);
82
+ };
83
+ const mapMatch = (map, obj, set, del = mapSet) => {
84
+ objMap(obj, (value, id) => set(map, id, value));
85
+ mapForEach(map, (id) => (objHas(obj, id) ? 0 : del(map, id)));
86
+ return map;
87
+ };
88
+
89
+ const setNew = (entryOrEntries) =>
90
+ new Set(
91
+ isArray(entryOrEntries) || isUndefined(entryOrEntries)
92
+ ? entryOrEntries
93
+ : [entryOrEntries],
94
+ );
95
+ const setAdd = (set, value) => set?.add(value);
96
+
97
+ const SELECT_STAR_FROM = SELECT + '*FROM';
98
+ const FROM_PRAGMA_TABLE = 'FROM pragma_table_';
99
+ const WHERE = 'WHERE';
100
+ const getCommandFunctions = (
101
+ cmd,
102
+ managedTableNames,
103
+ onIgnoredError,
104
+ useOnConflict,
105
+ ) => {
106
+ const schemaMap = mapNew();
107
+ const canSelect = (tableName, rowIdColumnName) =>
108
+ !isUndefined(mapGet(mapGet(schemaMap, tableName), rowIdColumnName));
109
+ const refreshSchema = async () =>
110
+ mapMatch(
111
+ schemaMap,
112
+ objNew(
113
+ await promiseAll(
114
+ arrayMap(
115
+ await cmd(
116
+ 'SELECT name ' +
117
+ FROM_PRAGMA_TABLE +
118
+ `list WHERE schema='main'AND(type='table'OR type='view')AND name IN(` +
119
+ getPlaceholders(managedTableNames) +
120
+ `)ORDER BY name`,
121
+ managedTableNames,
122
+ ),
123
+ async ({name: tableName}) => [
124
+ tableName,
125
+ objNew(
126
+ arrayMap(
127
+ await cmd(
128
+ SELECT + ' name,type ' + FROM_PRAGMA_TABLE + 'info(?)',
129
+ [tableName],
130
+ ),
131
+ ({name: columnName, type}) => [columnName, type],
132
+ ),
133
+ ),
134
+ ],
135
+ ),
136
+ ),
137
+ ),
138
+ (_, tableName, tableSchema) =>
139
+ mapSet(
140
+ schemaMap,
141
+ tableName,
142
+ mapMatch(
143
+ mapEnsure(schemaMap, tableName, mapNew),
144
+ tableSchema,
145
+ (tableSchemaMap, columnName, value) => {
146
+ if (value != mapGet(tableSchemaMap, columnName)) {
147
+ mapSet(tableSchemaMap, columnName, value);
148
+ }
149
+ },
150
+ (tableSchema2, columnName) => mapSet(tableSchema2, columnName),
151
+ ),
152
+ ),
153
+ (_, name) => mapSet(schemaMap, name),
154
+ );
155
+ const loadTable = async (tableName, rowIdColumnName) =>
156
+ canSelect(tableName, rowIdColumnName)
157
+ ? objNew(
158
+ arrayFilter(
159
+ arrayMap(
160
+ await cmd(SELECT_STAR_FROM + escapeId(tableName)),
161
+ (row) => [
162
+ row[rowIdColumnName],
163
+ objDel({...row}, rowIdColumnName),
164
+ ],
165
+ ),
166
+ ([rowId, row]) => !isUndefined(rowId) && !objIsEmpty(row),
167
+ ),
168
+ )
169
+ : {};
170
+ const saveTable = async (
171
+ tableName,
172
+ rowIdColumnName,
173
+ table,
174
+ deleteEmptyColumns,
175
+ deleteEmptyTable,
176
+ partial = false,
177
+ ) => {
178
+ const tableCellIds = setNew();
179
+ objMap(table ?? {}, (row) =>
180
+ arrayMap(objIds(row ?? {}), (cellId) => setAdd(tableCellIds, cellId)),
181
+ );
182
+ const tableColumnNames = collValues(tableCellIds);
183
+ if (
184
+ !partial &&
185
+ deleteEmptyTable &&
186
+ arrayIsEmpty(tableColumnNames) &&
187
+ collHas(schemaMap, tableName)
188
+ ) {
189
+ await cmd('DROP TABLE' + escapeId(tableName));
190
+ mapSet(schemaMap, tableName);
191
+ return;
192
+ }
193
+ if (!arrayIsEmpty(tableColumnNames) && !collHas(schemaMap, tableName)) {
194
+ await cmd(
195
+ `CREATE TABLE${escapeId(tableName)}(${escapeId(rowIdColumnName)} PRIMARY KEY ON CONFLICT REPLACE${arrayJoin(
196
+ arrayMap(tableColumnNames, (cellId) => COMMA + escapeId(cellId)),
197
+ )});`,
198
+ );
199
+ mapSet(
200
+ schemaMap,
201
+ tableName,
202
+ mapNew([
203
+ [rowIdColumnName, EMPTY_STRING],
204
+ ...arrayMap(tableColumnNames, (columnName) => [
205
+ columnName,
206
+ EMPTY_STRING,
207
+ ]),
208
+ ]),
209
+ );
210
+ } else {
211
+ const tableSchemaMap = mapGet(schemaMap, tableName);
212
+ const columnNamesAccountedFor = setNew(mapKeys(tableSchemaMap));
213
+ await promiseAll([
214
+ ...arrayMap(tableColumnNames, async (columnName) => {
215
+ if (!collDel(columnNamesAccountedFor, columnName)) {
216
+ await cmd(
217
+ `ALTER TABLE${escapeId(tableName)}ADD${escapeId(columnName)}`,
218
+ );
219
+ mapSet(tableSchemaMap, columnName, EMPTY_STRING);
220
+ }
221
+ }),
222
+ ...(!partial && deleteEmptyColumns
223
+ ? arrayMap(
224
+ collValues(columnNamesAccountedFor),
225
+ async (columnName) => {
226
+ if (columnName != rowIdColumnName) {
227
+ await cmd(
228
+ `ALTER TABLE${escapeId(tableName)}DROP${escapeId(
229
+ columnName,
230
+ )}`,
231
+ );
232
+ mapSet(tableSchemaMap, columnName);
233
+ }
234
+ },
235
+ )
236
+ : []),
237
+ ]);
238
+ }
239
+ if (partial) {
240
+ if (isUndefined(table)) {
241
+ await cmd('DELETE FROM' + escapeId(tableName) + 'WHERE 1');
242
+ } else {
243
+ await promiseAll(
244
+ objMap(table, async (row, rowId) => {
245
+ if (isUndefined(row)) {
246
+ await cmd(
247
+ 'DELETE FROM' +
248
+ escapeId(tableName) +
249
+ WHERE +
250
+ escapeId(rowIdColumnName) +
251
+ '=?',
252
+ [rowId],
253
+ );
254
+ } else if (!arrayIsEmpty(tableColumnNames)) {
255
+ await upsert(
256
+ cmd,
257
+ tableName,
258
+ rowIdColumnName,
259
+ objIds(row),
260
+ [rowId, ...objValues(row)],
261
+ useOnConflict,
262
+ );
263
+ }
264
+ }),
265
+ );
266
+ }
267
+ } else {
268
+ if (!arrayIsEmpty(tableColumnNames)) {
269
+ const changingColumnNames = arrayFilter(
270
+ mapKeys(mapGet(schemaMap, tableName)),
271
+ (columnName) => columnName != rowIdColumnName,
272
+ );
273
+ const args = [];
274
+ const deleteRowIds = [];
275
+ objMap(table ?? {}, (row, rowId) => {
276
+ arrayPush(
277
+ args,
278
+ rowId,
279
+ ...arrayMap(changingColumnNames, (cellId) => row?.[cellId]),
280
+ );
281
+ arrayPush(deleteRowIds, rowId);
282
+ });
283
+ await upsert(
284
+ cmd,
285
+ tableName,
286
+ rowIdColumnName,
287
+ changingColumnNames,
288
+ args,
289
+ );
290
+ await cmd(
291
+ 'DELETE FROM' +
292
+ escapeId(tableName) +
293
+ WHERE +
294
+ escapeId(rowIdColumnName) +
295
+ 'NOT IN(' +
296
+ getPlaceholders(deleteRowIds) +
297
+ ')',
298
+ deleteRowIds,
299
+ );
300
+ } else if (collHas(schemaMap, tableName)) {
301
+ await cmd('DELETE FROM' + escapeId(tableName) + 'WHERE 1');
302
+ }
303
+ }
304
+ };
305
+ const transaction = async (actions) => {
306
+ let result;
307
+ await cmd('BEGIN');
308
+ try {
309
+ result = await actions();
310
+ } catch (error) {
311
+ onIgnoredError?.(error);
312
+ }
313
+ await cmd('END');
314
+ return result;
315
+ };
316
+ return [refreshSchema, loadTable, saveTable, transaction];
317
+ };
318
+ const upsert = async (
319
+ cmd,
320
+ tableName,
321
+ rowIdColumnName,
322
+ changingColumnNames,
323
+ args,
324
+ useOnConflict = true,
325
+ ) =>
326
+ await cmd(
327
+ 'INSERT ' +
328
+ (useOnConflict ? EMPTY_STRING : 'OR REPLACE ') +
329
+ 'INTO' +
330
+ escapeId(tableName) +
331
+ '(' +
332
+ escapeId(rowIdColumnName) +
333
+ arrayJoin(
334
+ arrayMap(
335
+ changingColumnNames,
336
+ (columnName) => COMMA + escapeId(columnName),
337
+ ),
338
+ ) +
339
+ ')VALUES' +
340
+ slice(
341
+ strRepeat(
342
+ `,(?${strRepeat(',?', size(changingColumnNames))})`,
343
+ size(args) / (size(changingColumnNames) + 1),
344
+ ),
345
+ 1,
346
+ ) +
347
+ (useOnConflict
348
+ ? 'ON CONFLICT(' +
349
+ escapeId(rowIdColumnName) +
350
+ ')DO UPDATE SET' +
351
+ arrayJoin(
352
+ arrayMap(
353
+ changingColumnNames,
354
+ (columnName) =>
355
+ escapeId(columnName) + '=excluded.' + escapeId(columnName),
356
+ ),
357
+ COMMA,
358
+ )
359
+ : EMPTY_STRING),
360
+ arrayMap(args, (arg) => arg ?? null),
361
+ );
362
+ const getPlaceholders = (array) =>
363
+ arrayJoin(
364
+ arrayMap(array, () => '?'),
365
+ COMMA,
366
+ );
367
+
368
+ const jsonString = (obj) =>
369
+ JSON.stringify(obj, (_key, value) =>
370
+ isInstanceOf(value, Map) ? object.fromEntries([...value]) : value,
371
+ );
372
+ const jsonParse = JSON.parse;
373
+
374
+ const scheduleRunning = mapNew();
375
+ const scheduleActions = mapNew();
376
+ const createCustomPersister = (
377
+ store,
378
+ getPersisted,
379
+ setPersisted,
380
+ addPersisterListener,
381
+ delPersisterListener,
382
+ onIgnoredError,
383
+ [getThing, thing] = [],
384
+ scheduleId = [],
385
+ ) => {
386
+ let listenerId;
387
+ let loadSave = 0;
388
+ let loads = 0;
389
+ let saves = 0;
390
+ let listening = 0;
391
+ let action;
392
+ let listeningHandle;
393
+ mapEnsure(scheduleRunning, scheduleId, () => 0);
394
+ mapEnsure(scheduleActions, scheduleId, () => []);
395
+ const run = async () => {
396
+ /* istanbul ignore else */
397
+ if (!mapGet(scheduleRunning, scheduleId)) {
398
+ mapSet(scheduleRunning, scheduleId, 1);
399
+ while (
400
+ !isUndefined((action = arrayShift(mapGet(scheduleActions, scheduleId))))
401
+ ) {
402
+ try {
403
+ await action();
404
+ } catch (error) {
405
+ /* istanbul ignore next */
406
+ onIgnoredError?.(error);
407
+ }
408
+ }
409
+ mapSet(scheduleRunning, scheduleId, 0);
410
+ }
411
+ };
412
+ const loadLock = async (actions) => {
413
+ /* istanbul ignore else */
414
+ if (loadSave != 2) {
415
+ loadSave = 1;
416
+ {
417
+ loads++;
418
+ }
419
+ await persister.schedule(async () => {
420
+ await actions();
421
+ loadSave = 0;
422
+ });
423
+ }
424
+ return persister;
425
+ };
426
+ const persister = {
427
+ load: async (initialTables, initialValues) =>
428
+ await loadLock(async () => {
429
+ try {
430
+ store.setContent(await getPersisted());
431
+ } catch {
432
+ store.setContent([initialTables, initialValues]);
433
+ }
434
+ }),
435
+ startAutoLoad: async (initialTables = {}, initialValues = {}) => {
436
+ persister.stopAutoLoad();
437
+ await persister.load(initialTables, initialValues);
438
+ listening = 1;
439
+ listeningHandle = addPersisterListener(
440
+ async (getContent, getTransactionChanges) => {
441
+ if (getTransactionChanges) {
442
+ const transactionChanges = getTransactionChanges();
443
+ await loadLock(async () =>
444
+ store.setTransactionChanges(transactionChanges),
445
+ );
446
+ } else {
447
+ await loadLock(async () => {
448
+ try {
449
+ store.setContent(getContent?.() ?? (await getPersisted()));
450
+ } catch (error) {
451
+ onIgnoredError?.(error);
452
+ }
453
+ });
454
+ }
455
+ },
456
+ );
457
+ return persister;
458
+ },
459
+ stopAutoLoad: () => {
460
+ if (listening) {
461
+ delPersisterListener(listeningHandle);
462
+ listeningHandle = void 0;
463
+ listening = 0;
464
+ }
465
+ return persister;
466
+ },
467
+ save: async (getTransactionChanges) => {
468
+ /* istanbul ignore else */
469
+ if (loadSave != 1) {
470
+ loadSave = 2;
471
+ {
472
+ saves++;
473
+ }
474
+ await persister.schedule(async () => {
475
+ try {
476
+ await setPersisted(store.getContent, getTransactionChanges);
477
+ } catch (error) {
478
+ /* istanbul ignore next */
479
+ onIgnoredError?.(error);
480
+ }
481
+ loadSave = 0;
482
+ });
483
+ }
484
+ return persister;
485
+ },
486
+ startAutoSave: async () => {
487
+ await persister.stopAutoSave().save();
488
+ listenerId = store.addDidFinishTransactionListener(
489
+ (_store, getTransactionChanges) => {
490
+ const [tableChanges, valueChanges] = getTransactionChanges();
491
+ if (!objIsEmpty(tableChanges) || !objIsEmpty(valueChanges)) {
492
+ persister.save(() => [tableChanges, valueChanges]);
493
+ }
494
+ },
495
+ );
496
+ return persister;
497
+ },
498
+ stopAutoSave: () => {
499
+ ifNotUndefined(listenerId, store.delListener);
500
+ listenerId = void 0;
501
+ return persister;
502
+ },
503
+ schedule: async (...actions) => {
504
+ arrayPush(mapGet(scheduleActions, scheduleId), ...actions);
505
+ await run();
506
+ return persister;
507
+ },
508
+ getStore: () => store,
509
+ destroy: () => persister.stopAutoLoad().stopAutoSave(),
510
+ getStats: () => ({loads, saves}),
511
+ };
512
+ if (getThing) {
513
+ persister[getThing] = () => thing;
514
+ }
515
+ return objFreeze(persister);
516
+ };
517
+
518
+ const STORE_COLUMN = 'store';
519
+ const createJsonSqlitePersister = (
520
+ store,
521
+ cmd,
522
+ addPersisterListener,
523
+ delPersisterListener,
524
+ onIgnoredError,
525
+ [storeTableName],
526
+ managedTableNames,
527
+ db,
528
+ getThing,
529
+ ) => {
530
+ const [refreshSchema, loadTable, saveTable, transaction] =
531
+ getCommandFunctions(cmd, managedTableNames, onIgnoredError);
532
+ const getPersisted = async () =>
533
+ await transaction(async () => {
534
+ await refreshSchema();
535
+ return jsonParse(
536
+ (await loadTable(storeTableName, DEFAULT_ROW_ID_COLUMN_NAME))[
537
+ SINGLE_ROW_ID
538
+ ]?.[STORE_COLUMN] ?? 'null',
539
+ );
540
+ });
541
+ const setPersisted = async (getContent) =>
542
+ await transaction(async () => {
543
+ await refreshSchema();
544
+ await saveTable(
545
+ storeTableName,
546
+ DEFAULT_ROW_ID_COLUMN_NAME,
547
+ {
548
+ [SINGLE_ROW_ID]: {[STORE_COLUMN]: jsonString(getContent() ?? null)},
549
+ },
550
+ true,
551
+ true,
552
+ );
553
+ });
554
+ const persister = createCustomPersister(
555
+ store,
556
+ getPersisted,
557
+ setPersisted,
558
+ addPersisterListener,
559
+ delPersisterListener,
560
+ onIgnoredError,
561
+ [getThing, db],
562
+ db,
563
+ );
564
+ return persister;
565
+ };
566
+
567
+ const createTabularSqlitePersister = (
568
+ store,
569
+ cmd,
570
+ addPersisterListener,
571
+ delPersisterListener,
572
+ onIgnoredError,
573
+ [
574
+ tablesLoadConfig,
575
+ tablesSaveConfig,
576
+ [valuesLoad, valuesSave, valuesTableName],
577
+ ],
578
+ managedTableNames,
579
+ db,
580
+ getThing,
581
+ useOnConflict,
582
+ ) => {
583
+ const [refreshSchema, loadTable, saveTable, transaction] =
584
+ getCommandFunctions(cmd, managedTableNames, onIgnoredError, useOnConflict);
585
+ const saveTables = async (tables, partial) =>
586
+ await promiseAll(
587
+ mapMap(
588
+ tablesSaveConfig,
589
+ async (
590
+ [tableName, rowIdColumnName, deleteEmptyColumns, deleteEmptyTable],
591
+ tableId,
592
+ ) => {
593
+ const table = tables[tableId];
594
+ if (!partial || table !== void 0) {
595
+ await saveTable(
596
+ tableName,
597
+ rowIdColumnName,
598
+ table,
599
+ deleteEmptyColumns,
600
+ deleteEmptyTable,
601
+ partial,
602
+ );
603
+ }
604
+ },
605
+ ),
606
+ );
607
+ const saveValues = async (values, partial) =>
608
+ valuesSave
609
+ ? await saveTable(
610
+ valuesTableName,
611
+ DEFAULT_ROW_ID_COLUMN_NAME,
612
+ {[SINGLE_ROW_ID]: values},
613
+ true,
614
+ true,
615
+ partial,
616
+ )
617
+ : null;
618
+ const loadTables = async () =>
619
+ objNew(
620
+ arrayFilter(
621
+ await promiseAll(
622
+ mapMap(
623
+ tablesLoadConfig,
624
+ async ([tableId, rowIdColumnName], tableName) => [
625
+ tableId,
626
+ await loadTable(tableName, rowIdColumnName),
627
+ ],
628
+ ),
629
+ ),
630
+ (pair) => !objIsEmpty(pair[1]),
631
+ ),
632
+ );
633
+ const loadValues = async () =>
634
+ valuesLoad
635
+ ? (await loadTable(valuesTableName, DEFAULT_ROW_ID_COLUMN_NAME))[
636
+ SINGLE_ROW_ID
637
+ ]
638
+ : {};
639
+ const getPersisted = async () =>
640
+ await transaction(async () => {
641
+ await refreshSchema();
642
+ const tables = await loadTables();
643
+ const values = await loadValues();
644
+ return !objIsEmpty(tables) || !isUndefined(values)
645
+ ? [tables, values]
646
+ : void 0;
647
+ });
648
+ const setPersisted = async (getContent, getTransactionChanges) =>
649
+ await transaction(async () => {
650
+ await refreshSchema();
651
+ if (!isUndefined(getTransactionChanges)) {
652
+ const [tableChanges, valueChanges] = getTransactionChanges();
653
+ await saveTables(tableChanges, true);
654
+ await saveValues(valueChanges, true);
655
+ } else {
656
+ const [tables, values] = getContent();
657
+ await saveTables(tables);
658
+ await saveValues(values);
659
+ }
660
+ });
661
+ const persister = createCustomPersister(
662
+ store,
663
+ getPersisted,
664
+ setPersisted,
665
+ addPersisterListener,
666
+ delPersisterListener,
667
+ onIgnoredError,
668
+ [getThing, db],
669
+ db,
670
+ );
671
+ return persister;
672
+ };
673
+
674
+ const JSON$1 = 'json';
675
+ const AUTO_LOAD_INTERVAL_SECONDS = 'autoLoadIntervalSeconds';
676
+ const STORE_TABLE_NAME = 'storeTableName';
677
+ const ROW_ID_COLUMN_NAME = 'rowIdColumnName';
678
+ const TABLE_ID = 'tableId';
679
+ const TABLE_NAME = 'tableName';
680
+ const DELETE_EMPTY_COLUMNS = 'deleteEmptyColumns';
681
+ const DELETE_EMPTY_TABLE = 'deleteEmptyTable';
682
+ const DEFAULT_CONFIG = {
683
+ mode: JSON$1,
684
+ [AUTO_LOAD_INTERVAL_SECONDS]: 1,
685
+ };
686
+ const DEFAULT_TABULAR_VALUES_CONFIG = {
687
+ load: 0,
688
+ save: 0,
689
+ [TABLE_NAME]: TINYBASE + '_values',
690
+ };
691
+ const getDefaultedConfig = (configOrStoreTableName) =>
692
+ objMerge(
693
+ DEFAULT_CONFIG,
694
+ isString(configOrStoreTableName)
695
+ ? {[STORE_TABLE_NAME]: configOrStoreTableName}
696
+ : configOrStoreTableName ?? {},
697
+ );
698
+ const getDefaultedTabularConfigMap = (
699
+ configsObj,
700
+ defaultObj,
701
+ tableField,
702
+ filter,
703
+ ) => {
704
+ const configMap = mapNew();
705
+ objMap(configsObj, (configObj, id) => {
706
+ const defaultedConfig = slice(
707
+ objValues(
708
+ objMerge(
709
+ defaultObj,
710
+ isString(configObj) ? {[tableField]: configObj} : configObj,
711
+ ),
712
+ ),
713
+ 0,
714
+ objSize(defaultObj),
715
+ );
716
+ if (!isUndefined(defaultedConfig[0]) && !filter(id, defaultedConfig[0])) {
717
+ mapSet(configMap, id, defaultedConfig);
718
+ }
719
+ });
720
+ return configMap;
721
+ };
722
+ const getConfigStructures = (configOrStoreTableName) => {
723
+ const config = getDefaultedConfig(configOrStoreTableName);
724
+ const autoLoadIntervalSeconds = config[AUTO_LOAD_INTERVAL_SECONDS];
725
+ if (config.mode == JSON$1) {
726
+ const {storeTableName = TINYBASE} = config;
727
+ return [
728
+ 1,
729
+ autoLoadIntervalSeconds,
730
+ [storeTableName],
731
+ setNew(storeTableName),
732
+ ];
733
+ }
734
+ const {tables: {load = {}, save = {}} = {}, values = {}} = config;
735
+ const valuesConfig = slice(
736
+ objValues(objMerge(DEFAULT_TABULAR_VALUES_CONFIG, values)),
737
+ 0,
738
+ objSize(DEFAULT_TABULAR_VALUES_CONFIG),
739
+ );
740
+ const valuesTable = valuesConfig[2];
741
+ const managedTableNames = setNew(valuesTable);
742
+ const tabularConfig = [
743
+ getDefaultedTabularConfigMap(
744
+ load,
745
+ {[TABLE_ID]: null, [ROW_ID_COLUMN_NAME]: DEFAULT_ROW_ID_COLUMN_NAME},
746
+ TABLE_ID,
747
+ (tableName) =>
748
+ setAdd(managedTableNames, tableName) && tableName == valuesTable,
749
+ ),
750
+ getDefaultedTabularConfigMap(
751
+ save,
752
+ {
753
+ [TABLE_NAME]: null,
754
+ [ROW_ID_COLUMN_NAME]: DEFAULT_ROW_ID_COLUMN_NAME,
755
+ [DELETE_EMPTY_COLUMNS]: 0,
756
+ [DELETE_EMPTY_TABLE]: 0,
757
+ },
758
+ TABLE_NAME,
759
+ (_, tableName) =>
760
+ setAdd(managedTableNames, tableName) && tableName == valuesTable,
761
+ ),
762
+ valuesConfig,
763
+ ];
764
+ return [0, autoLoadIntervalSeconds, tabularConfig, managedTableNames];
765
+ };
766
+
767
+ const PRAGMA = 'pragma ';
768
+ const DATA_VERSION = 'data_version';
769
+ const SCHEMA_VERSION = 'schema_version';
770
+ const createSqlitePersister = (
771
+ store,
772
+ configOrStoreTableName,
773
+ cmd,
774
+ addUpdateListener,
775
+ delUpdateListener,
776
+ onSqlCommand,
777
+ onIgnoredError,
778
+ db,
779
+ getThing = 'getDb',
780
+ useOnConflict,
781
+ ) => {
782
+ let dataVersion;
783
+ let schemaVersion;
784
+ let totalChanges;
785
+ const CHANGES_COLUMN = 'c';
786
+ const [
787
+ isJson,
788
+ autoLoadIntervalSeconds,
789
+ defaultedConfig,
790
+ managedTableNamesSet,
791
+ ] = getConfigStructures(configOrStoreTableName);
792
+ const addPersisterListener = (listener) => [
793
+ startInterval(
794
+ async () => {
795
+ try {
796
+ const newDataVersion = (await cmd(PRAGMA + DATA_VERSION))[0][
797
+ DATA_VERSION
798
+ ];
799
+ const newSchemaVersion = (await cmd(PRAGMA + SCHEMA_VERSION))[0][
800
+ SCHEMA_VERSION
801
+ ];
802
+ const newTotalChanges = (
803
+ await cmd(SELECT + ' TOTAL_CHANGES() ' + CHANGES_COLUMN)
804
+ )[0][CHANGES_COLUMN];
805
+ if (
806
+ newDataVersion != (dataVersion ??= newDataVersion) ||
807
+ newSchemaVersion != (schemaVersion ??= newSchemaVersion) ||
808
+ newTotalChanges != (totalChanges ??= newTotalChanges)
809
+ ) {
810
+ listener();
811
+ dataVersion = newDataVersion;
812
+ schemaVersion = newSchemaVersion;
813
+ }
814
+ } catch {}
815
+ },
816
+ autoLoadIntervalSeconds,
817
+ 1,
818
+ ),
819
+ addUpdateListener((tableName) =>
820
+ managedTableNamesSet.has(tableName) ? listener() : 0,
821
+ ),
822
+ ];
823
+ const delPersisterListener = ([interval, listeningHandle]) => {
824
+ stopInterval(interval);
825
+ dataVersion = schemaVersion = null;
826
+ delUpdateListener(listeningHandle);
827
+ };
828
+ return (isJson ? createJsonSqlitePersister : createTabularSqlitePersister)(
829
+ store,
830
+ onSqlCommand
831
+ ? async (sql, args) => {
832
+ onSqlCommand(sql, args);
833
+ return await cmd(sql, args);
834
+ }
835
+ : cmd,
836
+ addPersisterListener,
837
+ delPersisterListener,
838
+ onIgnoredError,
839
+ defaultedConfig,
840
+ collValues(managedTableNamesSet),
841
+ db,
842
+ getThing,
843
+ useOnConflict,
844
+ );
845
+ };
846
+
847
+ const createLibSqlPersister = (
848
+ store,
849
+ client,
850
+ configOrStoreTableName,
851
+ onSqlCommand,
852
+ onIgnoredError,
853
+ ) =>
854
+ createSqlitePersister(
855
+ store,
856
+ configOrStoreTableName,
857
+ async (sql, args = []) => (await client.execute({sql, args})).rows,
858
+ () => () => 0,
859
+ (unsubscribeFunction) => unsubscribeFunction(),
860
+ onSqlCommand,
861
+ onIgnoredError,
862
+ client,
863
+ 'getClient',
864
+ );
865
+
866
+ export {createLibSqlPersister};