@bbn/bbn 2.0.235 → 2.0.238

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 (83) hide show
  1. package/dist/$.js +1 -1
  2. package/dist/bbn.js +3 -3
  3. package/dist/bbn.js.map +1 -1
  4. package/dist/bbn.sw.js +3 -3
  5. package/dist/bbn.sw.js.map +1 -1
  6. package/dist/com.js +13 -21
  7. package/dist/{vars.d.ts → data.d.ts} +1 -0
  8. package/dist/{vars.js → data.js} +1 -0
  9. package/dist/db/classes/Object.d.ts +3 -0
  10. package/dist/db/classes/Object.js +458 -0
  11. package/dist/db/classes/ObjectProxy.d.ts +18 -0
  12. package/dist/db/classes/ObjectProxy.js +77 -0
  13. package/dist/db/classes/Proxy.d.ts +17 -0
  14. package/dist/db/classes/Proxy.js +66 -0
  15. package/dist/db/functions/fieldsFromFilter.d.ts +1 -0
  16. package/dist/db/functions/fieldsFromFilter.js +33 -0
  17. package/dist/db/functions/getPrimaryKey.d.ts +2 -0
  18. package/dist/db/functions/getPrimaryKey.js +8 -0
  19. package/dist/db/functions/requestToPromise.d.ts +1 -0
  20. package/dist/db/functions/requestToPromise.js +6 -0
  21. package/dist/db/functions/transactionDone.d.ts +1 -0
  22. package/dist/db/functions/transactionDone.js +7 -0
  23. package/dist/db/functions/transformResult.d.ts +1 -0
  24. package/dist/db/functions/transformResult.js +16 -0
  25. package/dist/db/types.d.ts +54 -0
  26. package/dist/db/types.js +1 -0
  27. package/dist/db.d.ts +1 -65
  28. package/dist/db.js +1 -526
  29. package/dist/dt/classes/{date.d.ts → Date.d.ts} +1 -1
  30. package/dist/dt/classes/{date.js → Date.js} +8 -3
  31. package/dist/dt/classes/{dateTime.d.ts → DateTime.d.ts} +1 -1
  32. package/dist/dt/classes/{dateTime.js → DateTime.js} +9 -3
  33. package/dist/dt/classes/{dt.d.ts → Dt.d.ts} +1 -1
  34. package/dist/dt/classes/{dt.js → Dt.js} +36 -48
  35. package/dist/dt/classes/Duration.js +193 -0
  36. package/dist/dt/classes/{monthDay.d.ts → MonthDay.d.ts} +1 -1
  37. package/dist/dt/classes/{monthDay.js → MonthDay.js} +2 -2
  38. package/dist/dt/classes/{time.d.ts → Time.d.ts} +1 -1
  39. package/dist/dt/classes/{time.js → Time.js} +2 -2
  40. package/dist/dt/classes/{yearMonth.d.ts → YearMonth.d.ts} +1 -1
  41. package/dist/dt/classes/{yearMonth.js → YearMonth.js} +2 -2
  42. package/dist/dt/classes/{zoned.d.ts → Zoned.d.ts} +1 -1
  43. package/dist/dt/classes/{zoned.js → Zoned.js} +9 -3
  44. package/dist/dt/functions/buildLocaleFromIntl.js +1 -1
  45. package/dist/dt/functions/guessFormat.js +1 -1
  46. package/dist/dt/functions/intl-weekinfo-polyfill.js +11 -4
  47. package/dist/dt/functions/parse.js +20 -22
  48. package/dist/dt/functions/setupIntl.js +33 -44
  49. package/dist/dt.d.ts +1 -1
  50. package/dist/dt.js +9 -7
  51. package/dist/env.d.ts +1 -1
  52. package/dist/env.js +6 -7
  53. package/dist/fn/ajax/abort.js +1 -1
  54. package/dist/fn/ajax/ajax.d.ts +1 -1
  55. package/dist/fn/ajax/ajax.js +2 -2
  56. package/dist/fn/ajax/download.js +1 -1
  57. package/dist/fn/ajax/getFileContent.js +2 -3
  58. package/dist/fn/ajax/link.js +2 -2
  59. package/dist/fn/ajax/stream.js +2 -2
  60. package/dist/fn/browser/executeSlowly.js +11 -22
  61. package/dist/fn/browser/log.js +2 -3
  62. package/dist/fn/browser/yieldToBrowser.js +7 -19
  63. package/dist/fn/html/makeReactive.js +2 -2
  64. package/dist/fn/init.d.ts +1 -1
  65. package/dist/fn/init.js +4 -5
  66. package/dist/fn/object/_filter.js +1 -1
  67. package/dist/fn/phone/fphone.js +1 -2
  68. package/dist/fn/string/cast.js +1 -2
  69. package/dist/fn/string/shorten.js +1 -1
  70. package/dist/fn/string/treatForHash.js +1 -1
  71. package/dist/fn/style/addColors.d.ts +1 -1
  72. package/dist/fn/style/addColors.js +4 -4
  73. package/dist/fn/type/checkType.js +1 -2
  74. package/dist/fn/type/isCp.js +1 -2
  75. package/dist/fn/type/isHostname.js +1 -1
  76. package/dist/fn/type/isIP.js +1 -1
  77. package/dist/fn/type/isSame.js +2 -2
  78. package/dist/fn/type/isURL.js +1 -1
  79. package/dist/index-no-dep.js +2 -2
  80. package/dist/index.js +2 -2
  81. package/package.json +1 -1
  82. package/dist/dt/classes/duration.js +0 -199
  83. /package/dist/dt/classes/{duration.d.ts → Duration.d.ts} +0 -0
