@bbn/bbn 2.0.237 → 2.0.239

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 (86) 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/Center.d.ts +10 -0
  10. package/dist/db/classes/Center.js +88 -0
  11. package/dist/db/classes/Object.d.ts +3 -0
  12. package/dist/db/classes/Object.js +469 -0
  13. package/dist/db/classes/ObjectProxy.d.ts +18 -0
  14. package/dist/db/classes/ObjectProxy.js +79 -0
  15. package/dist/db/classes/Proxy.d.ts +10 -0
  16. package/dist/db/classes/Proxy.js +52 -0
  17. package/dist/db/functions/fieldsFromFilter.d.ts +1 -0
  18. package/dist/db/functions/fieldsFromFilter.js +33 -0
  19. package/dist/db/functions/getPrimaryKey.d.ts +2 -0
  20. package/dist/db/functions/getPrimaryKey.js +8 -0
  21. package/dist/db/functions/requestToPromise.d.ts +1 -0
  22. package/dist/db/functions/requestToPromise.js +6 -0
  23. package/dist/db/functions/transactionDone.d.ts +1 -0
  24. package/dist/db/functions/transactionDone.js +7 -0
  25. package/dist/db/functions/transformResult.d.ts +1 -0
  26. package/dist/db/functions/transformResult.js +16 -0
  27. package/dist/db/types.d.ts +56 -0
  28. package/dist/db/types.js +1 -0
  29. package/dist/db.d.ts +3 -66
  30. package/dist/db.js +3 -558
  31. package/dist/dt/classes/{date.d.ts → Date.d.ts} +1 -1
  32. package/dist/dt/classes/{date.js → Date.js} +8 -3
  33. package/dist/dt/classes/{dateTime.d.ts → DateTime.d.ts} +1 -1
  34. package/dist/dt/classes/{dateTime.js → DateTime.js} +9 -3
  35. package/dist/dt/classes/{dt.d.ts → Dt.d.ts} +1 -1
  36. package/dist/dt/classes/{dt.js → Dt.js} +36 -48
  37. package/dist/dt/classes/Duration.js +193 -0
  38. package/dist/dt/classes/{monthDay.d.ts → MonthDay.d.ts} +1 -1
  39. package/dist/dt/classes/{monthDay.js → MonthDay.js} +2 -2
  40. package/dist/dt/classes/{time.d.ts → Time.d.ts} +1 -1
  41. package/dist/dt/classes/{time.js → Time.js} +2 -2
  42. package/dist/dt/classes/{yearMonth.d.ts → YearMonth.d.ts} +1 -1
  43. package/dist/dt/classes/{yearMonth.js → YearMonth.js} +2 -2
  44. package/dist/dt/classes/{zoned.d.ts → Zoned.d.ts} +1 -1
  45. package/dist/dt/classes/{zoned.js → Zoned.js} +9 -3
  46. package/dist/dt/functions/buildLocaleFromIntl.js +1 -1
  47. package/dist/dt/functions/guessFormat.js +1 -1
  48. package/dist/dt/functions/intl-weekinfo-polyfill.js +11 -4
  49. package/dist/dt/functions/parse.js +20 -22
  50. package/dist/dt/functions/setupIntl.js +33 -44
  51. package/dist/dt.d.ts +1 -1
  52. package/dist/dt.js +9 -8
  53. package/dist/env.d.ts +1 -1
  54. package/dist/env.js +6 -7
  55. package/dist/fn/ajax/abort.js +1 -1
  56. package/dist/fn/ajax/ajax.d.ts +1 -1
  57. package/dist/fn/ajax/ajax.js +2 -2
  58. package/dist/fn/ajax/download.js +1 -1
  59. package/dist/fn/ajax/getFileContent.js +2 -3
  60. package/dist/fn/ajax/link.js +2 -2
  61. package/dist/fn/ajax/stream.js +5 -4
  62. package/dist/fn/browser/executeSlowly.js +11 -22
  63. package/dist/fn/browser/log.js +5 -3
  64. package/dist/fn/browser/yieldToBrowser.js +7 -19
  65. package/dist/fn/html/makeReactive.js +2 -2
  66. package/dist/fn/init.d.ts +1 -1
  67. package/dist/fn/init.js +4 -5
  68. package/dist/fn/object/_filter.js +3 -2
  69. package/dist/fn/object/filter.js +2 -1
  70. package/dist/fn/phone/fphone.js +1 -2
  71. package/dist/fn/string/cast.js +1 -2
  72. package/dist/fn/string/shorten.js +1 -1
  73. package/dist/fn/string/treatForHash.js +1 -1
  74. package/dist/fn/style/addColors.d.ts +1 -1
  75. package/dist/fn/style/addColors.js +4 -4
  76. package/dist/fn/type/checkType.js +1 -2
  77. package/dist/fn/type/isCp.js +1 -2
  78. package/dist/fn/type/isHostname.js +1 -1
  79. package/dist/fn/type/isIP.js +1 -1
  80. package/dist/fn/type/isSame.js +2 -2
  81. package/dist/fn/type/isURL.js +1 -1
  82. package/dist/index-no-dep.js +11 -4
  83. package/dist/index.js +13 -4
  84. package/package.json +1 -1
  85. package/dist/dt/classes/duration.js +0 -199
  86. /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,10 @@
