tinybase 9.5.0 → 9.6.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/@types/omni/index.d.ts +4 -0
  2. package/@types/omni/with-schemas/index.d.ts +4 -0
  3. package/@types/persisters/index.d.ts +8 -1
  4. package/@types/persisters/persister-better-sqlite3/index.d.ts +178 -0
  5. package/@types/persisters/persister-better-sqlite3/with-schemas/index.d.ts +202 -0
  6. package/@types/persisters/persister-capacitor-sqlite/index.d.ts +181 -0
  7. package/@types/persisters/persister-capacitor-sqlite/with-schemas/index.d.ts +205 -0
  8. package/@types/persisters/persister-indexed-db/index.d.ts +17 -9
  9. package/@types/persisters/persister-indexed-db/with-schemas/index.d.ts +18 -10
  10. package/@types/persisters/persister-libsql/index.d.ts +27 -0
  11. package/@types/persisters/persister-libsql/with-schemas/index.d.ts +27 -0
  12. package/@types/persisters/persister-pg/index.d.ts +197 -0
  13. package/@types/persisters/persister-pg/with-schemas/index.d.ts +222 -0
  14. package/@types/persisters/persister-supabase/index.d.ts +201 -0
  15. package/@types/persisters/persister-supabase/with-schemas/index.d.ts +217 -0
  16. package/@types/persisters/with-schemas/index.d.ts +8 -1
  17. package/@types/ui-react-dom/index.d.ts +10 -10
  18. package/@types/ui-react-dom/with-schemas/index.d.ts +10 -10
  19. package/@types/ui-react-dom-charts/index.d.ts +7 -7
  20. package/@types/ui-react-dom-charts/with-schemas/index.d.ts +7 -7
  21. package/@types/ui-react-inspector/index.d.ts +1 -1
  22. package/@types/ui-react-inspector/with-schemas/index.d.ts +1 -1
  23. package/@types/ui-solid-dom/index.d.ts +10 -10
  24. package/@types/ui-solid-dom/with-schemas/index.d.ts +10 -10
  25. package/@types/ui-solid-inspector/index.d.ts +1 -1
  26. package/@types/ui-solid-inspector/with-schemas/index.d.ts +1 -1
  27. package/@types/ui-svelte-dom/index.d.ts +10 -10
  28. package/@types/ui-svelte-dom/with-schemas/index.d.ts +10 -10
  29. package/@types/ui-svelte-inspector/index.d.ts +1 -1
  30. package/@types/ui-svelte-inspector/with-schemas/index.d.ts +1 -1
  31. package/agents.md +55 -2
  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-better-sqlite3/index.js +1 -0
  39. package/min/persisters/persister-better-sqlite3/index.js.gz +0 -0
  40. package/min/persisters/persister-better-sqlite3/with-schemas/index.js +1 -0
  41. package/min/persisters/persister-better-sqlite3/with-schemas/index.js.gz +0 -0
  42. package/min/persisters/persister-capacitor-sqlite/index.js +1 -0
  43. package/min/persisters/persister-capacitor-sqlite/index.js.gz +0 -0
  44. package/min/persisters/persister-capacitor-sqlite/with-schemas/index.js +1 -0
  45. package/min/persisters/persister-capacitor-sqlite/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 -1
  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 -1
  53. package/min/persisters/persister-durable-object-sql-storage/with-schemas/index.js.gz +0 -0
  54. package/min/persisters/persister-electric-sql/index.js +1 -1
  55. package/min/persisters/persister-electric-sql/index.js.gz +0 -0
  56. package/min/persisters/persister-electric-sql/with-schemas/index.js +1 -1
  57. package/min/persisters/persister-electric-sql/with-schemas/index.js.gz +0 -0
  58. package/min/persisters/persister-expo-sqlite/index.js +1 -1
  59. package/min/persisters/persister-expo-sqlite/index.js.gz +0 -0
  60. package/min/persisters/persister-expo-sqlite/with-schemas/index.js +1 -1
  61. package/min/persisters/persister-expo-sqlite/with-schemas/index.js.gz +0 -0
  62. package/min/persisters/persister-indexed-db/index.js +1 -1
  63. package/min/persisters/persister-indexed-db/index.js.gz +0 -0
  64. package/min/persisters/persister-indexed-db/with-schemas/index.js +1 -1
  65. package/min/persisters/persister-indexed-db/with-schemas/index.js.gz +0 -0
  66. package/min/persisters/persister-libsql/index.js +1 -1
  67. package/min/persisters/persister-libsql/index.js.gz +0 -0
  68. package/min/persisters/persister-libsql/with-schemas/index.js +1 -1
  69. package/min/persisters/persister-libsql/with-schemas/index.js.gz +0 -0
  70. package/min/persisters/persister-pg/index.js +1 -0
  71. package/min/persisters/persister-pg/index.js.gz +0 -0
  72. package/min/persisters/persister-pg/with-schemas/index.js +1 -0
  73. package/min/persisters/persister-pg/with-schemas/index.js.gz +0 -0
  74. package/min/persisters/persister-pglite/index.js +1 -1
  75. package/min/persisters/persister-pglite/index.js.gz +0 -0
  76. package/min/persisters/persister-pglite/with-schemas/index.js +1 -1
  77. package/min/persisters/persister-pglite/with-schemas/index.js.gz +0 -0
  78. package/min/persisters/persister-postgres/index.js +1 -1
  79. package/min/persisters/persister-postgres/index.js.gz +0 -0
  80. package/min/persisters/persister-postgres/with-schemas/index.js +1 -1
  81. package/min/persisters/persister-postgres/with-schemas/index.js.gz +0 -0
  82. package/min/persisters/persister-powersync/index.js +1 -1
  83. package/min/persisters/persister-powersync/index.js.gz +0 -0
  84. package/min/persisters/persister-powersync/with-schemas/index.js +1 -1
  85. package/min/persisters/persister-powersync/with-schemas/index.js.gz +0 -0
  86. package/min/persisters/persister-react-native-sqlite/index.js +1 -1
  87. package/min/persisters/persister-react-native-sqlite/index.js.gz +0 -0
  88. package/min/persisters/persister-react-native-sqlite/with-schemas/index.js +1 -1
  89. package/min/persisters/persister-react-native-sqlite/with-schemas/index.js.gz +0 -0
  90. package/min/persisters/persister-sqlite-bun/index.js +1 -1
  91. package/min/persisters/persister-sqlite-bun/index.js.gz +0 -0
  92. package/min/persisters/persister-sqlite-bun/with-schemas/index.js +1 -1
  93. package/min/persisters/persister-sqlite-bun/with-schemas/index.js.gz +0 -0
  94. package/min/persisters/persister-sqlite-wasm/index.js +1 -1
  95. package/min/persisters/persister-sqlite-wasm/index.js.gz +0 -0
  96. package/min/persisters/persister-sqlite-wasm/with-schemas/index.js +1 -1
  97. package/min/persisters/persister-sqlite-wasm/with-schemas/index.js.gz +0 -0
  98. package/min/persisters/persister-sqlite3/index.js +1 -1
  99. package/min/persisters/persister-sqlite3/index.js.gz +0 -0
  100. package/min/persisters/persister-sqlite3/with-schemas/index.js +1 -1
  101. package/min/persisters/persister-sqlite3/with-schemas/index.js.gz +0 -0
  102. package/min/persisters/persister-supabase/index.js +1 -0
  103. package/min/persisters/persister-supabase/index.js.gz +0 -0
  104. package/min/persisters/persister-supabase/with-schemas/index.js +1 -0
  105. package/min/persisters/persister-supabase/with-schemas/index.js.gz +0 -0
  106. package/min/persisters/with-schemas/index.js +1 -1
  107. package/min/persisters/with-schemas/index.js.gz +0 -0
  108. package/omni/index.js +269 -34
  109. package/omni/with-schemas/index.js +269 -34
  110. package/package.json +164 -4
  111. package/persisters/index.js +23 -7
  112. package/persisters/persister-better-sqlite3/index.js +1767 -0
  113. package/persisters/persister-better-sqlite3/with-schemas/index.js +1767 -0
  114. package/persisters/persister-capacitor-sqlite/index.js +1769 -0
  115. package/persisters/persister-capacitor-sqlite/with-schemas/index.js +1769 -0
  116. package/persisters/persister-cr-sqlite-wasm/index.js +20 -6
  117. package/persisters/persister-cr-sqlite-wasm/with-schemas/index.js +20 -6
  118. package/persisters/persister-durable-object-sql-storage/index.js +20 -7
  119. package/persisters/persister-durable-object-sql-storage/with-schemas/index.js +20 -7
  120. package/persisters/persister-electric-sql/index.js +20 -6
  121. package/persisters/persister-electric-sql/with-schemas/index.js +20 -6
  122. package/persisters/persister-expo-sqlite/index.js +20 -6
  123. package/persisters/persister-expo-sqlite/with-schemas/index.js +20 -6
  124. package/persisters/persister-indexed-db/index.js +48 -20
  125. package/persisters/persister-indexed-db/with-schemas/index.js +48 -20
  126. package/persisters/persister-libsql/index.js +20 -6
  127. package/persisters/persister-libsql/with-schemas/index.js +20 -6
  128. package/persisters/persister-pg/index.js +1905 -0
  129. package/persisters/persister-pg/with-schemas/index.js +1905 -0
  130. package/persisters/persister-pglite/index.js +20 -6
  131. package/persisters/persister-pglite/with-schemas/index.js +20 -6
  132. package/persisters/persister-postgres/index.js +20 -6
  133. package/persisters/persister-postgres/with-schemas/index.js +20 -6
  134. package/persisters/persister-powersync/index.js +27 -10
  135. package/persisters/persister-powersync/with-schemas/index.js +27 -10
  136. package/persisters/persister-react-native-sqlite/index.js +20 -6
  137. package/persisters/persister-react-native-sqlite/with-schemas/index.js +20 -6
  138. package/persisters/persister-sqlite-bun/index.js +20 -6
  139. package/persisters/persister-sqlite-bun/with-schemas/index.js +20 -6
  140. package/persisters/persister-sqlite-wasm/index.js +20 -6
  141. package/persisters/persister-sqlite-wasm/with-schemas/index.js +20 -6
  142. package/persisters/persister-sqlite3/index.js +20 -6
  143. package/persisters/persister-sqlite3/with-schemas/index.js +20 -6
  144. package/persisters/persister-supabase/index.js +883 -0
  145. package/persisters/persister-supabase/with-schemas/index.js +883 -0
  146. package/persisters/with-schemas/index.js +23 -7
  147. package/readme.md +14 -14
  148. package/releases.md +163 -66