package/dist/com.js CHANGED
@@ -1,13 +1,5 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
1
  class Cancel extends Error {
2
+ __BBN_CANCEL__;
11
3
  constructor(message) {
12
4
  super(message || 'Request canceled');
13
5
  this.name = 'Cancel';
@@ -48,7 +40,7 @@ const fetchRequest = (method, url, config = {}, aborter) => {
48
40
  const fetchConfig = {
49
41
  method,
50
42
  headers: new Headers(config.headers || {}),
51
- signal: aborter === null || aborter === void 0 ? void 0 : aborter.signal,
43
+ signal: aborter?.signal,
52
44
  };
53
45
  const hasBody = methodsWithBody.includes(method.toUpperCase());
54
46
  if (config.data != null && hasBody) {
@@ -66,25 +58,25 @@ const fetchRequest = (method, url, config = {}, aborter) => {
66
58
  const usp = new URLSearchParams(config.params).toString();
67
59
  url += (url.includes('?') ? '&' : '?') + usp;
68
60
  }
69
- const fetchPromise = fetch(url, fetchConfig).then((res) => __awaiter(void 0, void 0, void 0, function* () {
61
+ const fetchPromise = fetch(url, fetchConfig).then(async (res) => {
70
62
  let data;
71
63
  const contentType = res.headers.get('content-type') || '';
72
64
  try {
73
65
  if (contentType.includes('application/json')) {
74
- data = yield res.json();
66
+ data = await res.json();
75
67
  }
76
68
  else if (contentType.startsWith('text/')) {
77
- data = yield res.text();
69
+ data = await res.text();
78
70
  }
79
71
  else if (contentType.includes("multipart/")) {
80
- data = yield res.arrayBuffer();
72
+ data = await res.arrayBuffer();
81
73
  }
82
74
  else {
83
- data = yield res.blob();
75
+ data = await res.blob();
84
76
  }
85
77
  }
86
- catch (_a) {
87
- data = yield res.text();
78
+ catch {
79
+ data = await res.text();
88
80
  }
89
81
  const response = {
90
82
  data,
@@ -103,7 +95,7 @@ const fetchRequest = (method, url, config = {}, aborter) => {
103
95
  throw error;
104
96
  }
105
97
  return response;
106
- })).catch((err) => {
98
+ }).catch((err) => {
107
99
  if (err.name === 'AbortError') {
108
100
  throw new Cancel('Request canceled');
109
101
  }
@@ -302,7 +294,7 @@ onProgress, onChunkProgress, headers = {}, uploadId }) => {
302
294
  xhr.send(chunk);
303
295
  });
304
296
  }
305
- const promise = (() => __awaiter(void 0, void 0, void 0, function* () {
297
+ const promise = (async () => {
306
298
  try {
307
299
  let lastResponse = null;
308
300
  for (let chunkIndex = 0; chunkIndex < totalChunks; chunkIndex++) {
@@ -312,7 +304,7 @@ onProgress, onChunkProgress, headers = {}, uploadId }) => {
312
304
  const start = chunkIndex * chunkSize;
313
305
  const end = Math.min(start + chunkSize, totalSize);
314
306
  const chunk = file.slice(start, end);
315
- lastResponse = yield createChunkRequest(chunk, chunkIndex);
307
+ lastResponse = await createChunkRequest(chunk, chunkIndex);
316
308
  }
317
309
  // All chunks uploaded
318
310
  return {
@@ -323,7 +315,7 @@ onProgress, onChunkProgress, headers = {}, uploadId }) => {
323
315
  finally {
324
316
  currentXhr = null;
325
317
  }
326
- }))();
318
+ })();
327
319
  // Attach cancel method to the promise
328
320
  promise.cancel = () => {
329
321
  aborted = true;
@@ -15,6 +15,7 @@ declare const _default: {
15
15
  };
16
16
  comparators: string[];
17
17
  operators: string[];
18
+ dbRequestId: number;
18
19
  tags: string[];
19
20
  colors: {
20
21
  darkgrey: string;
@@ -52,6 +52,7 @@ export default {
52
52
  },
53
53
  comparators: [">=", "<=", ">", "<", "="],
54
54
  operators: ["+", "-", "/", "*"],
55
+ dbRequestId: 0,
55
56
  tags: ['a', 'abbr', 'address', 'area', 'article', 'aside', 'audio', 'b', 'base', 'bdi', 'bdo', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'cite', 'code', 'col', 'colgroup', 'data', 'datalist', 'dd', 'del', 'details', 'dfn', 'dialog', 'div', 'dl', 'dt', 'em', 'embed', 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'iframe', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'link', 'main', 'map', 'mark', 'menu', 'meta', 'meter', 'nav', 'noscript', 'object', 'ol', 'optgroup', 'option', 'output', 'p', 'param', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'script', 'section', 'select', 'slot', 'small', 'source', 'span', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'title', 'tr', 'track', 'u', 'ul', 'var', 'video', 'wbr'],
56
57
  colors: {
57
58
  darkgrey: '#5a6a62',
@@ -0,0 +1,3 @@
1
+ import { DbManager } from '../types.js';
2
+ declare const db: DbManager;
3
+ export default db;
@@ -0,0 +1,458 @@
1
+ import _ from '../../_.js';
2
+ import iterate from '../../fn/loop/iterate.js';
3
+ import log from '../../fn/browser/log.js';
4
+ import isObject from '../../fn/type/isObject.js';
5
+ import isArray from '../../fn/type/isArray.js';
6
+ import extend from '../../fn/object/extend.js';
7
+ import transactionDone from '../functions/transactionDone.js';
8
+ import getPrimaryKey from '../functions/getPrimaryKey.js';
9
+ import requestToPromise from '../functions/requestToPromise.js';
10
+ import transformResult from '../functions/transformResult.js';
11
+ import fieldsFromFilter from '../functions/fieldsFromFilter.js';
12
+ import bbnDbProxy from './Proxy.js';
13
+ const idb = globalThis.indexedDB ||
14
+ globalThis.webkitIndexedDB ||
15
+ globalThis.mozIndexedDB ||
16
+ globalThis.OIndexedDB ||
17
+ globalThis.msIndexedDB;
18
+ class DbObject {
19
+ dbName;
20
+ lastErr = null;
21
+ constructor(dbName) {
22
+ this.dbName = dbName;
23
+ }
24
+ hasMissingStores(database, conn) {
25
+ const structures = db._structures[database] || {};
26
+ return Object.keys(structures).some(storeName => !conn.objectStoreNames.contains(storeName));
27
+ }
28
+ async getConnection(database = '') {
29
+ let conn = db._connections[database || this.dbName];
30
+ if (!conn) {
31
+ debugger;
32
+ await db.open(database || this.dbName);
33
+ if (!db._connections[database || this.dbName]) {
34
+ throw new Error(_('The database %s is not open', this.dbName));
35
+ }
36
+ conn = db._connections[database || this.dbName];
37
+ }
38
+ return conn;
39
+ }
40
+ get structure() {
41
+ const structure = db._structures[this.dbName];
42
+ if (!structure) {
43
+ throw new Error(_('No structure defined for database %s', this.dbName));
44
+ }
45
+ return structure;
46
+ }
47
+ async getStore(table, mode) {
48
+ const connection = await this.getConnection();
49
+ if (!this.structure[table]) {
50
+ throw new Error(_('Table %s is not defined in database structure %s', table, this.dbName));
51
+ }
52
+ if (!connection.objectStoreNames.contains(table)) {
53
+ throw new Error(_('Table %s does not exist in database %s. Schema upgrade required.', table, this.dbName));
54
+ }
55
+ const tx = connection.transaction([table], mode);
56
+ tx.onabort = () => {
57
+ this.lastErr = tx.error;
58
+ log("Abort in store transaction: " + table, tx.error);
59
+ };
60
+ tx.onerror = () => {
61
+ this.lastErr = tx.error;
62
+ log("Error in store transaction: " + table, tx.error);
63
+ };
64
+ try {
65
+ const os = tx.objectStore(table);
66
+ return [tx, os];
67
+ }
68
+ catch (e) {
69
+ this.lastErr = e;
70
+ log("Error getting object store: " + table, e);
71
+ throw new Error(_('Error getting table %s from database %s', table, this.dbName));
72
+ }
73
+ }
74
+ lastError() {
75
+ return this.lastErr;
76
+ }
77
+ close() {
78
+ const conn = db._connections[this.dbName];
79
+ if (conn) {
80
+ conn.close();
81
+ delete db._connections[this.dbName];
82
+ }
83
+ }
84
+ async insert(table, data) {
85
+ const rows = Array.isArray(data) ? data : [data];
86
+ if (!rows.length) {
87
+ return 0;
88
+ }
89
+ const [tx, store] = await this.getStore(table, 'readwrite');
90
+ let inserted = 0;
91
+ for (const row of rows) {
92
+ const req = store.put(row);
93
+ req.onsuccess = () => {
94
+ inserted++;
95
+ };
96
+ req.onerror = () => {
97
+ this.lastErr = req.error;
98
+ log(req.error);
99
+ };
100
+ }
101
+ await transactionDone(tx);
102
+ return inserted;
103
+ }
104
+ async update(table, data, where, replace = false) {
105
+ const rows = await this.selectAll(table, [], where);
106
+ if (!rows.length) {
107
+ return 0;
108
+ }
109
+ const structure = this.structure[table];
110
+ const primary = getPrimaryKey(structure);
111
+ if (Array.isArray(primary)) {
112
+ throw new Error(_('Composite primary keys are not supported by this update implementation'));
113
+ }
114
+ const [tx, store] = await this.getStore(table, 'readwrite');
115
+ let updated = 0;
116
+ for (const row of rows) {
117
+ const nextRow = extend({}, replace ? { [primary]: row[primary] } : row, data);
118
+ if (!(primary in nextRow)) {
119
+ throw new Error(_('No primary key in the data'));
120
+ }
121
+ const req = store.put(nextRow);
122
+ req.onsuccess = () => {
123
+ updated++;
124
+ };
125
+ req.onerror = () => {
126
+ this.lastErr = req.error;
127
+ log(req.error);
128
+ };
129
+ }
130
+ await transactionDone(tx);
131
+ return updated;
132
+ }
133
+ async delete(table, where) {
134
+ const structure = this.structure[table];
135
+ const primary = getPrimaryKey(structure);
136
+ if (Array.isArray(primary)) {
137
+ throw new Error(_('Composite primary keys are not supported by this delete implementation'));
138
+ }
139
+ if (!(primary in where)) {
140
+ throw new Error(_('No primary key in the filter'));
141
+ }
142
+ const [tx, store] = await this.getStore(table, 'readwrite');
143
+ store.delete(where[primary]);
144
+ await transactionDone(tx);
145
+ return 1;
146
+ }
147
+ async selectOne(table, field, where = null, order = null, start = 0, limit = 1) {
148
+ const rows = await this.selectAll(table, [field], where, order, start, limit);
149
+ return rows?.[0]?.[field];
150
+ }
151
+ async select(table, fields = [], where = null, order = null, start = 0) {
152
+ const rows = await this.selectAll(table, fields, where, order, start, 1);
153
+ return rows.length ? rows[0] : null;
154
+ }
155
+ async selectAll(table, fields = [], where = null, order = null, start = 0, limit = null) {
156
+ void order;
157
+ const [tx, store] = await this.getStore(table, 'readonly');
158
+ const structure = this.structure[table];
159
+ const primary = getPrimaryKey(structure);
160
+ const results = [];
161
+ const searchField = isObject(where)
162
+ ? Object.keys(where)[0]
163
+ : (!where || isArray(where) ? null : primary);
164
+ if (!Array.isArray(primary) && searchField === primary) {
165
+ if (Array.isArray(where?.[primary])) {
166
+ const ids = where[primary];
167
+ const max = Math.min(ids.length - start, limit ?? ids.length);
168
+ const slice = ids.slice(start, start + max);
169
+ for (const id of slice) {
170
+ const row = await requestToPromise(store.get(id));
171
+ const transformed = transformResult(row, fields);
172
+ if (transformed) {
173
+ results.push(transformed);
174
+ }
175
+ }
176
+ await transactionDone(tx);
177
+ return results;
178
+ }
179
+ const key = isObject(where)
180
+ ? where[primary]
181
+ : where;
182
+ const row = await requestToPromise(store.get(key));
183
+ const transformed = transformResult(row, fields);
184
+ if (transformed) {
185
+ results.push(transformed);
186
+ }
187
+ await transactionDone(tx);
188
+ return results;
189
+ }
190
+ await new Promise((resolve, reject) => {
191
+ const req = store.openCursor();
192
+ let i = 0;
193
+ req.onsuccess = (e) => {
194
+ const cursor = e.target.result;
195
+ if (!cursor) {
196
+ resolve();
197
+ return;
198
+ }
199
+ const matches = !where || !globalThis.bbn?.fn?.search
200
+ ? true
201
+ : 0 === globalThis.bbn.fn.search([cursor.value], where);
202
+ if (matches) {
203
+ if (i >= start) {
204
+ const transformed = transformResult(cursor.value, fields);
205
+ if (transformed) {
206
+ results.push(transformed);
207
+ }
208
+ if (limit !== null && results.length >= limit) {
209
+ resolve();
210
+ return;
211
+ }
212
+ }
213
+ i++;
214
+ }
215
+ cursor.continue();
216
+ };
217
+ req.onerror = () => {
218
+ this.lastErr = req.error;
219
+ log(req.error);
220
+ reject(req.error);
221
+ };
222
+ });
223
+ await transactionDone(tx);
224
+ return results;
225
+ }
226
+ async getColumnValues(table, field, where = null, order = null, start = 0, limit = null) {
227
+ const rows = await this.selectAll(table, fieldsFromFilter(where, [field]), where, order, start, limit);
228
+ return rows
229
+ .map(row => row[field])
230
+ .filter(v => v !== undefined);
231
+ }
232
+ async copyTable(target, table, fields = [], where = null, order = null, start = 0, limit = null) {
233
+ if (!this.structure[table]) {
234
+ throw new Error(_('Source table %s does not exist in structure', table));
235
+ }
236
+ let connection = await this.getConnection();
237
+ if (!connection.objectStoreNames.contains(target)) {
238
+ connection.close();
239
+ await db.add(this.dbName, target, this.structure[table]);
240
+ connection = await this.getConnection();
241
+ }
242
+ if (!connection.objectStoreNames.contains(target)) {
243
+ throw new Error(_('The target table %s does not exist', target));
244
+ }
245
+ const rows = await this.selectAll(table, fields, where, order, start, limit);
246
+ if (!rows.length) {
247
+ return 0;
248
+ }
249
+ const res = await this.insert(target, rows);
250
+ connection.close();
251
+ return res;
252
+ }
253
+ async deleteTable(table) {
254
+ await db.remove(this.dbName, table);
255
+ return true;
256
+ }
257
+ }
258
+ const db = {
259
+ _structures: {},
260
+ _connections: {},
261
+ _objects: {},
262
+ _stores: {},
263
+ ok: idb !== undefined,
264
+ close(database) {
265
+ const conn = this._connections[database];
266
+ if (conn) {
267
+ conn.close();
268
+ delete this._connections[database];
269
+ }
270
+ if (this._objects[database]) {
271
+ delete this._objects[database];
272
+ }
273
+ },
274
+ updateStructure(storeName, structure, database) {
275
+ const primary = getPrimaryKey(structure);
276
+ if (!database.objectStoreNames.contains(storeName)) {
277
+ const store = database.createObjectStore(storeName, {
278
+ keyPath: primary
279
+ });
280
+ iterate(structure.keys, (a, n) => {
281
+ if (n !== 'PRIMARY') {
282
+ store.createIndex(n, a.columns.length > 1 ? a.columns : a.columns[0], { unique: !!a.unique });
283
+ }
284
+ });
285
+ }
286
+ },
287
+ async importStructure(database, structure) {
288
+ if (!this._structures[database]) {
289
+ this._structures[database] = {};
290
+ }
291
+ if (this._connections[database]) {
292
+ throw new Error(_('Database %s is already open. Close it before adding a new store.', database));
293
+ }
294
+ await this.open(database);
295
+ const conn = this._connections[database];
296
+ let upgrade = false;
297
+ for (const storeName in structure) {
298
+ if (!structure[storeName]?.keys?.PRIMARY || !structure[storeName]?.fields) {
299
+ log("Error on importStructure with store " + storeName, structure[storeName]);
300
+ throw new Error(_('The database structure for %s is not valid (missing keys, fields, or primary key)', storeName));
301
+ }
302
+ this._structures[database][storeName] = structure[storeName];
303
+ // DB exists already: open temporarily and check if store exists
304
+ const hasStore = conn.objectStoreNames.contains(storeName);
305
+ if (!hasStore) {
306
+ upgrade = true;
307
+ }
308
+ }
309
+ if (upgrade) {
310
+ await this.reopenWithUpgrade(database);
311
+ }
312
+ this.close(database);
313
+ },
314
+ async getExistingVersion(name) {
315
+ const live = this._connections[name];
316
+ if (live) {
317
+ return live.version;
318
+ }
319
+ return new Promise((resolve, reject) => {
320
+ if (!idb) {
321
+ reject(new Error(_('IndexedDB is not available')));
322
+ return;
323
+ }
324
+ const req = idb.open(name);
325
+ req.onsuccess = () => {
326
+ const database = req.result;
327
+ const version = database.version;
328
+ database.close();
329
+ resolve(version);
330
+ };
331
+ req.onupgradeneeded = () => {
332
+ // Database did not exist before; this open created it temporarily.
333
+ // Version is therefore effectively 1.
334
+ const database = req.result;
335
+ const version = database.version;
336
+ database.close();
337
+ resolve(version);
338
+ };
339
+ req.onerror = () => reject(req.error);
340
+ });
341
+ },
342
+ async reopenWithUpgrade(name) {
343
+ if (!this._structures[name]) {
344
+ throw new Error(_('Impossible to find a structure for the database %s', name));
345
+ }
346
+ const existingVersion = await this.getExistingVersion(name);
347
+ const nextVersion = existingVersion + 1;
348
+ log(_('Going from version %s to version %s for database %s', existingVersion, nextVersion, name));
349
+ if (this._connections[name]) {
350
+ this.close(name);
351
+ }
352
+ return await this.open(name, nextVersion);
353
+ },
354
+ async open(database, version) {
355
+ if (!idb) {
356
+ throw new Error(_('IndexedDB is not available'));
357
+ }
358
+ if (navigator.serviceWorker?.controller) {
359
+ return new Promise(resolve => resolve(new bbnDbProxy(database)));
360
+ }
361
+ if (!this._structures[database]) {
362
+ throw new Error(_('Impossible to find a structure for the database %s', database));
363
+ }
364
+ if (this._objects[database] && this._connections[database]) {
365
+ return this._objects[database];
366
+ }
367
+ await new Promise((resolve, reject) => {
368
+ const req = version ? idb.open(database, version) : idb.open(database);
369
+ req.onupgradeneeded = () => {
370
+ const db = req.result;
371
+ this._connections[database] = req.result;
372
+ const dbStructure = this._structures[database] || {};
373
+ iterate(dbStructure, (structure, storeName) => {
374
+ if (!db.objectStoreNames.contains(storeName)) {
375
+ this.updateStructure(storeName, structure, db);
376
+ }
377
+ });
378
+ };
379
+ req.onsuccess = () => {
380
+ this._connections[database] = req.result;
381
+ resolve(req.result);
382
+ };
383
+ req.onerror = () => reject(req.error);
384
+ req.onblocked = (event) => {
385
+ reject(_('open: Upgrade blocked for database %s. Please close other connections to proceed.', database));
386
+ };
387
+ });
388
+ this._objects[database] = new DbObject(database);
389
+ return this._objects[database];
390
+ },
391
+ async add(database, name, structure) {
392
+ if (!structure?.keys?.PRIMARY || !structure?.fields) {
393
+ log("Error on add", structure);
394
+ throw new Error(_('The database structure for %s is not valid (missing keys, fields, or primary key)', name));
395
+ }
396
+ if (!this._structures[database]) {
397
+ this._structures[database] = {};
398
+ }
399
+ this._structures[database][name] = structure;
400
+ if (this._connections[database]) {
401
+ throw new Error(_('Database %s is already open. Close it before adding a new store.', database));
402
+ }
403
+ await this.open(database);
404
+ let conn = this._connections[database];
405
+ // DB exists already: open temporarily and check if store exists
406
+ const hasStore = conn.objectStoreNames.contains(name);
407
+ if (!hasStore) {
408
+ const tmp = await this.reopenWithUpgrade(database);
409
+ if (!this._connections[database]) {
410
+ throw new Error(_('The database %s is not open after upgrade', database));
411
+ }
412
+ conn = this._connections[database];
413
+ }
414
+ this.close(database);
415
+ return true;
416
+ },
417
+ async remove(database, name) {
418
+ const currentStructure = this._structures[database];
419
+ if (!currentStructure?.[name]) {
420
+ return;
421
+ }
422
+ const old = currentStructure[name];
423
+ delete currentStructure[name];
424
+ try {
425
+ const conn = this._connections[database];
426
+ if (!conn) {
427
+ return;
428
+ }
429
+ const nextVersion = conn.version + 1;
430
+ this.close(database);
431
+ await new Promise((resolve, reject) => {
432
+ if (!idb) {
433
+ reject(new Error(_('IndexedDB is not available')));
434
+ return;
435
+ }
436
+ const req = idb.open(database, nextVersion);
437
+ req.onupgradeneeded = () => {
438
+ const dbInstance = req.result;
439
+ if (dbInstance.objectStoreNames.contains(name)) {
440
+ dbInstance.deleteObjectStore(name);
441
+ dbInstance.close();
442
+ }
443
+ };
444
+ req.onsuccess = () => {
445
+ req.result.close();
446
+ resolve();
447
+ };
448
+ req.onerror = () => reject(req.error);
449
+ req.onblocked = () => reject(new Error(_('Remove blocked for database %s', database)));
450
+ });
451
+ }
452
+ catch (e) {
453
+ currentStructure[name] = old;
454
+ throw e;
455
+ }
456
+ }
457
+ };
458
+ export default db;
@@ -0,0 +1,18 @@
1
+ import { IDbApi, OrderClause } from '../types.js';
2
+ export default class bbnDbObjectProxy implements IDbApi {
3
+ private readonly _dbName;
4
+ lastErr: any;
5
+ constructor(dbName: string);
6
+ get dbName(): string;
7
+ insert(table: string, data: any): Promise<number>;
8
+ update(table: string, data: any, where: any, replace?: boolean): Promise<number>;
9
+ delete(table: string, where: any): Promise<number>;
10
+ selectOne(table: string, field: string, where?: unknown, order?: OrderClause, start?: number, limit?: number): Promise<unknown>;
11
+ select(table: string, fields?: string[], where?: unknown, order?: OrderClause, start?: number): Promise<unknown>;
12
+ selectAll(table: string, fields?: string[], where?: unknown, order?: OrderClause, start?: number, limit?: number | null): Promise<unknown>;
13
+ getColumnValues(table: string, field: string, where?: unknown, order?: OrderClause, start?: number, limit?: number | null): Promise<unknown>;
14
+ copyTable(target: string, table: string, fields?: string[], where?: unknown, order?: OrderClause, start?: number, limit?: number | null): Promise<number>;
15
+ deleteTable(table: string): Promise<boolean>;
16
+ close(): void;
17
+ lastError(): unknown;
18
+ }
@@ -0,0 +1,77 @@
1
+ let requestId = 0;
2
+ function callServiceWorker(obj, method, args) {
3
+ return new Promise((resolve, reject) => {
4
+ if (!navigator.serviceWorker?.controller) {
5
+ reject(new Error('No active service worker controller'));
6
+ return;
7
+ }
8
+ const id = ++requestId;
9
+ const channel = new MessageChannel();
10
+ channel.port1.onmessage = event => {
11
+ const msg = event.data;
12
+ if (msg?.id !== id) {
13
+ return;
14
+ }
15
+ if (msg.error) {
16
+ obj.lastErr = msg.error;
17
+ }
18
+ if (msg.ok) {
19
+ resolve(msg.result);
20
+ }
21
+ else {
22
+ reject(new Error(msg.error || 'IndexedDB service worker error'));
23
+ }
24
+ channel.port1.close();
25
+ };
26
+ const command = {
27
+ id,
28
+ database: obj.dbName,
29
+ method,
30
+ args
31
+ };
32
+ navigator.serviceWorker.controller.postMessage(command, [channel.port2]);
33
+ });
34
+ }
35
+ export default class bbnDbObjectProxy {
36
+ _dbName;
37
+ lastErr = null;
38
+ constructor(dbName) {
39
+ this._dbName = dbName;
40
+ }
41
+ get dbName() {
42
+ return this._dbName;
43
+ }
44
+ insert(table, data) {
45
+ return callServiceWorker(this, 'insert', [table, data]);
46
+ }
47
+ update(table, data, where, replace = false) {
48
+ return callServiceWorker(this, 'update', [table, data, where, replace]);
49
+ }
50
+ delete(table, where) {
51
+ return callServiceWorker(this, 'delete', [table, where]);
52
+ }
53
+ selectOne(table, field, where, order, start, limit) {
54
+ return callServiceWorker(this, 'selectOne', [table, field, where, order, start, limit]);
55
+ }
56
+ select(table, fields, where, order, start) {
57
+ return callServiceWorker(this, 'select', [table, fields, where, order, start]);
58
+ }
59
+ selectAll(table, fields, where, order, start, limit) {
60
+ return callServiceWorker(this, 'selectAll', [table, fields, where, order, start, limit]);
61
+ }
62
+ getColumnValues(table, field, where, order, start, limit) {
63
+ return callServiceWorker(this, 'getColumnValues', [table, field, where, order, start, limit]);
64
+ }
65
+ copyTable(target, table, fields, where, order, start, limit) {
66
+ return callServiceWorker(this, 'copyTable', [target, table, fields, where, order, start, limit]);
67
+ }
68
+ deleteTable(table) {
69
+ return callServiceWorker(this, 'deleteTable', [table]);
70
+ }
71
+ close() {
72
+ void callServiceWorker(this, 'close', []);
73
+ }
74
+ lastError() {
75
+ return this.lastErr;
76
+ }
77
+ }
@@ -0,0 +1,17 @@
1
+ import { IDbApi, DbStructure } from '../types.js';
2
+ declare const bbnDbProxy: {
3
+ _structures: {};
4
+ _connections: {};
5
+ _objects: {};
6
+ _stores: {};
7
+ ok: boolean;
8
+ open(database: string, version?: number): Promise<IDbApi>;
9
+ add(database: string, name: string, structure: DbStructure): Promise<boolean>;
10
+ remove(database: string, name: string): Promise<void>;
11
+ importStructure(database: string, structure: Record<string, DbStructure>): Promise<void>;
12
+ reopenWithUpgrade(database: string): Promise<IDbApi>;
13
+ getExistingVersion(database: string): Promise<number>;
14
+ close(database: string): void;
15
+ updateStructure(): never;
16
+ };
17
+ export default bbnDbProxy;