@aws-amplify/datastore 3.14.5-unstable.4 → 3.14.5
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/CHANGELOG.md +19 -0
- package/build.js +5 -0
- package/dist/aws-amplify-datastore.js +92853 -0
- package/dist/aws-amplify-datastore.js.map +1 -0
- package/dist/aws-amplify-datastore.min.js +65 -0
- package/dist/aws-amplify-datastore.min.js.map +1 -0
- package/index.js +7 -0
- package/lib/authModeStrategies/multiAuthStrategy.js +64 -6
- package/lib/authModeStrategies/multiAuthStrategy.js.map +1 -1
- package/lib/datastore/datastore.js +297 -703
- package/lib/datastore/datastore.js.map +1 -1
- package/lib/index.js +4 -6
- package/lib/index.js.map +1 -1
- package/lib/predicates/index.js +6 -127
- package/lib/predicates/index.js.map +1 -1
- package/lib/predicates/sort.js +4 -10
- package/lib/predicates/sort.js.map +1 -1
- package/lib/storage/adapter/AsyncStorageAdapter.js +381 -138
- package/lib/storage/adapter/AsyncStorageAdapter.js.map +1 -1
- package/lib/storage/adapter/AsyncStorageDatabase.js +98 -37
- package/lib/storage/adapter/AsyncStorageDatabase.js.map +1 -1
- package/lib/storage/adapter/InMemoryStore.js +67 -16
- package/lib/storage/adapter/InMemoryStore.js.map +1 -1
- package/lib/storage/adapter/InMemoryStore.native.js +4 -2
- package/lib/storage/adapter/InMemoryStore.native.js.map +1 -1
- package/lib/storage/adapter/IndexedDBAdapter.js +420 -272
- package/lib/storage/adapter/IndexedDBAdapter.js.map +1 -1
- package/lib/storage/adapter/getDefaultAdapter/index.js +5 -3
- package/lib/storage/adapter/getDefaultAdapter/index.js.map +1 -1
- package/lib/storage/adapter/getDefaultAdapter/index.native.js +4 -2
- package/lib/storage/adapter/getDefaultAdapter/index.native.js.map +1 -1
- package/lib/storage/storage.js +143 -72
- package/lib/storage/storage.js.map +1 -1
- package/lib/sync/datastoreConnectivity.js +55 -6
- package/lib/sync/datastoreConnectivity.js.map +1 -1
- package/lib/sync/datastoreReachability/index.native.js +4 -2
- package/lib/sync/datastoreReachability/index.native.js.map +1 -1
- package/lib/sync/index.js +124 -49
- package/lib/sync/index.js.map +1 -1
- package/lib/sync/merger.js +74 -8
- package/lib/sync/merger.js.map +1 -1
- package/lib/sync/outbox.js +97 -24
- package/lib/sync/outbox.js.map +1 -1
- package/lib/sync/processors/errorMaps.js +35 -5
- package/lib/sync/processors/errorMaps.js.map +1 -1
- package/lib/sync/processors/mutation.js +131 -47
- package/lib/sync/processors/mutation.js.map +1 -1
- package/lib/sync/processors/subscription.js +102 -29
- package/lib/sync/processors/subscription.js.map +1 -1
- package/lib/sync/processors/sync.js +102 -26
- package/lib/sync/processors/sync.js.map +1 -1
- package/lib/sync/utils.js +103 -40
- package/lib/sync/utils.js.map +1 -1
- package/lib/types.js +39 -9
- package/lib/types.js.map +1 -1
- package/lib/util.js +188 -192
- package/lib/util.js.map +1 -1
- package/lib-esm/authModeStrategies/multiAuthStrategy.js +57 -2
- package/lib-esm/authModeStrategies/multiAuthStrategy.js.map +1 -1
- package/lib-esm/datastore/datastore.d.ts +8 -59
- package/lib-esm/datastore/datastore.js +234 -642
- package/lib-esm/datastore/datastore.js.map +1 -1
- package/lib-esm/index.d.ts +2 -3
- package/lib-esm/index.js +1 -2
- package/lib-esm/index.js.map +1 -1
- package/lib-esm/predicates/index.d.ts +2 -16
- package/lib-esm/predicates/index.js +7 -128
- package/lib-esm/predicates/index.js.map +1 -1
- package/lib-esm/predicates/sort.js +4 -10
- package/lib-esm/predicates/sort.js.map +1 -1
- package/lib-esm/storage/adapter/AsyncStorageAdapter.d.ts +1 -2
- package/lib-esm/storage/adapter/AsyncStorageAdapter.js +349 -109
- package/lib-esm/storage/adapter/AsyncStorageAdapter.js.map +1 -1
- package/lib-esm/storage/adapter/AsyncStorageDatabase.js +68 -7
- package/lib-esm/storage/adapter/AsyncStorageDatabase.js.map +1 -1
- package/lib-esm/storage/adapter/InMemoryStore.d.ts +1 -1
- package/lib-esm/storage/adapter/InMemoryStore.js +52 -1
- package/lib-esm/storage/adapter/InMemoryStore.js.map +1 -1
- package/lib-esm/storage/adapter/IndexedDBAdapter.d.ts +2 -4
- package/lib-esm/storage/adapter/IndexedDBAdapter.js +368 -227
- package/lib-esm/storage/adapter/IndexedDBAdapter.js.map +1 -1
- package/lib-esm/storage/adapter/getDefaultAdapter/index.js.map +1 -1
- package/lib-esm/storage/storage.d.ts +6 -7
- package/lib-esm/storage/storage.js +101 -33
- package/lib-esm/storage/storage.js.map +1 -1
- package/lib-esm/sync/datastoreConnectivity.js +47 -1
- package/lib-esm/sync/datastoreConnectivity.js.map +1 -1
- package/lib-esm/sync/index.js +76 -4
- package/lib-esm/sync/index.js.map +1 -1
- package/lib-esm/sync/merger.js +67 -1
- package/lib-esm/sync/merger.js.map +1 -1
- package/lib-esm/sync/outbox.js +74 -1
- package/lib-esm/sync/outbox.js.map +1 -1
- package/lib-esm/sync/processors/errorMaps.js +32 -2
- package/lib-esm/sync/processors/errorMaps.js.map +1 -1
- package/lib-esm/sync/processors/mutation.js +93 -12
- package/lib-esm/sync/processors/mutation.js.map +1 -1
- package/lib-esm/sync/processors/subscription.js +69 -6
- package/lib-esm/sync/processors/subscription.js.map +1 -1
- package/lib-esm/sync/processors/sync.js +75 -2
- package/lib-esm/sync/processors/sync.js.map +1 -1
- package/lib-esm/sync/utils.d.ts +1 -1
- package/lib-esm/sync/utils.js +95 -32
- package/lib-esm/sync/utils.js.map +1 -1
- package/lib-esm/types.d.ts +10 -63
- package/lib-esm/types.js +38 -7
- package/lib-esm/types.js.map +1 -1
- package/lib-esm/util.d.ts +6 -39
- package/lib-esm/util.js +171 -171
- package/lib-esm/util.js.map +1 -1
- package/package.json +14 -21
- package/src/authModeStrategies/multiAuthStrategy.ts +2 -2
- package/src/datastore/datastore.ts +206 -699
- package/src/index.ts +0 -4
- package/src/predicates/index.ts +17 -143
- package/src/predicates/sort.ts +2 -8
- package/src/storage/adapter/AsyncStorageAdapter.ts +178 -56
- package/src/storage/adapter/AsyncStorageDatabase.ts +15 -16
- package/src/storage/adapter/InMemoryStore.ts +2 -5
- package/src/storage/adapter/IndexedDBAdapter.ts +191 -166
- package/src/storage/adapter/getDefaultAdapter/index.ts +2 -2
- package/src/storage/storage.ts +37 -56
- package/src/sync/datastoreConnectivity.ts +4 -4
- package/src/sync/index.ts +28 -22
- package/src/sync/merger.ts +1 -1
- package/src/sync/outbox.ts +6 -6
- package/src/sync/processors/errorMaps.ts +1 -1
- package/src/sync/processors/mutation.ts +19 -23
- package/src/sync/processors/subscription.ts +16 -20
- package/src/sync/processors/sync.ts +17 -17
- package/src/sync/utils.ts +48 -42
- package/src/types.ts +16 -128
- package/src/util.ts +150 -108
- package/webpack.config.dev.js +6 -0
- package/lib/authModeStrategies/defaultAuthStrategy.d.ts +0 -2
- package/lib/authModeStrategies/index.d.ts +0 -2
- package/lib/authModeStrategies/multiAuthStrategy.d.ts +0 -13
- package/lib/datastore/datastore.d.ts +0 -207
- package/lib/index.d.ts +0 -16
- package/lib/predicates/index.d.ts +0 -30
- package/lib/predicates/next.d.ts +0 -301
- package/lib/predicates/next.js +0 -816
- package/lib/predicates/next.js.map +0 -1
- package/lib/predicates/sort.d.ts +0 -8
- package/lib/ssr/index.d.ts +0 -3
- package/lib/storage/adapter/AsyncStorageAdapter.d.ts +0 -42
- package/lib/storage/adapter/AsyncStorageDatabase.d.ts +0 -39
- package/lib/storage/adapter/InMemoryStore.d.ts +0 -11
- package/lib/storage/adapter/InMemoryStore.native.d.ts +0 -1
- package/lib/storage/adapter/IndexedDBAdapter.d.ts +0 -61
- package/lib/storage/adapter/getDefaultAdapter/index.d.ts +0 -3
- package/lib/storage/adapter/getDefaultAdapter/index.native.d.ts +0 -3
- package/lib/storage/adapter/index.d.ts +0 -9
- package/lib/storage/relationship.d.ts +0 -140
- package/lib/storage/relationship.js +0 -335
- package/lib/storage/relationship.js.map +0 -1
- package/lib/storage/storage.d.ts +0 -50
- package/lib/sync/datastoreConnectivity.d.ts +0 -16
- package/lib/sync/datastoreReachability/index.d.ts +0 -3
- package/lib/sync/datastoreReachability/index.native.d.ts +0 -3
- package/lib/sync/index.d.ts +0 -89
- package/lib/sync/merger.d.ts +0 -17
- package/lib/sync/outbox.d.ts +0 -27
- package/lib/sync/processors/errorMaps.d.ts +0 -17
- package/lib/sync/processors/mutation.d.ts +0 -58
- package/lib/sync/processors/subscription.d.ts +0 -33
- package/lib/sync/processors/sync.d.ts +0 -28
- package/lib/sync/utils.d.ts +0 -42
- package/lib/types.d.ts +0 -554
- package/lib/util.d.ts +0 -189
- package/lib-esm/predicates/next.d.ts +0 -301
- package/lib-esm/predicates/next.js +0 -812
- package/lib-esm/predicates/next.js.map +0 -1
- package/lib-esm/storage/relationship.d.ts +0 -140
- package/lib-esm/storage/relationship.js +0 -333
- package/lib-esm/storage/relationship.js.map +0 -1
- package/src/predicates/next.ts +0 -967
- package/src/storage/relationship.ts +0 -272
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { ConsoleLogger as Logger } from '@aws-amplify/core';
|
|
2
2
|
import * as idb from 'idb';
|
|
3
3
|
import { ModelInstanceCreator } from '../../datastore/datastore';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
ModelPredicateCreator,
|
|
6
|
+
ModelSortPredicateCreator,
|
|
7
|
+
} from '../../predicates';
|
|
5
8
|
import {
|
|
6
9
|
InternalSchema,
|
|
7
10
|
isPredicateObj,
|
|
8
|
-
isPredicateGroup,
|
|
9
11
|
ModelInstanceMetadata,
|
|
10
12
|
ModelPredicate,
|
|
11
13
|
NamespaceResolver,
|
|
@@ -19,14 +21,14 @@ import {
|
|
|
19
21
|
RelationType,
|
|
20
22
|
} from '../../types';
|
|
21
23
|
import {
|
|
24
|
+
exhaustiveCheck,
|
|
22
25
|
getIndex,
|
|
23
26
|
getIndexFromAssociation,
|
|
24
27
|
isModelConstructor,
|
|
25
28
|
isPrivateMode,
|
|
26
29
|
traverseModel,
|
|
27
30
|
validatePredicate,
|
|
28
|
-
|
|
29
|
-
NAMESPACES,
|
|
31
|
+
sortCompareFunction,
|
|
30
32
|
keysEqual,
|
|
31
33
|
getStorename,
|
|
32
34
|
getIndexKeys,
|
|
@@ -39,17 +41,17 @@ const logger = new Logger('DataStore');
|
|
|
39
41
|
|
|
40
42
|
const DB_NAME = 'amplify-datastore';
|
|
41
43
|
class IndexedDBAdapter implements Adapter {
|
|
42
|
-
private schema
|
|
43
|
-
private namespaceResolver
|
|
44
|
-
private modelInstanceCreator
|
|
45
|
-
private getModelConstructorByModelName
|
|
46
|
-
namsespaceName:
|
|
44
|
+
private schema: InternalSchema;
|
|
45
|
+
private namespaceResolver: NamespaceResolver;
|
|
46
|
+
private modelInstanceCreator: ModelInstanceCreator;
|
|
47
|
+
private getModelConstructorByModelName: (
|
|
48
|
+
namsespaceName: string,
|
|
47
49
|
modelName: string
|
|
48
50
|
) => PersistentModelConstructor<any>;
|
|
49
|
-
private db
|
|
50
|
-
private initPromise
|
|
51
|
-
private resolve
|
|
52
|
-
private reject
|
|
51
|
+
private db: idb.IDBPDatabase;
|
|
52
|
+
private initPromise: Promise<void>;
|
|
53
|
+
private resolve: (value?: any) => void;
|
|
54
|
+
private reject: (value?: any) => void;
|
|
53
55
|
private dbName: string = DB_NAME;
|
|
54
56
|
private safariCompatabilityMode: boolean = false;
|
|
55
57
|
|
|
@@ -123,7 +125,7 @@ class IndexedDBAdapter implements Adapter {
|
|
|
123
125
|
namespaceResolver: NamespaceResolver,
|
|
124
126
|
modelInstanceCreator: ModelInstanceCreator,
|
|
125
127
|
getModelConstructorByModelName: (
|
|
126
|
-
namsespaceName:
|
|
128
|
+
namsespaceName: string,
|
|
127
129
|
modelName: string
|
|
128
130
|
) => PersistentModelConstructor<any>,
|
|
129
131
|
sessionId?: string
|
|
@@ -283,7 +285,7 @@ class IndexedDBAdapter implements Adapter {
|
|
|
283
285
|
model,
|
|
284
286
|
this.schema.namespaces[namespaceName],
|
|
285
287
|
this.modelInstanceCreator,
|
|
286
|
-
this.getModelConstructorByModelName
|
|
288
|
+
this.getModelConstructorByModelName
|
|
287
289
|
);
|
|
288
290
|
|
|
289
291
|
const set = new Set<string>();
|
|
@@ -311,13 +313,9 @@ class IndexedDBAdapter implements Adapter {
|
|
|
311
313
|
|
|
312
314
|
if (condition && fromDB) {
|
|
313
315
|
const predicates = ModelPredicateCreator.getPredicates(condition);
|
|
314
|
-
const { predicates: predicateObjs, type } = predicates
|
|
316
|
+
const { predicates: predicateObjs, type } = predicates;
|
|
315
317
|
|
|
316
|
-
const isValid = validatePredicate(
|
|
317
|
-
fromDB as any,
|
|
318
|
-
type as any,
|
|
319
|
-
predicateObjs as any
|
|
320
|
-
);
|
|
318
|
+
const isValid = validatePredicate(fromDB, type, predicateObjs);
|
|
321
319
|
|
|
322
320
|
if (!isValid) {
|
|
323
321
|
const msg = 'Conditional update failed';
|
|
@@ -328,6 +326,7 @@ class IndexedDBAdapter implements Adapter {
|
|
|
328
326
|
}
|
|
329
327
|
|
|
330
328
|
const result: [T, OpType.INSERT | OpType.UPDATE][] = [];
|
|
329
|
+
|
|
331
330
|
for await (const resItem of connectionStoreNames) {
|
|
332
331
|
const { storeName, item, instance, keys } = resItem;
|
|
333
332
|
const store = tx.objectStore(storeName);
|
|
@@ -352,6 +351,7 @@ class IndexedDBAdapter implements Adapter {
|
|
|
352
351
|
.index('byPk')
|
|
353
352
|
.getKey(this.canonicalKeyPath(itemKeyValues));
|
|
354
353
|
await store.put(item, key);
|
|
354
|
+
|
|
355
355
|
result.push([instance, opType]);
|
|
356
356
|
}
|
|
357
357
|
}
|
|
@@ -362,16 +362,16 @@ class IndexedDBAdapter implements Adapter {
|
|
|
362
362
|
}
|
|
363
363
|
|
|
364
364
|
private async load<T>(
|
|
365
|
-
namespaceName:
|
|
365
|
+
namespaceName: string,
|
|
366
366
|
srcModelName: string,
|
|
367
367
|
records: T[]
|
|
368
368
|
): Promise<T[]> {
|
|
369
369
|
const namespace = this.schema.namespaces[namespaceName];
|
|
370
|
-
const relations = namespace.relationships
|
|
370
|
+
const relations = namespace.relationships[srcModelName].relationTypes;
|
|
371
371
|
const connectionStoreNames = relations.map(({ modelName }) => {
|
|
372
372
|
return getStorename(namespaceName, modelName);
|
|
373
373
|
});
|
|
374
|
-
const modelConstructor = this.getModelConstructorByModelName
|
|
374
|
+
const modelConstructor = this.getModelConstructorByModelName(
|
|
375
375
|
namespaceName,
|
|
376
376
|
srcModelName
|
|
377
377
|
);
|
|
@@ -382,6 +382,116 @@ class IndexedDBAdapter implements Adapter {
|
|
|
382
382
|
);
|
|
383
383
|
}
|
|
384
384
|
|
|
385
|
+
const tx = this.db.transaction([...connectionStoreNames], 'readonly');
|
|
386
|
+
|
|
387
|
+
for await (const relation of relations) {
|
|
388
|
+
// target name, metadata, set by init
|
|
389
|
+
const { fieldName, modelName, targetName, targetNames } = relation;
|
|
390
|
+
const storeName = getStorename(namespaceName, modelName);
|
|
391
|
+
const store = tx.objectStore(storeName);
|
|
392
|
+
const modelConstructor = this.getModelConstructorByModelName(
|
|
393
|
+
namespaceName,
|
|
394
|
+
modelName
|
|
395
|
+
);
|
|
396
|
+
|
|
397
|
+
switch (relation.relationType) {
|
|
398
|
+
case 'HAS_ONE':
|
|
399
|
+
for await (const recordItem of records) {
|
|
400
|
+
// POST CPK codegen changes:
|
|
401
|
+
if (targetNames?.length) {
|
|
402
|
+
let getByFields = [];
|
|
403
|
+
let allPresent;
|
|
404
|
+
// iterate through all targetnames to make sure they are all present in the recordItem
|
|
405
|
+
allPresent = targetNames.every(targetName => {
|
|
406
|
+
return recordItem[targetName] != null;
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
if (!allPresent) {
|
|
410
|
+
break;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
getByFields = targetNames as any;
|
|
414
|
+
|
|
415
|
+
// keys are the key values
|
|
416
|
+
const keys = getByFields.map(
|
|
417
|
+
getByField => recordItem[getByField]
|
|
418
|
+
);
|
|
419
|
+
|
|
420
|
+
const connectionRecord = await this._get(store, keys);
|
|
421
|
+
|
|
422
|
+
recordItem[fieldName] =
|
|
423
|
+
connectionRecord &&
|
|
424
|
+
this.modelInstanceCreator(modelConstructor, connectionRecord);
|
|
425
|
+
} else {
|
|
426
|
+
// If single target name, using old codegen
|
|
427
|
+
const getByfield = recordItem[targetName]
|
|
428
|
+
? targetName
|
|
429
|
+
: fieldName;
|
|
430
|
+
|
|
431
|
+
// We break here, because the recordItem does not have 'team', the `getByField`
|
|
432
|
+
// extract the keys on the related model.
|
|
433
|
+
if (!recordItem[getByfield]) break;
|
|
434
|
+
|
|
435
|
+
const key = [recordItem[getByfield]];
|
|
436
|
+
|
|
437
|
+
const connectionRecord = await this._get(store, key);
|
|
438
|
+
|
|
439
|
+
recordItem[fieldName] =
|
|
440
|
+
connectionRecord &&
|
|
441
|
+
this.modelInstanceCreator(modelConstructor, connectionRecord);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
break;
|
|
445
|
+
case 'BELONGS_TO':
|
|
446
|
+
for await (const recordItem of records) {
|
|
447
|
+
// POST CPK codegen changes:
|
|
448
|
+
if (targetNames?.length) {
|
|
449
|
+
let allPresent;
|
|
450
|
+
// iterate through all targetnames to make sure they are all present in the recordItem
|
|
451
|
+
allPresent = targetNames.every(targetName => {
|
|
452
|
+
return recordItem[targetName] != null;
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
// If not present, there is not yet a connected record
|
|
456
|
+
if (!allPresent) {
|
|
457
|
+
break;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
const keys = targetNames
|
|
461
|
+
.filter(targetName => recordItem[targetName] ?? false)
|
|
462
|
+
.map(targetName => recordItem[targetName]);
|
|
463
|
+
|
|
464
|
+
// Retrieve the connected record
|
|
465
|
+
const connectionRecord = await this._get(store, keys);
|
|
466
|
+
|
|
467
|
+
recordItem[fieldName] =
|
|
468
|
+
connectionRecord &&
|
|
469
|
+
this.modelInstanceCreator(modelConstructor, connectionRecord);
|
|
470
|
+
|
|
471
|
+
targetNames?.map(targetName => {
|
|
472
|
+
delete recordItem[targetName];
|
|
473
|
+
});
|
|
474
|
+
} else if (recordItem[targetName]) {
|
|
475
|
+
const key = [recordItem[targetName]];
|
|
476
|
+
|
|
477
|
+
const connectionRecord = await this._get(store, key);
|
|
478
|
+
|
|
479
|
+
recordItem[fieldName] =
|
|
480
|
+
connectionRecord &&
|
|
481
|
+
this.modelInstanceCreator(modelConstructor, connectionRecord);
|
|
482
|
+
delete recordItem[targetName];
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
break;
|
|
486
|
+
case 'HAS_MANY':
|
|
487
|
+
// TODO: Lazy loading
|
|
488
|
+
break;
|
|
489
|
+
default:
|
|
490
|
+
exhaustiveCheck(relation.relationType);
|
|
491
|
+
break;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
385
495
|
return records.map(record =>
|
|
386
496
|
this.modelInstanceCreator(modelConstructor, record)
|
|
387
497
|
);
|
|
@@ -394,9 +504,7 @@ class IndexedDBAdapter implements Adapter {
|
|
|
394
504
|
): Promise<T[]> {
|
|
395
505
|
await this.checkPrivate();
|
|
396
506
|
const storeName = this.getStorenameForModel(modelConstructor);
|
|
397
|
-
const namespaceName = this.namespaceResolver(
|
|
398
|
-
modelConstructor
|
|
399
|
-
) as NAMESPACES;
|
|
507
|
+
const namespaceName = this.namespaceResolver(modelConstructor);
|
|
400
508
|
|
|
401
509
|
const predicates =
|
|
402
510
|
predicate && ModelPredicateCreator.getPredicates(predicate);
|
|
@@ -410,7 +518,7 @@ class IndexedDBAdapter implements Adapter {
|
|
|
410
518
|
const hasSort = pagination && pagination.sort;
|
|
411
519
|
const hasPagination = pagination && pagination.limit;
|
|
412
520
|
|
|
413
|
-
const records: T[] =
|
|
521
|
+
const records: T[] = await (async () => {
|
|
414
522
|
if (queryByKey) {
|
|
415
523
|
const record = await this.getByKey(storeName, queryByKey);
|
|
416
524
|
return record ? [record] : [];
|
|
@@ -431,7 +539,7 @@ class IndexedDBAdapter implements Adapter {
|
|
|
431
539
|
}
|
|
432
540
|
|
|
433
541
|
return this.getAll(storeName);
|
|
434
|
-
})()
|
|
542
|
+
})();
|
|
435
543
|
|
|
436
544
|
return await this.load(namespaceName, modelConstructor.name, records);
|
|
437
545
|
}
|
|
@@ -440,7 +548,8 @@ class IndexedDBAdapter implements Adapter {
|
|
|
440
548
|
storeName: string,
|
|
441
549
|
keyValue: string[]
|
|
442
550
|
): Promise<T> {
|
|
443
|
-
|
|
551
|
+
const record = <T>await this._get(storeName, keyValue);
|
|
552
|
+
return record;
|
|
444
553
|
}
|
|
445
554
|
|
|
446
555
|
private async getAll<T extends PersistentModel>(
|
|
@@ -459,7 +568,7 @@ class IndexedDBAdapter implements Adapter {
|
|
|
459
568
|
return;
|
|
460
569
|
}
|
|
461
570
|
|
|
462
|
-
const keyValues = []
|
|
571
|
+
const keyValues = [];
|
|
463
572
|
|
|
464
573
|
for (const key of keyPath) {
|
|
465
574
|
const predicateObj = predicateObjs.find(
|
|
@@ -472,116 +581,17 @@ class IndexedDBAdapter implements Adapter {
|
|
|
472
581
|
return keyValues.length === keyPath.length ? keyValues : undefined;
|
|
473
582
|
}
|
|
474
583
|
|
|
475
|
-
private matchingIndex(
|
|
476
|
-
storeName: string,
|
|
477
|
-
fieldName: string,
|
|
478
|
-
transaction: idb.IDBPTransaction<unknown, [string]>
|
|
479
|
-
) {
|
|
480
|
-
const store = transaction.objectStore(storeName);
|
|
481
|
-
for (const name of store.indexNames) {
|
|
482
|
-
const idx = store.index(name);
|
|
483
|
-
if (idx.keyPath === fieldName) {
|
|
484
|
-
return idx;
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
|
|
489
584
|
private async filterOnPredicate<T extends PersistentModel>(
|
|
490
585
|
storeName: string,
|
|
491
586
|
predicates: PredicatesGroup<T>
|
|
492
587
|
) {
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
// the predicate objects we care about tend to be nested at least
|
|
496
|
-
// one level down: `{and: {or: {and: { <the predicates we want> }}}}`
|
|
497
|
-
// so, we unpack and/or groups until we find a group with more than 1
|
|
498
|
-
// child OR a child that is not a group (and is therefore a predicate "object").
|
|
499
|
-
while (predicateObjs.length === 1 && isPredicateGroup(predicateObjs[0])) {
|
|
500
|
-
type = (predicateObjs[0] as PredicatesGroup<T>).type;
|
|
501
|
-
predicateObjs = (predicateObjs[0] as PredicatesGroup<T>).predicates;
|
|
502
|
-
}
|
|
588
|
+
const { predicates: predicateObjs, type } = predicates;
|
|
503
589
|
|
|
504
|
-
|
|
505
|
-
let candidateResults: T[];
|
|
506
|
-
|
|
507
|
-
// AFAIK, this will always be a homogenous group of predicate objects at this point.
|
|
508
|
-
// but, if that ever changes, this pulls out just the predicates from the list that
|
|
509
|
-
// are field-level predicate objects we can potentially smash against an index.
|
|
510
|
-
const fieldPredicates = predicateObjs.filter(p =>
|
|
511
|
-
isPredicateObj(p)
|
|
512
|
-
) as PredicateObject<T>[];
|
|
513
|
-
|
|
514
|
-
// several sub-queries could occur here. explicitly start a txn here to avoid
|
|
515
|
-
// opening/closing multiple txns.
|
|
516
|
-
const txn = this.db.transaction(storeName);
|
|
517
|
-
|
|
518
|
-
// our potential indexes or lacks thereof.
|
|
519
|
-
const predicateIndexes = fieldPredicates.map(p => {
|
|
520
|
-
return {
|
|
521
|
-
predicate: p,
|
|
522
|
-
index: this.matchingIndex(storeName, String(p.field), txn),
|
|
523
|
-
};
|
|
524
|
-
});
|
|
525
|
-
|
|
526
|
-
// Explicitly wait for txns from index queries to complete before proceding.
|
|
527
|
-
// This helps ensure IndexedDB is in a stable, ready state. Else, subseqeuent
|
|
528
|
-
// qeuries can sometimes appear to deadlock (at least in FakeIndexedDB).
|
|
529
|
-
await txn.done;
|
|
530
|
-
|
|
531
|
-
// semi-naive implementation:
|
|
532
|
-
if (type === 'and') {
|
|
533
|
-
// each condition must be satsified, we can form a base set with any
|
|
534
|
-
// ONE of those conditions and then filter.
|
|
535
|
-
const actualPredicateIndexes = predicateIndexes.filter(
|
|
536
|
-
i => i.index && i.predicate.operator === 'eq'
|
|
537
|
-
);
|
|
538
|
-
|
|
539
|
-
if (actualPredicateIndexes.length > 0) {
|
|
540
|
-
const predicateIndex = actualPredicateIndexes[0];
|
|
541
|
-
candidateResults = <T[]>(
|
|
542
|
-
await predicateIndex.index!.getAll(predicateIndex.predicate.operand)
|
|
543
|
-
);
|
|
544
|
-
} else {
|
|
545
|
-
// no usable indexes
|
|
546
|
-
candidateResults = <T[]>await this.getAll(storeName);
|
|
547
|
-
}
|
|
548
|
-
} else if (type === 'or') {
|
|
549
|
-
// NOTE: each condition implies a potentially distinct set. we only benefit
|
|
550
|
-
// from using indexes here if EVERY condition uses an index. if any one
|
|
551
|
-
// index requires a table scan, we gain nothing from the indexes.
|
|
552
|
-
// NOTE: results must be DISTINCT-ified if we leverage indexes.
|
|
553
|
-
if (
|
|
554
|
-
predicateIndexes.length > 0 &&
|
|
555
|
-
predicateIndexes.every(i => i.index && i.predicate.operator === 'eq')
|
|
556
|
-
) {
|
|
557
|
-
const distinctResults = new Map<string, T>();
|
|
558
|
-
for (const predicateIndex of predicateIndexes) {
|
|
559
|
-
const resultGroup = <T[]>(
|
|
560
|
-
await predicateIndex.index!.getAll(predicateIndex.predicate.operand)
|
|
561
|
-
);
|
|
562
|
-
for (const item of resultGroup) {
|
|
563
|
-
// TODO: custom PK
|
|
564
|
-
distinctResults.set(item.id, item);
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
// we could conceivably check for special conditions and return early here.
|
|
569
|
-
// but, this is simpler and has not yet had a measurable performance impact.
|
|
570
|
-
candidateResults = Array.from(distinctResults.values());
|
|
571
|
-
} else {
|
|
572
|
-
// either no usable indexes or not all conditions can use one.
|
|
573
|
-
candidateResults = <T[]>await this.getAll(storeName);
|
|
574
|
-
}
|
|
575
|
-
} else {
|
|
576
|
-
// nothing intelligent we can do with `not` groups unless or until we start
|
|
577
|
-
// smashing comparison operators against indexes -- at which point we could
|
|
578
|
-
// perform some reversal here.
|
|
579
|
-
candidateResults = <T[]>await this.getAll(storeName);
|
|
580
|
-
}
|
|
590
|
+
const all = <T[]>await this.getAll(storeName);
|
|
581
591
|
|
|
582
592
|
const filtered = predicateObjs
|
|
583
|
-
?
|
|
584
|
-
:
|
|
593
|
+
? all.filter(m => validatePredicate(m, type, predicateObjs))
|
|
594
|
+
: all;
|
|
585
595
|
|
|
586
596
|
return filtered;
|
|
587
597
|
}
|
|
@@ -590,7 +600,26 @@ class IndexedDBAdapter implements Adapter {
|
|
|
590
600
|
records: T[],
|
|
591
601
|
pagination?: PaginationInput<T>
|
|
592
602
|
): T[] {
|
|
593
|
-
|
|
603
|
+
if (pagination && records.length > 1) {
|
|
604
|
+
if (pagination.sort) {
|
|
605
|
+
const sortPredicates = ModelSortPredicateCreator.getPredicates(
|
|
606
|
+
pagination.sort
|
|
607
|
+
);
|
|
608
|
+
|
|
609
|
+
if (sortPredicates.length) {
|
|
610
|
+
const compareFn = sortCompareFunction(sortPredicates);
|
|
611
|
+
records.sort(compareFn);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
const { page = 0, limit = 0 } = pagination;
|
|
616
|
+
const start = Math.max(0, page * limit) || 0;
|
|
617
|
+
|
|
618
|
+
const end = limit > 0 ? start + limit : records.length;
|
|
619
|
+
|
|
620
|
+
return records.slice(start, end);
|
|
621
|
+
}
|
|
622
|
+
return records;
|
|
594
623
|
}
|
|
595
624
|
|
|
596
625
|
private async enginePagination<T extends PersistentModel>(
|
|
@@ -658,15 +687,14 @@ class IndexedDBAdapter implements Adapter {
|
|
|
658
687
|
const deleteQueue: { storeName: string; items: T[] }[] = [];
|
|
659
688
|
|
|
660
689
|
if (isModelConstructor(modelOrModelConstructor)) {
|
|
661
|
-
const modelConstructor =
|
|
662
|
-
|
|
663
|
-
const nameSpace = this.namespaceResolver(modelConstructor) as NAMESPACES;
|
|
690
|
+
const modelConstructor = modelOrModelConstructor;
|
|
691
|
+
const nameSpace = this.namespaceResolver(modelConstructor);
|
|
664
692
|
|
|
665
693
|
const storeName = this.getStorenameForModel(modelConstructor);
|
|
666
694
|
|
|
667
695
|
const models = await this.query(modelConstructor, condition);
|
|
668
696
|
const relations =
|
|
669
|
-
this.schema.namespaces
|
|
697
|
+
this.schema.namespaces[nameSpace].relationships[modelConstructor.name]
|
|
670
698
|
.relationTypes;
|
|
671
699
|
|
|
672
700
|
if (condition !== undefined) {
|
|
@@ -709,13 +737,11 @@ class IndexedDBAdapter implements Adapter {
|
|
|
709
737
|
return [models, deletedModels];
|
|
710
738
|
}
|
|
711
739
|
} else {
|
|
712
|
-
const model = modelOrModelConstructor
|
|
740
|
+
const model = modelOrModelConstructor;
|
|
713
741
|
|
|
714
742
|
const modelConstructor = Object.getPrototypeOf(model)
|
|
715
743
|
.constructor as PersistentModelConstructor<T>;
|
|
716
|
-
const namespaceName = this.namespaceResolver(
|
|
717
|
-
modelConstructor
|
|
718
|
-
) as NAMESPACES;
|
|
744
|
+
const namespaceName = this.namespaceResolver(modelConstructor);
|
|
719
745
|
|
|
720
746
|
const storeName = this.getStorenameForModel(modelConstructor);
|
|
721
747
|
|
|
@@ -734,10 +760,9 @@ class IndexedDBAdapter implements Adapter {
|
|
|
734
760
|
}
|
|
735
761
|
|
|
736
762
|
const predicates = ModelPredicateCreator.getPredicates(condition);
|
|
737
|
-
const { predicates: predicateObjs, type } =
|
|
738
|
-
predicates as PredicatesGroup<T>;
|
|
763
|
+
const { predicates: predicateObjs, type } = predicates;
|
|
739
764
|
|
|
740
|
-
const isValid = validatePredicate(fromDB
|
|
765
|
+
const isValid = validatePredicate(fromDB, type, predicateObjs);
|
|
741
766
|
|
|
742
767
|
if (!isValid) {
|
|
743
768
|
const msg = 'Conditional update failed';
|
|
@@ -748,7 +773,7 @@ class IndexedDBAdapter implements Adapter {
|
|
|
748
773
|
await tx.done;
|
|
749
774
|
|
|
750
775
|
const relations =
|
|
751
|
-
this.schema.namespaces[namespaceName].relationships
|
|
776
|
+
this.schema.namespaces[namespaceName].relationships[
|
|
752
777
|
modelConstructor.name
|
|
753
778
|
].relationTypes;
|
|
754
779
|
|
|
@@ -761,7 +786,7 @@ class IndexedDBAdapter implements Adapter {
|
|
|
761
786
|
);
|
|
762
787
|
} else {
|
|
763
788
|
const relations =
|
|
764
|
-
this.schema.namespaces[namespaceName].relationships
|
|
789
|
+
this.schema.namespaces[namespaceName].relationships[
|
|
765
790
|
modelConstructor.name
|
|
766
791
|
].relationTypes;
|
|
767
792
|
|
|
@@ -791,18 +816,18 @@ class IndexedDBAdapter implements Adapter {
|
|
|
791
816
|
items: T[] | IDBValidKey[];
|
|
792
817
|
}[]
|
|
793
818
|
) {
|
|
794
|
-
const connectionStoreNames = deleteQueue
|
|
819
|
+
const connectionStoreNames = deleteQueue.map(({ storeName }) => {
|
|
795
820
|
return storeName;
|
|
796
821
|
});
|
|
797
822
|
|
|
798
823
|
const tx = this.db.transaction([...connectionStoreNames], 'readwrite');
|
|
799
|
-
for await (const deleteItem of deleteQueue
|
|
824
|
+
for await (const deleteItem of deleteQueue) {
|
|
800
825
|
const { storeName, items } = deleteItem;
|
|
801
826
|
const store = tx.objectStore(storeName);
|
|
802
827
|
|
|
803
828
|
for await (const item of items) {
|
|
804
829
|
if (item) {
|
|
805
|
-
let key: IDBValidKey
|
|
830
|
+
let key: IDBValidKey;
|
|
806
831
|
|
|
807
832
|
if (typeof item === 'object') {
|
|
808
833
|
const keyValues = this.getIndexKeyValuesFromModel(item as T);
|
|
@@ -826,7 +851,7 @@ class IndexedDBAdapter implements Adapter {
|
|
|
826
851
|
relations: RelationType[],
|
|
827
852
|
models: T[],
|
|
828
853
|
srcModel: string,
|
|
829
|
-
nameSpace:
|
|
854
|
+
nameSpace: string,
|
|
830
855
|
deleteQueue: { storeName: string; items: T[] }[]
|
|
831
856
|
): Promise<void> {
|
|
832
857
|
for await (const rel of relations) {
|
|
@@ -862,7 +887,7 @@ class IndexedDBAdapter implements Adapter {
|
|
|
862
887
|
);
|
|
863
888
|
|
|
864
889
|
await this.deleteTraverse(
|
|
865
|
-
this.schema.namespaces[nameSpace].relationships
|
|
890
|
+
this.schema.namespaces[nameSpace].relationships[modelName]
|
|
866
891
|
.relationTypes,
|
|
867
892
|
recordToDelete ? [recordToDelete] : [],
|
|
868
893
|
modelName,
|
|
@@ -885,7 +910,7 @@ class IndexedDBAdapter implements Adapter {
|
|
|
885
910
|
// If we deprecate, we'll need to re-gen the MIPR in __tests__/schema.ts > newSchema
|
|
886
911
|
// otherwise some unit tests will fail
|
|
887
912
|
index = getIndex(
|
|
888
|
-
this.schema.namespaces[nameSpace].relationships
|
|
913
|
+
this.schema.namespaces[nameSpace].relationships[modelName]
|
|
889
914
|
.relationTypes,
|
|
890
915
|
srcModel
|
|
891
916
|
);
|
|
@@ -903,7 +928,7 @@ class IndexedDBAdapter implements Adapter {
|
|
|
903
928
|
);
|
|
904
929
|
|
|
905
930
|
await this.deleteTraverse(
|
|
906
|
-
this.schema.namespaces[nameSpace].relationships
|
|
931
|
+
this.schema.namespaces[nameSpace].relationships[modelName]
|
|
907
932
|
.relationTypes,
|
|
908
933
|
recordToDelete ? [recordToDelete] : [],
|
|
909
934
|
modelName,
|
|
@@ -918,15 +943,15 @@ class IndexedDBAdapter implements Adapter {
|
|
|
918
943
|
const index =
|
|
919
944
|
// explicit bi-directional @hasMany and @manyToMany
|
|
920
945
|
getIndex(
|
|
921
|
-
this.schema.namespaces[nameSpace].relationships
|
|
946
|
+
this.schema.namespaces[nameSpace].relationships[modelName]
|
|
922
947
|
.relationTypes,
|
|
923
948
|
srcModel
|
|
924
949
|
) ||
|
|
925
950
|
// uni and/or implicit @hasMany
|
|
926
951
|
getIndexFromAssociation(
|
|
927
|
-
this.schema.namespaces[nameSpace].relationships
|
|
952
|
+
this.schema.namespaces[nameSpace].relationships[modelName]
|
|
928
953
|
.indexes,
|
|
929
|
-
associatedWith
|
|
954
|
+
associatedWith
|
|
930
955
|
);
|
|
931
956
|
const keyValues = this.getIndexKeyValuesFromModel(model);
|
|
932
957
|
|
|
@@ -937,7 +962,7 @@ class IndexedDBAdapter implements Adapter {
|
|
|
937
962
|
.getAll(this.canonicalKeyPath(keyValues));
|
|
938
963
|
|
|
939
964
|
await this.deleteTraverse(
|
|
940
|
-
this.schema.namespaces[nameSpace].relationships
|
|
965
|
+
this.schema.namespaces[nameSpace].relationships[modelName]
|
|
941
966
|
.relationTypes,
|
|
942
967
|
childrenArray,
|
|
943
968
|
modelName,
|
|
@@ -950,7 +975,7 @@ class IndexedDBAdapter implements Adapter {
|
|
|
950
975
|
// Intentionally blank
|
|
951
976
|
break;
|
|
952
977
|
default:
|
|
953
|
-
|
|
978
|
+
exhaustiveCheck(relationType);
|
|
954
979
|
break;
|
|
955
980
|
}
|
|
956
981
|
}
|
|
@@ -959,7 +984,7 @@ class IndexedDBAdapter implements Adapter {
|
|
|
959
984
|
storeName: getStorename(nameSpace, srcModel),
|
|
960
985
|
items: models.map(record =>
|
|
961
986
|
this.modelInstanceCreator(
|
|
962
|
-
this.getModelConstructorByModelName
|
|
987
|
+
this.getModelConstructorByModelName(nameSpace, srcModel),
|
|
963
988
|
record
|
|
964
989
|
)
|
|
965
990
|
),
|
|
@@ -973,8 +998,8 @@ class IndexedDBAdapter implements Adapter {
|
|
|
973
998
|
|
|
974
999
|
await idb.deleteDB(this.dbName);
|
|
975
1000
|
|
|
976
|
-
this.db = undefined
|
|
977
|
-
this.initPromise = undefined
|
|
1001
|
+
this.db = undefined;
|
|
1002
|
+
this.initPromise = undefined;
|
|
978
1003
|
}
|
|
979
1004
|
|
|
980
1005
|
async batchSave<T extends PersistentModel>(
|
|
@@ -1004,7 +1029,7 @@ class IndexedDBAdapter implements Adapter {
|
|
|
1004
1029
|
model,
|
|
1005
1030
|
this.schema.namespaces[namespaceName],
|
|
1006
1031
|
this.modelInstanceCreator,
|
|
1007
|
-
this.getModelConstructorByModelName
|
|
1032
|
+
this.getModelConstructorByModelName
|
|
1008
1033
|
);
|
|
1009
1034
|
|
|
1010
1035
|
const keyValues = this.getIndexKeyValuesFromModel(model);
|
|
@@ -1018,7 +1043,7 @@ class IndexedDBAdapter implements Adapter {
|
|
|
1018
1043
|
const { instance } = connectedModels.find(({ instance }) => {
|
|
1019
1044
|
const instanceKeyValues = this.getIndexKeyValuesFromModel(instance);
|
|
1020
1045
|
return keysEqual(instanceKeyValues, keyValues);
|
|
1021
|
-
})
|
|
1046
|
+
});
|
|
1022
1047
|
|
|
1023
1048
|
result.push([
|
|
1024
1049
|
<T>(<unknown>instance),
|
|
@@ -1050,7 +1075,7 @@ class IndexedDBAdapter implements Adapter {
|
|
|
1050
1075
|
});
|
|
1051
1076
|
|
|
1052
1077
|
const { indexes } =
|
|
1053
|
-
this.schema.namespaces[namespaceName].relationships
|
|
1078
|
+
this.schema.namespaces[namespaceName].relationships[modelName];
|
|
1054
1079
|
|
|
1055
1080
|
indexes.forEach(([idxName, keyPath, options]) => {
|
|
1056
1081
|
store.createIndex(idxName, keyPath, options);
|
|
@@ -7,10 +7,10 @@ const getDefaultAdapter: () => Adapter = () => {
|
|
|
7
7
|
const { isBrowser } = browserOrNode();
|
|
8
8
|
|
|
9
9
|
if ((isBrowser && window.indexedDB) || (isWebWorker() && self.indexedDB)) {
|
|
10
|
-
return IndexedDBAdapter
|
|
10
|
+
return IndexedDBAdapter;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
return AsyncStorageAdapter
|
|
13
|
+
return AsyncStorageAdapter;
|
|
14
14
|
};
|
|
15
15
|
|
|
16
16
|
export default getDefaultAdapter;
|