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