@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.
- package/dist/$.js +1 -1
- package/dist/bbn.js +3 -3
- package/dist/bbn.js.map +1 -1
- package/dist/bbn.sw.js +3 -3
- package/dist/bbn.sw.js.map +1 -1
- package/dist/com.js +13 -21
- package/dist/{vars.d.ts → data.d.ts} +1 -0
- package/dist/{vars.js → data.js} +1 -0
- package/dist/db/classes/Center.d.ts +10 -0
- package/dist/db/classes/Center.js +88 -0
- package/dist/db/classes/Object.d.ts +3 -0
- package/dist/db/classes/Object.js +469 -0
- package/dist/db/classes/ObjectProxy.d.ts +18 -0
- package/dist/db/classes/ObjectProxy.js +79 -0
- package/dist/db/classes/Proxy.d.ts +10 -0
- package/dist/db/classes/Proxy.js +52 -0
- package/dist/db/functions/fieldsFromFilter.d.ts +1 -0
- package/dist/db/functions/fieldsFromFilter.js +33 -0
- package/dist/db/functions/getPrimaryKey.d.ts +2 -0
- package/dist/db/functions/getPrimaryKey.js +8 -0
- package/dist/db/functions/requestToPromise.d.ts +1 -0
- package/dist/db/functions/requestToPromise.js +6 -0
- package/dist/db/functions/transactionDone.d.ts +1 -0
- package/dist/db/functions/transactionDone.js +7 -0
- package/dist/db/functions/transformResult.d.ts +1 -0
- package/dist/db/functions/transformResult.js +16 -0
- package/dist/db/types.d.ts +56 -0
- package/dist/db/types.js +1 -0
- package/dist/db.d.ts +3 -66
- package/dist/db.js +3 -558
- package/dist/dt/classes/{date.d.ts → Date.d.ts} +1 -1
- package/dist/dt/classes/{date.js → Date.js} +8 -3
- package/dist/dt/classes/{dateTime.d.ts → DateTime.d.ts} +1 -1
- package/dist/dt/classes/{dateTime.js → DateTime.js} +9 -3
- package/dist/dt/classes/{dt.d.ts → Dt.d.ts} +1 -1
- package/dist/dt/classes/{dt.js → Dt.js} +36 -48
- package/dist/dt/classes/Duration.js +193 -0
- package/dist/dt/classes/{monthDay.d.ts → MonthDay.d.ts} +1 -1
- package/dist/dt/classes/{monthDay.js → MonthDay.js} +2 -2
- package/dist/dt/classes/{time.d.ts → Time.d.ts} +1 -1
- package/dist/dt/classes/{time.js → Time.js} +2 -2
- package/dist/dt/classes/{yearMonth.d.ts → YearMonth.d.ts} +1 -1
- package/dist/dt/classes/{yearMonth.js → YearMonth.js} +2 -2
- package/dist/dt/classes/{zoned.d.ts → Zoned.d.ts} +1 -1
- package/dist/dt/classes/{zoned.js → Zoned.js} +9 -3
- package/dist/dt/functions/buildLocaleFromIntl.js +1 -1
- package/dist/dt/functions/guessFormat.js +1 -1
- package/dist/dt/functions/intl-weekinfo-polyfill.js +11 -4
- package/dist/dt/functions/parse.js +20 -22
- package/dist/dt/functions/setupIntl.js +33 -44
- package/dist/dt.d.ts +1 -1
- package/dist/dt.js +9 -8
- package/dist/env.d.ts +1 -1
- package/dist/env.js +6 -7
- package/dist/fn/ajax/abort.js +1 -1
- package/dist/fn/ajax/ajax.d.ts +1 -1
- package/dist/fn/ajax/ajax.js +2 -2
- package/dist/fn/ajax/download.js +1 -1
- package/dist/fn/ajax/getFileContent.js +2 -3
- package/dist/fn/ajax/link.js +2 -2
- package/dist/fn/ajax/stream.js +5 -4
- package/dist/fn/browser/executeSlowly.js +11 -22
- package/dist/fn/browser/log.js +5 -3
- package/dist/fn/browser/yieldToBrowser.js +7 -19
- package/dist/fn/html/makeReactive.js +2 -2
- package/dist/fn/init.d.ts +1 -1
- package/dist/fn/init.js +4 -5
- package/dist/fn/object/_filter.js +3 -2
- package/dist/fn/object/filter.js +2 -1
- package/dist/fn/phone/fphone.js +1 -2
- package/dist/fn/string/cast.js +1 -2
- package/dist/fn/string/shorten.js +1 -1
- package/dist/fn/string/treatForHash.js +1 -1
- package/dist/fn/style/addColors.d.ts +1 -1
- package/dist/fn/style/addColors.js +4 -4
- package/dist/fn/type/checkType.js +1 -2
- package/dist/fn/type/isCp.js +1 -2
- package/dist/fn/type/isHostname.js +1 -1
- package/dist/fn/type/isIP.js +1 -1
- package/dist/fn/type/isSame.js +2 -2
- package/dist/fn/type/isURL.js +1 -1
- package/dist/index-no-dep.js +11 -4
- package/dist/index.js +13 -4
- package/package.json +1 -1
- package/dist/dt/classes/duration.js +0 -199
- /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
|
|
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) =>
|
|
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 =
|
|
66
|
+
data = await res.json();
|
|
75
67
|
}
|
|
76
68
|
else if (contentType.startsWith('text/')) {
|
|
77
|
-
data =
|
|
69
|
+
data = await res.text();
|
|
78
70
|
}
|
|
79
71
|
else if (contentType.includes("multipart/")) {
|
|
80
|
-
data =
|
|
72
|
+
data = await res.arrayBuffer();
|
|
81
73
|
}
|
|
82
74
|
else {
|
|
83
|
-
data =
|
|
75
|
+
data = await res.blob();
|
|
84
76
|
}
|
|
85
77
|
}
|
|
86
|
-
catch
|
|
87
|
-
data =
|
|
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
|
-
})
|
|
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 = (() =>
|
|
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 =
|
|
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;
|
package/dist/{vars.js → data.js}
RENAMED
|
@@ -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,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,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;
|