@cocreate/indexeddb 1.27.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/FUNDING.yml +1 -1
- package/.github/workflows/automated.yml +18 -7
- package/CHANGELOG.md +491 -456
- package/CONTRIBUTING.md +6 -6
- package/CoCreate.config.js +2 -2
- package/README.md +18 -18
- package/demo/index.html +1 -1
- package/docs/index.html +4 -4
- package/package.json +7 -7
- package/src/index.js +1012 -921
- package/test/documents.js +1 -1
- package/test/index.html +1 -1
- package/test/unsorted-response.test.cjs +104 -0
- /package/{release.config.js → release.config.cjs} +0 -0
package/src/index.js
CHANGED
|
@@ -19,989 +19,1080 @@
|
|
|
19
19
|
* Commercial Licensing Information:
|
|
20
20
|
* For commercial use of this software without the copyleft provisions of the AGPLv3,
|
|
21
21
|
* you must obtain a commercial license from CoCreate LLC.
|
|
22
|
-
* For details, visit <https://
|
|
22
|
+
* For details, visit <https://CoCreateJS.com/licenses/> or contact us at sales@CoCreateJS.com
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
25
|
import {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
26
|
+
ObjectId,
|
|
27
|
+
dotNotationToObject,
|
|
28
|
+
searchData,
|
|
29
|
+
sortData,
|
|
30
|
+
queryData,
|
|
31
|
+
createUpdate,
|
|
32
|
+
isEqualArray,
|
|
33
|
+
isEqualObject
|
|
34
34
|
} from "@cocreate/utils";
|
|
35
35
|
|
|
36
36
|
let indexedDbFunction;
|
|
37
37
|
const isBrowser = typeof window !== "undefined";
|
|
38
38
|
|
|
39
|
-
function
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
39
|
+
function getSortPlan(filter) {
|
|
40
|
+
const sort = filter?.sort?.filter((item) => item?.key) || [];
|
|
41
|
+
const primary = sort[0];
|
|
42
|
+
return {
|
|
43
|
+
sort,
|
|
44
|
+
primary,
|
|
45
|
+
indexName: primary && primary.key !== "_id" && !primary.key.includes("-")
|
|
46
|
+
? primary.key
|
|
47
|
+
: undefined,
|
|
48
|
+
direction: primary?.direction === "desc" ? "prev" : "next"
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function countRecords(source) {
|
|
53
|
+
return new Promise((resolve, reject) => {
|
|
54
|
+
const request = source.count();
|
|
55
|
+
request.onsuccess = () => resolve(request.result);
|
|
56
|
+
request.onerror = () => reject(request.error);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function getFilterStorage(filter, storage, database, array) {
|
|
61
|
+
if (!filter) return;
|
|
62
|
+
if (!Array.isArray(filter.storages)) filter.storages = [];
|
|
63
|
+
|
|
64
|
+
let metadata = filter.storages.find(
|
|
65
|
+
(item) =>
|
|
66
|
+
item.storage === storage &&
|
|
67
|
+
item.database === database &&
|
|
68
|
+
item.array === array
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
if (!metadata) {
|
|
72
|
+
metadata = { storage, database, array };
|
|
73
|
+
filter.storages.push(metadata);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return metadata;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function includesReference(value, expected) {
|
|
80
|
+
const values = Array.isArray(value) ? value : [value];
|
|
81
|
+
return values.includes(expected);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function init() {
|
|
85
|
+
// Check for support.
|
|
86
|
+
if (!isBrowser) {
|
|
87
|
+
return;
|
|
88
|
+
} else if (!("indexedDB" in window)) {
|
|
89
|
+
console.log("This browser doesn't support IndexedDB.");
|
|
90
|
+
setStatus(false);
|
|
91
|
+
return;
|
|
92
|
+
} else {
|
|
93
|
+
try {
|
|
94
|
+
indexedDB.databases();
|
|
95
|
+
if (
|
|
96
|
+
window.CoCreateConfig &&
|
|
97
|
+
window.CoCreateConfig.indexeddb !== false
|
|
98
|
+
)
|
|
99
|
+
setStatus(true);
|
|
100
|
+
} catch (e) {
|
|
101
|
+
setStatus(false);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
59
104
|
}
|
|
60
105
|
|
|
61
106
|
function setStatus(value) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
107
|
+
if (window.CoCreateConfig) window.CoCreateConfig.indexeddb = value;
|
|
108
|
+
else window.CoCreateConfig = { indexeddb: value };
|
|
109
|
+
|
|
110
|
+
if (!value) {
|
|
111
|
+
indexedDbFunction = indexedDb;
|
|
112
|
+
send = value;
|
|
113
|
+
} else if (!send) {
|
|
114
|
+
send = indexedDbFunction;
|
|
115
|
+
indexedDbFunction = true;
|
|
116
|
+
}
|
|
72
117
|
}
|
|
73
118
|
|
|
119
|
+
/**
|
|
120
|
+
* Route a CoCreate CRUD request to the matching database, array, or object operation.
|
|
121
|
+
*
|
|
122
|
+
* @public
|
|
123
|
+
* @environment client
|
|
124
|
+
*/
|
|
74
125
|
async function send(data) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
126
|
+
try {
|
|
127
|
+
let newData = [];
|
|
128
|
+
|
|
129
|
+
let type = data.method.split(".")[0];
|
|
130
|
+
if (type === "database") {
|
|
131
|
+
if (data.method === "database.get") {
|
|
132
|
+
return await processDatabase(data);
|
|
133
|
+
} else await processDatabase(data, newData, type);
|
|
134
|
+
} else {
|
|
135
|
+
if (!data["timeStamp"])
|
|
136
|
+
data["timeStamp"] = new Date().toISOString();
|
|
137
|
+
|
|
138
|
+
let databases = data.database;
|
|
139
|
+
if (!databases && data.organization_id)
|
|
140
|
+
databases = [data.organization_id];
|
|
141
|
+
if (!Array.isArray(databases)) databases = [databases];
|
|
142
|
+
|
|
143
|
+
let arrays = data.array || [];
|
|
144
|
+
if (!Array.isArray(arrays)) arrays = [arrays];
|
|
145
|
+
|
|
146
|
+
for (let i = 0; i < databases.length; i++) {
|
|
147
|
+
let database = databases[i];
|
|
148
|
+
if (type === "array") {
|
|
149
|
+
await processArray(data, newData, database, type);
|
|
150
|
+
} else {
|
|
151
|
+
for (let j = 0; j < arrays.length; j++) {
|
|
152
|
+
if (type === "index")
|
|
153
|
+
await processIndex(data, newData, database, type);
|
|
154
|
+
else if (type === "object")
|
|
155
|
+
await processObject(
|
|
156
|
+
data,
|
|
157
|
+
newData,
|
|
158
|
+
database,
|
|
159
|
+
arrays[j],
|
|
160
|
+
type
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return createData(data, newData, type);
|
|
168
|
+
} catch (error) {
|
|
169
|
+
errorHandler(data, error);
|
|
170
|
+
}
|
|
120
171
|
}
|
|
121
172
|
|
|
122
173
|
const dbVersion = new Map();
|
|
123
174
|
const processDatabase = (data, newData, type) => {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
175
|
+
return new Promise(
|
|
176
|
+
(resolve, reject) => {
|
|
177
|
+
if (!data) data = {};
|
|
178
|
+
if (!data[type]) data[type] = [];
|
|
179
|
+
|
|
180
|
+
if (data.method == "database.read") {
|
|
181
|
+
indexedDB.databases().then((databases) => {
|
|
182
|
+
for (let database of databases) {
|
|
183
|
+
if (data.filter && data.filter.query) {
|
|
184
|
+
let isFilter = queryData(
|
|
185
|
+
database,
|
|
186
|
+
data.filter.query
|
|
187
|
+
);
|
|
188
|
+
if (isFilter)
|
|
189
|
+
newData.push({
|
|
190
|
+
$storage: ["indexeddb"],
|
|
191
|
+
...database
|
|
192
|
+
});
|
|
193
|
+
} else
|
|
194
|
+
newData.push({
|
|
195
|
+
$storage: ["indexeddb"],
|
|
196
|
+
...database
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
resolve();
|
|
201
|
+
});
|
|
202
|
+
} else {
|
|
203
|
+
let databases;
|
|
204
|
+
if (data.method == "database.update")
|
|
205
|
+
databases = Object.keys(data.database);
|
|
206
|
+
else databases = data.database;
|
|
207
|
+
|
|
208
|
+
if (!Array.isArray(databases)) databases = [databases];
|
|
209
|
+
|
|
210
|
+
for (let i = 0; i < databases.length; i++) {
|
|
211
|
+
let openRequest,
|
|
212
|
+
database = databases[i];
|
|
213
|
+
switch (data.method) {
|
|
214
|
+
case "database.get":
|
|
215
|
+
case "database.create":
|
|
216
|
+
case "database.update":
|
|
217
|
+
openRequest = indexedDB.open(database);
|
|
218
|
+
break;
|
|
219
|
+
case "database.delete":
|
|
220
|
+
openRequest =
|
|
221
|
+
indexedDB.deleteprocessDatabase(database);
|
|
222
|
+
break;
|
|
223
|
+
default:
|
|
224
|
+
errorHandler(data, "unknown method", database);
|
|
225
|
+
resolve(data);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
openRequest.onsuccess = function () {
|
|
229
|
+
let db = openRequest.result;
|
|
230
|
+
|
|
231
|
+
if (data.method == "database.update") {
|
|
232
|
+
let objectStoreNames = Array.from(
|
|
233
|
+
db.objectStoreNames
|
|
234
|
+
);
|
|
235
|
+
for (let array of objectStoreNames) {
|
|
236
|
+
let request = array.openCursor();
|
|
237
|
+
|
|
238
|
+
request.onsuccess = function () {
|
|
239
|
+
let cursor = request.result;
|
|
240
|
+
if (cursor) {
|
|
241
|
+
let value = cursor.value;
|
|
242
|
+
|
|
243
|
+
cursor.continue();
|
|
244
|
+
} else {
|
|
245
|
+
// -1
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
request.onerror = function () {
|
|
250
|
+
errorHandler(
|
|
251
|
+
data,
|
|
252
|
+
request.error,
|
|
253
|
+
database,
|
|
254
|
+
array
|
|
255
|
+
);
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
} else if (data.upgrade === true) {
|
|
259
|
+
db.close();
|
|
260
|
+
|
|
261
|
+
let version =
|
|
262
|
+
dbVersion.get(database) ||
|
|
263
|
+
openRequest.result.version;
|
|
264
|
+
resolve(
|
|
265
|
+
indexedDB.open(database, Number((version += 1)))
|
|
266
|
+
);
|
|
267
|
+
dbVersion.set(database, version);
|
|
268
|
+
} else if (data.array) {
|
|
269
|
+
db.close();
|
|
270
|
+
|
|
271
|
+
let version =
|
|
272
|
+
dbVersion.get(database) ||
|
|
273
|
+
openRequest.result.version;
|
|
274
|
+
let request = indexedDB.open(
|
|
275
|
+
database,
|
|
276
|
+
Number((version += 1))
|
|
277
|
+
);
|
|
278
|
+
dbVersion.set(database, version);
|
|
279
|
+
|
|
280
|
+
request.onupgradeneeded = function (event) {
|
|
281
|
+
const db = event.target.result;
|
|
282
|
+
|
|
283
|
+
let arrays = data.array;
|
|
284
|
+
if (!Array.isArray(arrays)) arrays = [arrays];
|
|
285
|
+
|
|
286
|
+
for (let j = 0; j < arrays.length; j++) {
|
|
287
|
+
if (
|
|
288
|
+
!db.objectStoreNames.contains(arrays[j])
|
|
289
|
+
) {
|
|
290
|
+
try {
|
|
291
|
+
db.createObjectStore(arrays[j], {
|
|
292
|
+
keyPath: "_id",
|
|
293
|
+
autoIncrement: true
|
|
294
|
+
});
|
|
295
|
+
} catch (error) {
|
|
296
|
+
errorHandler(
|
|
297
|
+
data,
|
|
298
|
+
error,
|
|
299
|
+
database,
|
|
300
|
+
arrays[j]
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (data.indexName) {
|
|
306
|
+
const transaction =
|
|
307
|
+
event.target.transaction;
|
|
308
|
+
const objectStore =
|
|
309
|
+
transaction.objectStore(arrays[j]);
|
|
310
|
+
if (
|
|
311
|
+
!objectStore.indexNames.contains(
|
|
312
|
+
data.indexName
|
|
313
|
+
)
|
|
314
|
+
)
|
|
315
|
+
objectStore.createIndex(
|
|
316
|
+
data.indexName,
|
|
317
|
+
data.indexFields,
|
|
318
|
+
{ unique: data.unique || false }
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
request.onsuccess = function (event) {
|
|
325
|
+
if (databases.length - 1 === i) {
|
|
326
|
+
if (data.method == "database.get")
|
|
327
|
+
resolve(event.target.result);
|
|
328
|
+
else resolve(data);
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
request.onerror = function (event) {
|
|
333
|
+
errorHandler(
|
|
334
|
+
data,
|
|
335
|
+
event.target.error,
|
|
336
|
+
database,
|
|
337
|
+
array
|
|
338
|
+
);
|
|
339
|
+
if (databases.length - 1 === i) {
|
|
340
|
+
if (data.method == "database.get")
|
|
341
|
+
resolve(event.target.result);
|
|
342
|
+
else resolve(data);
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
} else if (databases.length - 1 === i) {
|
|
346
|
+
if (data.method == "database.get") resolve(db);
|
|
347
|
+
else resolve(data);
|
|
348
|
+
}
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
openRequest.onerror = function () {
|
|
352
|
+
errorHandler(data, openRequest.error, database);
|
|
353
|
+
resolve(data);
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
},
|
|
358
|
+
(error) => {
|
|
359
|
+
errorHandler(data, error);
|
|
360
|
+
}
|
|
361
|
+
);
|
|
311
362
|
};
|
|
312
363
|
|
|
313
364
|
async function processArray(data, newData, database, type) {
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
365
|
+
let db = await processDatabase({ method: "database.get", database });
|
|
366
|
+
|
|
367
|
+
let objectStoreNames = Array.from(db.objectStoreNames);
|
|
368
|
+
if (data.method == "array.read") {
|
|
369
|
+
if (!data[type]) data[type] = [];
|
|
370
|
+
|
|
371
|
+
if (!Array.isArray(data[type])) {
|
|
372
|
+
data[type] = [data[type]];
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
for (let i = 0; i < objectStoreNames.length; i++) {
|
|
376
|
+
if (data.filter && data.filter.query) {
|
|
377
|
+
let isFilter = queryData(
|
|
378
|
+
{ name: objectStoreNames[i] },
|
|
379
|
+
data.filter.query
|
|
380
|
+
);
|
|
381
|
+
if (isFilter)
|
|
382
|
+
newData.push({
|
|
383
|
+
name: objectStoreNames[i],
|
|
384
|
+
$storage: ["indexeddb"],
|
|
385
|
+
$database: [database]
|
|
386
|
+
});
|
|
387
|
+
} else
|
|
388
|
+
newData.push({
|
|
389
|
+
name: objectStoreNames[i],
|
|
390
|
+
$storage: ["indexeddb"],
|
|
391
|
+
$database: [database]
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
} else {
|
|
395
|
+
db.close();
|
|
396
|
+
|
|
397
|
+
let request = await processDatabase({
|
|
398
|
+
method: "database.get",
|
|
399
|
+
database,
|
|
400
|
+
upgrade: true
|
|
401
|
+
});
|
|
402
|
+
await new Promise((resolve) => {
|
|
403
|
+
request.onupgradeneeded = function (event) {
|
|
404
|
+
let db = event.target.result;
|
|
405
|
+
let arrays;
|
|
406
|
+
if (data.method == "array.update")
|
|
407
|
+
arrays = Object.keys(data[type]);
|
|
408
|
+
else arrays = data[type];
|
|
409
|
+
if (!Array.isArray(arrays)) arrays = [arrays];
|
|
410
|
+
|
|
411
|
+
for (let i = 0; i < arrays.length; i++) {
|
|
412
|
+
let error,
|
|
413
|
+
array = arrays[i];
|
|
414
|
+
|
|
415
|
+
let arrayExist = db.objectStoreNames.contains(array);
|
|
416
|
+
if (arrayExist && data.method == "array.create") {
|
|
417
|
+
error = "array already exists";
|
|
418
|
+
} else if (data.method == "array.delete") {
|
|
419
|
+
if (objectStoreNames.includes(array)) {
|
|
420
|
+
db.deleteObjectStore(array);
|
|
421
|
+
} else {
|
|
422
|
+
error = "the array does not exist";
|
|
423
|
+
}
|
|
424
|
+
} else {
|
|
425
|
+
if (!arrayExist && data.method == "array.update")
|
|
426
|
+
array = data[type][arrays[i]];
|
|
427
|
+
|
|
428
|
+
if (arrayExist) {
|
|
429
|
+
if (data.method == "array.update") {
|
|
430
|
+
let transaction = event.target.transaction;
|
|
431
|
+
let objectStore =
|
|
432
|
+
transaction.objectStore(array);
|
|
433
|
+
|
|
434
|
+
if (
|
|
435
|
+
!objectStoreNames.includes(
|
|
436
|
+
data[type][arrays[i]]
|
|
437
|
+
)
|
|
438
|
+
)
|
|
439
|
+
array = objectStore.name =
|
|
440
|
+
data[type][arrays[i]];
|
|
441
|
+
else
|
|
442
|
+
error =
|
|
443
|
+
"An array with the new name already exist";
|
|
444
|
+
}
|
|
445
|
+
} else if (data.method == "array.update")
|
|
446
|
+
error = "array does not exists";
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
if (!error)
|
|
450
|
+
newData.push({
|
|
451
|
+
name: array,
|
|
452
|
+
$storage: ["indexeddb"],
|
|
453
|
+
$database: [database]
|
|
454
|
+
});
|
|
455
|
+
else errorHandler(data, error, database);
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
request.onsuccess = function (event) {
|
|
460
|
+
event.target.result.close();
|
|
461
|
+
resolve();
|
|
462
|
+
};
|
|
463
|
+
|
|
464
|
+
request.onerror = function (event) {
|
|
465
|
+
errorHandler(data, event.target.error, database);
|
|
466
|
+
resolve();
|
|
467
|
+
};
|
|
468
|
+
});
|
|
469
|
+
}
|
|
419
470
|
}
|
|
420
471
|
|
|
421
472
|
async function processIndex(data, newData, database, array, type) {
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
473
|
+
let db = await processDatabase({ method: "database.get", database });
|
|
474
|
+
let arrayExist = db.objectStoreNames.contains(array);
|
|
475
|
+
if (arrayExist) {
|
|
476
|
+
// ToDO: switch here to types
|
|
477
|
+
let transaction = db.transaction(array, "readonly");
|
|
478
|
+
let objectStore = transaction.objectStore(array);
|
|
479
|
+
let indexNames = Array.from(objectStore.indexNames);
|
|
480
|
+
|
|
481
|
+
if (data.method == "index.read") {
|
|
482
|
+
for (let i = 0; i < indexNames.length; i++) {
|
|
483
|
+
let name = indexNames[i];
|
|
484
|
+
if (data.filter && data.filter.query)
|
|
485
|
+
if (!queryData({ name }, data.filter.query)) continue;
|
|
486
|
+
|
|
487
|
+
newData.push({
|
|
488
|
+
name,
|
|
489
|
+
$storage: ["indexeddb"],
|
|
490
|
+
$database: [database],
|
|
491
|
+
$array: [array]
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
db.close();
|
|
495
|
+
} else {
|
|
496
|
+
db.close();
|
|
497
|
+
let indexes;
|
|
498
|
+
if (data.method == "index.update")
|
|
499
|
+
indexes = Object.keys(data[type]);
|
|
500
|
+
else indexes = data[type];
|
|
501
|
+
|
|
502
|
+
if (!Array.isArray(indexes)) indexes = [indexes];
|
|
503
|
+
|
|
504
|
+
for (let i = 0; i < indexes.length; i++) {
|
|
505
|
+
let error,
|
|
506
|
+
index = indexes[i];
|
|
507
|
+
db = await processDatabase({
|
|
508
|
+
method: "database.get",
|
|
509
|
+
database,
|
|
510
|
+
upgrade: true
|
|
511
|
+
});
|
|
512
|
+
let transaction = db.transaction;
|
|
513
|
+
let objectStore = transaction.objectStore(array);
|
|
514
|
+
let indexExist = indexNames.includes(index);
|
|
515
|
+
|
|
516
|
+
if (data.method == "index.create") {
|
|
517
|
+
if (!indexExist)
|
|
518
|
+
objectStore.createIndex(index, index, {
|
|
519
|
+
unique: false
|
|
520
|
+
});
|
|
521
|
+
else error = "index already exist";
|
|
522
|
+
} else if (indexExist) {
|
|
523
|
+
if (data.method == "index.delete")
|
|
524
|
+
objectStore.deleteIndex(index);
|
|
525
|
+
else if (
|
|
526
|
+
data.method == "index.update" &&
|
|
527
|
+
!indexNames.includes(data[type][indexes[i]])
|
|
528
|
+
) {
|
|
529
|
+
if (!indexNames.includes(data[type][indexes[i]])) {
|
|
530
|
+
let indexObj = objectStore.index(index);
|
|
531
|
+
index = indexObj.name = data[type][indexes[i]];
|
|
532
|
+
} else error = "new index name already exist";
|
|
533
|
+
}
|
|
534
|
+
} else error = "index does not exist";
|
|
535
|
+
|
|
536
|
+
if (!error)
|
|
537
|
+
newData.push({
|
|
538
|
+
name: index,
|
|
539
|
+
$storage: ["indexeddb"],
|
|
540
|
+
$database: [database],
|
|
541
|
+
$array: [array]
|
|
542
|
+
});
|
|
543
|
+
else errorHandler(data, error, database, array);
|
|
544
|
+
|
|
545
|
+
db.result.close();
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
}
|
|
498
549
|
}
|
|
499
550
|
|
|
500
551
|
async function processObject(data, newData, database, array, type) {
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
}
|
|
552
|
+
let db = await processDatabase({ method: "database.get", database });
|
|
553
|
+
|
|
554
|
+
try {
|
|
555
|
+
if (!data[type]) data[type] = [];
|
|
556
|
+
else if (typeof data[type] === "string")
|
|
557
|
+
data[type] = [{ _id: data[type] }];
|
|
558
|
+
else if (!Array.isArray(data[type])) data[type] = [data[type]];
|
|
559
|
+
|
|
560
|
+
let isFilter = Boolean(
|
|
561
|
+
data.filter && data.filter.isFilter !== false
|
|
562
|
+
);
|
|
563
|
+
const filterStorage = isFilter
|
|
564
|
+
? getFilterStorage(data.filter, "indexeddb", database, array)
|
|
565
|
+
: undefined;
|
|
566
|
+
const filterStartingIndex =
|
|
567
|
+
filterStorage?.index ?? data.filter?.index ?? 0;
|
|
568
|
+
if (filterStorage) filterStorage.startingIndex = filterStartingIndex;
|
|
569
|
+
|
|
570
|
+
let arrayExist = db.objectStoreNames.contains(array);
|
|
571
|
+
if (!arrayExist) {
|
|
572
|
+
db.close();
|
|
573
|
+
if (
|
|
574
|
+
data.method == "object.create" ||
|
|
575
|
+
data.method == "object.update"
|
|
576
|
+
) {
|
|
577
|
+
db = await processDatabase({
|
|
578
|
+
method: "database.get",
|
|
579
|
+
database,
|
|
580
|
+
array
|
|
581
|
+
});
|
|
582
|
+
} else {
|
|
583
|
+
return errorHandler(
|
|
584
|
+
data,
|
|
585
|
+
"array does not exist",
|
|
586
|
+
database,
|
|
587
|
+
array
|
|
588
|
+
);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
if (!array || !db)
|
|
593
|
+
throw new Error({ error: "This is an error message.", db, array });
|
|
594
|
+
|
|
595
|
+
let lastItem;
|
|
596
|
+
if (
|
|
597
|
+
(isFilter && !data[type].length) ||
|
|
598
|
+
data.filter?.isFilter === true
|
|
599
|
+
) {
|
|
600
|
+
if (data[type].length) lastItem = data[type][data[type].length - 1];
|
|
601
|
+
data[type] = [{ isFilter: "isEmptyObjectFilter" }];
|
|
602
|
+
|
|
603
|
+
// data[type].splice(0, 0, { isFilter: 'isEmptyObjectFilter' });
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
let transactionType = "readwrite";
|
|
607
|
+
if (data.method == "object.read") transactionType = "readonly";
|
|
608
|
+
|
|
609
|
+
let transaction = db.transaction([array], transactionType);
|
|
610
|
+
let objectStore = transaction.objectStore(array);
|
|
611
|
+
|
|
612
|
+
let globalOperators = getGlobalOperators(data);
|
|
613
|
+
let upsert = data.upsert;
|
|
614
|
+
|
|
615
|
+
for (let i = 0; i < data[type].length; i++) {
|
|
616
|
+
const reference = {
|
|
617
|
+
$storage: ["indexeddb"],
|
|
618
|
+
$database: [database],
|
|
619
|
+
$array: [array]
|
|
620
|
+
};
|
|
621
|
+
|
|
622
|
+
delete data[type][i].$storage;
|
|
623
|
+
delete data[type][i].$database;
|
|
624
|
+
delete data[type][i].$array;
|
|
625
|
+
|
|
626
|
+
if (data.method == "object.create") {
|
|
627
|
+
if (data.organization_id)
|
|
628
|
+
data[type][i]["organization_id"] = data.organization_id;
|
|
629
|
+
|
|
630
|
+
if (!data[type][i]._id)
|
|
631
|
+
data[type][i]["_id"] = ObjectId().toString();
|
|
632
|
+
data[type][i] = dotNotationToObject(data[type][i]);
|
|
633
|
+
delete data[type][i].modified;
|
|
634
|
+
|
|
635
|
+
// TODO: user_id || clientId shohould be retrieved from CoCreate
|
|
636
|
+
const audit = {
|
|
637
|
+
on: data.timeStamp,
|
|
638
|
+
by: data.user_id || data.clientId
|
|
639
|
+
};
|
|
640
|
+
if (!data.synced) {
|
|
641
|
+
data[type][i]["created"] = { ...audit };
|
|
642
|
+
data[type][i]["updated"] = { ...audit };
|
|
643
|
+
}
|
|
644
|
+
data[type][i] = await add(objectStore, data[type][i]);
|
|
645
|
+
data[type][i] = { ...reference, ...data[type][i] };
|
|
646
|
+
} else {
|
|
647
|
+
if (data[type][i].filter) isFilter = true;
|
|
648
|
+
|
|
649
|
+
if (data.method == "object.update") {
|
|
650
|
+
if (data.organization_id)
|
|
651
|
+
data[type][i]["organization_id"] = data.organization_id;
|
|
652
|
+
|
|
653
|
+
if (data[type][i].$upsert) upsert = data[type][i].$upsert;
|
|
654
|
+
// TODO: user_id || clientId shohould be retrieved from CoCreate
|
|
655
|
+
if (!data.synced)
|
|
656
|
+
data[type][i].updated = {
|
|
657
|
+
on: data.timeStamp,
|
|
658
|
+
by: data.user_id || data.clientId
|
|
659
|
+
};
|
|
660
|
+
// Remove legacy object audit metadata from existing records.
|
|
661
|
+
data[type][i].modified = undefined;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
let index = 0,
|
|
665
|
+
limit,
|
|
666
|
+
range,
|
|
667
|
+
direction;
|
|
668
|
+
if (data[type][i]._id) {
|
|
669
|
+
range = IDBKeyRange.only(data[type][i]._id);
|
|
670
|
+
} else if (isFilter) {
|
|
671
|
+
let indexName;
|
|
672
|
+
let indexFields;
|
|
673
|
+
|
|
674
|
+
if (data.filter) {
|
|
675
|
+
const sortPlan = getSortPlan(data.filter);
|
|
676
|
+
if (sortPlan.primary) {
|
|
677
|
+
direction = sortPlan.direction;
|
|
678
|
+
indexName = sortPlan.indexName;
|
|
679
|
+
indexFields = sortPlan.primary.key;
|
|
680
|
+
} else if (
|
|
681
|
+
data.filter.query &&
|
|
682
|
+
data.filter.query[0] &&
|
|
683
|
+
data.filter.query[0].key
|
|
684
|
+
) {
|
|
685
|
+
if (data.filter.query[0].operator === "$eq") {
|
|
686
|
+
range = IDBKeyRange.only(
|
|
687
|
+
data.filter.query[0].value
|
|
688
|
+
);
|
|
689
|
+
} else if (
|
|
690
|
+
data.filter.query[0].operator === "$gt"
|
|
691
|
+
) {
|
|
692
|
+
range = IDBKeyRange.lowerBound(
|
|
693
|
+
data.filter.query[0].value,
|
|
694
|
+
true
|
|
695
|
+
);
|
|
696
|
+
} else if (
|
|
697
|
+
data.filter.query[0].operator === "$lt"
|
|
698
|
+
) {
|
|
699
|
+
range = IDBKeyRange.upperBound(
|
|
700
|
+
data.filter.query[0].value,
|
|
701
|
+
true
|
|
702
|
+
);
|
|
703
|
+
} else if (
|
|
704
|
+
data.filter.query[0].operator === "$gte"
|
|
705
|
+
) {
|
|
706
|
+
range = IDBKeyRange.lowerBound(
|
|
707
|
+
data.filter.query[0].value
|
|
708
|
+
);
|
|
709
|
+
} else if (
|
|
710
|
+
data.filter.query[0].operator === "$lte"
|
|
711
|
+
) {
|
|
712
|
+
range = IDBKeyRange.upperBound(
|
|
713
|
+
data.filter.query[0].value
|
|
714
|
+
);
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
indexName = data.filter.query[0].key;
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
if (indexName && !indexName.includes("-")) {
|
|
722
|
+
// Check if the compound index exists; create it if not
|
|
723
|
+
let indexNames = Array.from(objectStore.indexNames);
|
|
724
|
+
if (!indexNames.includes(indexName)) {
|
|
725
|
+
db.close();
|
|
726
|
+
db = await processDatabase({
|
|
727
|
+
method: "database.get",
|
|
728
|
+
database,
|
|
729
|
+
array,
|
|
730
|
+
indexName,
|
|
731
|
+
indexFields
|
|
732
|
+
});
|
|
733
|
+
transaction = db.transaction(array, "readwrite");
|
|
734
|
+
objectStore = transaction.objectStore(array);
|
|
735
|
+
}
|
|
736
|
+
const selectedIndex = objectStore.index(indexName);
|
|
737
|
+
if (getSortPlan(data.filter).primary) {
|
|
738
|
+
const [recordCount, indexCount] = await Promise.all([
|
|
739
|
+
countRecords(objectStore),
|
|
740
|
+
countRecords(selectedIndex)
|
|
741
|
+
]);
|
|
742
|
+
if (recordCount === indexCount)
|
|
743
|
+
objectStore = selectedIndex;
|
|
744
|
+
else direction = undefined;
|
|
745
|
+
} else {
|
|
746
|
+
objectStore = selectedIndex;
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
let count = objectStore.count();
|
|
751
|
+
count.onsuccess = function () {
|
|
752
|
+
if (data.filter) {
|
|
753
|
+
data.filter.count = count.result;
|
|
754
|
+
filterStorage.count = count.result;
|
|
755
|
+
}
|
|
756
|
+
else if (data.object.filter)
|
|
757
|
+
data.object.filter.count = count.result;
|
|
758
|
+
};
|
|
759
|
+
|
|
760
|
+
if (data.filter) {
|
|
761
|
+
const sortPlan = getSortPlan(data.filter);
|
|
762
|
+
if (!sortPlan.sort.length) {
|
|
763
|
+
if (filterStartingIndex && !lastItem)
|
|
764
|
+
index = filterStartingIndex;
|
|
765
|
+
if (data.filter.limit) limit = data.filter.limit;
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
if (data[type][i].filter) {
|
|
770
|
+
if (data[type][i].filter.index)
|
|
771
|
+
index = data[type][i].filter.index;
|
|
772
|
+
if (data[type][i].filter.limit)
|
|
773
|
+
limit = data[type][i].filter.limit;
|
|
774
|
+
}
|
|
775
|
+
} else if (
|
|
776
|
+
data.method == "object.delete" ||
|
|
777
|
+
(data.method == "object.update" &&
|
|
778
|
+
!range &&
|
|
779
|
+
!upsert &&
|
|
780
|
+
!isFilter)
|
|
781
|
+
) {
|
|
782
|
+
continue;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
await openCursor(
|
|
786
|
+
objectStore,
|
|
787
|
+
range,
|
|
788
|
+
direction,
|
|
789
|
+
data,
|
|
790
|
+
newData,
|
|
791
|
+
isFilter,
|
|
792
|
+
index,
|
|
793
|
+
limit,
|
|
794
|
+
database,
|
|
795
|
+
array,
|
|
796
|
+
upsert,
|
|
797
|
+
type,
|
|
798
|
+
i,
|
|
799
|
+
globalOperators,
|
|
800
|
+
reference
|
|
801
|
+
);
|
|
802
|
+
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
if (db.close) db.close();
|
|
807
|
+
} catch (error) {
|
|
808
|
+
errorHandler(data, error, database, array);
|
|
809
|
+
if (db.close) db.close();
|
|
810
|
+
}
|
|
761
811
|
}
|
|
762
812
|
|
|
763
813
|
function openCursor(
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
814
|
+
objectStore,
|
|
815
|
+
range,
|
|
816
|
+
direction,
|
|
817
|
+
data,
|
|
818
|
+
newData,
|
|
819
|
+
isFilter,
|
|
820
|
+
index,
|
|
821
|
+
limit,
|
|
822
|
+
database,
|
|
823
|
+
array,
|
|
824
|
+
upsert,
|
|
825
|
+
type,
|
|
826
|
+
i,
|
|
827
|
+
globalOperators,
|
|
828
|
+
reference
|
|
779
829
|
) {
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
830
|
+
return new Promise(async (resolve, reject) => {
|
|
831
|
+
const request = objectStore.openCursor(range || null, direction); // next, prev
|
|
832
|
+
|
|
833
|
+
let hasUpdated,
|
|
834
|
+
matchedLength = 0,
|
|
835
|
+
hasSkipped;
|
|
836
|
+
request.onsuccess = async () => {
|
|
837
|
+
let cursor = request.result;
|
|
838
|
+
|
|
839
|
+
if (
|
|
840
|
+
data.method === "object.create" ||
|
|
841
|
+
(data.method === "object.update" &&
|
|
842
|
+
((!range && !isFilter && upsert) ||
|
|
843
|
+
(!cursor && upsert && !hasUpdated)))
|
|
844
|
+
) {
|
|
845
|
+
let isMatch = true;
|
|
846
|
+
if (isFilter) {
|
|
847
|
+
if (cursor)
|
|
848
|
+
isMatch = filter(
|
|
849
|
+
objectStore,
|
|
850
|
+
data[type][i],
|
|
851
|
+
data[type][i],
|
|
852
|
+
cursor.value
|
|
853
|
+
);
|
|
854
|
+
else if (data.method === "object.update" && !upsert)
|
|
855
|
+
isMatch = false;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
if (isMatch !== false) {
|
|
859
|
+
try {
|
|
860
|
+
data[type][i]._id = ObjectId(
|
|
861
|
+
data[type][i]._id
|
|
862
|
+
).toString();
|
|
863
|
+
} catch (error) {
|
|
864
|
+
data[type][i]._id = ObjectId().toString();
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
let result;
|
|
868
|
+
if (data.method == "object.create") {
|
|
869
|
+
data[type][i] = await add(objectStore, data[type][i]);
|
|
870
|
+
} else if (data.method == "object.update") {
|
|
871
|
+
let update = createUpdate(
|
|
872
|
+
cursor && cursor.value ? cursor.value : {},
|
|
873
|
+
data[type][i],
|
|
874
|
+
globalOperators
|
|
875
|
+
);
|
|
876
|
+
delete data[type][i].modified;
|
|
877
|
+
if (update) {
|
|
878
|
+
await put(objectStore, update);
|
|
879
|
+
data[type][i].$update = update;
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
data[type][i] = { ...reference, ...data[type][i] };
|
|
883
|
+
|
|
884
|
+
matchedLength++;
|
|
885
|
+
}
|
|
886
|
+
resolve();
|
|
887
|
+
} else if (cursor) {
|
|
888
|
+
if (index && !hasSkipped) {
|
|
889
|
+
cursor.advance(index);
|
|
890
|
+
hasSkipped = true;
|
|
891
|
+
} else {
|
|
892
|
+
let isMatch = true;
|
|
893
|
+
if (isFilter)
|
|
894
|
+
isMatch = filter(
|
|
895
|
+
objectStore,
|
|
896
|
+
data,
|
|
897
|
+
data[type][i],
|
|
898
|
+
cursor.value
|
|
899
|
+
);
|
|
900
|
+
|
|
901
|
+
if (isMatch !== false) {
|
|
902
|
+
let result = cursor.value;
|
|
903
|
+
|
|
904
|
+
if (data.method == "object.update") {
|
|
905
|
+
let update = createUpdate(
|
|
906
|
+
cursor.value,
|
|
907
|
+
data[type][i],
|
|
908
|
+
globalOperators
|
|
909
|
+
);
|
|
910
|
+
delete data[type][i].modified;
|
|
911
|
+
if (update) await cursorUpdate(cursor, update);
|
|
912
|
+
result = data[type][i];
|
|
913
|
+
result.$update = update;
|
|
914
|
+
// result = cursor.update(update)
|
|
915
|
+
hasUpdated = true;
|
|
916
|
+
// set dotnotation for keys with $operators for items that end in [] to be used by socket.id
|
|
917
|
+
// TODO: if update.$<operator>.someKey[] requires the index of inerted item added to the field name. update.$<operator>.someKey[<index>]
|
|
918
|
+
// TODO: if $addToSet get field name and item if it does not exist.
|
|
919
|
+
// TODO: if $pull get field name and find if item exist and delete.
|
|
920
|
+
} else if (data.method == "object.delete") {
|
|
921
|
+
result = cursor.value;
|
|
922
|
+
cursor.delete();
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
if (data[type][i]._id)
|
|
926
|
+
data[type][i] = { ...reference, ...result };
|
|
927
|
+
else {
|
|
928
|
+
let object = data[type].find(
|
|
929
|
+
(obj) => obj._id && obj._id === result._id
|
|
930
|
+
);
|
|
931
|
+
if (object)
|
|
932
|
+
object = { ...reference, ...object, ...result };
|
|
933
|
+
else newData.push({ ...reference, ...result });
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
if (
|
|
938
|
+
!limit ||
|
|
939
|
+
(limit && limit > newData.length + matchedLength)
|
|
940
|
+
) {
|
|
941
|
+
cursor.continue();
|
|
942
|
+
} else {
|
|
943
|
+
resolve();
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
} else {
|
|
947
|
+
resolve();
|
|
948
|
+
}
|
|
949
|
+
};
|
|
950
|
+
|
|
951
|
+
request.onerror = () => {
|
|
952
|
+
reject(request.error);
|
|
953
|
+
};
|
|
954
|
+
});
|
|
903
955
|
}
|
|
904
956
|
|
|
905
957
|
function add(objectStore, object) {
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
958
|
+
return new Promise((resolve, reject) => {
|
|
959
|
+
const store = objectStore.objectStore || objectStore;
|
|
960
|
+
let request = store.add(object);
|
|
961
|
+
request.onsuccess = () => resolve(object);
|
|
962
|
+
request.onerror = () => reject(request.error);
|
|
963
|
+
});
|
|
912
964
|
}
|
|
913
965
|
|
|
914
966
|
function get(objectStore, object) {
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
967
|
+
return new Promise((resolve, reject) => {
|
|
968
|
+
const request = objectStore.get(object._id);
|
|
969
|
+
request.onsuccess = () => resolve(request.result);
|
|
970
|
+
request.onerror = () => reject(request.error);
|
|
971
|
+
});
|
|
920
972
|
}
|
|
921
973
|
|
|
922
974
|
function put(objectStore, object) {
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
975
|
+
return new Promise((resolve, reject) => {
|
|
976
|
+
const store = objectStore.objectStore || objectStore;
|
|
977
|
+
let request = store.put(object);
|
|
926
978
|
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
979
|
+
request.onsuccess = () => resolve(object);
|
|
980
|
+
request.onerror = () => reject(request.error);
|
|
981
|
+
});
|
|
930
982
|
}
|
|
931
983
|
|
|
932
984
|
function deleteObject(objectStore, object) {
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
985
|
+
return new Promise((resolve, reject) => {
|
|
986
|
+
const request = objectStore.delete(object._id);
|
|
987
|
+
request.onsuccess = () => resolve(object);
|
|
988
|
+
request.onerror = () => reject(request.error);
|
|
989
|
+
});
|
|
938
990
|
}
|
|
939
991
|
|
|
940
992
|
function cursorUpdate(cursor, update) {
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
993
|
+
return new Promise((resolve, reject) => {
|
|
994
|
+
const request = cursor.update(update);
|
|
995
|
+
request.onsuccess = () => resolve(request.source.value);
|
|
996
|
+
request.onerror = () => reject(request.error);
|
|
997
|
+
});
|
|
946
998
|
}
|
|
947
999
|
|
|
948
1000
|
function filter(objectStore, data, object, result) {
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
1001
|
+
let isMatch = true;
|
|
1002
|
+
let filter = data.filter || object.filter;
|
|
1003
|
+
if (filter) {
|
|
1004
|
+
if (filter.query) isMatch = queryData(result, filter.query);
|
|
1005
|
+
if (isMatch && filter.search)
|
|
1006
|
+
isMatch = searchData(result, filter.search);
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
return isMatch;
|
|
958
1010
|
}
|
|
959
1011
|
|
|
960
1012
|
function getGlobalOperators(data, object = {}) {
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
1013
|
+
for (let operator of Object.keys(data)) {
|
|
1014
|
+
if (!operator.startsWith("$")) return;
|
|
1015
|
+
for (let key of Object.keys(data[operator]))
|
|
1016
|
+
object[operator + "." + key] = data[operator][key];
|
|
1017
|
+
}
|
|
966
1018
|
|
|
967
|
-
|
|
1019
|
+
return object;
|
|
968
1020
|
}
|
|
969
1021
|
|
|
970
1022
|
function createData(data, newData, type) {
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
1023
|
+
// data.request = data[type] || {}
|
|
1024
|
+
if (
|
|
1025
|
+
data[type] &&
|
|
1026
|
+
data[type][0] &&
|
|
1027
|
+
data[type][0].isFilter === "isEmptyObjectFilter"
|
|
1028
|
+
) {
|
|
1029
|
+
data[type].shift();
|
|
1030
|
+
data.filter.isFilter = true;
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
data[type].push(...newData);
|
|
1034
|
+
|
|
1035
|
+
if (data.filter) {
|
|
1036
|
+
const sortPlan = getSortPlan(data.filter);
|
|
1037
|
+
const startingIndex = data.filter.index || 0;
|
|
1038
|
+
if (sortPlan.sort.length) {
|
|
1039
|
+
data[type] = sortData(data[type], data.filter.sort);
|
|
1040
|
+
data[type] = data[type].slice(
|
|
1041
|
+
startingIndex,
|
|
1042
|
+
data.filter.limit
|
|
1043
|
+
? startingIndex + data.filter.limit
|
|
1044
|
+
: undefined
|
|
1045
|
+
);
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
if (Array.isArray(data.filter.storages)) {
|
|
1049
|
+
for (const metadata of data.filter.storages) {
|
|
1050
|
+
if (metadata.storage !== "indexeddb") continue;
|
|
1051
|
+
|
|
1052
|
+
const returned = data[type].filter(
|
|
1053
|
+
(item) =>
|
|
1054
|
+
includesReference(item.$storage, metadata.storage) &&
|
|
1055
|
+
includesReference(item.$database, metadata.database) &&
|
|
1056
|
+
includesReference(item.$array, metadata.array)
|
|
1057
|
+
).length;
|
|
1058
|
+
|
|
1059
|
+
metadata.index = (metadata.startingIndex || 0) + returned;
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
data.filter.startingIndex = startingIndex;
|
|
1064
|
+
data.filter.index = startingIndex + data[type].length;
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
return data;
|
|
992
1068
|
}
|
|
993
1069
|
|
|
994
1070
|
function errorHandler(data, error, database, array) {
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1071
|
+
// console.error(error, data);
|
|
1072
|
+
// if (typeof error == "object") error["storage"] = "indexeddb";
|
|
1073
|
+
// else error = { storage: "indexeddb", message: error };
|
|
1074
|
+
|
|
1075
|
+
// if (database) error["database"] = database;
|
|
1076
|
+
// if (array) error["array"] = array;
|
|
1077
|
+
// if (data.error) data.error.push(error);
|
|
1078
|
+
// else data.error = [error];
|
|
1003
1079
|
}
|
|
1004
1080
|
|
|
1005
1081
|
init();
|
|
1006
1082
|
|
|
1007
1083
|
export default { send, ObjectId };
|
|
1084
|
+
|
|
1085
|
+
/**
|
|
1086
|
+
* Object identifier constructor exposed by the IndexedDB adapter.
|
|
1087
|
+
*
|
|
1088
|
+
* @name ObjectId
|
|
1089
|
+
* @public
|
|
1090
|
+
* @environment client
|
|
1091
|
+
*/
|
|
1092
|
+
|
|
1093
|
+
/**
|
|
1094
|
+
* A simple indexeddb component in vanilla javascript. Easily configured using HTML5 data-attributes and/or JavaScript API.
|
|
1095
|
+
*
|
|
1096
|
+
* @module indexeddb
|
|
1097
|
+
* @environment client
|
|
1098
|
+
*/
|