@cocreate/indexeddb 1.10.2 → 1.10.4

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
@@ -1,1195 +1,1195 @@
1
- /********************************************************************************
2
- * Copyright (C) 2020 CoCreate LLC and others.
3
- *
4
- *
5
- * SPDX-License-Identifier: MIT
6
- ********************************************************************************/
7
- import { ObjectId, dotNotationToObject, searchData, sortData, queryData } from '@cocreate/utils'
8
- import uuid from '@cocreate/uuid'
9
-
10
- let status = true;
11
-
12
- function createDatabase(data) {
13
- return Database('createDatabase', data)
14
- }
15
-
16
- const readDatabase = (data) => {
17
- return Database('readDatabase', data)
18
- }
19
-
20
- const updateDatabase = (data) => {
21
- return Database('updateDatabase', data)
22
- }
23
-
24
- const deleteDatabase = (data) => {
25
- return Database('deleteDatabase', data)
26
- }
27
-
28
- const getDatabase = (data) => {
29
- return Database('getDatabase', data)
30
- }
31
-
32
- const dbVersion = new Map()
33
- const Database = (action, data) => {
34
- return new Promise((resolve, reject) => {
35
- let type = 'database'
36
- let databaseArray = [];
37
- if (!data) data = {}
38
- if (data.request)
39
- data[type] = data.request
40
-
41
- if (!data['timeStamp'])
42
- data['timeStamp'] = new Date().toISOString()
43
-
44
- if (action == 'readDatabase') {
45
- indexedDB.databases().then((databases) => {
46
- for (let database of databases) {
47
- if (data.filter && data.filter.query) {
48
- let isFilter = queryData(database, data.filter.query)
49
- if (isFilter)
50
- databaseArray.push({ database, db: 'indexeddb' })
51
- } else
52
- databaseArray.push({ database, db: 'indexeddb' })
53
- }
54
-
55
- resolve(createData(data, databaseArray, type))
56
- })
57
- } else {
58
- let databases
59
- if (action == 'updateDatabase')
60
- databases = Object.keys(data.database).map((key) => [key, data.database[key]])
61
- else
62
- databases = data.database;
63
-
64
- if (!Array.isArray(databases))
65
- databases = [databases]
66
-
67
- let databasesLength = databases.length
68
- for (let database of databases) {
69
- if (action == 'updateDatabase')
70
- [database, value] = database
71
-
72
- let openRequest;
73
- switch (action) {
74
- case 'getDatabase':
75
- case 'createDatabase':
76
- case 'updateDatabase':
77
- openRequest = indexedDB.open(database);
78
- break;
79
- case 'deleteDatabase':
80
- openRequest = indexedDB.deleteDatabase(database);
81
- break;
82
- default:
83
- errorHandler(data, 'unknown action', database)
84
- resolve(data)
85
- }
86
-
87
- openRequest.onsuccess = function () {
88
- let db = openRequest.result;
89
- let dbs = [] // TODO: return an array of dbinstance??
90
- if (action == 'updateDatabase') {
91
- // TODO: open a cursor at database collection and save each collection and document at value
92
-
93
- let objectStoreNames = Array.from(db.objectStoreNames)
94
- for (let collection of objectStoreNames) {
95
- let request = collection.openCursor();
96
-
97
- request.onsuccess = function () {
98
- let cursor = request.result;
99
- if (cursor) {
100
- let value = cursor.value;
101
-
102
- cursor.continue();
103
- } else {
104
- // -1
105
- }
106
- };
107
-
108
- request.onerror = function () {
109
- errorHandler(data, request.error, database, collection)
110
- };
111
-
112
- }
113
-
114
- } else if (data.collection && data.collection.length) {
115
- let collections = data.collection;
116
- if (!Array.isArray(collections))
117
- collections = [collections]
118
-
119
- let collectionsLength = collections.length
120
- for (let collection of collections) {
121
-
122
- let collectionExist = db.objectStoreNames.contains(collection)
123
- if (!collectionExist || collectionsLength == 1 && data.upgrade == true) {
124
- db.close()
125
-
126
- let version = dbVersion.get(database) || openRequest.result.version
127
- let request = indexedDB.open(database, Number(version += 1));
128
- dbVersion.set(database, version)
129
-
130
- request.onupgradeneeded = function () {
131
- db = request.result;
132
-
133
- if (!collectionExist) {
134
- db = request.result;
135
- try {
136
- db.createObjectStore(collection, { keyPath: '_id', autoIncrement: true });
137
- } catch (error) {
138
- errorHandler(data, error, database, collection)
139
- }
140
- }
141
- };
142
-
143
- request.onsuccess = function () {
144
- collectionsLength -= 1
145
-
146
- if (!collectionsLength)
147
- databasesLength -= 1
148
-
149
- if (!databasesLength && !collectionsLength) {
150
- if (action == 'getDatabase') {
151
- let transaction = request.transaction
152
-
153
- if (data.upgrade == true && !transaction) {
154
- request.result.close()
155
-
156
- let version = dbVersion.get(database) || openRequest.result.version
157
- let upgrade = indexedDB.open(database, Number(version += 1));
158
- dbVersion.set(database, version)
159
-
160
- upgrade.onupgradeneeded = function () {
161
- resolve(upgrade)
162
- };
163
-
164
- } else {
165
- resolve(request.result)
166
- }
167
- }
168
- else
169
- resolve(data)
170
- }
171
- };
172
-
173
- request.onerror = function () {
174
- errorHandler(data, request.error, database, collection)
175
- collectionsLength -= 1
176
-
177
- if (!collectionsLength)
178
- databasesLength -= 1
179
- if (!databasesLength && !collectionsLength) {
180
- if (action == 'getDatabase')
181
- resolve(request)
182
- else
183
- resolve(data)
184
- }
185
- };
186
- } else {
187
- collectionsLength -= 1
188
-
189
- if (!collectionsLength)
190
- databasesLength -= 1
191
- if (!databasesLength && !collectionsLength) {
192
- if (action == 'getDatabase')
193
- resolve(db)
194
- else
195
- resolve(data)
196
- }
197
- }
198
- }
199
- } else {
200
- databasesLength -= 1
201
- if (!databasesLength) {
202
- if (action == 'getDatabase') {
203
- let transaction = openRequest.transaction
204
-
205
- if (data.upgrade == true && !transaction) {
206
- db.close()
207
-
208
- let version = dbVersion.get(database) || openRequest.result.version
209
- let upgrade = indexedDB.open(database, Number(version += 1));
210
- dbVersion.set(database, version)
211
-
212
- upgrade.onupgradeneeded = function () {
213
- resolve(upgrade)
214
- };
215
-
216
- } else {
217
- resolve(db)
218
- }
219
- }
220
- else
221
- resolve(data)
222
- }
223
- }
224
-
225
- }
226
-
227
- openRequest.onerror = function () {
228
- errorHandler(data, openRequest.error, database)
229
- resolve(data)
230
- };
231
-
232
- }
233
- }
234
-
235
- }, (error) => {
236
- errorHandler(data, error)
237
- });
238
- }
239
-
240
-
241
- function createCollection(data) {
242
- return collection('createCollection', data)
243
- }
244
-
245
- function readCollection(data) {
246
- return collection('readCollection', data)
247
- }
248
-
249
- function updateCollection(data) {
250
- return collection('updateCollection', data)
251
- }
252
-
253
- function deleteCollection(data) {
254
- return collection('deleteCollection', data)
255
- }
256
-
257
- const collection = (action, data) => {
258
- return new Promise((resolve, reject) => {
259
- let type = 'collection'
260
- let collectionArray = [];
261
-
262
- if (data.request)
263
- data[type] = data.request
264
-
265
- if (!data['timeStamp'])
266
- data['timeStamp'] = new Date().toISOString()
267
-
268
- let databases = data.database;
269
-
270
- if (!Array.isArray(databases))
271
- databases = [databases]
272
- let databasesLength = databases.length
273
- for (let database of databases) {
274
- let openRequest = indexedDB.open(database)
275
-
276
- openRequest.onsuccess = function () {
277
- let db = openRequest.result;
278
- db.close()
279
- let objectStoreNames = Array.from(db.objectStoreNames)
280
- if (action == 'readCollection') {
281
- if (!data[type])
282
- data[type] = []
283
-
284
- for (let collection of objectStoreNames) {
285
- if (data.filter && data.filter.query) {
286
- let isFilter = queryData({ name: collection }, data.filter.query)
287
- if (isFilter)
288
- collectionArray.push({ name: collection, db: 'indexeddb', database })
289
- } else
290
- collectionArray.push({ name: collection, db: 'indexeddb', database })
291
- }
292
-
293
- databasesLength -= 1
294
- if (!databasesLength) {
295
- resolve(createData(data, collectionArray, type))
296
- }
297
-
298
- } else {
299
- let collections
300
- if (action == 'updateCollection')
301
- collections = Object.keys(data[type]).map((key) => [key, data[type][key]])
302
- else
303
- collections = data[type];
304
- if (!Array.isArray(collections))
305
- collections = [collections]
306
- let collectionsLength = collections.length
307
- for (let collection of collections) {
308
- let value
309
- if (action == 'updateCollection')
310
- [collection, value] = collection
311
- let collectionExist = db.objectStoreNames.contains(collection)
312
- if (
313
- collectionExist && ['updateCollection', 'deleteCollection'].includes(action) ||
314
- !collectionExist && ['createCollection', 'updateCollection'].includes(action)
315
- ) {
316
- getDatabase({ database, collection, upgrade: true }).then((db) => {
317
- if (collectionExist && action == 'updateCollection') {
318
- let transaction = db.transaction;
319
- let objectStore = transaction.objectStore(collection);
320
-
321
- let valueExist = objectStoreNames.includes(value)
322
- if (!valueExist) {
323
- objectStore.name = value;
324
- }
325
- else
326
- errorHandler(data, 'collection does not exist', database)
327
-
328
- db.result.close()
329
-
330
- collectionArray.push({ name: value, db: 'indexeddb', database })
331
- collectionsLength -= 1
332
-
333
- if (!collectionsLength)
334
- databasesLength -= 1
335
-
336
- if (!databasesLength && !collectionsLength) {
337
- resolve(createData(data, collectionArray, type))
338
- }
339
-
340
- } else {
341
- db = db.result
342
-
343
- if (action == 'deleteCollection') {
344
- db.deleteObjectStore(collection)
345
- }
346
-
347
- db.close()
348
-
349
- collectionArray.push({ name: collection, db: 'indexeddb', database })
350
- collectionsLength -= 1
351
-
352
- if (!collectionsLength)
353
- databasesLength -= 1
354
-
355
- if (!databasesLength && !collectionsLength) {
356
- resolve(createData(data, collectionArray, type))
357
- }
358
-
359
- }
360
- }, (error) => {
361
- errorHandler(data, error, database)
362
- })
363
-
364
- } else {
365
- collectionArray.push({ name: collection, db: 'indexeddb', database })
366
- collectionsLength -= 1
367
-
368
- if (!collectionsLength)
369
- databasesLength -= 1
370
-
371
- if (!databasesLength && !collectionsLength) {
372
- resolve(createData(data, collectionArray, type))
373
- }
374
- }
375
-
376
- }
377
-
378
- }
379
-
380
- };
381
-
382
- openRequest.onerror = function () {
383
- errorHandler(data, openRequest.error, database)
384
- databasesLength -= 1
385
-
386
- if (!databasesLength) {
387
- resolve(data)
388
- }
389
- };
390
- }
391
- }, (error) => {
392
- errorHandler(data, error)
393
- });
394
- }
395
-
396
-
397
- function createIndex(data) {
398
- return index('createIndex', data)
399
- }
400
-
401
- function readIndex(data) {
402
- return index('readIndex', data)
403
- }
404
-
405
- function updateIndex(data) {
406
- return index('updateIndex', data)
407
- }
408
-
409
- function deleteIndex(data) {
410
- return index('deleteIndex', data)
411
- }
412
-
413
- const index = (action, data) => {
414
- return new Promise((resolve, reject) => {
415
- let type = 'index'
416
- let indexArray = [];
417
-
418
- if (data.request)
419
- data[type] = data.request
420
-
421
- if (!data['timeStamp'])
422
- data['timeStamp'] = new Date().toISOString()
423
-
424
- let databases = data.database;
425
- if (!Array.isArray(databases))
426
- databases = [databases]
427
-
428
- let collections = data.collection;
429
- if (!Array.isArray(collections))
430
- collections = [collections]
431
-
432
- let databasesLength = databases.length
433
- for (let database of databases) {
434
- let collectionsLength = collections.length
435
- for (let collection of collections) {
436
- getDatabase({ database }).then((db) => {
437
- let objectStoreNames = Array.from(db.objectStoreNames)
438
- if (objectStoreNames.includes(collection)) {
439
- let transaction = db.transaction(collection, "readonly");
440
- let objectStore = transaction.objectStore(collection);
441
- let indexNames = Array.from(objectStore.indexNames)
442
-
443
- if (action == 'readIndex') {
444
- for (let index of indexNames) {
445
- if (data.filter && data.filter.query) {
446
- let isFilter = queryData({ name: index }, data.filter.query)
447
- if (isFilter)
448
- indexArray.push({ name: index, db: 'indexeddb', database, collection })
449
- } else
450
- indexArray.push({ name: index, db: 'indexeddb', database, collection })
451
- }
452
- collectionsLength -= 1
453
- db.close()
454
-
455
- if (!collectionsLength)
456
- databasesLength -= 1
457
-
458
- if (!databasesLength && !collectionsLength)
459
- resolve(createData(data, indexArray, type))
460
-
461
- } else {
462
- db.close()
463
- let indexes
464
- if (action == 'updateIndex')
465
- indexes = Object.keys(data[type]).map((key) => [key, data[type][key]])
466
- else
467
- indexes = data[type];
468
- if (!Array.isArray(indexes))
469
- indexes = [indexes]
470
-
471
- let indexesLength = indexes.length
472
- for (let index of indexes) {
473
-
474
- getDatabase({ database, upgrade: true }).then((db2) => {
475
- let transaction = db2.transaction;
476
- let objectStore = transaction.objectStore(collection);
477
-
478
- let value
479
- if (action == 'updateIndex')
480
- [index, value] = index
481
-
482
- let indexExist = indexNames.includes(index)
483
-
484
- if (action == 'createIndex') {
485
- if (!indexExist) {
486
- objectStore.createIndex(index, index, { unique: false })
487
- indexArray.push({ name: index, db: 'indexeddb', database, collection })
488
- }
489
- else
490
- errorHandler(data, 'index exist', database, collection)
491
- }
492
- if (action == 'updateIndex') {
493
- let valueExist = indexNames.includes(value)
494
- let indexObj = objectStore.index(index);
495
- if (indexExist && !valueExist) {
496
- indexObj.name = value;
497
- indexArray.push({ name: value, db: 'indexeddb', database, collection })
498
- }
499
- else
500
- errorHandler(data, 'index does not exist', database, collection)
501
-
502
- }
503
- if (action == 'deleteIndex') {
504
- if (indexExist) {
505
- objectStore.deleteIndex(index)
506
- indexArray.push({ name: index, db: 'indexeddb', database, collection })
507
- }
508
- else
509
- errorHandler(data, 'index does not exist', database, collection)
510
- }
511
- db2.result.close()
512
-
513
- indexesLength -= 1
514
-
515
- if (!indexesLength)
516
- collectionsLength -= 1
517
-
518
- if (!collectionsLength)
519
- databasesLength -= 1
520
-
521
- if (!databasesLength && !collectionsLength && !indexesLength) {
522
- resolve(createData(data, indexArray, type))
523
- }
524
- });
525
- }
526
-
527
- }
528
-
529
- } else {
530
- db.close()
531
- errorHandler(data, `collection: ${collection} does not exist`, database, collection)
532
- resolve(data)
533
- }
534
- });
535
-
536
- }
537
- }
538
-
539
- }, (error) => {
540
- errorHandler(data, error)
541
- });
542
- }
543
-
544
- function createDocument(data) {
545
- return document('createDocument', data)
546
- }
547
-
548
- function readDocument(data) {
549
- return document('readDocument', data)
550
- }
551
-
552
- function updateDocument(data) {
553
- return document('updateDocument', data)
554
- }
555
-
556
- function deleteDocument(data) {
557
- return document('deleteDocument', data)
558
- }
559
-
560
-
561
- const document = (action, data) => {
562
- return new Promise((resolve, reject) => {
563
- let type = 'document'
564
- let documents = []
565
-
566
- if (data.request)
567
- data[type] = data.request
568
-
569
- if (data.document && !Array.isArray(data.document))
570
- data.document = [data.document]
571
-
572
- if (!data['timeStamp'])
573
- data['timeStamp'] = new Date().toISOString()
574
-
575
- let databases = data.database;
576
- if (!Array.isArray(databases))
577
- databases = [databases]
578
-
579
- let collections = data.collection;
580
- if (!Array.isArray(collections))
581
- collections = [collections]
582
-
583
- let databasesLength = databases.length
584
- for (let database of databases) {
585
- getDatabase({ database }).then((db) => {
586
- let collectionsLength = collections.length
587
- for (let collection of collections) {
588
- let collectionExist = db.objectStoreNames.contains(collection)
589
- if (!collectionExist) {
590
- db.close()
591
- getDatabase({ database, collection }).then((db) => {
592
- if (data.filter || action == 'readDocument' && (!data.document || !data.document.length)) {
593
- db.close()
594
- readDocuments(data, database, collection).then((filterDocs) => {
595
- getDatabase({ database }).then((db) => {
596
- let transaction = db.transaction([collection], "readwrite");
597
- let objectStore = transaction.objectStore(collection);
598
-
599
- runDocs({ action, data, objectStore, documents, filterDocs, database, collection, type }).then(() => {
600
- db.close()
601
- collectionsLength -= 1
602
-
603
- if (!collectionsLength)
604
- databasesLength -= 1
605
-
606
- if (!databasesLength && !collectionsLength) {
607
- resolve(createData(data, documents, type))
608
- }
609
- })
610
- })
611
-
612
- })
613
- } else {
614
- let transaction = db.transaction([collection], "readwrite");
615
- let objectStore = transaction.objectStore(collection);
616
-
617
- runDocs({ action, data, objectStore, documents, database, collection, type }).then(() => {
618
- db.close()
619
- collectionsLength -= 1
620
-
621
- if (!collectionsLength)
622
- databasesLength -= 1
623
-
624
- if (!databasesLength && !collectionsLength) {
625
- resolve(createData(data, documents, type))
626
- }
627
- })
628
- }
629
-
630
- }).catch((error) => {
631
- errorHandler(data, { message: error, data }, database)
632
- collectionsLength -= 1
633
-
634
- if (!collectionsLength)
635
- databasesLength -= 1
636
-
637
- if (!databasesLength && !collectionsLength)
638
- resolve(createData(data, documents, type))
639
-
640
- })
641
-
642
- } else {
643
- if (data.filter || action == 'readDocument' && (!data.document || !data.document.length)) {
644
-
645
- db.close()
646
- readDocuments(data, database, collection).then((filterDocs) => {
647
- getDatabase({ database }).then((db) => {
648
- let transaction = db.transaction([collection], "readwrite");
649
- let objectStore = transaction.objectStore(collection);
650
-
651
- runDocs({ action, data, objectStore, documents, filterDocs, database, collection, type }).then(() => {
652
- db.close()
653
- collectionsLength -= 1
654
-
655
- if (!collectionsLength)
656
- databasesLength -= 1
657
-
658
- if (!databasesLength && !collectionsLength) {
659
- resolve(createData(data, documents, type))
660
- }
661
- })
662
- })
663
- })
664
- } else {
665
- let transaction = db.transaction([collection], "readwrite");
666
- let objectStore = transaction.objectStore(collection);
667
-
668
- runDocs({ action, data, objectStore, documents, database, collection, type }).then(() => {
669
- db.close()
670
- collectionsLength -= 1
671
-
672
- if (!collectionsLength)
673
- databasesLength -= 1
674
- if (!databasesLength && !collectionsLength) {
675
- resolve(createData(data, documents, type))
676
- }
677
- })
678
- }
679
- }
680
- }
681
-
682
- }).catch((error) => {
683
- errorHandler(data, { message: error, data }, database)
684
-
685
- databasesLength -= 1
686
-
687
- if (!databasesLength)
688
- resolve(createData(data, documents, type))
689
- })
690
-
691
- }
692
- })
693
- }
694
-
695
- function runDocs({ action, data, objectStore, documents, filterDocs, database, collection, type }) {
696
- return new Promise((resolve, reject) => {
697
- let docs = []
698
-
699
- if (Array.isArray(data[type]))
700
- docs.push(...data[type]);
701
- // else if (data[type] != undefined && action == 'createDocument')
702
- // docs.push(data[type])
703
- // else if (data[type] != undefined)
704
- // docs.push({...data[type]})
705
-
706
-
707
- if (filterDocs && filterDocs.length) {
708
- if (action == 'readDocument') {
709
- for (let i = 0; i < filterDocs.length; i++)
710
- documents.push({ db: 'indexeddb', database, collection, ...filterDocs[i] })
711
-
712
- }
713
-
714
- if (action == 'updateDocument') {
715
- let updateData = docs[0];
716
- if (updateData)
717
- docs.shift();
718
- for (let filterDoc of filterDocs) {
719
- filterDoc = dotNotationToObject(updateData, filterDoc)
720
- docs.push(filterDoc)
721
- }
722
- }
723
-
724
- if (action == 'deleteDocument')
725
- docs.push(...filterDocs)
726
- }
727
-
728
- let docsLength = docs.length
729
-
730
- if (docsLength > 0) {
731
- for (let doc of docs) {
732
- // TODO deDuplcate document_id per collection
733
- delete doc.db
734
- delete doc.database
735
- delete doc.collection
736
-
737
- if (action == 'updateDocument' || action == 'deleteDocument') {
738
- if (doc._id) {
739
- if (data.organization_id)
740
- doc['organization_id'] = data.organization_id
741
- doc['modified'] = { on: data.timeStamp, by: data.user_id || data.clientId }
742
-
743
- updateDoc(action, data, doc, objectStore, database, collection).then((doc) => {
744
- doc.db = 'indexeddb'
745
- doc.database = database
746
- doc.collection = collection
747
- documents.push(doc)
748
- docsLength -= 1
749
- if (!docsLength) {
750
- resolve()
751
- }
752
- })
753
- } else {
754
- docsLength -= 1
755
- if (!docsLength)
756
- resolve()
757
- }
758
- } else {
759
- let request;
760
- if (action == 'createDocument') {
761
- if (!doc._id)
762
- doc['_id'] = ObjectId()
763
- doc = dotNotationToObject(doc)
764
-
765
- if (data.organization_id)
766
- doc['organization_id'] = data.organization_id
767
- doc['created'] = { on: data.timeStamp, by: data.user_id || data.clientId }
768
- request = objectStore.add(doc);
769
- }
770
-
771
- if (action == 'readDocument') {
772
- if (action == 'readDocument' && doc._id)
773
- request = objectStore.get(doc._id);
774
- else {
775
- docsLength -= 1
776
- errorHandler(data, { message: 'requires _id', document: doc }, database, objectStore.name)
777
- if (!docsLength) {
778
- resolve()
779
- }
780
- }
781
- }
782
-
783
- request.onsuccess = function () {
784
- if (request.result) {
785
- if (action == 'readDocument')
786
- doc = request.result
787
- doc.db = 'indexeddb'
788
- doc.database = database
789
- doc.collection = collection
790
-
791
- documents.push(doc)
792
- }
793
- docsLength -= 1
794
- if (!docsLength) {
795
- resolve()
796
- }
797
- };
798
-
799
- request.onerror = function () {
800
- errorHandler(data, { message: request.error, document: doc }, database, objectStore.name)
801
- resolve()
802
- };
803
- }
804
- }
805
- } else {
806
- resolve()
807
- }
808
- }, (error) => {
809
- errorHandler(data, { message: error, document: doc }, database, objectStore.name)
810
- })
811
- }
812
-
813
- function updateDoc(action, data, doc, objectStore, database, collection) {
814
- return new Promise((resolve, reject) => {
815
- let get = objectStore.get(doc._id)
816
- get.onsuccess = function () {
817
- ;
818
-
819
- let result = get.result
820
- if (result || data.upsert == true) {
821
- let modify = true
822
- if (result && doc.modified && result.modified)
823
- if (doc.modified.on < result.modified.on)
824
- modify = false
825
-
826
- if (modify) {
827
- if (action == 'updateDocument') {
828
- doc = dotNotationToObject(doc, get.result)
829
-
830
- if (data.updateName) {
831
- for (let key of Object.keys(data.updateName)) {
832
- let val = doc[key]
833
- delete doc[key]
834
- doc[data.updateName[key]] = val
835
- }
836
- }
837
-
838
- if (data.deleteName) {
839
- for (let key of Object.keys(data.deleteName)) {
840
- delete doc[key]
841
- }
842
-
843
- }
844
-
845
- let put = objectStore.put(doc);
846
-
847
- put.onsuccess = function () {
848
- resolve(doc)
849
- };
850
-
851
- put.onerror = function () {
852
- errorHandler(data, put.error, database, collection, doc)
853
- resolve(doc)
854
- };
855
-
856
- }
857
-
858
- if (action == 'deleteDocument') {
859
- let deleteDoc = objectStore.delete(doc._id);
860
-
861
- deleteDoc.onsuccess = function () {
862
- resolve(doc)
863
- };
864
-
865
- deleteDoc.onerror = function () {
866
- errorHandler(data, deleteDoc.error, database, collection, doc)
867
- resolve(doc)
868
- };
869
- }
870
-
871
- } else {
872
- // TODO: handle if _id not found upsert
873
- errorHandler(data, 'doc in db is newer', database, collection, doc)
874
- resolve(doc)
875
- }
876
- } else {
877
- errorHandler(data, 'document doest not exist and upsert is not true', database, collection, doc)
878
- resolve(doc)
879
- }
880
-
881
- };
882
-
883
- get.onerror = function () {
884
- errorHandler(data, get.error, database, collection, doc)
885
- resolve(doc)
886
- };
887
-
888
- })
889
- }
890
-
891
- const readDocuments = (data, database, collection) => {
892
- return new Promise((resolve, reject) => {
893
- let openRequest = indexedDB.open(database);
894
-
895
- openRequest.onsuccess = function () {
896
- let db = openRequest.result;
897
- try {
898
- let transaction = db.transaction([collection], "readonly");
899
- let objectStore = transaction.objectStore(collection);
900
-
901
- if (data.filter) {
902
- let count = objectStore.count();
903
- count.onsuccess = function () {
904
- data.filter.count = count.result
905
- }
906
- }
907
-
908
- let isIndex = false
909
- let indexName;
910
- if (data.filter && data.filter.sort && data.filter.sort[0] && data.filter.sort[0].name) {
911
- indexName = data.filter.sort[0].name
912
- // if (indexName.includes('-'))
913
- // isIndex = false
914
- // else
915
- // isIndex = true
916
- }
917
-
918
- if (isIndex) {
919
- let direction = data.filter.sort[0].direction
920
- if (direction == 'desc')
921
- direction = 'prev'
922
- else
923
- direction = 'next'
924
-
925
- let indexNames = Array.from(objectStore.indexNames)
926
- let indexExist = indexNames.includes(indexName)
927
- if (!indexExist) {
928
- db.close()
929
- getDatabase({ database, upgrade: true }).then((db2) => {
930
- let transaction = db2.transaction;
931
- let objectStore = transaction.objectStore(collection);
932
- try {
933
- objectStore.createIndex(indexName, indexName, { unique: false })
934
- } catch (error) {
935
- // console.log(error)
936
- }
937
- const indexStore = objectStore.index(indexName);
938
-
939
- readDocs(data, database, collection, indexStore, direction).then((results) => {
940
- if (db2 && db2.close)
941
- db2.close()
942
- resolve(results)
943
- })
944
- })
945
- } else {
946
- const indexStore = objectStore.index(indexName);
947
- readDocs(data, database, collection, indexStore, direction).then((results) => {
948
- db.close()
949
- resolve(results)
950
- })
951
- }
952
- } else {
953
- readDocs(data, database, collection, objectStore).then((results) => {
954
- db.close()
955
- resolve(results)
956
- })
957
- }
958
- } catch (error) {
959
- errorHandler(data, error, database)
960
- db.close()
961
- resolve([])
962
- return
963
- }
964
-
965
- };
966
-
967
- openRequest.onerror = function () {
968
- errorHandler(data, openRequest.error)
969
- resolve(data)
970
- };
971
- })
972
- }
973
-
974
- async function readDocs(data, database, collection, objectStore, direction) {
975
- return new Promise((resolve, reject) => {
976
-
977
- let results = [], index = 0, limit
978
- if (data.filter) {
979
- if (data.filter.startIndex)
980
- index = data.filter.startIndex
981
- if (data.filter.limit)
982
- limit = data.filter.limit
983
- if (limit)
984
- limit = index + limit;
985
- }
986
-
987
- const request = objectStore.openCursor(null, direction);
988
- request.onsuccess = function () {
989
- let cursor = request.result;
990
- if (cursor) {
991
- let isFilter = true;
992
- let value = cursor.value;
993
- if (data.filter) {
994
- if (data.filter.query)
995
- isFilter = queryData(value, data.filter.query);
996
- if (isFilter && data.filter.search)
997
- isFilter = searchData(value, data.filter.search);
998
- }
999
- if (isFilter !== false)
1000
- results.push(value)
1001
- if (limit && limit == results.length) {
1002
- results = results.slice(index, limit)
1003
- resolve(results)
1004
- } else
1005
- cursor.continue();
1006
- } else {
1007
- if (index)
1008
- results = results.slice(index)
1009
- resolve(results)
1010
- }
1011
- }
1012
-
1013
- request.onerror = function () {
1014
- errorHandler(data, request.error, database, collection)
1015
- db.close()
1016
- resolve([])
1017
- };
1018
-
1019
- });
1020
- }
1021
-
1022
- async function generateDB(organization = { document: {} }, user = { document: {} }) {
1023
- const organization_id = organization.document._id || ObjectId();
1024
- const defaultKey = organization.document.key || uuid.generate();
1025
- const user_id = user.document._id || ObjectId();
1026
-
1027
- let apiKey = await readDocument({ database: organization_id, collection: 'keys', organization_id })
1028
- if (apiKey && apiKey.document && apiKey.document[0])
1029
- return
1030
-
1031
- try {
1032
- // Create organization
1033
- organization.database = organization_id
1034
- organization.collection = 'organizations'
1035
- organization.document._id = organization_id
1036
- organization.document.name = organization.document.name || 'untitiled'
1037
- organization.organization_id = organization_id
1038
- createDocument(organization);
1039
-
1040
- // Create user
1041
- user.database = organization_id
1042
- user.collection = 'users'
1043
- user.document._id = user_id
1044
- user.document.firstname = user.document.firstname || 'untitiled'
1045
- user.document.lastname = user.document.lastname || 'untitiled'
1046
- user.organization_id = organization_id
1047
- createDocument(user);
1048
-
1049
- // Create default key
1050
- let key = {
1051
- database: organization_id,
1052
- collection: 'keys',
1053
- document: {
1054
- _id: ObjectId(),
1055
- type: "key",
1056
- key: defaultKey,
1057
- actions: {
1058
- signIn: true,
1059
- signUp: true
1060
- },
1061
- default: true
1062
- },
1063
- organization_id
1064
- }
1065
- createDocument(key);
1066
-
1067
- // Create role
1068
- let role_id = ObjectId();
1069
- let role = {
1070
- database: organization_id,
1071
- collection: 'keys',
1072
- document: {
1073
- _id: role_id,
1074
- type: "role",
1075
- name: "admin",
1076
- admin: "true"
1077
- },
1078
- organization_id
1079
- };
1080
- createDocument(role);
1081
-
1082
- // Create user key
1083
- let userKey = {
1084
- database: organization_id,
1085
- collection: 'keys',
1086
- document: {
1087
- _id: ObjectId(),
1088
- type: "user",
1089
- key: user_id,
1090
- collection: 'users', // could be any collection
1091
- roles: [role_id],
1092
- email: user.document.email,
1093
- password: user.document.password || btoa('0000')
1094
- },
1095
- organization_id
1096
- };
1097
- createDocument(userKey);
1098
-
1099
- return [organization, key, user, role, userKey]
1100
-
1101
- } catch (error) {
1102
- return false
1103
- }
1104
- }
1105
-
1106
- function errorHandler(data, error, database, collection) {
1107
- if (typeof error == 'object')
1108
- error['db'] = 'indexeddb'
1109
- else
1110
- error = { db: 'indexeddb', message: error }
1111
-
1112
- if (database)
1113
- error['database'] = database
1114
- if (collection)
1115
- error['collection'] = collection
1116
- if (data.error)
1117
- data.error.push(error)
1118
- else
1119
- data.error = [error]
1120
-
1121
- }
1122
-
1123
- function createData(data, array, type) {
1124
- data.request = data[type] || {}
1125
-
1126
- if (data.filter && data.filter.sort)
1127
- data[type] = sortData(array, data.filter.sort)
1128
- else
1129
- data[type] = array
1130
-
1131
- if (data.returnLog) {
1132
- if (!data.log)
1133
- data.log = []
1134
- data.log.push(...data[type])
1135
- }
1136
-
1137
- return data
1138
- }
1139
-
1140
-
1141
- function init() {
1142
- // Check for support.
1143
- if (!('indexedDB' in window)) {
1144
- console.log("This browser doesn't support IndexedDB.");
1145
- setStatus(false);
1146
- return;
1147
- } else {
1148
- try {
1149
- indexedDB.databases()
1150
- if (window.CoCreateConfig && window.CoCreateConfig.indexeddb !== false)
1151
- setStatus(true)
1152
- } catch (e) {
1153
- setStatus(false);
1154
- }
1155
- }
1156
- }
1157
-
1158
- function setStatus(value) {
1159
- if (window.CoCreateConfig)
1160
- window.CoCreateConfig.indexeddb = value;
1161
- else
1162
- window.CoCreateConfig = { indexeddb: value };
1163
-
1164
- status = value;
1165
- }
1166
-
1167
-
1168
- init();
1169
-
1170
- export default {
1171
- status,
1172
- ObjectId,
1173
- getDatabase,
1174
- createDatabase,
1175
- readDatabase,
1176
- updateDatabase,
1177
- deleteDatabase,
1178
-
1179
- createCollection,
1180
- readCollection,
1181
- updateCollection,
1182
- deleteCollection,
1183
-
1184
- createIndex,
1185
- readIndex,
1186
- updateIndex,
1187
- deleteIndex,
1188
-
1189
- createDocument,
1190
- readDocument,
1191
- updateDocument,
1192
- deleteDocument,
1193
-
1194
- generateDB
1
+ /********************************************************************************
2
+ * Copyright (C) 2020 CoCreate LLC and others.
3
+ *
4
+ *
5
+ * SPDX-License-Identifier: MIT
6
+ ********************************************************************************/
7
+ import { ObjectId, dotNotationToObject, searchData, sortData, queryData } from '@cocreate/utils'
8
+ import uuid from '@cocreate/uuid'
9
+
10
+ let status = true;
11
+
12
+ function createDatabase(data) {
13
+ return Database('createDatabase', data)
14
+ }
15
+
16
+ const readDatabase = (data) => {
17
+ return Database('readDatabase', data)
18
+ }
19
+
20
+ const updateDatabase = (data) => {
21
+ return Database('updateDatabase', data)
22
+ }
23
+
24
+ const deleteDatabase = (data) => {
25
+ return Database('deleteDatabase', data)
26
+ }
27
+
28
+ const getDatabase = (data) => {
29
+ return Database('getDatabase', data)
30
+ }
31
+
32
+ const dbVersion = new Map()
33
+ const Database = (action, data) => {
34
+ return new Promise((resolve, reject) => {
35
+ let type = 'database'
36
+ let databaseArray = [];
37
+ if (!data) data = {}
38
+ if (data.request)
39
+ data[type] = data.request
40
+
41
+ if (!data['timeStamp'])
42
+ data['timeStamp'] = new Date().toISOString()
43
+
44
+ if (action == 'readDatabase') {
45
+ indexedDB.databases().then((databases) => {
46
+ for (let database of databases) {
47
+ if (data.filter && data.filter.query) {
48
+ let isFilter = queryData(database, data.filter.query)
49
+ if (isFilter)
50
+ databaseArray.push({ database, db: 'indexeddb' })
51
+ } else
52
+ databaseArray.push({ database, db: 'indexeddb' })
53
+ }
54
+
55
+ resolve(createData(data, databaseArray, type))
56
+ })
57
+ } else {
58
+ let databases
59
+ if (action == 'updateDatabase')
60
+ databases = Object.keys(data.database).map((key) => [key, data.database[key]])
61
+ else
62
+ databases = data.database;
63
+
64
+ if (!Array.isArray(databases))
65
+ databases = [databases]
66
+
67
+ let databasesLength = databases.length
68
+ for (let database of databases) {
69
+ if (action == 'updateDatabase')
70
+ [database, value] = database
71
+
72
+ let openRequest;
73
+ switch (action) {
74
+ case 'getDatabase':
75
+ case 'createDatabase':
76
+ case 'updateDatabase':
77
+ openRequest = indexedDB.open(database);
78
+ break;
79
+ case 'deleteDatabase':
80
+ openRequest = indexedDB.deleteDatabase(database);
81
+ break;
82
+ default:
83
+ errorHandler(data, 'unknown action', database)
84
+ resolve(data)
85
+ }
86
+
87
+ openRequest.onsuccess = function () {
88
+ let db = openRequest.result;
89
+ let dbs = [] // TODO: return an array of dbinstance??
90
+ if (action == 'updateDatabase') {
91
+ // TODO: open a cursor at database collection and save each collection and document at value
92
+
93
+ let objectStoreNames = Array.from(db.objectStoreNames)
94
+ for (let collection of objectStoreNames) {
95
+ let request = collection.openCursor();
96
+
97
+ request.onsuccess = function () {
98
+ let cursor = request.result;
99
+ if (cursor) {
100
+ let value = cursor.value;
101
+
102
+ cursor.continue();
103
+ } else {
104
+ // -1
105
+ }
106
+ };
107
+
108
+ request.onerror = function () {
109
+ errorHandler(data, request.error, database, collection)
110
+ };
111
+
112
+ }
113
+
114
+ } else if (data.collection && data.collection.length) {
115
+ let collections = data.collection;
116
+ if (!Array.isArray(collections))
117
+ collections = [collections]
118
+
119
+ let collectionsLength = collections.length
120
+ for (let collection of collections) {
121
+
122
+ let collectionExist = db.objectStoreNames.contains(collection)
123
+ if (!collectionExist || collectionsLength == 1 && data.upgrade == true) {
124
+ db.close()
125
+
126
+ let version = dbVersion.get(database) || openRequest.result.version
127
+ let request = indexedDB.open(database, Number(version += 1));
128
+ dbVersion.set(database, version)
129
+
130
+ request.onupgradeneeded = function () {
131
+ db = request.result;
132
+
133
+ if (!collectionExist) {
134
+ db = request.result;
135
+ try {
136
+ db.createObjectStore(collection, { keyPath: '_id', autoIncrement: true });
137
+ } catch (error) {
138
+ errorHandler(data, error, database, collection)
139
+ }
140
+ }
141
+ };
142
+
143
+ request.onsuccess = function () {
144
+ collectionsLength -= 1
145
+
146
+ if (!collectionsLength)
147
+ databasesLength -= 1
148
+
149
+ if (!databasesLength && !collectionsLength) {
150
+ if (action == 'getDatabase') {
151
+ let transaction = request.transaction
152
+
153
+ if (data.upgrade == true && !transaction) {
154
+ request.result.close()
155
+
156
+ let version = dbVersion.get(database) || openRequest.result.version
157
+ let upgrade = indexedDB.open(database, Number(version += 1));
158
+ dbVersion.set(database, version)
159
+
160
+ upgrade.onupgradeneeded = function () {
161
+ resolve(upgrade)
162
+ };
163
+
164
+ } else {
165
+ resolve(request.result)
166
+ }
167
+ }
168
+ else
169
+ resolve(data)
170
+ }
171
+ };
172
+
173
+ request.onerror = function () {
174
+ errorHandler(data, request.error, database, collection)
175
+ collectionsLength -= 1
176
+
177
+ if (!collectionsLength)
178
+ databasesLength -= 1
179
+ if (!databasesLength && !collectionsLength) {
180
+ if (action == 'getDatabase')
181
+ resolve(request)
182
+ else
183
+ resolve(data)
184
+ }
185
+ };
186
+ } else {
187
+ collectionsLength -= 1
188
+
189
+ if (!collectionsLength)
190
+ databasesLength -= 1
191
+ if (!databasesLength && !collectionsLength) {
192
+ if (action == 'getDatabase')
193
+ resolve(db)
194
+ else
195
+ resolve(data)
196
+ }
197
+ }
198
+ }
199
+ } else {
200
+ databasesLength -= 1
201
+ if (!databasesLength) {
202
+ if (action == 'getDatabase') {
203
+ let transaction = openRequest.transaction
204
+
205
+ if (data.upgrade == true && !transaction) {
206
+ db.close()
207
+
208
+ let version = dbVersion.get(database) || openRequest.result.version
209
+ let upgrade = indexedDB.open(database, Number(version += 1));
210
+ dbVersion.set(database, version)
211
+
212
+ upgrade.onupgradeneeded = function () {
213
+ resolve(upgrade)
214
+ };
215
+
216
+ } else {
217
+ resolve(db)
218
+ }
219
+ }
220
+ else
221
+ resolve(data)
222
+ }
223
+ }
224
+
225
+ }
226
+
227
+ openRequest.onerror = function () {
228
+ errorHandler(data, openRequest.error, database)
229
+ resolve(data)
230
+ };
231
+
232
+ }
233
+ }
234
+
235
+ }, (error) => {
236
+ errorHandler(data, error)
237
+ });
238
+ }
239
+
240
+
241
+ function createCollection(data) {
242
+ return collection('createCollection', data)
243
+ }
244
+
245
+ function readCollection(data) {
246
+ return collection('readCollection', data)
247
+ }
248
+
249
+ function updateCollection(data) {
250
+ return collection('updateCollection', data)
251
+ }
252
+
253
+ function deleteCollection(data) {
254
+ return collection('deleteCollection', data)
255
+ }
256
+
257
+ const collection = (action, data) => {
258
+ return new Promise((resolve, reject) => {
259
+ let type = 'collection'
260
+ let collectionArray = [];
261
+
262
+ if (data.request)
263
+ data[type] = data.request
264
+
265
+ if (!data['timeStamp'])
266
+ data['timeStamp'] = new Date().toISOString()
267
+
268
+ let databases = data.database;
269
+
270
+ if (!Array.isArray(databases))
271
+ databases = [databases]
272
+ let databasesLength = databases.length
273
+ for (let database of databases) {
274
+ let openRequest = indexedDB.open(database)
275
+
276
+ openRequest.onsuccess = function () {
277
+ let db = openRequest.result;
278
+ db.close()
279
+ let objectStoreNames = Array.from(db.objectStoreNames)
280
+ if (action == 'readCollection') {
281
+ if (!data[type])
282
+ data[type] = []
283
+
284
+ for (let collection of objectStoreNames) {
285
+ if (data.filter && data.filter.query) {
286
+ let isFilter = queryData({ name: collection }, data.filter.query)
287
+ if (isFilter)
288
+ collectionArray.push({ name: collection, db: 'indexeddb', database })
289
+ } else
290
+ collectionArray.push({ name: collection, db: 'indexeddb', database })
291
+ }
292
+
293
+ databasesLength -= 1
294
+ if (!databasesLength) {
295
+ resolve(createData(data, collectionArray, type))
296
+ }
297
+
298
+ } else {
299
+ let collections
300
+ if (action == 'updateCollection')
301
+ collections = Object.keys(data[type]).map((key) => [key, data[type][key]])
302
+ else
303
+ collections = data[type];
304
+ if (!Array.isArray(collections))
305
+ collections = [collections]
306
+ let collectionsLength = collections.length
307
+ for (let collection of collections) {
308
+ let value
309
+ if (action == 'updateCollection')
310
+ [collection, value] = collection
311
+ let collectionExist = db.objectStoreNames.contains(collection)
312
+ if (
313
+ collectionExist && ['updateCollection', 'deleteCollection'].includes(action) ||
314
+ !collectionExist && ['createCollection', 'updateCollection'].includes(action)
315
+ ) {
316
+ getDatabase({ database, collection, upgrade: true }).then((db) => {
317
+ if (collectionExist && action == 'updateCollection') {
318
+ let transaction = db.transaction;
319
+ let objectStore = transaction.objectStore(collection);
320
+
321
+ let valueExist = objectStoreNames.includes(value)
322
+ if (!valueExist) {
323
+ objectStore.name = value;
324
+ }
325
+ else
326
+ errorHandler(data, 'collection does not exist', database)
327
+
328
+ db.result.close()
329
+
330
+ collectionArray.push({ name: value, db: 'indexeddb', database })
331
+ collectionsLength -= 1
332
+
333
+ if (!collectionsLength)
334
+ databasesLength -= 1
335
+
336
+ if (!databasesLength && !collectionsLength) {
337
+ resolve(createData(data, collectionArray, type))
338
+ }
339
+
340
+ } else {
341
+ db = db.result
342
+
343
+ if (action == 'deleteCollection') {
344
+ db.deleteObjectStore(collection)
345
+ }
346
+
347
+ db.close()
348
+
349
+ collectionArray.push({ name: collection, db: 'indexeddb', database })
350
+ collectionsLength -= 1
351
+
352
+ if (!collectionsLength)
353
+ databasesLength -= 1
354
+
355
+ if (!databasesLength && !collectionsLength) {
356
+ resolve(createData(data, collectionArray, type))
357
+ }
358
+
359
+ }
360
+ }, (error) => {
361
+ errorHandler(data, error, database)
362
+ })
363
+
364
+ } else {
365
+ collectionArray.push({ name: collection, db: 'indexeddb', database })
366
+ collectionsLength -= 1
367
+
368
+ if (!collectionsLength)
369
+ databasesLength -= 1
370
+
371
+ if (!databasesLength && !collectionsLength) {
372
+ resolve(createData(data, collectionArray, type))
373
+ }
374
+ }
375
+
376
+ }
377
+
378
+ }
379
+
380
+ };
381
+
382
+ openRequest.onerror = function () {
383
+ errorHandler(data, openRequest.error, database)
384
+ databasesLength -= 1
385
+
386
+ if (!databasesLength) {
387
+ resolve(data)
388
+ }
389
+ };
390
+ }
391
+ }, (error) => {
392
+ errorHandler(data, error)
393
+ });
394
+ }
395
+
396
+
397
+ function createIndex(data) {
398
+ return index('createIndex', data)
399
+ }
400
+
401
+ function readIndex(data) {
402
+ return index('readIndex', data)
403
+ }
404
+
405
+ function updateIndex(data) {
406
+ return index('updateIndex', data)
407
+ }
408
+
409
+ function deleteIndex(data) {
410
+ return index('deleteIndex', data)
411
+ }
412
+
413
+ const index = (action, data) => {
414
+ return new Promise((resolve, reject) => {
415
+ let type = 'index'
416
+ let indexArray = [];
417
+
418
+ if (data.request)
419
+ data[type] = data.request
420
+
421
+ if (!data['timeStamp'])
422
+ data['timeStamp'] = new Date().toISOString()
423
+
424
+ let databases = data.database;
425
+ if (!Array.isArray(databases))
426
+ databases = [databases]
427
+
428
+ let collections = data.collection;
429
+ if (!Array.isArray(collections))
430
+ collections = [collections]
431
+
432
+ let databasesLength = databases.length
433
+ for (let database of databases) {
434
+ let collectionsLength = collections.length
435
+ for (let collection of collections) {
436
+ getDatabase({ database }).then((db) => {
437
+ let objectStoreNames = Array.from(db.objectStoreNames)
438
+ if (objectStoreNames.includes(collection)) {
439
+ let transaction = db.transaction(collection, "readonly");
440
+ let objectStore = transaction.objectStore(collection);
441
+ let indexNames = Array.from(objectStore.indexNames)
442
+
443
+ if (action == 'readIndex') {
444
+ for (let index of indexNames) {
445
+ if (data.filter && data.filter.query) {
446
+ let isFilter = queryData({ name: index }, data.filter.query)
447
+ if (isFilter)
448
+ indexArray.push({ name: index, db: 'indexeddb', database, collection })
449
+ } else
450
+ indexArray.push({ name: index, db: 'indexeddb', database, collection })
451
+ }
452
+ collectionsLength -= 1
453
+ db.close()
454
+
455
+ if (!collectionsLength)
456
+ databasesLength -= 1
457
+
458
+ if (!databasesLength && !collectionsLength)
459
+ resolve(createData(data, indexArray, type))
460
+
461
+ } else {
462
+ db.close()
463
+ let indexes
464
+ if (action == 'updateIndex')
465
+ indexes = Object.keys(data[type]).map((key) => [key, data[type][key]])
466
+ else
467
+ indexes = data[type];
468
+ if (!Array.isArray(indexes))
469
+ indexes = [indexes]
470
+
471
+ let indexesLength = indexes.length
472
+ for (let index of indexes) {
473
+
474
+ getDatabase({ database, upgrade: true }).then((db2) => {
475
+ let transaction = db2.transaction;
476
+ let objectStore = transaction.objectStore(collection);
477
+
478
+ let value
479
+ if (action == 'updateIndex')
480
+ [index, value] = index
481
+
482
+ let indexExist = indexNames.includes(index)
483
+
484
+ if (action == 'createIndex') {
485
+ if (!indexExist) {
486
+ objectStore.createIndex(index, index, { unique: false })
487
+ indexArray.push({ name: index, db: 'indexeddb', database, collection })
488
+ }
489
+ else
490
+ errorHandler(data, 'index exist', database, collection)
491
+ }
492
+ if (action == 'updateIndex') {
493
+ let valueExist = indexNames.includes(value)
494
+ let indexObj = objectStore.index(index);
495
+ if (indexExist && !valueExist) {
496
+ indexObj.name = value;
497
+ indexArray.push({ name: value, db: 'indexeddb', database, collection })
498
+ }
499
+ else
500
+ errorHandler(data, 'index does not exist', database, collection)
501
+
502
+ }
503
+ if (action == 'deleteIndex') {
504
+ if (indexExist) {
505
+ objectStore.deleteIndex(index)
506
+ indexArray.push({ name: index, db: 'indexeddb', database, collection })
507
+ }
508
+ else
509
+ errorHandler(data, 'index does not exist', database, collection)
510
+ }
511
+ db2.result.close()
512
+
513
+ indexesLength -= 1
514
+
515
+ if (!indexesLength)
516
+ collectionsLength -= 1
517
+
518
+ if (!collectionsLength)
519
+ databasesLength -= 1
520
+
521
+ if (!databasesLength && !collectionsLength && !indexesLength) {
522
+ resolve(createData(data, indexArray, type))
523
+ }
524
+ });
525
+ }
526
+
527
+ }
528
+
529
+ } else {
530
+ db.close()
531
+ errorHandler(data, `collection: ${collection} does not exist`, database, collection)
532
+ resolve(data)
533
+ }
534
+ });
535
+
536
+ }
537
+ }
538
+
539
+ }, (error) => {
540
+ errorHandler(data, error)
541
+ });
542
+ }
543
+
544
+ function createDocument(data) {
545
+ return document('createDocument', data)
546
+ }
547
+
548
+ function readDocument(data) {
549
+ return document('readDocument', data)
550
+ }
551
+
552
+ function updateDocument(data) {
553
+ return document('updateDocument', data)
554
+ }
555
+
556
+ function deleteDocument(data) {
557
+ return document('deleteDocument', data)
558
+ }
559
+
560
+
561
+ const document = (action, data) => {
562
+ return new Promise((resolve, reject) => {
563
+ let type = 'document'
564
+ let documents = []
565
+
566
+ if (data.request)
567
+ data[type] = data.request
568
+
569
+ if (data.document && !Array.isArray(data.document))
570
+ data.document = [data.document]
571
+
572
+ if (!data['timeStamp'])
573
+ data['timeStamp'] = new Date().toISOString()
574
+
575
+ let databases = data.database;
576
+ if (!Array.isArray(databases))
577
+ databases = [databases]
578
+
579
+ let collections = data.collection;
580
+ if (!Array.isArray(collections))
581
+ collections = [collections]
582
+
583
+ let databasesLength = databases.length
584
+ for (let database of databases) {
585
+ getDatabase({ database }).then((db) => {
586
+ let collectionsLength = collections.length
587
+ for (let collection of collections) {
588
+ let collectionExist = db.objectStoreNames.contains(collection)
589
+ if (!collectionExist) {
590
+ db.close()
591
+ getDatabase({ database, collection }).then((db) => {
592
+ if (data.filter || action == 'readDocument' && (!data.document || !data.document.length)) {
593
+ db.close()
594
+ readDocuments(data, database, collection).then((filterDocs) => {
595
+ getDatabase({ database }).then((db) => {
596
+ let transaction = db.transaction([collection], "readwrite");
597
+ let objectStore = transaction.objectStore(collection);
598
+
599
+ runDocs({ action, data, objectStore, documents, filterDocs, database, collection, type }).then(() => {
600
+ db.close()
601
+ collectionsLength -= 1
602
+
603
+ if (!collectionsLength)
604
+ databasesLength -= 1
605
+
606
+ if (!databasesLength && !collectionsLength) {
607
+ resolve(createData(data, documents, type))
608
+ }
609
+ })
610
+ })
611
+
612
+ })
613
+ } else {
614
+ let transaction = db.transaction([collection], "readwrite");
615
+ let objectStore = transaction.objectStore(collection);
616
+
617
+ runDocs({ action, data, objectStore, documents, database, collection, type }).then(() => {
618
+ db.close()
619
+ collectionsLength -= 1
620
+
621
+ if (!collectionsLength)
622
+ databasesLength -= 1
623
+
624
+ if (!databasesLength && !collectionsLength) {
625
+ resolve(createData(data, documents, type))
626
+ }
627
+ })
628
+ }
629
+
630
+ }).catch((error) => {
631
+ errorHandler(data, { message: error, data }, database)
632
+ collectionsLength -= 1
633
+
634
+ if (!collectionsLength)
635
+ databasesLength -= 1
636
+
637
+ if (!databasesLength && !collectionsLength)
638
+ resolve(createData(data, documents, type))
639
+
640
+ })
641
+
642
+ } else {
643
+ if (data.filter || action == 'readDocument' && (!data.document || !data.document.length)) {
644
+
645
+ db.close()
646
+ readDocuments(data, database, collection).then((filterDocs) => {
647
+ getDatabase({ database }).then((db) => {
648
+ let transaction = db.transaction([collection], "readwrite");
649
+ let objectStore = transaction.objectStore(collection);
650
+
651
+ runDocs({ action, data, objectStore, documents, filterDocs, database, collection, type }).then(() => {
652
+ db.close()
653
+ collectionsLength -= 1
654
+
655
+ if (!collectionsLength)
656
+ databasesLength -= 1
657
+
658
+ if (!databasesLength && !collectionsLength) {
659
+ resolve(createData(data, documents, type))
660
+ }
661
+ })
662
+ })
663
+ })
664
+ } else {
665
+ let transaction = db.transaction([collection], "readwrite");
666
+ let objectStore = transaction.objectStore(collection);
667
+
668
+ runDocs({ action, data, objectStore, documents, database, collection, type }).then(() => {
669
+ db.close()
670
+ collectionsLength -= 1
671
+
672
+ if (!collectionsLength)
673
+ databasesLength -= 1
674
+ if (!databasesLength && !collectionsLength) {
675
+ resolve(createData(data, documents, type))
676
+ }
677
+ })
678
+ }
679
+ }
680
+ }
681
+
682
+ }).catch((error) => {
683
+ errorHandler(data, { message: error, data }, database)
684
+
685
+ databasesLength -= 1
686
+
687
+ if (!databasesLength)
688
+ resolve(createData(data, documents, type))
689
+ })
690
+
691
+ }
692
+ })
693
+ }
694
+
695
+ function runDocs({ action, data, objectStore, documents, filterDocs, database, collection, type }) {
696
+ return new Promise((resolve, reject) => {
697
+ let docs = []
698
+
699
+ if (Array.isArray(data[type]))
700
+ docs.push(...data[type]);
701
+ // else if (data[type] != undefined && action == 'createDocument')
702
+ // docs.push(data[type])
703
+ // else if (data[type] != undefined)
704
+ // docs.push({...data[type]})
705
+
706
+
707
+ if (filterDocs && filterDocs.length) {
708
+ if (action == 'readDocument') {
709
+ for (let i = 0; i < filterDocs.length; i++)
710
+ documents.push({ db: 'indexeddb', database, collection, ...filterDocs[i] })
711
+
712
+ }
713
+
714
+ if (action == 'updateDocument') {
715
+ let updateData = docs[0];
716
+ if (updateData)
717
+ docs.shift();
718
+ for (let filterDoc of filterDocs) {
719
+ filterDoc = dotNotationToObject(updateData, filterDoc)
720
+ docs.push(filterDoc)
721
+ }
722
+ }
723
+
724
+ if (action == 'deleteDocument')
725
+ docs.push(...filterDocs)
726
+ }
727
+
728
+ let docsLength = docs.length
729
+
730
+ if (docsLength > 0) {
731
+ for (let doc of docs) {
732
+ // TODO deDuplcate document_id per collection
733
+ delete doc.db
734
+ delete doc.database
735
+ delete doc.collection
736
+
737
+ if (action == 'updateDocument' || action == 'deleteDocument') {
738
+ if (doc._id) {
739
+ if (data.organization_id)
740
+ doc['organization_id'] = data.organization_id
741
+ doc['modified'] = { on: data.timeStamp, by: data.user_id || data.clientId }
742
+
743
+ updateDoc(action, data, doc, objectStore, database, collection).then((doc) => {
744
+ doc.db = 'indexeddb'
745
+ doc.database = database
746
+ doc.collection = collection
747
+ documents.push(doc)
748
+ docsLength -= 1
749
+ if (!docsLength) {
750
+ resolve()
751
+ }
752
+ })
753
+ } else {
754
+ docsLength -= 1
755
+ if (!docsLength)
756
+ resolve()
757
+ }
758
+ } else {
759
+ let request;
760
+ if (action == 'createDocument') {
761
+ if (!doc._id)
762
+ doc['_id'] = ObjectId()
763
+ doc = dotNotationToObject(doc)
764
+
765
+ if (data.organization_id)
766
+ doc['organization_id'] = data.organization_id
767
+ doc['created'] = { on: data.timeStamp, by: data.user_id || data.clientId }
768
+ request = objectStore.add(doc);
769
+ }
770
+
771
+ if (action == 'readDocument') {
772
+ if (action == 'readDocument' && doc._id)
773
+ request = objectStore.get(doc._id);
774
+ else {
775
+ docsLength -= 1
776
+ errorHandler(data, { message: 'requires _id', document: doc }, database, objectStore.name)
777
+ if (!docsLength) {
778
+ resolve()
779
+ }
780
+ }
781
+ }
782
+
783
+ request.onsuccess = function () {
784
+ if (request.result) {
785
+ if (action == 'readDocument')
786
+ doc = request.result
787
+ doc.db = 'indexeddb'
788
+ doc.database = database
789
+ doc.collection = collection
790
+
791
+ documents.push(doc)
792
+ }
793
+ docsLength -= 1
794
+ if (!docsLength) {
795
+ resolve()
796
+ }
797
+ };
798
+
799
+ request.onerror = function () {
800
+ errorHandler(data, { message: request.error, document: doc }, database, objectStore.name)
801
+ resolve()
802
+ };
803
+ }
804
+ }
805
+ } else {
806
+ resolve()
807
+ }
808
+ }, (error) => {
809
+ errorHandler(data, { message: error, document: doc }, database, objectStore.name)
810
+ })
811
+ }
812
+
813
+ function updateDoc(action, data, doc, objectStore, database, collection) {
814
+ return new Promise((resolve, reject) => {
815
+ let get = objectStore.get(doc._id)
816
+ get.onsuccess = function () {
817
+ ;
818
+
819
+ let result = get.result
820
+ if (result || data.upsert == true) {
821
+ let modify = true
822
+ if (result && doc.modified && result.modified)
823
+ if (doc.modified.on < result.modified.on)
824
+ modify = false
825
+
826
+ if (modify) {
827
+ if (action == 'updateDocument') {
828
+ doc = dotNotationToObject(doc, get.result)
829
+
830
+ if (data.updateName) {
831
+ for (let key of Object.keys(data.updateName)) {
832
+ let val = doc[key]
833
+ delete doc[key]
834
+ doc[data.updateName[key]] = val
835
+ }
836
+ }
837
+
838
+ if (data.deleteName) {
839
+ for (let key of Object.keys(data.deleteName)) {
840
+ delete doc[key]
841
+ }
842
+
843
+ }
844
+
845
+ let put = objectStore.put(doc);
846
+
847
+ put.onsuccess = function () {
848
+ resolve(doc)
849
+ };
850
+
851
+ put.onerror = function () {
852
+ errorHandler(data, put.error, database, collection, doc)
853
+ resolve(doc)
854
+ };
855
+
856
+ }
857
+
858
+ if (action == 'deleteDocument') {
859
+ let deleteDoc = objectStore.delete(doc._id);
860
+
861
+ deleteDoc.onsuccess = function () {
862
+ resolve(doc)
863
+ };
864
+
865
+ deleteDoc.onerror = function () {
866
+ errorHandler(data, deleteDoc.error, database, collection, doc)
867
+ resolve(doc)
868
+ };
869
+ }
870
+
871
+ } else {
872
+ // TODO: handle if _id not found upsert
873
+ errorHandler(data, 'doc in db is newer', database, collection, doc)
874
+ resolve(doc)
875
+ }
876
+ } else {
877
+ errorHandler(data, 'document doest not exist and upsert is not true', database, collection, doc)
878
+ resolve(doc)
879
+ }
880
+
881
+ };
882
+
883
+ get.onerror = function () {
884
+ errorHandler(data, get.error, database, collection, doc)
885
+ resolve(doc)
886
+ };
887
+
888
+ })
889
+ }
890
+
891
+ const readDocuments = (data, database, collection) => {
892
+ return new Promise((resolve, reject) => {
893
+ let openRequest = indexedDB.open(database);
894
+
895
+ openRequest.onsuccess = function () {
896
+ let db = openRequest.result;
897
+ try {
898
+ let transaction = db.transaction([collection], "readonly");
899
+ let objectStore = transaction.objectStore(collection);
900
+
901
+ if (data.filter) {
902
+ let count = objectStore.count();
903
+ count.onsuccess = function () {
904
+ data.filter.count = count.result
905
+ }
906
+ }
907
+
908
+ let isIndex = false
909
+ let indexName;
910
+ if (data.filter && data.filter.sort && data.filter.sort[0] && data.filter.sort[0].name) {
911
+ indexName = data.filter.sort[0].name
912
+ // if (indexName.includes('-'))
913
+ // isIndex = false
914
+ // else
915
+ // isIndex = true
916
+ }
917
+
918
+ if (isIndex) {
919
+ let direction = data.filter.sort[0].direction
920
+ if (direction == 'desc')
921
+ direction = 'prev'
922
+ else
923
+ direction = 'next'
924
+
925
+ let indexNames = Array.from(objectStore.indexNames)
926
+ let indexExist = indexNames.includes(indexName)
927
+ if (!indexExist) {
928
+ db.close()
929
+ getDatabase({ database, upgrade: true }).then((db2) => {
930
+ let transaction = db2.transaction;
931
+ let objectStore = transaction.objectStore(collection);
932
+ try {
933
+ objectStore.createIndex(indexName, indexName, { unique: false })
934
+ } catch (error) {
935
+ // console.log(error)
936
+ }
937
+ const indexStore = objectStore.index(indexName);
938
+
939
+ readDocs(data, database, collection, indexStore, direction).then((results) => {
940
+ if (db2 && db2.close)
941
+ db2.close()
942
+ resolve(results)
943
+ })
944
+ })
945
+ } else {
946
+ const indexStore = objectStore.index(indexName);
947
+ readDocs(data, database, collection, indexStore, direction).then((results) => {
948
+ db.close()
949
+ resolve(results)
950
+ })
951
+ }
952
+ } else {
953
+ readDocs(data, database, collection, objectStore).then((results) => {
954
+ db.close()
955
+ resolve(results)
956
+ })
957
+ }
958
+ } catch (error) {
959
+ errorHandler(data, error, database)
960
+ db.close()
961
+ resolve([])
962
+ return
963
+ }
964
+
965
+ };
966
+
967
+ openRequest.onerror = function () {
968
+ errorHandler(data, openRequest.error)
969
+ resolve(data)
970
+ };
971
+ })
972
+ }
973
+
974
+ async function readDocs(data, database, collection, objectStore, direction) {
975
+ return new Promise((resolve, reject) => {
976
+
977
+ let results = [], index = 0, limit
978
+ if (data.filter) {
979
+ if (data.filter.startIndex)
980
+ index = data.filter.startIndex
981
+ if (data.filter.limit)
982
+ limit = data.filter.limit
983
+ if (limit)
984
+ limit = index + limit;
985
+ }
986
+
987
+ const request = objectStore.openCursor(null, direction);
988
+ request.onsuccess = function () {
989
+ let cursor = request.result;
990
+ if (cursor) {
991
+ let isFilter = true;
992
+ let value = cursor.value;
993
+ if (data.filter) {
994
+ if (data.filter.query)
995
+ isFilter = queryData(value, data.filter.query);
996
+ if (isFilter && data.filter.search)
997
+ isFilter = searchData(value, data.filter.search);
998
+ }
999
+ if (isFilter !== false)
1000
+ results.push(value)
1001
+ if (limit && limit == results.length) {
1002
+ results = results.slice(index, limit)
1003
+ resolve(results)
1004
+ } else
1005
+ cursor.continue();
1006
+ } else {
1007
+ if (index)
1008
+ results = results.slice(index)
1009
+ resolve(results)
1010
+ }
1011
+ }
1012
+
1013
+ request.onerror = function () {
1014
+ errorHandler(data, request.error, database, collection)
1015
+ db.close()
1016
+ resolve([])
1017
+ };
1018
+
1019
+ });
1020
+ }
1021
+
1022
+ async function generateDB(organization = { document: {} }, user = { document: {} }) {
1023
+ const organization_id = organization.document._id || ObjectId();
1024
+ const defaultKey = organization.document.key || uuid.generate();
1025
+ const user_id = user.document._id || ObjectId();
1026
+
1027
+ let apiKey = await readDocument({ database: organization_id, collection: 'keys', organization_id })
1028
+ if (apiKey && apiKey.document && apiKey.document[0])
1029
+ return
1030
+
1031
+ try {
1032
+ // Create organization
1033
+ organization.database = organization_id
1034
+ organization.collection = 'organizations'
1035
+ organization.document._id = organization_id
1036
+ organization.document.name = organization.document.name || 'untitiled'
1037
+ organization.organization_id = organization_id
1038
+ createDocument(organization);
1039
+
1040
+ // Create user
1041
+ user.database = organization_id
1042
+ user.collection = 'users'
1043
+ user.document._id = user_id
1044
+ user.document.firstname = user.document.firstname || 'untitiled'
1045
+ user.document.lastname = user.document.lastname || 'untitiled'
1046
+ user.organization_id = organization_id
1047
+ createDocument(user);
1048
+
1049
+ // Create default key
1050
+ let key = {
1051
+ database: organization_id,
1052
+ collection: 'keys',
1053
+ document: {
1054
+ _id: ObjectId(),
1055
+ type: "key",
1056
+ key: defaultKey,
1057
+ actions: {
1058
+ signIn: true,
1059
+ signUp: true
1060
+ },
1061
+ default: true
1062
+ },
1063
+ organization_id
1064
+ }
1065
+ createDocument(key);
1066
+
1067
+ // Create role
1068
+ let role_id = ObjectId();
1069
+ let role = {
1070
+ database: organization_id,
1071
+ collection: 'keys',
1072
+ document: {
1073
+ _id: role_id,
1074
+ type: "role",
1075
+ name: "admin",
1076
+ admin: "true"
1077
+ },
1078
+ organization_id
1079
+ };
1080
+ createDocument(role);
1081
+
1082
+ // Create user key
1083
+ let userKey = {
1084
+ database: organization_id,
1085
+ collection: 'keys',
1086
+ document: {
1087
+ _id: ObjectId(),
1088
+ type: "user",
1089
+ key: user_id,
1090
+ collection: 'users', // could be any collection
1091
+ roles: [role_id],
1092
+ email: user.document.email,
1093
+ password: user.document.password || btoa('0000')
1094
+ },
1095
+ organization_id
1096
+ };
1097
+ createDocument(userKey);
1098
+
1099
+ return [organization, key, user, role, userKey]
1100
+
1101
+ } catch (error) {
1102
+ return false
1103
+ }
1104
+ }
1105
+
1106
+ function errorHandler(data, error, database, collection) {
1107
+ if (typeof error == 'object')
1108
+ error['db'] = 'indexeddb'
1109
+ else
1110
+ error = { db: 'indexeddb', message: error }
1111
+
1112
+ if (database)
1113
+ error['database'] = database
1114
+ if (collection)
1115
+ error['collection'] = collection
1116
+ if (data.error)
1117
+ data.error.push(error)
1118
+ else
1119
+ data.error = [error]
1120
+
1121
+ }
1122
+
1123
+ function createData(data, array, type) {
1124
+ data.request = data[type] || {}
1125
+
1126
+ if (data.filter && data.filter.sort)
1127
+ data[type] = sortData(array, data.filter.sort)
1128
+ else
1129
+ data[type] = array
1130
+
1131
+ if (data.returnLog) {
1132
+ if (!data.log)
1133
+ data.log = []
1134
+ data.log.push(...data[type])
1135
+ }
1136
+
1137
+ return data
1138
+ }
1139
+
1140
+
1141
+ function init() {
1142
+ // Check for support.
1143
+ if (!('indexedDB' in window)) {
1144
+ console.log("This browser doesn't support IndexedDB.");
1145
+ setStatus(false);
1146
+ return;
1147
+ } else {
1148
+ try {
1149
+ indexedDB.databases()
1150
+ if (window.CoCreateConfig && window.CoCreateConfig.indexeddb !== false)
1151
+ setStatus(true)
1152
+ } catch (e) {
1153
+ setStatus(false);
1154
+ }
1155
+ }
1156
+ }
1157
+
1158
+ function setStatus(value) {
1159
+ if (window.CoCreateConfig)
1160
+ window.CoCreateConfig.indexeddb = value;
1161
+ else
1162
+ window.CoCreateConfig = { indexeddb: value };
1163
+
1164
+ status = value;
1165
+ }
1166
+
1167
+
1168
+ init();
1169
+
1170
+ export default {
1171
+ status,
1172
+ ObjectId,
1173
+ getDatabase,
1174
+ createDatabase,
1175
+ readDatabase,
1176
+ updateDatabase,
1177
+ deleteDatabase,
1178
+
1179
+ createCollection,
1180
+ readCollection,
1181
+ updateCollection,
1182
+ deleteCollection,
1183
+
1184
+ createIndex,
1185
+ readIndex,
1186
+ updateIndex,
1187
+ deleteIndex,
1188
+
1189
+ createDocument,
1190
+ readDocument,
1191
+ updateDocument,
1192
+ deleteDocument,
1193
+
1194
+ generateDB
1195
1195
  };