@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/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://cocreate.app/licenses/> or contact us at sales@cocreate.app
22
+ * For details, visit <https://CoCreateJS.com/licenses/> or contact us at sales@CoCreateJS.com
23
23
  */
24
24
 
25
25
  import {
26
- ObjectId,
27
- dotNotationToObject,
28
- searchData,
29
- sortData,
30
- queryData,
31
- createUpdate,
32
- isEqualArray,
33
- isEqualObject
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 init() {
40
- // Check for support.
41
- if (!isBrowser) {
42
- return;
43
- } else if (!("indexedDB" in window)) {
44
- console.log("This browser doesn't support IndexedDB.");
45
- setStatus(false);
46
- return;
47
- } else {
48
- try {
49
- indexedDB.databases();
50
- if (
51
- window.CoCreateConfig &&
52
- window.CoCreateConfig.indexeddb !== false
53
- )
54
- setStatus(true);
55
- } catch (e) {
56
- setStatus(false);
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
- if (window.CoCreateConfig) window.CoCreateConfig.indexeddb = value;
63
- else window.CoCreateConfig = { indexeddb: value };
64
-
65
- if (!value) {
66
- indexedDbFunction = indexedDb;
67
- send = value;
68
- } else if (!send) {
69
- send = indexedDbFunction;
70
- indexedDbFunction = true;
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
- try {
76
- let newData = [];
77
-
78
- let type = data.method.split(".")[0];
79
- if (type === "database") {
80
- if (data.method === "database.get") {
81
- return await processDatabase(data);
82
- } else await processDatabase(data, newData, type);
83
- } else {
84
- if (!data["timeStamp"])
85
- data["timeStamp"] = new Date().toISOString();
86
-
87
- let databases = data.database;
88
- if (!databases && data.organization_id)
89
- databases = [data.organization_id];
90
- if (!Array.isArray(databases)) databases = [databases];
91
-
92
- let arrays = data.array || [];
93
- if (!Array.isArray(arrays)) arrays = [arrays];
94
-
95
- for (let i = 0; i < databases.length; i++) {
96
- let database = databases[i];
97
- if (type === "array") {
98
- await processArray(data, newData, database, type);
99
- } else {
100
- for (let j = 0; j < arrays.length; j++) {
101
- if (type === "index")
102
- await processIndex(data, newData, database, type);
103
- else if (type === "object")
104
- await processObject(
105
- data,
106
- newData,
107
- database,
108
- arrays[j],
109
- type
110
- );
111
- }
112
- }
113
- }
114
- }
115
-
116
- return createData(data, newData, type);
117
- } catch (error) {
118
- errorHandler(data, error);
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
- return new Promise(
125
- (resolve, reject) => {
126
- if (!data) data = {};
127
- if (!data[type]) data[type] = [];
128
-
129
- if (data.method == "database.read") {
130
- indexedDB.databases().then((databases) => {
131
- for (let database of databases) {
132
- if (data.$filter && data.$filter.query) {
133
- let isFilter = queryData(
134
- database,
135
- data.$filter.query
136
- );
137
- if (isFilter)
138
- newData.push({
139
- $storage: ["indexeddb"],
140
- ...database
141
- });
142
- } else
143
- newData.push({
144
- $storage: ["indexeddb"],
145
- ...database
146
- });
147
- }
148
-
149
- resolve();
150
- });
151
- } else {
152
- let databases;
153
- if (data.method == "database.update")
154
- databases = Object.keys(data.database);
155
- else databases = data.database;
156
-
157
- if (!Array.isArray(databases)) databases = [databases];
158
-
159
- for (let i = 0; i < databases.length; i++) {
160
- let openRequest,
161
- database = databases[i];
162
- switch (data.method) {
163
- case "database.get":
164
- case "database.create":
165
- case "database.update":
166
- openRequest = indexedDB.open(database);
167
- break;
168
- case "database.delete":
169
- openRequest =
170
- indexedDB.deleteprocessDatabase(database);
171
- break;
172
- default:
173
- errorHandler(data, "unknown method", database);
174
- resolve(data);
175
- }
176
-
177
- openRequest.onsuccess = function () {
178
- let db = openRequest.result;
179
-
180
- if (data.method == "database.update") {
181
- let objectStoreNames = Array.from(
182
- db.objectStoreNames
183
- );
184
- for (let array of objectStoreNames) {
185
- let request = array.openCursor();
186
-
187
- request.onsuccess = function () {
188
- let cursor = request.result;
189
- if (cursor) {
190
- let value = cursor.value;
191
-
192
- cursor.continue();
193
- } else {
194
- // -1
195
- }
196
- };
197
-
198
- request.onerror = function () {
199
- errorHandler(
200
- data,
201
- request.error,
202
- database,
203
- array
204
- );
205
- };
206
- }
207
- } else if (data.upgrade === true) {
208
- db.close();
209
-
210
- let version =
211
- dbVersion.get(database) ||
212
- openRequest.result.version;
213
- resolve(
214
- indexedDB.open(database, Number((version += 1)))
215
- );
216
- dbVersion.set(database, version);
217
- } else if (data.array) {
218
- db.close();
219
-
220
- let version =
221
- dbVersion.get(database) ||
222
- openRequest.result.version;
223
- let request = indexedDB.open(
224
- database,
225
- Number((version += 1))
226
- );
227
- dbVersion.set(database, version);
228
-
229
- request.onupgradeneeded = function (event) {
230
- const db = event.target.result;
231
-
232
- let arrays = data.array;
233
- if (!Array.isArray(arrays)) arrays = [arrays];
234
-
235
- for (let j = 0; j < arrays.length; j++) {
236
- if (
237
- !db.objectStoreNames.contains(arrays[j])
238
- ) {
239
- try {
240
- db.createObjectStore(arrays[j], {
241
- keyPath: "_id",
242
- autoIncrement: true
243
- });
244
- } catch (error) {
245
- errorHandler(
246
- data,
247
- error,
248
- database,
249
- arrays[j]
250
- );
251
- }
252
- }
253
-
254
- if (data.indexName) {
255
- const transaction =
256
- event.target.transaction;
257
- const objectStore =
258
- transaction.objectStore(arrays[j]);
259
- if (
260
- !objectStore.indexNames.contains(
261
- data.indexName
262
- )
263
- )
264
- objectStore.createIndex(
265
- data.indexName,
266
- data.indexFields,
267
- { unique: data.unique || false }
268
- );
269
- }
270
- }
271
- };
272
-
273
- request.onsuccess = function (event) {
274
- if (databases.length - 1 === i) {
275
- if (data.method == "database.get")
276
- resolve(event.target.result);
277
- else resolve(data);
278
- }
279
- };
280
-
281
- request.onerror = function (event) {
282
- errorHandler(
283
- data,
284
- event.target.error,
285
- database,
286
- array
287
- );
288
- if (databases.length - 1 === i) {
289
- if (data.method == "database.get")
290
- resolve(event.target.result);
291
- else resolve(data);
292
- }
293
- };
294
- } else if (databases.length - 1 === i) {
295
- if (data.method == "database.get") resolve(db);
296
- else resolve(data);
297
- }
298
- };
299
-
300
- openRequest.onerror = function () {
301
- errorHandler(data, openRequest.error, database);
302
- resolve(data);
303
- };
304
- }
305
- }
306
- },
307
- (error) => {
308
- errorHandler(data, error);
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
- let db = await processDatabase({ method: "database.get", database });
315
-
316
- let objectStoreNames = Array.from(db.objectStoreNames);
317
- if (data.method == "array.read") {
318
- if (!data[type]) data[type] = [];
319
-
320
- if (!Array.isArray(data[type])) {
321
- data[type] = [data[type]];
322
- }
323
-
324
- for (let i = 0; i < objectStoreNames.length; i++) {
325
- if (data.$filter && data.$filter.query) {
326
- let isFilter = queryData(
327
- { name: objectStoreNames[i] },
328
- data.$filter.query
329
- );
330
- if (isFilter)
331
- newData.push({
332
- name: objectStoreNames,
333
- $storage: ["indexeddb"],
334
- $database: [database]
335
- });
336
- } else
337
- newData.push({
338
- name: objectStoreNames[i],
339
- $storage: ["indexeddb"],
340
- $database: [database]
341
- });
342
- }
343
- } else {
344
- db.close();
345
-
346
- let request = await processDatabase({
347
- method: "database.get",
348
- database,
349
- upgrade: true
350
- });
351
- await new Promise((resolve) => {
352
- request.onupgradeneeded = function (event) {
353
- let db = event.target.result;
354
- let arrays;
355
- if (data.method == "array.update")
356
- arrays = Object.keys(data[type]);
357
- else arrays = data[type];
358
- if (!Array.isArray(arrays)) arrays = [arrays];
359
-
360
- for (let i = 0; i < arrays.length; i++) {
361
- let error,
362
- array = arrays[i];
363
-
364
- let arrayExist = db.objectStoreNames.contains(array);
365
- if (arrayExist && data.method == "array.create") {
366
- error = "array already exists";
367
- } else if (data.method == "array.delete") {
368
- if (objectStoreNames.includes(array)) {
369
- db.deleteObjectStore(array);
370
- } else {
371
- error = "the array does not exist";
372
- }
373
- } else {
374
- if (!arrayExist && data.method == "array.update")
375
- array = data[type][arrays[i]];
376
-
377
- if (arrayExist) {
378
- if (data.method == "array.update") {
379
- let transaction = event.target.transaction;
380
- let objectStore =
381
- transaction.objectStore(array);
382
-
383
- if (
384
- !objectStoreNames.includes(
385
- data[type][arrays[i]]
386
- )
387
- )
388
- array = objectStore.name =
389
- data[type][arrays[i]];
390
- else
391
- error =
392
- "An array with the new name already exist";
393
- }
394
- } else if (data.method == "array.update")
395
- error = "array does not exists";
396
- }
397
-
398
- if (!error)
399
- newData.push({
400
- name: array,
401
- $storage: ["indexeddb"],
402
- $database: [database]
403
- });
404
- else errorHandler(data, error, database);
405
- }
406
- };
407
-
408
- request.onsuccess = function (event) {
409
- event.target.result.close();
410
- resolve();
411
- };
412
-
413
- request.onerror = function (event) {
414
- errorHandler(data, event.target.error, database);
415
- resolve();
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
- let db = await processDatabase({ method: "database.get", database });
423
- let arrayExist = db.objectStoreNames.contains(array);
424
- if (arrayExist) {
425
- // ToDO: switch here to types
426
- let transaction = db.transaction(array, "readonly");
427
- let objectStore = transaction.objectStore(array);
428
- let indexNames = Array.from(objectStore.indexNames);
429
-
430
- if (data.method == "index.read") {
431
- for (let i = 0; i < indexNames.length; i++) {
432
- let name = indexNames[i];
433
- if (data.$filter && data.$filter.query)
434
- if (!queryData({ name }, data.$filter.query)) continue;
435
-
436
- newData.push({
437
- name,
438
- $storage: ["indexeddb"],
439
- $database: [database],
440
- $array: [array]
441
- });
442
- }
443
- db.close();
444
- } else {
445
- db.close();
446
- let indexes;
447
- if (data.method == "index.update")
448
- indexes = Object.keys(data[type]);
449
- else indexes = data[type];
450
-
451
- if (!Array.isArray(indexes)) indexes = [indexes];
452
-
453
- for (let i = 0; i < indexes.length; i++) {
454
- let error,
455
- index = indexes[i];
456
- db = await processDatabase({
457
- method: "database.get",
458
- database,
459
- upgrade: true
460
- });
461
- let transaction = db.transaction;
462
- let objectStore = transaction.objectStore(array);
463
- let indexExist = indexNames.includes(index);
464
-
465
- if (data.method == "index.create") {
466
- if (!indexExist)
467
- objectStore.createIndex(index, index, {
468
- unique: false
469
- });
470
- else error = "index already exist";
471
- } else if (indexExist) {
472
- if (data.method == "index.delete")
473
- objectStore.deleteIndex(index);
474
- else if (
475
- data.method == "index.update" &&
476
- !indexNames.includes(data[type][indexes[i]])
477
- ) {
478
- if (!indexNames.includes(data[type][indexes[i]])) {
479
- let indexObj = objectStore.index(index);
480
- index = indexObj.name = data[type][indexes[i]];
481
- } else error = "new index name already exist";
482
- }
483
- } else error = "index does not exist";
484
-
485
- if (!error)
486
- newData.push({
487
- name: index,
488
- $storage: ["indexeddb"],
489
- $database: [database],
490
- $array: [array]
491
- });
492
- else errorHandler(data, error, database, array);
493
-
494
- db.result.close();
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
- let db = await processDatabase({ method: "database.get", database });
502
-
503
- try {
504
- if (!data[type]) data[type] = [];
505
- else if (typeof data[type] === "string")
506
- data[type] = [{ _id: data[type] }];
507
- else if (!Array.isArray(data[type])) data[type] = [data[type]];
508
-
509
- let isFilter;
510
- if (data.$filter) isFilter = true;
511
-
512
- let arrayExist = db.objectStoreNames.contains(array);
513
- if (!arrayExist) {
514
- db.close();
515
- if (
516
- data.method == "object.create" ||
517
- data.method == "object.update"
518
- ) {
519
- db = await processDatabase({
520
- method: "database.get",
521
- database,
522
- array
523
- });
524
- } else {
525
- return errorHandler(
526
- data,
527
- "array does not exist",
528
- database,
529
- array
530
- );
531
- }
532
- }
533
-
534
- if (!array || !db)
535
- throw new Error({ error: "This is an error message.", db, array });
536
-
537
- let lastItem;
538
- if ((isFilter && !data[type].length) || data.isFilter) {
539
- if (data[type].length) lastItem = data[type][data[type].length - 1];
540
- data[type] = [{ isFilter: "isEmptyObjectFilter" }];
541
-
542
- // data[type].splice(0, 0, { isFilter: 'isEmptyObjectFilter' });
543
- }
544
-
545
- let transactionType = "readwrite";
546
- if (data.method == "object.read") transactionType = "readonly";
547
-
548
- let transaction = db.transaction([array], transactionType);
549
- let objectStore = transaction.objectStore(array);
550
-
551
- let globalOperators = getGlobalOperators(data);
552
- let upsert = data.upsert;
553
-
554
- for (let i = 0; i < data[type].length; i++) {
555
- const reference = {
556
- $storage: ["indexeddb"],
557
- $database: [database],
558
- $array: [array]
559
- };
560
-
561
- delete data[type][i].$storage;
562
- delete data[type][i].$database;
563
- delete data[type][i].$array;
564
-
565
- if (data.method == "object.create") {
566
- if (data.organization_id)
567
- data[type][i]["organization_id"] = data.organization_id;
568
-
569
- if (!data[type][i]._id)
570
- data[type][i]["_id"] = ObjectId().toString();
571
- data[type][i] = dotNotationToObject(data[type][i]);
572
-
573
- // TODO: user_id || clientId shohould be retrieved from CoCreate
574
- data[type][i]["created"] = {
575
- on: data.timeStamp,
576
- by: data.user_id || data.clientId
577
- };
578
- data[type][i] = await add(objectStore, data[type][i]);
579
- data[type][i] = { ...reference, ...data[type][i] };
580
- } else {
581
- if (data[type][i].$filter) isFilter = true;
582
-
583
- if (data.method == "object.update") {
584
- if (data.organization_id)
585
- data[type][i]["organization_id"] = data.organization_id;
586
-
587
- if (data[type][i].$upsert) upsert = data[type][i].$upsert;
588
- // TODO: user_id || clientId shohould be retrieved from CoCreate
589
- if (!data[type][i].modified)
590
- data[type][i].modified = {
591
- on: data.timeStamp,
592
- by: data.user_id || data.clientId
593
- };
594
- }
595
-
596
- let index = 0,
597
- limit,
598
- range,
599
- direction;
600
- if (data[type][i]._id) {
601
- range = IDBKeyRange.only(data[type][i]._id);
602
- } else if (isFilter) {
603
- let indexName;
604
- let indexFields;
605
-
606
- if (data.$filter) {
607
- // TODO: requires a more robust sorting thats suports various directions
608
- // Build compound index name from sort keys
609
- if (data.$filter.sort && data.$filter.sort.length > 0) {
610
- // Create a standard index name without _id
611
- let sortKeys = data.$filter.sort
612
- .map((sort) => sort.key)
613
- .filter((key) => key !== "_id");
614
- indexName = sortKeys.join("_"); // e.g., "createdAt_category"
615
-
616
- // Define compound index fields
617
- indexFields = [...sortKeys];
618
-
619
- // Set cursor direction based on the first sort key direction
620
- direction =
621
- data.$filter.sort[0].direction === "desc"
622
- ? "prev"
623
- : "next";
624
-
625
- // Use last item's values, including _id, to set up the range for pagination
626
- if (indexName && lastItem && lastItem._id) {
627
- indexName += "_id";
628
- indexFields.push("_id");
629
- let lastKeyValues = [
630
- ...sortKeys.map((key) => lastItem[key]),
631
- lastItem._id
632
- ]; // Compound key array with _id
633
-
634
- // Create IDBKeyRange based on direction and lastKeyValues
635
- if (direction === "next") {
636
- range = IDBKeyRange.lowerBound(
637
- lastKeyValues,
638
- true
639
- );
640
- } else {
641
- range = IDBKeyRange.upperBound(
642
- lastKeyValues,
643
- true
644
- );
645
- }
646
- }
647
- } else if (
648
- data.$filter.query &&
649
- data.$filter.query[0] &&
650
- data.$filter.query[0].key
651
- ) {
652
- if (data.$filter.query[0].operator === "$eq") {
653
- range = IDBKeyRange.only(
654
- data.$filter.query[0].value
655
- );
656
- } else if (
657
- data.$filter.query[0].operator === "$gt"
658
- ) {
659
- range = IDBKeyRange.lowerBound(
660
- data.$filter.query[0].value,
661
- true
662
- );
663
- } else if (
664
- data.$filter.query[0].operator === "$lt"
665
- ) {
666
- range = IDBKeyRange.upperBound(
667
- data.$filter.query[0].value,
668
- true
669
- );
670
- } else if (
671
- data.$filter.query[0].operator === "$gte"
672
- ) {
673
- range = IDBKeyRange.lowerBound(
674
- data.$filter.query[0].value
675
- );
676
- } else if (
677
- data.$filter.query[0].operator === "$lte"
678
- ) {
679
- range = IDBKeyRange.upperBound(
680
- data.$filter.query[0].value
681
- );
682
- }
683
-
684
- indexName = data.$filter.query[0].key;
685
- }
686
- }
687
-
688
- if (indexName && !indexName.includes("-")) {
689
- // Check if the compound index exists; create it if not
690
- let indexNames = Array.from(objectStore.indexNames);
691
- if (!indexNames.includes(indexName)) {
692
- db.close();
693
- db = await processDatabase({
694
- method: "database.get",
695
- database,
696
- array,
697
- indexName,
698
- indexFields
699
- });
700
- transaction = db.transaction(array, "readwrite");
701
- objectStore = transaction.objectStore(array);
702
- }
703
- objectStore = objectStore.index(indexName);
704
- }
705
-
706
- let count = objectStore.count();
707
- count.onsuccess = function () {
708
- if (data.$filter) data.$filter.count = count.result;
709
- else if (data.object.$filter)
710
- data.object.$filter.count = count.result;
711
- };
712
-
713
- if (data.$filter) {
714
- if (data.$filter.index) index = data.$filter.index;
715
- if (data.$filter.limit) limit = data.$filter.limit;
716
- }
717
-
718
- if (data[type][i].$filter) {
719
- if (data[type][i].$filter.index)
720
- index = data[type][i].$filter.index;
721
- if (data[type][i].$filter.limit)
722
- limit = data[type][i].$filter.limit;
723
- }
724
- } else if (
725
- data.method == "object.delete" ||
726
- (data.method == "object.update" &&
727
- !range &&
728
- !upsert &&
729
- !isFilter)
730
- ) {
731
- continue;
732
- }
733
-
734
- await openCursor(
735
- objectStore,
736
- range,
737
- direction,
738
- data,
739
- newData,
740
- isFilter,
741
- index,
742
- limit,
743
- database,
744
- array,
745
- upsert,
746
- type,
747
- i,
748
- globalOperators,
749
- reference
750
- );
751
-
752
- if (index) newData = newData.slice(index);
753
- }
754
- }
755
-
756
- if (db.close) db.close();
757
- } catch (error) {
758
- errorHandler(data, error, database, array);
759
- if (db.close) db.close();
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
- objectStore,
765
- range,
766
- direction,
767
- data,
768
- newData,
769
- isFilter,
770
- index,
771
- limit,
772
- database,
773
- array,
774
- upsert,
775
- type,
776
- i,
777
- globalOperators,
778
- reference
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
- return new Promise(async (resolve, reject) => {
781
- const request = objectStore.openCursor(range || null, direction); // next, prev
782
-
783
- let hasUpdated,
784
- matchedLength = 0,
785
- hasSkipped;
786
- request.onsuccess = async () => {
787
- let cursor = request.result;
788
-
789
- if (
790
- data.method === "object.create" ||
791
- (data.method === "object.update" &&
792
- ((!range && !isFilter && upsert) ||
793
- (!cursor && upsert && !hasUpdated)))
794
- ) {
795
- let isMatch = true;
796
- if (isFilter) {
797
- if (cursor)
798
- isMatch = filter(
799
- objectStore,
800
- data[type][i],
801
- data[type][i],
802
- cursor.value
803
- );
804
- else if (data.method === "object.update" && !upsert)
805
- isMatch = false;
806
- }
807
-
808
- if (isMatch !== false) {
809
- try {
810
- data[type][i]._id = ObjectId(
811
- data[type][i]._id
812
- ).toString();
813
- } catch (error) {
814
- data[type][i]._id = ObjectId().toString();
815
- }
816
-
817
- let result;
818
- if (data.method == "object.create") {
819
- data[type][i] = await add(objectStore, data[type][i]);
820
- } else if (data.method == "object.update") {
821
- let update = createUpdate(
822
- cursor && cursor.value ? cursor.value : {},
823
- data[type][i],
824
- globalOperators
825
- );
826
- if (update) {
827
- await put(objectStore, update);
828
- data[type][i].$update = update;
829
- }
830
- }
831
- data[type][i] = { ...reference, ...data[type][i] };
832
-
833
- matchedLength++;
834
- }
835
- resolve();
836
- } else if (cursor) {
837
- if (index && !hasSkipped) {
838
- cursor.advance(index);
839
- hasSkipped = true;
840
- } else {
841
- let isMatch = true;
842
- if (isFilter)
843
- isMatch = filter(
844
- objectStore,
845
- data,
846
- data[type][i],
847
- cursor.value
848
- );
849
-
850
- if (isMatch !== false) {
851
- let result = cursor.value;
852
-
853
- if (data.method == "object.update") {
854
- let update = createUpdate(
855
- cursor.value,
856
- data[type][i],
857
- globalOperators
858
- );
859
- if (update) await cursorUpdate(cursor, update);
860
- result = data[type][i];
861
- result.$update = update;
862
- // result = cursor.update(update)
863
- hasUpdated = true;
864
- // set dotnotation for keys with $operators for items that end in [] to be used by socket.id
865
- // TODO: if update.$<operator>.someKey[] requires the index of inerted item added to the field name. update.$<operator>.someKey[<index>]
866
- // TODO: if $addToSet get field name and item if it does not exist.
867
- // TODO: if $pull get field name and find if item exist and delete.
868
- } else if (data.method == "object.delete") {
869
- result = cursor.value;
870
- cursor.delete();
871
- }
872
-
873
- if (data[type][i]._id)
874
- data[type][i] = { ...reference, ...result };
875
- else {
876
- let object = data[type].find(
877
- (obj) => obj._id && obj._id === result._id
878
- );
879
- if (object)
880
- object = { ...reference, ...object, ...result };
881
- else newData.push({ ...reference, ...result });
882
- }
883
- }
884
-
885
- if (
886
- !limit ||
887
- (limit && limit > newData.length + matchedLength)
888
- ) {
889
- cursor.continue();
890
- } else {
891
- resolve();
892
- }
893
- }
894
- } else {
895
- resolve();
896
- }
897
- };
898
-
899
- request.onerror = () => {
900
- reject(request.error);
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
- return new Promise((resolve, reject) => {
907
- const store = objectStore.objectStore || objectStore;
908
- let request = store.add(object);
909
- request.onsuccess = () => resolve(object);
910
- request.onerror = () => reject(request.error);
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
- return new Promise((resolve, reject) => {
916
- const request = objectStore.get(object._id);
917
- request.onsuccess = () => resolve(request.result);
918
- request.onerror = () => reject(request.error);
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
- return new Promise((resolve, reject) => {
924
- const store = objectStore.objectStore || objectStore;
925
- let request = store.put(object);
975
+ return new Promise((resolve, reject) => {
976
+ const store = objectStore.objectStore || objectStore;
977
+ let request = store.put(object);
926
978
 
927
- request.onsuccess = () => resolve(object);
928
- request.onerror = () => reject(request.error);
929
- });
979
+ request.onsuccess = () => resolve(object);
980
+ request.onerror = () => reject(request.error);
981
+ });
930
982
  }
931
983
 
932
984
  function deleteObject(objectStore, object) {
933
- return new Promise((resolve, reject) => {
934
- const request = objectStore.delete(object._id);
935
- request.onsuccess = () => resolve(object);
936
- request.onerror = () => reject(request.error);
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
- return new Promise((resolve, reject) => {
942
- const request = cursor.update(update);
943
- request.onsuccess = () => resolve(request.source.value);
944
- request.onerror = () => reject(request.error);
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
- let isMatch = true;
950
- let filter = data.$filter || object.$filter;
951
- if (filter) {
952
- if (filter.query) isMatch = queryData(result, filter.query);
953
- if (isMatch && filter.search)
954
- isMatch = searchData(result, filter.search);
955
- }
956
-
957
- return isMatch;
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
- for (let operator of Object.keys(data)) {
962
- if (!operator.startsWith("$")) return;
963
- for (let key of Object.keys(data[operator]))
964
- object[operator + "." + key] = data[operator][key];
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
- return object;
1019
+ return object;
968
1020
  }
969
1021
 
970
1022
  function createData(data, newData, type) {
971
- // data.request = data[type] || {}
972
- if (
973
- data[type] &&
974
- data[type][0] &&
975
- data[type][0].isFilter === "isEmptyObjectFilter"
976
- ) {
977
- data[type].shift();
978
- data.isFilter = true;
979
- }
980
-
981
- data[type].push(...newData);
982
-
983
- if (data.$filter) {
984
- if (data.$filter.sort)
985
- data[type] = sortData(data[type], data.$filter.sort);
986
-
987
- data.$filter.startingIndex = data.$filter.index;
988
- data.$filter.index += data[type].length;
989
- }
990
-
991
- return data;
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
- console.error(error, data);
996
- // if (typeof error == "object") error["storage"] = "indexeddb";
997
- // else error = { storage: "indexeddb", message: error };
998
-
999
- // if (database) error["database"] = database;
1000
- // if (array) error["array"] = array;
1001
- // if (data.error) data.error.push(error);
1002
- // else data.error = [error];
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
+ */