1
+ export default class bbnDbCenter {
2
+ private queues;
3
+ handleMessage(event: MessageEvent): Promise<boolean>;
4
+ private execute;
5
+ private executeManager;
6
+ private executeObject;
7
+ private postSuccess;
8
+ private postError;
9
+ private serializeError;
10
+ }
@@ -0,0 +1,88 @@
1
+ import bbnDb from './Object.js';
2
+ export default class bbnDbCenter {
3
+ queues = {};
4
+ async handleMessage(event) {
5
+ const command = event.data;
6
+ const port = event.ports?.[0];
7
+ bbn.fn.log("Received DB command:", command, port, "END");
8
+ if (!port || !command?.method?.includes('.')) {
9
+ return false;
10
+ }
11
+ const [scope] = command.method.split('.');
12
+ if (scope !== 'manager' && scope !== 'object') {
13
+ return false;
14
+ }
15
+ if (!command.id || !command.database) {
16
+ this.postError(port, command?.id ?? 0, 'Invalid DB command');
17
+ return true;
18
+ }
19
+ const database = command.database;
20
+ this.queues[database] = (this.queues[database] || Promise.resolve())
21
+ .catch(() => undefined)
22
+ .then(async () => {
23
+ try {
24
+ const result = await this.execute(command);
25
+ this.postSuccess(port, command.id, result);
26
+ }
27
+ catch (error) {
28
+ this.postError(port, command.id, this.serializeError(error));
29
+ }
30
+ });
31
+ return true;
32
+ }
33
+ async execute(command) {
34
+ const [scope, action] = command.method.split('.');
35
+ if (!action) {
36
+ throw new Error(`Invalid DB method: ${command.method}`);
37
+ }
38
+ if (scope === 'manager') {
39
+ return this.executeManager(action, command.args || []);
40
+ }
41
+ if (scope === 'object') {
42
+ return this.executeObject(action, [command.database, ...(command.args || [])]);
43
+ }
44
+ throw new Error(`Unknown DB command scope: ${scope}`);
45
+ }
46
+ async executeManager(action, args) {
47
+ const fn = bbnDb[action];
48
+ if (typeof fn !== 'function') {
49
+ throw new Error(`Unknown DB manager method: ${action}`);
50
+ }
51
+ return fn.apply(bbnDb, args);
52
+ }
53
+ async executeObject(action, args) {
54
+ const [database, ...methodArgs] = args;
55
+ if (!database) {
56
+ throw new Error('Missing database name for object method');
57
+ }
58
+ bbn.fn.log(`Executing object method: ${action} on database: ${database}`);
59
+ const dbo = await bbnDb.open(database);
60
+ const fn = dbo[action];
61
+ if (typeof fn !== 'function') {
62
+ throw new Error(`Unknown DB object method: ${action}`);
63
+ }
64
+ return fn.apply(dbo, methodArgs);
65
+ }
66
+ postSuccess(port, id, result) {
67
+ port.postMessage({
68
+ id,
69
+ ok: true,
70
+ result
71
+ });
72
+ port.close();
73
+ }
74
+ postError(port, id, error) {
75
+ port.postMessage({
76
+ id,
77
+ ok: false,
78
+ error
79
+ });
80
+ port.close();
81
+ }
82
+ serializeError(error) {
83
+ if (error instanceof Error) {
84
+ return error.message;
85
+ }
86
+ return String(error);
87
+ }
88
+ }
@@ -0,0 +1,3 @@
1
+ import { DbManagerInternal } from '../types.js';
2
+ declare const db: DbManagerInternal;
3
+ export default db;
@@ -0,0 +1,469 @@
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 bbnDbObjectProxy from './ObjectProxy.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
+ get dbName() {
25
+ return this._dbName;
26
+ }
27
+ hasMissingStores(database, conn) {
28
+ const structures = db._structures[database] || {};
29
+ return Object.keys(structures).some(storeName => !conn.objectStoreNames.contains(storeName));
30
+ }
31
+ async getConnection(database = '') {
32
+ let conn = db._connections[database || this.dbName];
33
+ if (!conn) {
34
+ debugger;
35
+ log("Opening connection through getConnection to database: " + (database || this.dbName));
36
+ await db.open(database || this.dbName);
37
+ if (!db._connections[database || this.dbName]) {
38
+ throw new Error(_('The database %s is not open', this.dbName));
39
+ }
40
+ conn = db._connections[database || this.dbName];
41
+ }
42
+ return conn;
43
+ }
44
+ get structure() {
45
+ const structure = db._structures[this.dbName];
46
+ if (!structure) {
47
+ throw new Error(_('No structure defined for database %s', this.dbName));
48
+ }
49
+ return structure;
50
+ }
51
+ async getStore(table, mode) {
52
+ const connection = await this.getConnection();
53
+ if (!this.structure[table]) {
54
+ log("No structure for table: " + table, this.structure, db._structures);
55
+ throw new Error(_('Table %s is not defined in database structure %s', table, this.dbName));
56
+ }
57
+ if (!connection.objectStoreNames.contains(table)) {
58
+ throw new Error(_('Table %s does not exist in database %s. Schema upgrade required.', table, this.dbName));
59
+ }
60
+ const tx = connection.transaction([table], mode);
61
+ tx.onabort = () => {
62
+ this.lastErr = tx.error;
63
+ log("Abort in store transaction: " + table, tx.error);
64
+ };
65
+ tx.onerror = () => {
66
+ this.lastErr = tx.error;
67
+ log("Error in store transaction: " + table, tx.error);
68
+ };
69
+ try {
70
+ const os = tx.objectStore(table);
71
+ return [tx, os];
72
+ }
73
+ catch (e) {
74
+ this.lastErr = e;
75
+ log("Error getting object store: " + table, e);
76
+ throw new Error(_('Error getting table %s from database %s', table, this.dbName));
77
+ }
78
+ }
79
+ lastError() {
80
+ return this.lastErr;
81
+ }
82
+ close() {
83
+ return db.close(this.dbName);
84
+ }
85
+ async insert(table, data) {
86
+ const rows = Array.isArray(data) ? data : [data];
87
+ if (!rows.length) {
88
+ return 0;
89
+ }
90
+ const [tx, store] = await this.getStore(table, 'readwrite');
91
+ let inserted = 0;
92
+ for (const row of rows) {
93
+ const req = store.put(row);
94
+ req.onsuccess = () => {
95
+ inserted++;
96
+ };
97
+ req.onerror = () => {
98
+ this.lastErr = req.error;
99
+ log(req.error);
100
+ };
101
+ }
102
+ await transactionDone(tx);
103
+ return inserted;
104
+ }
105
+ async update(table, data, where, replace = false) {
106
+ const rows = await this.selectAll(table, [], where);
107
+ if (!rows.length) {
108
+ return 0;
109
+ }
110
+ const structure = this.structure[table];
111
+ const primary = getPrimaryKey(structure);
112
+ if (Array.isArray(primary)) {
113
+ throw new Error(_('Composite primary keys are not supported by this update implementation'));
114
+ }
115
+ const [tx, store] = await this.getStore(table, 'readwrite');
116
+ let updated = 0;
117
+ for (const row of rows) {
118
+ const nextRow = extend({}, replace ? { [primary]: row[primary] } : row, data);
119
+ if (!(primary in nextRow)) {
120
+ throw new Error(_('No primary key in the data'));
121
+ }
122
+ const req = store.put(nextRow);
123
+ req.onsuccess = () => {
124
+ updated++;
125
+ };
126
+ req.onerror = () => {
127
+ this.lastErr = req.error;
128
+ log(req.error);
129
+ };
130
+ }
131
+ await transactionDone(tx);
132
+ return updated;
133
+ }
134
+ async delete(table, where) {
135
+ const structure = this.structure[table];
136
+ const primary = getPrimaryKey(structure);
137
+ if (Array.isArray(primary)) {
138
+ throw new Error(_('Composite primary keys are not supported by this delete implementation'));
139
+ }
140
+ if (!(primary in where)) {
141
+ throw new Error(_('No primary key in the filter'));
142
+ }
143
+ const [tx, store] = await this.getStore(table, 'readwrite');
144
+ store.delete(where[primary]);
145
+ await transactionDone(tx);
146
+ return 1;
147
+ }
148
+ async selectOne(table, field, where = null, order = null, start = 0, limit = 1) {
149
+ const rows = await this.selectAll(table, [field], where, order, start, limit);
150
+ return rows?.[0]?.[field];
151
+ }
152
+ async select(table, fields = [], where = null, order = null, start = 0) {
153
+ const rows = await this.selectAll(table, fields, where, order, start, 1);
154
+ return rows.length ? rows[0] : null;
155
+ }
156
+ async selectAll(table, fields = [], where = null, order = null, start = 0, limit = null) {
157
+ void order;
158
+ const [tx, store] = await this.getStore(table, 'readonly');
159
+ const structure = this.structure[table];
160
+ const primary = getPrimaryKey(structure);
161
+ const results = [];
162
+ const searchField = isObject(where)
163
+ ? Object.keys(where)[0]
164
+ : (!where || isArray(where) ? null : primary);
165
+ if (!Array.isArray(primary) && searchField === primary) {
166
+ if (Array.isArray(where?.[primary])) {
167
+ const ids = where[primary];
168
+ const max = Math.min(ids.length - start, limit ?? ids.length);
169
+ const slice = ids.slice(start, start + max);
170
+ for (const id of slice) {
171
+ const row = await requestToPromise(store.get(id));
172
+ const transformed = transformResult(row, fields);
173
+ if (transformed) {
174
+ results.push(transformed);
175
+ }
176
+ }
177
+ await transactionDone(tx);
178
+ return results;
179
+ }
180
+ const key = isObject(where)
181
+ ? where[primary]
182
+ : where;
183
+ const row = await requestToPromise(store.get(key));
184
+ const transformed = transformResult(row, fields);
185
+ if (transformed) {
186
+ results.push(transformed);
187
+ }
188
+ await transactionDone(tx);
189
+ return results;
190
+ }
191
+ await new Promise((resolve, reject) => {
192
+ const req = store.openCursor();
193
+ let i = 0;
194
+ req.onsuccess = (e) => {
195
+ const cursor = e.target.result;
196
+ if (!cursor) {
197
+ resolve();
198
+ return;
199
+ }
200
+ const matches = !where || !globalThis.bbn?.fn?.search
201
+ ? true
202
+ : 0 === globalThis.bbn.fn.search([cursor.value], where);
203
+ if (matches) {
204
+ if (i >= start) {
205
+ const transformed = transformResult(cursor.value, fields);
206
+ if (transformed) {
207
+ results.push(transformed);
208
+ }
209
+ if (limit !== null && results.length >= limit) {
210
+ resolve();
211
+ return;
212
+ }
213
+ }
214
+ i++;
215
+ }
216
+ cursor.continue();
217
+ };
218
+ req.onerror = () => {
219
+ this.lastErr = req.error;
220
+ log(req.error);
221
+ reject(req.error);
222
+ };
223
+ });
224
+ await transactionDone(tx);
225
+ return results;
226
+ }
227
+ async getColumnValues(table, field, where = null, order = null, start = 0, limit = null) {
228
+ const rows = await this.selectAll(table, fieldsFromFilter(where, [field]), where, order, start, limit);
229
+ return rows
230
+ .map(row => row[field])
231
+ .filter(v => v !== undefined);
232
+ }
233
+ async copyTable(target, table, fields = [], where = null, order = null, start = 0, limit = null) {
234
+ if (!this.structure[table]) {
235
+ throw new Error(_('Source table %s does not exist in structure', table));
236
+ }
237
+ let connection = await this.getConnection();
238
+ if (!connection.objectStoreNames.contains(target)) {
239
+ connection.close();
240
+ await db.add(this.dbName, target, this.structure[table]);
241
+ connection = await this.getConnection();
242
+ }
243
+ if (!connection.objectStoreNames.contains(target)) {
244
+ throw new Error(_('The target table %s does not exist', target));
245
+ }
246
+ const rows = await this.selectAll(table, fields, where, order, start, limit);
247
+ if (!rows.length) {
248
+ return 0;
249
+ }
250
+ const res = await this.insert(target, rows);
251
+ connection.close();
252
+ return res;
253
+ }
254
+ async deleteTable(table) {
255
+ await db.remove(this.dbName, table);
256
+ return true;
257
+ }
258
+ }
259
+ const db = {
260
+ _structures: {},
261
+ _connections: {},
262
+ _objects: {},
263
+ _stores: {},
264
+ ok: idb !== undefined,
265
+ close(database) {
266
+ const conn = this._connections[database];
267
+ if (conn) {
268
+ conn.close();
269
+ delete this._connections[database];
270
+ log("Closing connection to database: " + database);
271
+ }
272
+ if (this._objects[database]) {
273
+ delete this._objects[database];
274
+ }
275
+ },
276
+ updateStructure(storeName, structure, database) {
277
+ log("fn updateStructure, table: " + storeName + ", in SW? " + !globalThis.document);
278
+ const primary = getPrimaryKey(structure);
279
+ if (!database.objectStoreNames.contains(storeName)) {
280
+ const store = database.createObjectStore(storeName, {
281
+ keyPath: primary
282
+ });
283
+ iterate(structure.keys, (a, n) => {
284
+ if (n !== 'PRIMARY') {
285
+ store.createIndex(n, a.columns.length > 1 ? a.columns : a.columns[0], { unique: !!a.unique });
286
+ }
287
+ });
288
+ }
289
+ },
290
+ async importStructure(database, structure) {
291
+ log("fn importStructure");
292
+ if (!this._structures[database]) {
293
+ this._structures[database] = {};
294
+ }
295
+ if (!this._connections[database]) {
296
+ await this.open(database);
297
+ }
298
+ const conn = this._connections[database];
299
+ let upgrade = false;
300
+ for (const storeName in structure) {
301
+ if (!structure[storeName]?.keys?.PRIMARY || !structure[storeName]?.fields) {
302
+ log("Error on importStructure with store " + storeName, structure[storeName]);
303
+ throw new Error(_('The database structure for %s is not valid (missing keys, fields, or primary key)', storeName));
304
+ }
305
+ this._structures[database][storeName] = structure[storeName];
306
+ // DB exists already: open temporarily and check if store exists
307
+ const hasStore = conn.objectStoreNames.contains(storeName);
308
+ if (!hasStore) {
309
+ upgrade = true;
310
+ }
311
+ }
312
+ if (upgrade) {
313
+ await this.reopenWithUpgrade(database);
314
+ }
315
+ this.close(database);
316
+ },
317
+ async getExistingVersion(name) {
318
+ const live = this._connections[name];
319
+ if (live) {
320
+ return live.version;
321
+ }
322
+ return new Promise((resolve, reject) => {
323
+ if (!idb) {
324
+ reject(new Error(_('IndexedDB is not available')));
325
+ return;
326
+ }
327
+ const req = idb.open(name);
328
+ req.onsuccess = () => {
329
+ const database = req.result;
330
+ const version = database.version;
331
+ database.close();
332
+ resolve(version);
333
+ };
334
+ req.onupgradeneeded = () => {
335
+ // Database did not exist before; this open created it temporarily.
336
+ // Version is therefore effectively 1.
337
+ const database = req.result;
338
+ const version = database.version;
339
+ database.close();
340
+ resolve(version);
341
+ };
342
+ req.onerror = () => reject(req.error);
343
+ });
344
+ },
345
+ async reopenWithUpgrade(name) {
346
+ log("fn reopenWithUpgrade", this._structures);
347
+ if (!this._structures[name]) {
348
+ throw new Error(_('Impossible to find a structure for the database %s', name));
349
+ }
350
+ const existingVersion = await this.getExistingVersion(name) || 0;
351
+ const nextVersion = existingVersion + 1;
352
+ log(_('Going from version %s to version %s for database %s', existingVersion, nextVersion, name));
353
+ if (this._connections[name]) {
354
+ this.close(name);
355
+ }
356
+ return await this.open(name, nextVersion);
357
+ },
358
+ async open(database, version) {
359
+ log("fn open database " + database + " version " + version, this._structures);
360
+ if (database !== 'bbn') {
361
+ log("THERE SHOULD BE A DEBUGGER!!!!!!!");
362
+ debugger;
363
+ }
364
+ if (!idb) {
365
+ throw new Error(_('IndexedDB is not available'));
366
+ }
367
+ if (navigator.serviceWorker?.controller) {
368
+ return new Promise(resolve => resolve(new bbnDbObjectProxy(database)));
369
+ }
370
+ if (!this._structures[database]) {
371
+ throw new Error(_('Impossible to find a structure for the database %s', database));
372
+ }
373
+ if (this._objects[database] && this._connections[database]) {
374
+ return this._objects[database];
375
+ }
376
+ await new Promise((resolve, reject) => {
377
+ const req = version ? idb.open(database, version) : idb.open(database);
378
+ req.onupgradeneeded = () => {
379
+ const db = req.result;
380
+ this._connections[database] = req.result;
381
+ const dbStructure = this._structures[database] || {};
382
+ iterate(dbStructure, (structure, storeName) => {
383
+ if (!db.objectStoreNames.contains(storeName)) {
384
+ this.updateStructure(storeName, structure, db);
385
+ }
386
+ });
387
+ };
388
+ req.onsuccess = () => {
389
+ this._connections[database] = req.result;
390
+ resolve(req.result);
391
+ };
392
+ req.onerror = () => reject(req.error);
393
+ req.onblocked = (event) => {
394
+ reject(_('open: Upgrade blocked for database %s. Please close other connections to proceed.', database));
395
+ };
396
+ });
397
+ this._objects[database] = new DbObject(database);
398
+ return this._objects[database];
399
+ },
400
+ async add(database, name, structure) {
401
+ log("fn add " + name);
402
+ if (!structure?.keys?.PRIMARY || !structure?.fields) {
403
+ log("Error on add", structure);
404
+ throw new Error(_('The database structure for %s is not valid (missing keys, fields, or primary key)', name));
405
+ }
406
+ if (!this._structures[database]) {
407
+ this._structures[database] = {};
408
+ }
409
+ this._structures[database][name] = structure;
410
+ if (this._connections[database]) {
411
+ throw new Error(_('Database %s is already open. Close it before adding a new store.', database));
412
+ }
413
+ await this.open(database);
414
+ let conn = this._connections[database];
415
+ // DB exists already: open temporarily and check if store exists
416
+ const hasStore = conn.objectStoreNames.contains(name);
417
+ if (!hasStore) {
418
+ const tmp = await this.reopenWithUpgrade(database);
419
+ if (!this._connections[database]) {
420
+ throw new Error(_('The database %s is not open after upgrade', database));
421
+ }
422
+ conn = this._connections[database];
423
+ }
424
+ this.close(database);
425
+ return true;
426
+ },
427
+ async remove(database, name) {
428
+ log("fn remove");
429
+ const currentStructure = this._structures[database];
430
+ if (!currentStructure?.[name]) {
431
+ return;
432
+ }
433
+ const old = currentStructure[name];
434
+ delete currentStructure[name];
435
+ try {
436
+ const conn = this._connections[database];
437
+ if (!conn) {
438
+ return;
439
+ }
440
+ const nextVersion = conn.version + 1;
441
+ this.close(database);
442
+ await new Promise((resolve, reject) => {
443
+ if (!idb) {
444
+ reject(new Error(_('IndexedDB is not available')));
445
+ return;
446
+ }
447
+ const req = idb.open(database, nextVersion);
448
+ req.onupgradeneeded = () => {
449
+ const dbInstance = req.result;
450
+ if (dbInstance.objectStoreNames.contains(name)) {
451
+ dbInstance.deleteObjectStore(name);
452
+ dbInstance.close();
453
+ }
454
+ };
455
+ req.onsuccess = () => {
456
+ req.result.close();
457
+ resolve();
458
+ };
459
+ req.onerror = () => reject(req.error);
460
+ req.onblocked = () => reject(new Error(_('Remove blocked for database %s', database)));
461
+ });
462
+ }
463
+ catch (e) {
464
+ currentStructure[name] = old;
465
+ throw e;
466
+ }
467
+ }
468
+ };
469
+ export default db;