@@ -0,0 +1,1905 @@
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 UNDEFINED = '\uFFFC';
9
+ const strMatch = (str, regex) => str?.match(regex);
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 getIfNotFunction = (predicate) => (value, then, otherwise) =>
17
+ predicate(value)
18
+ ? /* istanbul ignore next */
19
+ otherwise?.()
20
+ : then(value);
21
+ const GLOBAL = globalThis;
22
+ const isNullish = (thing) => thing == null;
23
+ const isUndefined = (thing) => thing === void 0;
24
+ const isNull = (thing) => thing === null;
25
+ const isTrue = (thing) => thing === true;
26
+ const ifNotNullish = getIfNotFunction(isNullish);
27
+ const ifNotUndefined = getIfNotFunction(isUndefined);
28
+ const isString = (thing) => getTypeOf(thing) == STRING;
29
+ const isArray = (thing) => Array.isArray(thing);
30
+ const slice = (arrayOrString, start, end) => arrayOrString.slice(start, end);
31
+ const size = (thing) => thing.length;
32
+ const isEmpty = (thing) => size(thing) == 0;
33
+ const test = (regex, subject) => regex.test(subject);
34
+ const noop = () => {};
35
+ const promiseNew = (resolver) => new promise(resolver);
36
+ const promiseAll = async (promises) => promise.all(promises);
37
+ const promiseResolve = () => promise.resolve();
38
+
39
+ const ERROR_STORE_TYPE = 0;
40
+ const ERROR_CONTENT = 1;
41
+ const errorNew = (code, details) =>
42
+ new Error(
43
+ TINYBASE +
44
+ ':' +
45
+ code +
46
+ (isUndefined(details)
47
+ ? EMPTY_STRING
48
+ : /* istanbul ignore next */
49
+ ':' + details),
50
+ );
51
+ const errorThrow = (code, details) => {
52
+ throw errorNew(code, details);
53
+ };
54
+ const tryCatch = async (action, onError) => {
55
+ try {
56
+ return await action();
57
+ } catch (error) {
58
+ /* istanbul ignore next */
59
+ onError?.(error);
60
+ }
61
+ };
62
+ const tryCatchIgnore = (action, onIgnoredError) =>
63
+ tryCatch(action, (error) => void tryCatch(() => onIgnoredError?.(error)));
64
+ const tryCatchSync = (action, onError) => {
65
+ try {
66
+ return action();
67
+ } catch (error) {
68
+ onError?.(error);
69
+ }
70
+ };
71
+ const tryFinally = (action, finallyAction) => {
72
+ try {
73
+ return action();
74
+ } finally {
75
+ finallyAction();
76
+ }
77
+ };
78
+ const tryFinallyAsync = async (action, finallyAction) => {
79
+ try {
80
+ return await action();
81
+ } finally {
82
+ await finallyAction();
83
+ }
84
+ };
85
+
86
+ const arrayHas = (array, value) => array.includes(value);
87
+ const arrayForEach = (array, cb) => array.forEach(cb);
88
+ const arrayJoin = (array, sep = EMPTY_STRING) => array.join(sep);
89
+ const arrayMap = (array, cb) => array.map(cb);
90
+ const arrayFilter = (array, cb) => array.filter(cb);
91
+ const arrayClear = (array, to = size(array)) => array.splice(0, to);
92
+ const arrayPush = (array, ...values) => array.push(...values);
93
+ const arrayShift = (array) => array.shift();
94
+
95
+ const SINGLE_ROW_ID = '_';
96
+ const DEFAULT_ROW_ID_COLUMN_NAME = '_id';
97
+ const SELECT = 'SELECT';
98
+ const WHERE = 'WHERE';
99
+ const TABLE = 'TABLE';
100
+ const INSERT = 'INSERT';
101
+ const DELETE = 'DELETE';
102
+ const UPDATE = 'UPDATE';
103
+ const ALTER_TABLE = 'ALTER ' + TABLE;
104
+ const FROM = 'FROM';
105
+ const DELETE_FROM = DELETE + ' ' + FROM;
106
+ const SELECT_STAR_FROM = SELECT + '*' + FROM;
107
+ const CREATE = 'CREATE ';
108
+ const CREATE_TABLE = CREATE + TABLE;
109
+ const OR_REPLACE = 'OR REPLACE ';
110
+ const FUNCTION = 'FUNCTION';
111
+ const TABLE_NAME_PLACEHOLDER_REGEX = /\$tableName/g;
112
+ const getWrappedCommand = (executeCommand, onSqlCommand) =>
113
+ onSqlCommand
114
+ ? async (sql, params) => {
115
+ onSqlCommand(sql, params);
116
+ return await executeCommand(sql, params);
117
+ }
118
+ : executeCommand;
119
+ const escapeId = (str) =>
120
+ arrayJoin(
121
+ arrayMap(strSplit(str, DOT), (part) => `"${strReplace(part, /"/g, '""')}"`),
122
+ DOT,
123
+ );
124
+ const escapeIds = (...ids) => escapeId(arrayJoin(ids, '_'));
125
+ const escapeColumnNames = (...columnNames) =>
126
+ arrayJoin(arrayMap(columnNames, escapeId), COMMA);
127
+ const numberedPlaceholder = (offset) => '$' + offset[0]++;
128
+ const getPlaceholders = (array, getPlaceholder, offset = [1]) =>
129
+ arrayJoin(
130
+ arrayMap(array, () => getPlaceholder(offset)),
131
+ COMMA,
132
+ );
133
+ const getWhereCondition = (tableName, condition = TRUE) =>
134
+ WHERE + `(${replaceTableName(condition, escapeId(tableName))})`;
135
+ const replaceTableName = (condition, tableName) =>
136
+ strReplace(condition, TABLE_NAME_PLACEHOLDER_REGEX, tableName);
137
+
138
+ const collSize = (coll) => coll?.size ?? 0;
139
+ const collHas = (coll, keyOrValue) => coll?.has(keyOrValue) ?? false;
140
+ const collIsEmpty = (coll) => isUndefined(coll) || collSize(coll) == 0;
141
+ const collValues = (coll) => [...(coll?.values() ?? [])];
142
+ const collClear = (coll) => coll.clear();
143
+ const collForEach = (coll, cb) => coll?.forEach(cb);
144
+ const collDel = (coll, keyOrValue) => coll?.delete(keyOrValue);
145
+
146
+ const object = Object;
147
+ const getPrototypeOf = (obj) => object.getPrototypeOf(obj);
148
+ const objEntries = object.entries;
149
+ const isObject = (obj) =>
150
+ !isNullish(obj) &&
151
+ ifNotNullish(
152
+ getPrototypeOf(obj),
153
+ (objPrototype) =>
154
+ objPrototype == object.prototype ||
155
+ isNullish(getPrototypeOf(objPrototype)),
156
+
157
+ /* istanbul ignore next */
158
+ () => true,
159
+ );
160
+ const objIds = object.keys;
161
+ const objFreeze = object.freeze;
162
+ const objMerge = (...objs) => object.assign(object.create(null), ...objs);
163
+ const objHas = (obj, id) => object.hasOwn(obj, id);
164
+ const objSet = (obj, id, value) => {
165
+ object.defineProperty(obj, id, {
166
+ configurable: true,
167
+ enumerable: true,
168
+ value,
169
+ writable: true,
170
+ });
171
+ return value;
172
+ };
173
+ const objNew = (entries = []) => {
174
+ const obj = object.create(null);
175
+ arrayForEach(entries, ([id, value]) => objSet(obj, id, value));
176
+ return obj;
177
+ };
178
+ const objDel = (obj, id) => {
179
+ delete obj[id];
180
+ return obj;
181
+ };
182
+ const objForEach = (obj, cb) =>
183
+ arrayForEach(objIds(obj), (id) => cb(obj[id], id));
184
+ const objToArray = (obj, cb) =>
185
+ arrayMap(objEntries(obj), ([id, value]) => cb(value, id));
186
+ const objMap = (obj, cb) => {
187
+ const mapped = objNew();
188
+ objForEach(obj, (value, id) => objSet(mapped, id, cb(value, id)));
189
+ return mapped;
190
+ };
191
+ const objValues = (obj) => object.values(obj);
192
+ const objSize = (obj) => size(objIds(obj));
193
+ const objIsEmpty = (obj) => isObject(obj) && objSize(obj) == 0;
194
+
195
+ const jsonString = JSON.stringify;
196
+ const jsonParse = JSON.parse;
197
+ const jsonStringWithUndefined = (obj) =>
198
+ jsonString(obj, (_key, value) => (isUndefined(value) ? UNDEFINED : value));
199
+ const jsonParseWithUndefined = (str) =>
200
+ // JSON.parse reviver removes properties with undefined values
201
+ replaceUndefinedString(jsonParse(str));
202
+ const replaceUndefinedString = (obj) =>
203
+ obj === UNDEFINED
204
+ ? void 0
205
+ : isArray(obj)
206
+ ? arrayMap(obj, replaceUndefinedString)
207
+ : isObject(obj)
208
+ ? objMap(obj, replaceUndefinedString)
209
+ : obj;
210
+
211
+ const textEncoder = new GLOBAL.TextEncoder();
212
+ const getHash = (string) => {
213
+ let hash = 2166136261;
214
+ arrayForEach(textEncoder.encode(string), (char) => {
215
+ hash ^= char;
216
+ hash +=
217
+ (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24);
218
+ });
219
+ return hash >>> 0;
220
+ };
221
+
222
+ const map = Map;
223
+ const mapNew = (entries) => new map(entries);
224
+ const mapGet = (map2, key) => map2?.get(key);
225
+ const mapForEach = (map2, cb) =>
226
+ collForEach(map2, (value, key) => cb(key, value));
227
+ const mapMap = (coll, cb) => {
228
+ const mapped = [];
229
+ mapForEach(coll, (key, value) => arrayPush(mapped, cb(value, key)));
230
+ return mapped;
231
+ };
232
+ const mapSet = (map2, key, value) =>
233
+ isUndefined(value) ? (collDel(map2, key), map2) : map2?.set(key, value);
234
+ const mapEnsure = (map2, key, getDefaultValue, hadExistingValue) => {
235
+ let value = mapGet(map2, key);
236
+ if (collHas(map2, key)) {
237
+ hadExistingValue?.(value);
238
+ } else {
239
+ mapSet(map2, key, (value = getDefaultValue()));
240
+ }
241
+ return value;
242
+ };
243
+ const visitTree = (node, path, ensureLeaf, pruneLeaf, p = 0) =>
244
+ ifNotUndefined(
245
+ (ensureLeaf ? mapEnsure : mapGet)(
246
+ node,
247
+ path[p],
248
+ p > size(path) - 2 ? ensureLeaf : mapNew,
249
+ ),
250
+ (nodeOrLeaf) => {
251
+ if (p > size(path) - 2) {
252
+ if (pruneLeaf?.(nodeOrLeaf)) {
253
+ mapSet(node, path[p]);
254
+ }
255
+ return nodeOrLeaf;
256
+ }
257
+ const leaf = visitTree(nodeOrLeaf, path, ensureLeaf, pruneLeaf, p + 1);
258
+ if (collIsEmpty(nodeOrLeaf)) {
259
+ mapSet(node, path[p]);
260
+ }
261
+ return leaf;
262
+ },
263
+ );
264
+
265
+ const setNew = (entryOrEntries) =>
266
+ new Set(
267
+ isArray(entryOrEntries) || isUndefined(entryOrEntries)
268
+ ? entryOrEntries
269
+ : [entryOrEntries],
270
+ );
271
+ const setAdd = (set, value) => set?.add(value);
272
+
273
+ const COLUMN_NAME = 'ColumnName';
274
+ const STORE = 'store';
275
+ const JSON$1 = 'json';
276
+ const STORE_TABLE_NAME = STORE + 'TableName';
277
+ const STORE_ID_COLUMN_NAME = STORE + 'Id' + COLUMN_NAME;
278
+ const STORE_COLUMN_NAME = STORE + COLUMN_NAME;
279
+ const AUTO_LOAD_INTERVAL_SECONDS = 'autoLoadIntervalSeconds';
280
+ const ROW_ID_COLUMN_NAME = 'rowId' + COLUMN_NAME;
281
+ const TABLE_ID = 'tableId';
282
+ const TABLE_NAME = 'tableName';
283
+ const DELETE_EMPTY_COLUMNS = 'deleteEmptyColumns';
284
+ const DELETE_EMPTY_TABLE = 'deleteEmptyTable';
285
+ const CONDITION = 'condition';
286
+ const DEFAULT_CONFIG = {
287
+ mode: JSON$1,
288
+ [AUTO_LOAD_INTERVAL_SECONDS]: 1,
289
+ };
290
+ const DEFAULT_TABULAR_VALUES_CONFIG = {
291
+ load: 0,
292
+ save: 0,
293
+ [TABLE_NAME]: TINYBASE + '_values',
294
+ };
295
+ const getValuesIn = (config) => (isArray(config) ? config : void 0);
296
+ const hasValues = (config) => isTrue(config) || isArray(config);
297
+ const getDefaultedConfig = (configOrStoreTableName) =>
298
+ objMerge(
299
+ DEFAULT_CONFIG,
300
+ isString(configOrStoreTableName)
301
+ ? {[STORE_TABLE_NAME]: configOrStoreTableName}
302
+ : (configOrStoreTableName ?? {}),
303
+ );
304
+ const getDefaultedTabularConfigMap = (
305
+ configsObj,
306
+ defaultObj,
307
+ tableField,
308
+ exclude,
309
+ then,
310
+ ) => {
311
+ const configMap = mapNew();
312
+ objForEach(configsObj, (configObj, id) => {
313
+ const defaultedConfig = slice(
314
+ objValues(
315
+ objMerge(
316
+ defaultObj,
317
+ isString(configObj) ? {[tableField]: configObj} : configObj,
318
+ ),
319
+ ),
320
+ 0,
321
+ objSize(defaultObj),
322
+ );
323
+ if (!isNull(defaultedConfig[0]) && !exclude(id, defaultedConfig[0])) {
324
+ then(id, defaultedConfig[0]);
325
+ mapSet(configMap, id, defaultedConfig);
326
+ }
327
+ });
328
+ return configMap;
329
+ };
330
+ const getConfigStructures = (configOrStoreTableName) => {
331
+ const config = getDefaultedConfig(configOrStoreTableName);
332
+ const autoLoadIntervalSeconds = config[AUTO_LOAD_INTERVAL_SECONDS];
333
+ if (config.mode == JSON$1) {
334
+ const storeTableName = config[STORE_TABLE_NAME] ?? TINYBASE;
335
+ return [
336
+ 1,
337
+ autoLoadIntervalSeconds,
338
+ [
339
+ storeTableName,
340
+ config[STORE_ID_COLUMN_NAME] ?? DEFAULT_ROW_ID_COLUMN_NAME,
341
+ config[STORE_COLUMN_NAME] ?? STORE,
342
+ ],
343
+ setNew(storeTableName),
344
+ ];
345
+ }
346
+ const {tables: {load = {}, save = {}} = {}, values = {}} = config;
347
+ const valuesConfigObj = objMerge(DEFAULT_TABULAR_VALUES_CONFIG, values);
348
+ const {load: valuesLoad, save: valuesSave} = valuesConfigObj;
349
+ const valuesConfig = [
350
+ hasValues(valuesLoad),
351
+ hasValues(valuesSave),
352
+ valuesConfigObj[TABLE_NAME],
353
+ getValuesIn(valuesLoad),
354
+ getValuesIn(valuesSave),
355
+ ];
356
+ const valuesTable = valuesConfig[2];
357
+ const managedTableNames = setNew(valuesTable);
358
+ const excludedTableNames = setNew(valuesTable);
359
+ const tablesLoadConfig = getDefaultedTabularConfigMap(
360
+ load,
361
+ {
362
+ [TABLE_ID]: null,
363
+ [ROW_ID_COLUMN_NAME]: DEFAULT_ROW_ID_COLUMN_NAME,
364
+ [CONDITION]: TRUE,
365
+ },
366
+ TABLE_ID,
367
+ (tableName) => collHas(excludedTableNames, tableName),
368
+ (tableName) => setAdd(managedTableNames, tableName),
369
+ );
370
+ const tablesSaveConfig = getDefaultedTabularConfigMap(
371
+ save,
372
+ {
373
+ [TABLE_NAME]: null,
374
+ [ROW_ID_COLUMN_NAME]: DEFAULT_ROW_ID_COLUMN_NAME,
375
+ [DELETE_EMPTY_COLUMNS]: 0,
376
+ [DELETE_EMPTY_TABLE]: 0,
377
+ [CONDITION]: null,
378
+ },
379
+ TABLE_NAME,
380
+ (_, tableName) => collHas(excludedTableNames, tableName),
381
+ (_, tableName) => setAdd(managedTableNames, tableName),
382
+ );
383
+ mapForEach(
384
+ tablesSaveConfig,
385
+ (_, tableSaveConfig) =>
386
+ (tableSaveConfig[4] ??=
387
+ mapGet(tablesLoadConfig, tableSaveConfig[0])?.[2] ?? TRUE),
388
+ );
389
+ return [
390
+ 0,
391
+ autoLoadIntervalSeconds,
392
+ [tablesLoadConfig, tablesSaveConfig, valuesConfig],
393
+ managedTableNames,
394
+ ];
395
+ };
396
+
397
+ const INTEGER = /^\d+$/;
398
+ const getPoolFunctions = () => {
399
+ const pool = [];
400
+ let nextId = 0;
401
+ return [
402
+ (reuse) => (reuse ? arrayShift(pool) : null) ?? EMPTY_STRING + nextId++,
403
+ (id) => {
404
+ if (test(INTEGER, id) && size(pool) < 1e3) {
405
+ arrayPush(pool, id);
406
+ }
407
+ },
408
+ ];
409
+ };
410
+
411
+ const getWildcardedLeaves = (deepIdSet, path = [EMPTY_STRING]) => {
412
+ const leaves = [];
413
+ const deep = (node, p) =>
414
+ p == size(path)
415
+ ? arrayPush(leaves, node)
416
+ : isNull(path[p])
417
+ ? collForEach(node, (node2) => deep(node2, p + 1))
418
+ : arrayForEach([path[p], null], (id) => deep(mapGet(node, id), p + 1));
419
+ deep(deepIdSet, 0);
420
+ return leaves;
421
+ };
422
+ const getListenerFunctions = (getThing) => {
423
+ let thing;
424
+ const [getId, releaseId] = getPoolFunctions();
425
+ const allListeners = mapNew();
426
+ const addListener = (
427
+ listener,
428
+ idSetNode,
429
+ path,
430
+ pathGetters = [],
431
+ extraArgsGetter = () => [],
432
+ ) => {
433
+ thing ??= getThing();
434
+ const id = getId(1);
435
+ mapSet(allListeners, id, [
436
+ listener,
437
+ idSetNode,
438
+ path,
439
+ pathGetters,
440
+ extraArgsGetter,
441
+ ]);
442
+ setAdd(visitTree(idSetNode, path ?? [EMPTY_STRING], setNew), id);
443
+ return id;
444
+ };
445
+ const callListenersImpl = (continueAfterError, idSetNode, ids, extraArgs) => {
446
+ let errorToThrow;
447
+ let failed = false;
448
+ arrayForEach(getWildcardedLeaves(idSetNode, ids), (set) =>
449
+ collForEach(set, (id) => {
450
+ const callListener2 = () =>
451
+ mapGet(allListeners, id)[0](thing, ...(ids ?? []), ...extraArgs);
452
+ if (continueAfterError) {
453
+ tryCatchSync(callListener2, (error) => {
454
+ if (!failed) {
455
+ errorToThrow = error;
456
+ }
457
+ failed = true;
458
+ });
459
+ } else {
460
+ callListener2();
461
+ }
462
+ }),
463
+ );
464
+ if (failed) {
465
+ throw errorToThrow;
466
+ }
467
+ };
468
+ const callListeners = (idSetNode, ids, ...extraArgs) =>
469
+ callListenersImpl(false, idSetNode, ids, extraArgs);
470
+ const callListenersThenThrow = (idSetNode, ids, ...extraArgs) =>
471
+ callListenersImpl(true, idSetNode, ids, extraArgs);
472
+ const delListener = (id) =>
473
+ ifNotUndefined(mapGet(allListeners, id), ([, idSetNode, idOrNulls]) => {
474
+ visitTree(idSetNode, idOrNulls ?? [EMPTY_STRING], void 0, (idSet) => {
475
+ collDel(idSet, id);
476
+ return collIsEmpty(idSet) ? 1 : 0;
477
+ });
478
+ mapSet(allListeners, id);
479
+ releaseId(id);
480
+ return idOrNulls;
481
+ });
482
+ const callListener = (id) =>
483
+ ifNotUndefined(
484
+ mapGet(allListeners, id),
485
+ ([listener, , path = [], pathGetters, extraArgsGetter]) => {
486
+ const callWithIds = (...ids) => {
487
+ const index = size(ids);
488
+ if (index == size(path)) {
489
+ listener(thing, ...ids, ...extraArgsGetter(ids));
490
+ } else if (isNull(path[index])) {
491
+ arrayForEach(pathGetters[index]?.(...ids) ?? [], (id2) =>
492
+ callWithIds(...ids, id2),
493
+ );
494
+ } else {
495
+ callWithIds(...ids, path[index]);
496
+ }
497
+ };
498
+ callWithIds();
499
+ },
500
+ );
501
+ return [
502
+ addListener,
503
+ callListeners,
504
+ delListener,
505
+ callListener,
506
+ callListenersThenThrow,
507
+ ];
508
+ };
509
+
510
+ const scheduleRunning = mapNew();
511
+ const scheduleActions = mapNew();
512
+ const scheduleReferences = mapNew();
513
+ const getStoreFunctions = (
514
+ persist = 1 /* StoreOnly */,
515
+ store,
516
+ isSynchronizer,
517
+ ) =>
518
+ persist != 1 /* StoreOnly */ && store.isMergeable()
519
+ ? [
520
+ 1,
521
+ store.__[1],
522
+ () => store.__[2](!isSynchronizer),
523
+ ([[changedTables], [changedValues]]) =>
524
+ !objIsEmpty(changedTables) || !objIsEmpty(changedValues),
525
+ store.setDefaultContent,
526
+ ]
527
+ : persist != 2 /* MergeableStoreOnly */
528
+ ? [
529
+ 0,
530
+ store._[7],
531
+ store._[8],
532
+ ([changedTables, changedValues]) =>
533
+ !objIsEmpty(changedTables) || !objIsEmpty(changedValues),
534
+ store.setContent,
535
+ ]
536
+ : errorThrow(ERROR_STORE_TYPE);
537
+ const createCustomPersister = (
538
+ store,
539
+ getPersisted,
540
+ setPersisted,
541
+ addPersisterListener,
542
+ delPersisterListener,
543
+ onIgnoredError,
544
+ persist,
545
+ extra = {},
546
+ isSynchronizer = 0,
547
+ scheduleId = [],
548
+ ) => {
549
+ let status = 0; /* Idle */
550
+ let loads = 0;
551
+ let saves = 0;
552
+ let autoLoadHandle;
553
+ let autoLoadPromise;
554
+ let autoLoadGeneration = 0;
555
+ let pendingAutoLoads = 0;
556
+ let pendingScheduledAutoLoads = 0;
557
+ let autoLoadAfterSaveGeneration = 0;
558
+ let autoSaveListenerId;
559
+ let autoSaveGeneration = 0;
560
+ let destroyed = 0;
561
+ let destroying;
562
+ let activeAction;
563
+ const scheduleOwner = {};
564
+ const extraDestroy = extra.destroy;
565
+ mapEnsure(scheduleRunning, scheduleId, () => 0);
566
+ mapEnsure(scheduleActions, scheduleId, () => []);
567
+ mapSet(
568
+ scheduleReferences,
569
+ scheduleId,
570
+ (mapGet(scheduleReferences, scheduleId) ?? 0) + 1,
571
+ );
572
+ const statusListeners = mapNew();
573
+ const [
574
+ isMergeableStore,
575
+ getContent,
576
+ getChanges,
577
+ hasChanges,
578
+ setDefaultContent,
579
+ ] = getStoreFunctions(persist, store, isSynchronizer);
580
+ const [addListener, callListeners, delListenerImpl] = getListenerFunctions(
581
+ () => persister,
582
+ );
583
+ const setStatus = (newStatus) => {
584
+ if (newStatus != status) {
585
+ status = newStatus;
586
+ callListeners(statusListeners, void 0, status);
587
+ }
588
+ };
589
+ const run = async () => {
590
+ /* istanbul ignore else */
591
+ if (!mapGet(scheduleRunning, scheduleId)) {
592
+ mapSet(scheduleRunning, scheduleId, 1);
593
+ const scheduledActions = mapGet(scheduleActions, scheduleId);
594
+ let actionIndex = 0;
595
+ await tryFinallyAsync(
596
+ async () => {
597
+ while (actionIndex < size(scheduledActions)) {
598
+ const scheduledAction = scheduledActions[actionIndex++];
599
+ scheduledAction[2] = void 0;
600
+ await scheduledAction[0]();
601
+ }
602
+ },
603
+ () => {
604
+ arrayClear(scheduledActions, actionIndex);
605
+ mapSet(scheduleRunning, scheduleId, 0);
606
+ pruneSchedule();
607
+ },
608
+ );
609
+ }
610
+ };
611
+ const pruneSchedule = () => {
612
+ if (
613
+ !mapGet(scheduleReferences, scheduleId) &&
614
+ !mapGet(scheduleRunning, scheduleId) &&
615
+ isEmpty(mapGet(scheduleActions, scheduleId) ?? [])
616
+ ) {
617
+ mapSet(scheduleRunning, scheduleId);
618
+ mapSet(scheduleActions, scheduleId);
619
+ mapSet(scheduleReferences, scheduleId);
620
+ }
621
+ };
622
+ const setContentOrChanges = (contentOrChanges) => {
623
+ (isMergeableStore && isArray(contentOrChanges?.[0])
624
+ ? contentOrChanges?.[2] === 1
625
+ ? store.__[4]
626
+ : store.__[3]
627
+ : contentOrChanges?.[2] === 1
628
+ ? store._[10]
629
+ : store._[9])(contentOrChanges);
630
+ };
631
+ const saveAfterMutated = async () => {
632
+ if (isAutoSaving() && store.__?.[0]?.()) {
633
+ await save();
634
+ }
635
+ };
636
+ const trackAutoLoadPromise = (newAutoLoadPromise) =>
637
+ tryFinallyAsync(
638
+ () => (autoLoadPromise = newAutoLoadPromise),
639
+ () => {
640
+ if (autoLoadPromise == newAutoLoadPromise) {
641
+ autoLoadPromise = void 0;
642
+ }
643
+ },
644
+ );
645
+ const load = async (initialContent) => {
646
+ /* istanbul ignore else */
647
+ if (status != 2 /* Saving */) {
648
+ await tryFinallyAsync(
649
+ async () => {
650
+ setStatus(1 /* Loading */);
651
+ loads++;
652
+ await schedule(() =>
653
+ tryCatch(
654
+ async () => {
655
+ const content = await getPersisted();
656
+ if (isArray(content)) {
657
+ setContentOrChanges(content);
658
+ } else if (isUndefined(content) && initialContent) {
659
+ setDefaultContent(initialContent);
660
+ } else if (!isUndefined(content)) {
661
+ errorThrow(ERROR_CONTENT, content);
662
+ }
663
+ },
664
+ (error) => {
665
+ onIgnoredError?.(error);
666
+ if (initialContent) {
667
+ setDefaultContent(initialContent);
668
+ }
669
+ },
670
+ ),
671
+ );
672
+ },
673
+ () => setStatus(0 /* Idle */),
674
+ );
675
+ await saveAfterMutated();
676
+ }
677
+ return persister;
678
+ };
679
+ const runAutoLoadAfterSave = async () => {
680
+ const generation = autoLoadAfterSaveGeneration;
681
+ autoLoadAfterSaveGeneration = 0;
682
+ if (generation && generation == autoLoadGeneration && !destroyed) {
683
+ if (status == 2 /* Saving */) {
684
+ autoLoadAfterSaveGeneration = generation;
685
+ } else {
686
+ await load();
687
+ }
688
+ }
689
+ };
690
+ const startAutoLoad = async (initialContent) => {
691
+ const generation = ++autoLoadGeneration;
692
+ let initialLoadPending = true;
693
+ await stopAutoLoad(true);
694
+ if (generation == autoLoadGeneration && !destroyed) {
695
+ const listenerPromise = tryCatch(async () => {
696
+ const handle = await addPersisterListener(async (content, changes) => {
697
+ if (generation == autoLoadGeneration && !destroyed) {
698
+ if (changes || content) {
699
+ const shouldSchedule =
700
+ initialLoadPending ||
701
+ status == 2 /* Saving */ ||
702
+ !!pendingScheduledAutoLoads;
703
+ pendingAutoLoads++;
704
+ if (shouldSchedule) {
705
+ pendingScheduledAutoLoads++;
706
+ }
707
+ await tryFinallyAsync(
708
+ async () => {
709
+ setStatus(1 /* Loading */);
710
+ loads++;
711
+ await (shouldSchedule
712
+ ? schedule(() => setContentOrChanges(changes ?? content))
713
+ : tryFinally(
714
+ () => setContentOrChanges(changes ?? content),
715
+ () => setStatus(0 /* Idle */),
716
+ ));
717
+ },
718
+ () => {
719
+ if (shouldSchedule) {
720
+ pendingScheduledAutoLoads--;
721
+ }
722
+ if (!--pendingAutoLoads) {
723
+ setStatus(0 /* Idle */);
724
+ }
725
+ },
726
+ );
727
+ if (!pendingAutoLoads) {
728
+ await saveAfterMutated();
729
+ }
730
+ } else if (status == 2 /* Saving */) {
731
+ autoLoadAfterSaveGeneration = generation;
732
+ } else {
733
+ await load();
734
+ }
735
+ }
736
+ });
737
+ if (generation == autoLoadGeneration && !destroyed) {
738
+ autoLoadHandle = handle;
739
+ } else if (!isUndefined(handle)) {
740
+ await delPersisterListener(handle);
741
+ }
742
+ }, onIgnoredError);
743
+ await trackAutoLoadPromise(listenerPromise);
744
+ if (generation == autoLoadGeneration && !destroyed) {
745
+ await tryFinallyAsync(
746
+ () => load(initialContent),
747
+ () => {
748
+ initialLoadPending = false;
749
+ },
750
+ );
751
+ }
752
+ }
753
+ return persister;
754
+ };
755
+ const stopAutoLoad = async (keepGeneration = false) => {
756
+ autoLoadAfterSaveGeneration = 0;
757
+ if (!keepGeneration) {
758
+ autoLoadGeneration++;
759
+ }
760
+ const listenerPromise = autoLoadPromise;
761
+ const handle = autoLoadHandle;
762
+ autoLoadHandle = void 0;
763
+ await trackAutoLoadPromise(
764
+ tryFinallyAsync(
765
+ async () => {
766
+ await listenerPromise;
767
+ },
768
+ async () => {
769
+ if (!isUndefined(handle)) {
770
+ await tryCatch(() => delPersisterListener(handle), onIgnoredError);
771
+ }
772
+ },
773
+ ),
774
+ );
775
+ return persister;
776
+ };
777
+ const isAutoLoading = () => !isUndefined(autoLoadHandle);
778
+ const save = async (changes) => {
779
+ /* istanbul ignore else */
780
+ if (status != 1 /* Loading */) {
781
+ await tryFinallyAsync(
782
+ () =>
783
+ tryFinallyAsync(
784
+ async () => {
785
+ setStatus(2 /* Saving */);
786
+ saves++;
787
+ await schedule(() =>
788
+ tryCatch(
789
+ () => setPersisted(getContent, changes),
790
+ onIgnoredError,
791
+ ),
792
+ );
793
+ },
794
+ () => setStatus(0 /* Idle */),
795
+ ),
796
+ runAutoLoadAfterSave,
797
+ );
798
+ }
799
+ return persister;
800
+ };
801
+ const startAutoSave = async () => {
802
+ const generation = ++autoSaveGeneration;
803
+ await stopAutoSave(true);
804
+ if (generation == autoSaveGeneration && !destroyed) {
805
+ await save();
806
+ if (generation == autoSaveGeneration && !destroyed) {
807
+ autoSaveListenerId = store.addDidFinishTransactionListener(() => {
808
+ if (generation == autoSaveGeneration && !destroyed) {
809
+ const changes = getChanges();
810
+ if (hasChanges(changes)) {
811
+ void tryCatch(() => save(changes));
812
+ }
813
+ }
814
+ });
815
+ }
816
+ }
817
+ return persister;
818
+ };
819
+ const stopAutoSave = async (keepGeneration = false) => {
820
+ if (!keepGeneration) {
821
+ autoSaveGeneration++;
822
+ }
823
+ const listenerId = autoSaveListenerId;
824
+ autoSaveListenerId = void 0;
825
+ if (!isUndefined(listenerId)) {
826
+ store.delListener(listenerId);
827
+ }
828
+ return persister;
829
+ };
830
+ const isAutoSaving = () => !isUndefined(autoSaveListenerId);
831
+ const startAutoPersisting = async (
832
+ initialContent,
833
+ startSaveFirst = false,
834
+ ) => {
835
+ const [call1, call2, stop1, stop2] = startSaveFirst
836
+ ? [startAutoSave, startAutoLoad, stopAutoSave, stopAutoLoad]
837
+ : [startAutoLoad, startAutoSave, stopAutoLoad, stopAutoSave];
838
+ const start1 = call1(initialContent);
839
+ const generation1 = startSaveFirst
840
+ ? autoSaveGeneration
841
+ : autoLoadGeneration;
842
+ await start1;
843
+ const start2 = call2(initialContent);
844
+ const generation2 = startSaveFirst
845
+ ? autoLoadGeneration
846
+ : autoSaveGeneration;
847
+ try {
848
+ await start2;
849
+ } catch (error) {
850
+ if (
851
+ generation2 ==
852
+ (startSaveFirst ? autoLoadGeneration : autoSaveGeneration)
853
+ ) {
854
+ await tryCatchIgnore(stop2);
855
+ }
856
+ if (
857
+ generation1 ==
858
+ (startSaveFirst ? autoSaveGeneration : autoLoadGeneration)
859
+ ) {
860
+ await tryCatchIgnore(stop1);
861
+ }
862
+ throw error;
863
+ }
864
+ return persister;
865
+ };
866
+ const stopAutoPersisting = async (stopSaveFirst = false) => {
867
+ const [call1, call2] = stopSaveFirst
868
+ ? [stopAutoSave, stopAutoLoad]
869
+ : [stopAutoLoad, stopAutoSave];
870
+ await tryFinallyAsync(
871
+ () => call1(),
872
+ async () => {
873
+ await call2();
874
+ },
875
+ );
876
+ return persister;
877
+ };
878
+ const getStatus = () => status;
879
+ const addStatusListener = (listener) =>
880
+ addListener(listener, statusListeners);
881
+ const delListener = (listenerId) => {
882
+ delListenerImpl(listenerId);
883
+ return store;
884
+ };
885
+ const schedule = async (...actions) => {
886
+ let failed = false;
887
+ let firstError;
888
+ const completion = promiseNew((resolve) =>
889
+ !isEmpty(actions)
890
+ ? arrayPush(
891
+ mapGet(scheduleActions, scheduleId),
892
+ ...arrayMap(actions, (action, index) => {
893
+ const last = index == size(actions) - 1;
894
+ return [
895
+ async () => {
896
+ let completeActiveAction;
897
+ activeAction = promiseNew(
898
+ (resolve2) => (completeActiveAction = resolve2),
899
+ );
900
+ await tryFinallyAsync(
901
+ async () => {
902
+ try {
903
+ await tryCatch(action, onIgnoredError);
904
+ } catch (error) {
905
+ if (!failed) {
906
+ failed = true;
907
+ firstError = error;
908
+ }
909
+ }
910
+ if (last) {
911
+ resolve();
912
+ }
913
+ },
914
+ () => {
915
+ activeAction = void 0;
916
+ completeActiveAction();
917
+ },
918
+ );
919
+ },
920
+ last ? resolve : void 0,
921
+ scheduleOwner,
922
+ ];
923
+ }),
924
+ )
925
+ : resolve(),
926
+ );
927
+ await run();
928
+ await completion;
929
+ if (failed) {
930
+ throw firstError;
931
+ }
932
+ return persister;
933
+ };
934
+ const getStore = () => store;
935
+ const destroy = async () => {
936
+ if (isUndefined(destroying)) {
937
+ destroyed = 1;
938
+ destroying = (async () => {
939
+ const scheduledActions = mapGet(scheduleActions, scheduleId);
940
+ const remainingActions = arrayFilter(
941
+ scheduledActions,
942
+ ([, complete, owner]) => {
943
+ if (owner == scheduleOwner) {
944
+ complete?.();
945
+ return false;
946
+ }
947
+ return true;
948
+ },
949
+ );
950
+ const actionToAwait = activeAction;
951
+ arrayClear(scheduledActions);
952
+ arrayPush(scheduledActions, ...remainingActions);
953
+ await tryFinallyAsync(
954
+ () =>
955
+ tryFinallyAsync(
956
+ () => tryFinallyAsync(stopAutoPersisting, () => actionToAwait),
957
+ () => extraDestroy?.(),
958
+ ),
959
+ () => {
960
+ const references =
961
+ (mapGet(scheduleReferences, scheduleId) ?? 1) - 1;
962
+ mapSet(scheduleReferences, scheduleId, references || void 0);
963
+ pruneSchedule();
964
+ },
965
+ );
966
+ })();
967
+ }
968
+ await destroying;
969
+ return persister;
970
+ };
971
+ const getStats = () => ({loads, saves});
972
+ const persister = {
973
+ load,
974
+ startAutoLoad,
975
+ stopAutoLoad,
976
+ isAutoLoading,
977
+ save,
978
+ startAutoSave,
979
+ stopAutoSave,
980
+ isAutoSaving,
981
+ startAutoPersisting,
982
+ stopAutoPersisting,
983
+ getStatus,
984
+ addStatusListener,
985
+ delListener,
986
+ schedule,
987
+ getStore,
988
+ getStats,
989
+ ...extra,
990
+ destroy,
991
+ };
992
+ return objFreeze(persister);
993
+ };
994
+
995
+ const getCommandFunctions = (
996
+ databaseExecuteCommand,
997
+ managedTableNames,
998
+ querySchema,
999
+ columnType,
1000
+ getPlaceholder,
1001
+ upsert = defaultUpsert,
1002
+ encode,
1003
+ decode,
1004
+ executeTransaction,
1005
+ ) => {
1006
+ const schemaMap = mapNew();
1007
+ const uniqueSchemaMap = mapNew();
1008
+ let executeCommand = databaseExecuteCommand;
1009
+ const canSelect = (tableName, rowIdColumnName) =>
1010
+ collHas(mapGet(schemaMap, tableName), rowIdColumnName);
1011
+ const refreshSchema = async () => {
1012
+ collClear(schemaMap);
1013
+ collClear(uniqueSchemaMap);
1014
+ arrayForEach(
1015
+ await querySchema(executeCommand, managedTableNames),
1016
+ ({tn, cn, uq}) => {
1017
+ setAdd(mapEnsure(schemaMap, tn, setNew), cn);
1018
+ if (uq) {
1019
+ setAdd(mapEnsure(uniqueSchemaMap, tn, setNew), cn);
1020
+ }
1021
+ },
1022
+ );
1023
+ };
1024
+ const loadTable = async (
1025
+ tableName,
1026
+ rowIdColumnName,
1027
+ condition,
1028
+ contentSubIds,
1029
+ ) => {
1030
+ const contentSubIdSet = isUndefined(contentSubIds)
1031
+ ? void 0
1032
+ : setNew(contentSubIds);
1033
+ const includeContentSubId = (contentSubId) =>
1034
+ isUndefined(contentSubIdSet) || collHas(contentSubIdSet, contentSubId);
1035
+ return canSelect(tableName, rowIdColumnName)
1036
+ ? objNew(
1037
+ arrayFilter(
1038
+ arrayMap(
1039
+ await executeCommand(
1040
+ SELECT_STAR_FROM +
1041
+ escapeId(tableName) +
1042
+ getWhereCondition(tableName, condition),
1043
+ ),
1044
+ (row) => {
1045
+ const rowId = row[rowIdColumnName];
1046
+ const rowContent = decode
1047
+ ? objMap(objDel(row, rowIdColumnName), decode)
1048
+ : objDel(row, rowIdColumnName);
1049
+ return [
1050
+ rowId,
1051
+ isUndefined(contentSubIdSet)
1052
+ ? rowContent
1053
+ : objNew(
1054
+ arrayFilter(
1055
+ objToArray(
1056
+ rowContent,
1057
+ (cellOrValue, contentSubId) => [
1058
+ contentSubId,
1059
+ cellOrValue,
1060
+ ],
1061
+ ),
1062
+ ([contentSubId]) => includeContentSubId(contentSubId),
1063
+ ),
1064
+ ),
1065
+ ];
1066
+ },
1067
+ ),
1068
+ (entry) => !isUndefined(entry[0]) && !objIsEmpty(entry[1]),
1069
+ ),
1070
+ )
1071
+ : {};
1072
+ };
1073
+ const saveTable = async (
1074
+ tableName,
1075
+ rowIdColumnName,
1076
+ content,
1077
+ deleteEmptyColumns,
1078
+ deleteEmptyTable,
1079
+ partial = false,
1080
+ condition = TRUE,
1081
+ contentSubIds,
1082
+ ) => {
1083
+ const contentSubIdSet = isUndefined(contentSubIds)
1084
+ ? void 0
1085
+ : setNew(contentSubIds);
1086
+ const includeContentSubId = (contentSubId) =>
1087
+ isUndefined(contentSubIdSet) || collHas(contentSubIdSet, contentSubId);
1088
+ const settingColumnNameSet = setNew();
1089
+ objForEach(content ?? {}, (contentRow) =>
1090
+ arrayForEach(
1091
+ arrayFilter(objIds(contentRow ?? {}), includeContentSubId),
1092
+ (cellOrValueId) => setAdd(settingColumnNameSet, cellOrValueId),
1093
+ ),
1094
+ );
1095
+ const settingColumnNames = collValues(settingColumnNameSet);
1096
+ if (
1097
+ isUndefined(contentSubIdSet) &&
1098
+ !partial &&
1099
+ deleteEmptyTable &&
1100
+ condition == TRUE &&
1101
+ isEmpty(settingColumnNames) &&
1102
+ collHas(schemaMap, tableName)
1103
+ ) {
1104
+ await executeCommand('DROP ' + TABLE + escapeId(tableName));
1105
+ mapSet(schemaMap, tableName);
1106
+ mapSet(uniqueSchemaMap, tableName);
1107
+ return;
1108
+ }
1109
+ const currentColumnNames = mapGet(schemaMap, tableName);
1110
+ const unaccountedColumnNames = setNew(
1111
+ arrayFilter(
1112
+ collValues(currentColumnNames),
1113
+ (columnName) =>
1114
+ columnName == rowIdColumnName || includeContentSubId(columnName),
1115
+ ),
1116
+ );
1117
+ if (!isEmpty(settingColumnNames)) {
1118
+ if (!collHas(schemaMap, tableName)) {
1119
+ await executeCommand(
1120
+ CREATE_TABLE +
1121
+ escapeId(tableName) +
1122
+ `(${escapeId(rowIdColumnName)}${columnType} PRIMARY KEY${arrayJoin(
1123
+ arrayMap(
1124
+ settingColumnNames,
1125
+ (settingColumnName) =>
1126
+ COMMA + escapeId(settingColumnName) + columnType,
1127
+ ),
1128
+ )});`,
1129
+ );
1130
+ mapSet(
1131
+ schemaMap,
1132
+ tableName,
1133
+ setNew([rowIdColumnName, ...settingColumnNames]),
1134
+ );
1135
+ mapSet(uniqueSchemaMap, tableName, setNew([rowIdColumnName]));
1136
+ } else {
1137
+ await promiseAll(
1138
+ arrayMap(
1139
+ [rowIdColumnName, ...settingColumnNames],
1140
+ async (settingColumnName) => {
1141
+ if (!collDel(unaccountedColumnNames, settingColumnName)) {
1142
+ await executeCommand(
1143
+ ALTER_TABLE +
1144
+ escapeId(tableName) +
1145
+ 'ADD' +
1146
+ escapeId(settingColumnName) +
1147
+ columnType,
1148
+ );
1149
+ setAdd(currentColumnNames, settingColumnName);
1150
+ }
1151
+ },
1152
+ ),
1153
+ );
1154
+ if (
1155
+ upsert == defaultUpsert &&
1156
+ !collHas(mapGet(uniqueSchemaMap, tableName), rowIdColumnName)
1157
+ ) {
1158
+ const indexName =
1159
+ TINYBASE +
1160
+ '_pk_' +
1161
+ getHash(jsonString([tableName, rowIdColumnName]));
1162
+ await executeCommand(
1163
+ 'CREATE UNIQUE INDEX IF NOT EXISTS' +
1164
+ escapeId(indexName) +
1165
+ 'ON' +
1166
+ escapeId(tableName) +
1167
+ `(${escapeId(rowIdColumnName)})`,
1168
+ );
1169
+ setAdd(
1170
+ mapEnsure(uniqueSchemaMap, tableName, setNew),
1171
+ rowIdColumnName,
1172
+ );
1173
+ }
1174
+ }
1175
+ }
1176
+ await promiseAll([
1177
+ ...(!partial && deleteEmptyColumns
1178
+ ? arrayMap(
1179
+ collValues(unaccountedColumnNames),
1180
+ async (unaccountedColumnName) => {
1181
+ if (unaccountedColumnName != rowIdColumnName) {
1182
+ await executeCommand(
1183
+ ALTER_TABLE +
1184
+ escapeId(tableName) +
1185
+ 'DROP' +
1186
+ escapeId(unaccountedColumnName),
1187
+ );
1188
+ collDel(currentColumnNames, unaccountedColumnName);
1189
+ }
1190
+ },
1191
+ )
1192
+ : []),
1193
+ ]);
1194
+ if (partial) {
1195
+ if (isUndefined(content)) {
1196
+ if (isUndefined(contentSubIdSet)) {
1197
+ await executeCommand(
1198
+ DELETE_FROM +
1199
+ escapeId(tableName) +
1200
+ getWhereCondition(tableName, condition),
1201
+ );
1202
+ }
1203
+ } else {
1204
+ await promiseAll(
1205
+ objToArray(content, async (row, rowId) => {
1206
+ if (isUndefined(row)) {
1207
+ await executeCommand(
1208
+ DELETE_FROM +
1209
+ escapeId(tableName) +
1210
+ getWhereCondition(tableName, condition) +
1211
+ `AND(${escapeId(rowIdColumnName)}=${getPlaceholder([1])})`,
1212
+ [rowId],
1213
+ );
1214
+ } else if (!isEmpty(settingColumnNames)) {
1215
+ const changingColumnNames = arrayFilter(
1216
+ objIds(row),
1217
+ includeContentSubId,
1218
+ );
1219
+ if (!isEmpty(changingColumnNames)) {
1220
+ await upsert(
1221
+ executeCommand,
1222
+ tableName,
1223
+ rowIdColumnName,
1224
+ changingColumnNames,
1225
+ {
1226
+ [rowId]: arrayMap(changingColumnNames, (cellId) =>
1227
+ encode ? encode(row[cellId]) : row[cellId],
1228
+ ),
1229
+ },
1230
+ getPlaceholder,
1231
+ currentColumnNames,
1232
+ );
1233
+ }
1234
+ }
1235
+ }),
1236
+ );
1237
+ }
1238
+ } else {
1239
+ if (!isEmpty(settingColumnNames)) {
1240
+ const changingColumnNames = arrayFilter(
1241
+ collValues(mapGet(schemaMap, tableName)),
1242
+ (changingColumnName) =>
1243
+ changingColumnName != rowIdColumnName &&
1244
+ includeContentSubId(changingColumnName),
1245
+ );
1246
+ const rows = objNew();
1247
+ const deleteRowIds = [];
1248
+ objForEach(content ?? {}, (row, rowId) => {
1249
+ objSet(
1250
+ rows,
1251
+ rowId,
1252
+ arrayMap(changingColumnNames, (cellId) =>
1253
+ encode ? encode(row?.[cellId]) : row?.[cellId],
1254
+ ),
1255
+ );
1256
+ arrayPush(deleteRowIds, rowId);
1257
+ });
1258
+ await upsert(
1259
+ executeCommand,
1260
+ tableName,
1261
+ rowIdColumnName,
1262
+ changingColumnNames,
1263
+ rows,
1264
+ getPlaceholder,
1265
+ currentColumnNames,
1266
+ );
1267
+ await executeCommand(
1268
+ DELETE_FROM +
1269
+ escapeId(tableName) +
1270
+ getWhereCondition(tableName, condition) + // eslint-disable-next-line max-len
1271
+ `AND${escapeId(rowIdColumnName)}NOT IN(${getPlaceholders(deleteRowIds, getPlaceholder)})`,
1272
+ deleteRowIds,
1273
+ );
1274
+ } else if (
1275
+ isUndefined(contentSubIdSet) &&
1276
+ collHas(schemaMap, tableName)
1277
+ ) {
1278
+ await executeCommand(
1279
+ DELETE_FROM +
1280
+ escapeId(tableName) +
1281
+ getWhereCondition(tableName, condition),
1282
+ );
1283
+ }
1284
+ }
1285
+ };
1286
+ const transaction = async (actions) => {
1287
+ if (executeTransaction) {
1288
+ return await executeTransaction(async (transactionExecuteCommand) => {
1289
+ const executeCommandWas = executeCommand;
1290
+ executeCommand = transactionExecuteCommand;
1291
+ return await tryFinallyAsync(actions, () => {
1292
+ executeCommand = executeCommandWas;
1293
+ });
1294
+ });
1295
+ }
1296
+ await databaseExecuteCommand('BEGIN');
1297
+ try {
1298
+ const result = await actions();
1299
+ await databaseExecuteCommand('END');
1300
+ return result;
1301
+ } catch (error) {
1302
+ await tryCatch(() => databaseExecuteCommand('ROLLBACK'));
1303
+ throw error;
1304
+ }
1305
+ };
1306
+ return [refreshSchema, loadTable, saveTable, transaction];
1307
+ };
1308
+ const defaultUpsert = async (
1309
+ executeCommand,
1310
+ tableName,
1311
+ rowIdColumnName,
1312
+ changingColumnNames,
1313
+ rows,
1314
+ getPlaceholder,
1315
+ ) => {
1316
+ const offset = [1];
1317
+ await executeCommand(
1318
+ INSERT +
1319
+ ' INTO' +
1320
+ escapeId(tableName) +
1321
+ '(' +
1322
+ escapeColumnNames(rowIdColumnName, ...changingColumnNames) +
1323
+ ')VALUES' +
1324
+ arrayJoin(
1325
+ objToArray(
1326
+ rows,
1327
+ (row) =>
1328
+ '(' +
1329
+ getPlaceholder(offset) +
1330
+ ',' +
1331
+ getPlaceholders(row, getPlaceholder, offset) +
1332
+ ')',
1333
+ ),
1334
+ COMMA,
1335
+ ) +
1336
+ 'ON CONFLICT(' +
1337
+ escapeId(rowIdColumnName) +
1338
+ `)DO ${UPDATE} SET` +
1339
+ arrayJoin(
1340
+ arrayMap(
1341
+ changingColumnNames,
1342
+ (columnName) =>
1343
+ escapeId(columnName) + '=excluded.' + escapeId(columnName),
1344
+ ),
1345
+ COMMA,
1346
+ ),
1347
+ objToArray(rows, (row, id) => [
1348
+ id,
1349
+ ...arrayMap(row, (value) => value ?? null),
1350
+ ]).flat(),
1351
+ );
1352
+ };
1353
+
1354
+ const createJsonPersister = (
1355
+ store,
1356
+ executeCommand,
1357
+ addPersisterListener,
1358
+ delPersisterListener,
1359
+ onIgnoredError,
1360
+ extraDestroy,
1361
+ persist,
1362
+ [storeTableName, storeIdColumnName, storeColumnName],
1363
+ managedTableNames,
1364
+ querySchema,
1365
+ thing,
1366
+ getThing,
1367
+ columnType,
1368
+ getPlaceholder,
1369
+ upsert,
1370
+ _encode,
1371
+ _decode,
1372
+ executeTransaction,
1373
+ ) => {
1374
+ const [refreshSchema, loadTable, saveTable, transaction] =
1375
+ getCommandFunctions(
1376
+ executeCommand,
1377
+ managedTableNames,
1378
+ querySchema,
1379
+ columnType,
1380
+ getPlaceholder,
1381
+ upsert,
1382
+ void 0,
1383
+ void 0,
1384
+ executeTransaction,
1385
+ );
1386
+ const getPersisted = () =>
1387
+ transaction(async () => {
1388
+ await refreshSchema();
1389
+ return jsonParseWithUndefined(
1390
+ (await loadTable(storeTableName, storeIdColumnName))[SINGLE_ROW_ID]?.[
1391
+ storeColumnName
1392
+ ] ?? 'null',
1393
+ );
1394
+ });
1395
+ const setPersisted = (getContent) =>
1396
+ transaction(async () => {
1397
+ await refreshSchema();
1398
+ await saveTable(
1399
+ storeTableName,
1400
+ storeIdColumnName,
1401
+ {
1402
+ [SINGLE_ROW_ID]: {
1403
+ [storeColumnName]: jsonStringWithUndefined(getContent() ?? null),
1404
+ },
1405
+ },
1406
+ true,
1407
+ true,
1408
+ );
1409
+ });
1410
+ return createCustomPersister(
1411
+ store,
1412
+ getPersisted,
1413
+ setPersisted,
1414
+ addPersisterListener,
1415
+ delPersisterListener,
1416
+ onIgnoredError,
1417
+ persist,
1418
+ {[getThing]: () => thing, destroy: extraDestroy},
1419
+ 0,
1420
+ thing,
1421
+ );
1422
+ };
1423
+
1424
+ const createTabularPersister = (
1425
+ store,
1426
+ executeCommand,
1427
+ addPersisterListener,
1428
+ delPersisterListener,
1429
+ onIgnoredError,
1430
+ extraDestroy,
1431
+ persist,
1432
+ [
1433
+ tablesLoadConfig,
1434
+ tablesSaveConfig,
1435
+ [
1436
+ valuesLoad,
1437
+ valuesSave,
1438
+ valuesTableName,
1439
+ valuesLoadValueIds,
1440
+ valuesSaveValueIds,
1441
+ ],
1442
+ ],
1443
+ managedTableNames,
1444
+ querySchema,
1445
+ thing,
1446
+ getThing,
1447
+ columnType,
1448
+ getPlaceholder,
1449
+ upsert,
1450
+ encode,
1451
+ decode,
1452
+ executeTransaction,
1453
+ ) => {
1454
+ const [refreshSchema, loadTable, saveTable, transaction] =
1455
+ getCommandFunctions(
1456
+ executeCommand,
1457
+ managedTableNames,
1458
+ querySchema,
1459
+ columnType,
1460
+ getPlaceholder,
1461
+ upsert,
1462
+ encode,
1463
+ decode,
1464
+ executeTransaction,
1465
+ );
1466
+ const saveTables = (tables, partial) =>
1467
+ promiseAll(
1468
+ mapMap(
1469
+ tablesSaveConfig,
1470
+ async (
1471
+ [
1472
+ tableName,
1473
+ rowIdColumnName,
1474
+ deleteEmptyColumns,
1475
+ deleteEmptyTable,
1476
+ condition,
1477
+ ],
1478
+ tableId,
1479
+ ) => {
1480
+ if (!partial || objHas(tables, tableId)) {
1481
+ await saveTable(
1482
+ tableName,
1483
+ rowIdColumnName,
1484
+ tables[tableId],
1485
+ deleteEmptyColumns,
1486
+ deleteEmptyTable,
1487
+ partial,
1488
+ condition,
1489
+ );
1490
+ }
1491
+ },
1492
+ ),
1493
+ );
1494
+ const getValuesSubset = (values, valueIds, partial) =>
1495
+ objNew(
1496
+ partial == true
1497
+ ? arrayFilter(
1498
+ objToArray(values, (value, valueId) => [valueId, value]),
1499
+ ([valueId]) => arrayHas(valueIds, valueId),
1500
+ )
1501
+ : arrayFilter(
1502
+ arrayMap(valueIds, (valueId) => [valueId, values[valueId]]),
1503
+ ([, value]) => !isUndefined(value),
1504
+ ),
1505
+ );
1506
+ const saveValues = async (values, partial) =>
1507
+ valuesSave
1508
+ ? await saveTable(
1509
+ valuesTableName,
1510
+ DEFAULT_ROW_ID_COLUMN_NAME,
1511
+ {
1512
+ [SINGLE_ROW_ID]: isUndefined(valuesSaveValueIds)
1513
+ ? values
1514
+ : getValuesSubset(values, valuesSaveValueIds, partial),
1515
+ },
1516
+ true,
1517
+ true,
1518
+ partial,
1519
+ void 0,
1520
+ valuesSaveValueIds,
1521
+ )
1522
+ : null;
1523
+ const loadTables = async () =>
1524
+ objNew(
1525
+ arrayFilter(
1526
+ await promiseAll(
1527
+ mapMap(
1528
+ tablesLoadConfig,
1529
+ async ([tableId, rowIdColumnName, condition], tableName) => [
1530
+ tableId,
1531
+ await loadTable(tableName, rowIdColumnName, condition),
1532
+ ],
1533
+ ),
1534
+ ),
1535
+ (pair) => !objIsEmpty(pair[1]),
1536
+ ),
1537
+ );
1538
+ const loadValues = async () =>
1539
+ valuesLoad
1540
+ ? (
1541
+ await loadTable(
1542
+ valuesTableName,
1543
+ DEFAULT_ROW_ID_COLUMN_NAME,
1544
+ void 0,
1545
+ valuesLoadValueIds,
1546
+ )
1547
+ )[SINGLE_ROW_ID]
1548
+ : {};
1549
+ const getPersisted = () =>
1550
+ transaction(async () => {
1551
+ await refreshSchema();
1552
+ const tables = await loadTables();
1553
+ const values = await loadValues();
1554
+ return !objIsEmpty(tables) || !isUndefined(values)
1555
+ ? [tables, values]
1556
+ : void 0;
1557
+ });
1558
+ const setPersisted = (getContent, changes) =>
1559
+ transaction(async () => {
1560
+ await refreshSchema();
1561
+ if (!isUndefined(changes)) {
1562
+ await saveTables(changes[0], true);
1563
+ await saveValues(changes[1], true);
1564
+ } else {
1565
+ const [tables, values] = getContent();
1566
+ await saveTables(tables);
1567
+ await saveValues(values);
1568
+ }
1569
+ });
1570
+ return createCustomPersister(
1571
+ store,
1572
+ getPersisted,
1573
+ setPersisted,
1574
+ addPersisterListener,
1575
+ delPersisterListener,
1576
+ onIgnoredError,
1577
+ persist,
1578
+ {[getThing]: () => thing, destroy: extraDestroy},
1579
+ 0,
1580
+ thing,
1581
+ );
1582
+ };
1583
+
1584
+ const TABLE_CREATED = 'c';
1585
+ const DATA_CHANGED = 'd';
1586
+ const EVENT_REGEX = /^([cd]:)(.+)/;
1587
+ const SIMPLE_TABLE_NAME = /^[A-Za-z0-9_]+$/;
1588
+ const POSTGRES_IDENTIFIER_SIZE = 63;
1589
+ const listenerResourcesByThing = mapNew();
1590
+ const createCustomPostgreSqlPersister = (
1591
+ store,
1592
+ configOrStoreTableName,
1593
+ rawExecuteCommand,
1594
+ addChangeListener,
1595
+ delChangeListener,
1596
+ onSqlCommand,
1597
+ onIgnoredError,
1598
+ destroy,
1599
+ persist,
1600
+ thing,
1601
+ getThing = 'getDb',
1602
+ ) => {
1603
+ const executeCommand = getWrappedCommand(rawExecuteCommand, onSqlCommand);
1604
+ const [isJson, , defaultedConfig, managedTableNamesSet] = getConfigStructures(
1605
+ configOrStoreTableName,
1606
+ );
1607
+ if (!isJson && store.isMergeable()) {
1608
+ errorThrow(ERROR_STORE_TYPE);
1609
+ }
1610
+ const configHash =
1611
+ EMPTY_STRING + getHash(jsonStringWithUndefined(defaultedConfig));
1612
+ const channel = TINYBASE + '_' + configHash;
1613
+ const resourceOwner = thing ?? rawExecuteCommand;
1614
+ const createFunction = async (
1615
+ name,
1616
+ body,
1617
+ returnPrefix = '',
1618
+ declarations = '',
1619
+ ) => {
1620
+ const escapedFunctionName = escapeIds(TINYBASE, name, configHash);
1621
+ await executeCommand(
1622
+ CREATE +
1623
+ OR_REPLACE +
1624
+ FUNCTION +
1625
+ escapedFunctionName +
1626
+ `()RETURNS ${returnPrefix}trigger AS $$ ${declarations}BEGIN ${body}END;$$ LANGUAGE plpgsql;`,
1627
+ );
1628
+ return escapedFunctionName;
1629
+ };
1630
+ const createTrigger = async (
1631
+ prefix,
1632
+ escapedTriggerName,
1633
+ body,
1634
+ escapedFunctionName,
1635
+ ) => {
1636
+ await executeCommand(
1637
+ CREATE +
1638
+ prefix +
1639
+ 'TRIGGER' +
1640
+ escapedTriggerName +
1641
+ body +
1642
+ 'EXECUTE ' +
1643
+ FUNCTION +
1644
+ escapedFunctionName +
1645
+ `()`,
1646
+ );
1647
+ return escapedTriggerName;
1648
+ };
1649
+ const notify = (message) => `PERFORM pg_notify('${channel}',${message});`;
1650
+ const when = (tableName, newOrOldOrBoth) =>
1651
+ isJson
1652
+ ? TRUE
1653
+ : newOrOldOrBoth === 2
1654
+ ? when(tableName, 0) + ' OR ' + when(tableName, 1)
1655
+ : replaceTableName(
1656
+ mapGet(defaultedConfig[0], tableName)?.[2] ?? TRUE,
1657
+ newOrOldOrBoth == 0 ? 'NEW' : 'OLD',
1658
+ );
1659
+ const addDataChangedTriggers = (tableName, dataChangedFunction) =>
1660
+ promiseAll(
1661
+ arrayMap([INSERT, DELETE, UPDATE], (action, newOrOldOrBoth) => {
1662
+ const legacyTriggerId = arrayJoin(
1663
+ [TINYBASE, DATA_CHANGED, configHash, tableName, action],
1664
+ '_',
1665
+ );
1666
+ return createTrigger(
1667
+ OR_REPLACE,
1668
+ test(SIMPLE_TABLE_NAME, tableName) &&
1669
+ size(legacyTriggerId) <= POSTGRES_IDENTIFIER_SIZE
1670
+ ? escapeId(legacyTriggerId)
1671
+ : escapeIds(
1672
+ TINYBASE,
1673
+ DATA_CHANGED,
1674
+ configHash,
1675
+ action,
1676
+ EMPTY_STRING + getHash(tableName),
1677
+ ),
1678
+ `AFTER ${action} ON${escapeId(tableName)}FOR EACH ROW WHEN(${when(
1679
+ tableName,
1680
+ newOrOldOrBoth,
1681
+ )})`,
1682
+ dataChangedFunction,
1683
+ );
1684
+ }),
1685
+ );
1686
+ const dropFunctions = async (functionNames) => {
1687
+ if (!isEmpty(functionNames)) {
1688
+ await executeCommand(
1689
+ `DROP FUNCTION IF EXISTS${arrayJoin(functionNames, ',')}CASCADE`,
1690
+ );
1691
+ }
1692
+ };
1693
+ const forgetListenerResources = (resourcesByHash, resources) => {
1694
+ if (mapGet(resourcesByHash, configHash) === resources) {
1695
+ mapSet(resourcesByHash, configHash);
1696
+ if (
1697
+ collIsEmpty(resourcesByHash) &&
1698
+ mapGet(listenerResourcesByThing, resourceOwner) === resourcesByHash
1699
+ ) {
1700
+ mapSet(listenerResourcesByThing, resourceOwner);
1701
+ }
1702
+ }
1703
+ };
1704
+ const acquireListenerResources = async () => {
1705
+ const resourcesByHash = mapEnsure(
1706
+ listenerResourcesByThing,
1707
+ resourceOwner,
1708
+ () => mapNew(),
1709
+ );
1710
+ const existingResources = mapGet(resourcesByHash, configHash);
1711
+ if (existingResources) {
1712
+ await existingResources[1];
1713
+ if (existingResources[0]) {
1714
+ existingResources[0]++;
1715
+ return existingResources;
1716
+ }
1717
+ await tryCatch(() => existingResources[4]);
1718
+ return await acquireListenerResources();
1719
+ }
1720
+ const functionNames = [];
1721
+ const resources = [1, void 0, functionNames];
1722
+ mapSet(resourcesByHash, configHash, resources);
1723
+ resources[1] = (async () => {
1724
+ const tableCreatedFunctionName = await createFunction(
1725
+ TABLE_CREATED,
1726
+ // eslint-disable-next-line max-len
1727
+ `FOR row IN SELECT object_identity FROM pg_event_trigger_ddl_commands()${WHERE} command_tag='${CREATE_TABLE}' LOOP ${notify(`'c:'||SPLIT_PART(row.object_identity,'.',2)`)}END LOOP;`,
1728
+ 'event_',
1729
+ 'DECLARE row record;',
1730
+ );
1731
+ arrayPush(functionNames, tableCreatedFunctionName);
1732
+ await createTrigger(
1733
+ 'EVENT ',
1734
+ escapeIds(TINYBASE, TABLE_CREATED, configHash),
1735
+ `ON ddl_command_end WHEN TAG IN('${CREATE_TABLE}')`,
1736
+ tableCreatedFunctionName,
1737
+ );
1738
+ const dataChangedFunctionName = await createFunction(
1739
+ DATA_CHANGED,
1740
+ notify(`'d:'||TG_TABLE_NAME`) + `RETURN NULL;`,
1741
+ );
1742
+ arrayPush(functionNames, dataChangedFunctionName);
1743
+ resources[3] = dataChangedFunctionName;
1744
+ await promiseAll(
1745
+ arrayMap(collValues(managedTableNamesSet), async (tableName) => {
1746
+ await executeCommand(
1747
+ CREATE_TABLE +
1748
+ ` IF NOT EXISTS${escapeId(tableName)}("_id"text PRIMARY KEY)`,
1749
+ );
1750
+ return await addDataChangedTriggers(
1751
+ tableName,
1752
+ dataChangedFunctionName,
1753
+ );
1754
+ }),
1755
+ );
1756
+ })();
1757
+ try {
1758
+ await resources[1];
1759
+ } catch (error) {
1760
+ resources[0] = 0;
1761
+ resources[4] = tryFinallyAsync(
1762
+ () => dropFunctions(functionNames),
1763
+ () => forgetListenerResources(resourcesByHash, resources),
1764
+ );
1765
+ await tryCatch(() => resources[4]);
1766
+ throw error;
1767
+ }
1768
+ return resources;
1769
+ };
1770
+ const releaseListenerResources = async (resources) => {
1771
+ if (--resources[0] == 0) {
1772
+ const resourcesByHash = mapGet(listenerResourcesByThing, resourceOwner);
1773
+ resources[4] = tryFinallyAsync(
1774
+ () => dropFunctions(resources[2]),
1775
+ () => forgetListenerResources(resourcesByHash, resources),
1776
+ );
1777
+ await resources[4];
1778
+ }
1779
+ };
1780
+ const addPersisterListener = async (listener) => {
1781
+ const resources = await acquireListenerResources();
1782
+ try {
1783
+ const listenerHandle = await addChangeListener(
1784
+ channel,
1785
+ (prefixAndTableName) =>
1786
+ void tryCatchIgnore(
1787
+ () =>
1788
+ ifNotUndefined(
1789
+ strMatch(prefixAndTableName, EVENT_REGEX),
1790
+ async ([, eventType, tableName]) => {
1791
+ if (collHas(managedTableNamesSet, tableName)) {
1792
+ if (eventType == 'c:') {
1793
+ await addDataChangedTriggers(tableName, resources[3]);
1794
+ }
1795
+ await listener();
1796
+ }
1797
+ },
1798
+ ),
1799
+ onIgnoredError,
1800
+ ),
1801
+ );
1802
+ return [listenerHandle, resources];
1803
+ } catch (error) {
1804
+ await tryCatchIgnore(
1805
+ () => releaseListenerResources(resources),
1806
+ onIgnoredError,
1807
+ );
1808
+ throw error;
1809
+ }
1810
+ };
1811
+ const delPersisterListener = async ([listenerHandle, resources]) => {
1812
+ await tryFinallyAsync(
1813
+ () => delChangeListener(listenerHandle),
1814
+ () => releaseListenerResources(resources),
1815
+ );
1816
+ };
1817
+ return (isJson ? createJsonPersister : createTabularPersister)(
1818
+ store,
1819
+ executeCommand,
1820
+ addPersisterListener,
1821
+ delPersisterListener,
1822
+ onIgnoredError,
1823
+ destroy,
1824
+ persist,
1825
+ defaultedConfig,
1826
+ collValues(managedTableNamesSet),
1827
+ async (executeCommand2, managedTableNames) =>
1828
+ await executeCommand2(
1829
+ SELECT + // eslint-disable-next-line max-len
1830
+ ` c.table_name tn,c.column_name cn,CASE WHEN tc.constraint_type IN('PRIMARY KEY','UNIQUE')AND(${SELECT} count(*) FROM information_schema.key_column_usage kcu2 ${WHERE} kcu2.constraint_schema=kcu.constraint_schema AND kcu2.constraint_name=kcu.constraint_name)=1 THEN 1 ELSE 0 END uq FROM information_schema.columns c LEFT JOIN information_schema.key_column_usage kcu ON kcu.table_schema=c.table_schema AND kcu.table_name=c.table_name AND kcu.column_name=c.column_name LEFT JOIN information_schema.table_constraints tc ON tc.constraint_schema=kcu.constraint_schema AND tc.constraint_name=kcu.constraint_name ${WHERE} c.table_schema='public'AND c.table_name IN(${getPlaceholders(managedTableNames, numberedPlaceholder)})`,
1831
+ managedTableNames,
1832
+ ),
1833
+ thing,
1834
+ getThing,
1835
+ 'text',
1836
+ numberedPlaceholder,
1837
+ void 0,
1838
+ (cellOrValue) => jsonString(cellOrValue),
1839
+ (field) => jsonParse(field),
1840
+ );
1841
+ };
1842
+
1843
+ const isPool = (pg) => !isUndefined(pg?.idleCount);
1844
+ const reserveClient = async (pg, onIgnoredError) => {
1845
+ if (isPool(pg)) {
1846
+ const client = await pg.connect();
1847
+ client.on('error', onIgnoredError ?? noop);
1848
+ return [client, (discard) => client.release(discard)];
1849
+ }
1850
+ return [pg, noop];
1851
+ };
1852
+ const getSerializer = () => {
1853
+ let chain = promiseResolve();
1854
+ return (action) => (chain = chain.then(action, action));
1855
+ };
1856
+ const createPgPersister = async (
1857
+ store,
1858
+ pg,
1859
+ configOrStoreTableName,
1860
+ onSqlCommand,
1861
+ onIgnoredError,
1862
+ ) => {
1863
+ const [commandClient, releaseCommandClient] = await reserveClient(
1864
+ pg,
1865
+ onIgnoredError,
1866
+ );
1867
+ const serialize = getSerializer();
1868
+ try {
1869
+ return createCustomPostgreSqlPersister(
1870
+ store,
1871
+ configOrStoreTableName,
1872
+ (sql, params = []) =>
1873
+ serialize(async () => (await commandClient.query(sql, params)).rows),
1874
+ async (channel, listener) => {
1875
+ const handler = (notification) =>
1876
+ notification.channel == channel ? listener(notification.payload) : 0;
1877
+ const listenSql = escapeId(channel);
1878
+ const [client, release] = await reserveClient(pg, onIgnoredError);
1879
+ client.on('notification', handler);
1880
+ await client.query('LISTEN ' + listenSql);
1881
+ return async () => {
1882
+ client.off('notification', handler);
1883
+ if (isPool(pg)) {
1884
+ release(true);
1885
+ } else {
1886
+ await client.query('UNLISTEN ' + listenSql);
1887
+ }
1888
+ };
1889
+ },
1890
+ (unlisten) => tryCatch(unlisten, onIgnoredError),
1891
+ onSqlCommand,
1892
+ onIgnoredError,
1893
+ releaseCommandClient,
1894
+ 3,
1895
+ // StoreOrMergeableStore,
1896
+ pg,
1897
+ 'getPg',
1898
+ );
1899
+ } catch (error) {
1900
+ releaseCommandClient();
1901
+ throw error;
1902
+ }
1903
+ };
1904
+
1905
+ export {createPgPersister};