@aws-amplify/datastore 3.12.6-next.32 → 3.12.6-next.41

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.
Files changed (151) hide show
  1. package/lib/authModeStrategies/defaultAuthStrategy.d.ts +2 -0
  2. package/lib/authModeStrategies/index.d.ts +2 -0
  3. package/lib/authModeStrategies/multiAuthStrategy.d.ts +13 -0
  4. package/lib/authModeStrategies/multiAuthStrategy.js.map +1 -1
  5. package/lib/datastore/datastore.d.ts +207 -0
  6. package/lib/datastore/datastore.js +641 -144
  7. package/lib/datastore/datastore.js.map +1 -1
  8. package/lib/index.d.ts +16 -0
  9. package/lib/index.js +4 -0
  10. package/lib/index.js.map +1 -1
  11. package/lib/predicates/index.d.ts +30 -0
  12. package/lib/predicates/index.js +127 -6
  13. package/lib/predicates/index.js.map +1 -1
  14. package/lib/predicates/next.d.ts +342 -0
  15. package/lib/predicates/next.js +801 -0
  16. package/lib/predicates/next.js.map +1 -0
  17. package/lib/predicates/sort.d.ts +8 -0
  18. package/lib/predicates/sort.js +10 -4
  19. package/lib/predicates/sort.js.map +1 -1
  20. package/lib/ssr/index.d.ts +3 -0
  21. package/lib/storage/adapter/AsyncStorageAdapter.d.ts +42 -0
  22. package/lib/storage/adapter/AsyncStorageAdapter.js +106 -293
  23. package/lib/storage/adapter/AsyncStorageAdapter.js.map +1 -1
  24. package/lib/storage/adapter/AsyncStorageDatabase.d.ts +39 -0
  25. package/lib/storage/adapter/AsyncStorageDatabase.js +6 -5
  26. package/lib/storage/adapter/AsyncStorageDatabase.js.map +1 -1
  27. package/lib/storage/adapter/InMemoryStore.d.ts +11 -0
  28. package/lib/storage/adapter/InMemoryStore.js.map +1 -1
  29. package/lib/storage/adapter/InMemoryStore.native.d.ts +1 -0
  30. package/lib/storage/adapter/IndexedDBAdapter.d.ts +61 -0
  31. package/lib/storage/adapter/IndexedDBAdapter.js +223 -289
  32. package/lib/storage/adapter/IndexedDBAdapter.js.map +1 -1
  33. package/lib/storage/adapter/getDefaultAdapter/index.d.ts +3 -0
  34. package/lib/storage/adapter/getDefaultAdapter/index.js.map +1 -1
  35. package/lib/storage/adapter/getDefaultAdapter/index.native.d.ts +3 -0
  36. package/lib/storage/adapter/index.d.ts +9 -0
  37. package/lib/storage/relationship.d.ts +140 -0
  38. package/lib/storage/relationship.js +335 -0
  39. package/lib/storage/relationship.js.map +1 -0
  40. package/lib/storage/storage.d.ts +50 -0
  41. package/lib/storage/storage.js +32 -16
  42. package/lib/storage/storage.js.map +1 -1
  43. package/lib/sync/datastoreConnectivity.d.ts +16 -0
  44. package/lib/sync/datastoreConnectivity.js.map +1 -1
  45. package/lib/sync/datastoreReachability/index.d.ts +3 -0
  46. package/lib/sync/datastoreReachability/index.native.d.ts +3 -0
  47. package/lib/sync/index.d.ts +89 -0
  48. package/lib/sync/index.js +2 -8
  49. package/lib/sync/index.js.map +1 -1
  50. package/lib/sync/merger.d.ts +17 -0
  51. package/lib/sync/merger.js.map +1 -1
  52. package/lib/sync/outbox.d.ts +27 -0
  53. package/lib/sync/outbox.js.map +1 -1
  54. package/lib/sync/processors/errorMaps.d.ts +17 -0
  55. package/lib/sync/processors/errorMaps.js +1 -1
  56. package/lib/sync/processors/errorMaps.js.map +1 -1
  57. package/lib/sync/processors/mutation.d.ts +58 -0
  58. package/lib/sync/processors/mutation.js +9 -6
  59. package/lib/sync/processors/mutation.js.map +1 -1
  60. package/lib/sync/processors/subscription.d.ts +33 -0
  61. package/lib/sync/processors/subscription.js +4 -1
  62. package/lib/sync/processors/subscription.js.map +1 -1
  63. package/lib/sync/processors/sync.d.ts +28 -0
  64. package/lib/sync/processors/sync.js.map +1 -1
  65. package/lib/sync/utils.d.ts +42 -0
  66. package/lib/sync/utils.js +30 -31
  67. package/lib/sync/utils.js.map +1 -1
  68. package/lib/types.d.ts +553 -0
  69. package/lib/types.js +6 -1
  70. package/lib/types.js.map +1 -1
  71. package/lib/util.d.ts +189 -0
  72. package/lib/util.js +174 -104
  73. package/lib/util.js.map +1 -1
  74. package/lib-esm/authModeStrategies/multiAuthStrategy.js.map +1 -1
  75. package/lib-esm/datastore/datastore.d.ts +59 -8
  76. package/lib-esm/datastore/datastore.js +643 -147
  77. package/lib-esm/datastore/datastore.js.map +1 -1
  78. package/lib-esm/index.d.ts +3 -2
  79. package/lib-esm/index.js +2 -1
  80. package/lib-esm/index.js.map +1 -1
  81. package/lib-esm/predicates/index.d.ts +16 -2
  82. package/lib-esm/predicates/index.js +128 -7
  83. package/lib-esm/predicates/index.js.map +1 -1
  84. package/lib-esm/predicates/next.d.ts +342 -0
  85. package/lib-esm/predicates/next.js +797 -0
  86. package/lib-esm/predicates/next.js.map +1 -0
  87. package/lib-esm/predicates/sort.js +10 -4
  88. package/lib-esm/predicates/sort.js.map +1 -1
  89. package/lib-esm/storage/adapter/AsyncStorageAdapter.d.ts +2 -1
  90. package/lib-esm/storage/adapter/AsyncStorageAdapter.js +108 -295
  91. package/lib-esm/storage/adapter/AsyncStorageAdapter.js.map +1 -1
  92. package/lib-esm/storage/adapter/AsyncStorageDatabase.js +6 -5
  93. package/lib-esm/storage/adapter/AsyncStorageDatabase.js.map +1 -1
  94. package/lib-esm/storage/adapter/InMemoryStore.d.ts +1 -1
  95. package/lib-esm/storage/adapter/InMemoryStore.js.map +1 -1
  96. package/lib-esm/storage/adapter/IndexedDBAdapter.d.ts +4 -2
  97. package/lib-esm/storage/adapter/IndexedDBAdapter.js +226 -292
  98. package/lib-esm/storage/adapter/IndexedDBAdapter.js.map +1 -1
  99. package/lib-esm/storage/adapter/getDefaultAdapter/index.js.map +1 -1
  100. package/lib-esm/storage/relationship.d.ts +140 -0
  101. package/lib-esm/storage/relationship.js +333 -0
  102. package/lib-esm/storage/relationship.js.map +1 -0
  103. package/lib-esm/storage/storage.d.ts +7 -6
  104. package/lib-esm/storage/storage.js +32 -16
  105. package/lib-esm/storage/storage.js.map +1 -1
  106. package/lib-esm/sync/datastoreConnectivity.js.map +1 -1
  107. package/lib-esm/sync/index.js +3 -9
  108. package/lib-esm/sync/index.js.map +1 -1
  109. package/lib-esm/sync/merger.js.map +1 -1
  110. package/lib-esm/sync/outbox.js.map +1 -1
  111. package/lib-esm/sync/processors/errorMaps.js +1 -1
  112. package/lib-esm/sync/processors/errorMaps.js.map +1 -1
  113. package/lib-esm/sync/processors/mutation.js +10 -7
  114. package/lib-esm/sync/processors/mutation.js.map +1 -1
  115. package/lib-esm/sync/processors/subscription.js +4 -1
  116. package/lib-esm/sync/processors/subscription.js.map +1 -1
  117. package/lib-esm/sync/processors/sync.js.map +1 -1
  118. package/lib-esm/sync/utils.d.ts +1 -1
  119. package/lib-esm/sync/utils.js +31 -32
  120. package/lib-esm/sync/utils.js.map +1 -1
  121. package/lib-esm/types.d.ts +59 -7
  122. package/lib-esm/types.js +6 -2
  123. package/lib-esm/types.js.map +1 -1
  124. package/lib-esm/util.d.ts +39 -6
  125. package/lib-esm/util.js +170 -104
  126. package/lib-esm/util.js.map +1 -1
  127. package/package.json +14 -11
  128. package/src/authModeStrategies/multiAuthStrategy.ts +1 -1
  129. package/src/datastore/datastore.ts +680 -204
  130. package/src/index.ts +4 -0
  131. package/src/predicates/index.ts +143 -17
  132. package/src/predicates/next.ts +1016 -0
  133. package/src/predicates/sort.ts +8 -2
  134. package/src/storage/adapter/AsyncStorageAdapter.ts +56 -178
  135. package/src/storage/adapter/AsyncStorageDatabase.ts +16 -15
  136. package/src/storage/adapter/InMemoryStore.ts +5 -2
  137. package/src/storage/adapter/IndexedDBAdapter.ts +166 -191
  138. package/src/storage/adapter/getDefaultAdapter/index.ts +2 -2
  139. package/src/storage/relationship.ts +272 -0
  140. package/src/storage/storage.ts +56 -37
  141. package/src/sync/datastoreConnectivity.ts +4 -4
  142. package/src/sync/index.ts +22 -28
  143. package/src/sync/merger.ts +1 -1
  144. package/src/sync/outbox.ts +6 -6
  145. package/src/sync/processors/errorMaps.ts +1 -1
  146. package/src/sync/processors/mutation.ts +22 -17
  147. package/src/sync/processors/subscription.ts +19 -15
  148. package/src/sync/processors/sync.ts +16 -16
  149. package/src/sync/utils.ts +42 -48
  150. package/src/types.ts +116 -11
  151. package/src/util.ts +108 -150
@@ -1,13 +1,11 @@
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 {
5
- ModelPredicateCreator,
6
- ModelSortPredicateCreator,
7
- } from '../../predicates';
4
+ import { ModelPredicateCreator } from '../../predicates';
8
5
  import {
9
6
  InternalSchema,
10
7
  isPredicateObj,
8
+ isPredicateGroup,
11
9
  ModelInstanceMetadata,
12
10
  ModelPredicate,
13
11
  NamespaceResolver,
@@ -21,14 +19,14 @@ import {
21
19
  RelationType,
22
20
  } from '../../types';
23
21
  import {
24
- exhaustiveCheck,
25
22
  getIndex,
26
23
  getIndexFromAssociation,
27
24
  isModelConstructor,
28
25
  isPrivateMode,
29
26
  traverseModel,
30
27
  validatePredicate,
31
- sortCompareFunction,
28
+ inMemoryPagination,
29
+ NAMESPACES,
32
30
  keysEqual,
33
31
  getStorename,
34
32
  getIndexKeys,
@@ -41,17 +39,17 @@ const logger = new Logger('DataStore');
41
39
 
42
40
  const DB_NAME = 'amplify-datastore';
43
41
  class IndexedDBAdapter implements Adapter {
44
- private schema: InternalSchema;
45
- private namespaceResolver: NamespaceResolver;
46
- private modelInstanceCreator: ModelInstanceCreator;
47
- private getModelConstructorByModelName: (
48
- namsespaceName: string,
42
+ private schema!: InternalSchema;
43
+ private namespaceResolver!: NamespaceResolver;
44
+ private modelInstanceCreator!: ModelInstanceCreator;
45
+ private getModelConstructorByModelName?: (
46
+ namsespaceName: NAMESPACES,
49
47
  modelName: string
50
48
  ) => PersistentModelConstructor<any>;
51
- private db: idb.IDBPDatabase;
52
- private initPromise: Promise<void>;
53
- private resolve: (value?: any) => void;
54
- private reject: (value?: any) => void;
49
+ private db!: idb.IDBPDatabase;
50
+ private initPromise!: Promise<void>;
51
+ private resolve!: (value?: any) => void;
52
+ private reject!: (value?: any) => void;
55
53
  private dbName: string = DB_NAME;
56
54
  private safariCompatabilityMode: boolean = false;
57
55
 
@@ -125,7 +123,7 @@ class IndexedDBAdapter implements Adapter {
125
123
  namespaceResolver: NamespaceResolver,
126
124
  modelInstanceCreator: ModelInstanceCreator,
127
125
  getModelConstructorByModelName: (
128
- namsespaceName: string,
126
+ namsespaceName: NAMESPACES,
129
127
  modelName: string
130
128
  ) => PersistentModelConstructor<any>,
131
129
  sessionId?: string
@@ -285,7 +283,7 @@ class IndexedDBAdapter implements Adapter {
285
283
  model,
286
284
  this.schema.namespaces[namespaceName],
287
285
  this.modelInstanceCreator,
288
- this.getModelConstructorByModelName
286
+ this.getModelConstructorByModelName!
289
287
  );
290
288
 
291
289
  const set = new Set<string>();
@@ -313,9 +311,13 @@ class IndexedDBAdapter implements Adapter {
313
311
 
314
312
  if (condition && fromDB) {
315
313
  const predicates = ModelPredicateCreator.getPredicates(condition);
316
- const { predicates: predicateObjs, type } = predicates;
314
+ const { predicates: predicateObjs, type } = predicates || {};
317
315
 
318
- const isValid = validatePredicate(fromDB, type, predicateObjs);
316
+ const isValid = validatePredicate(
317
+ fromDB as any,
318
+ type as any,
319
+ predicateObjs as any
320
+ );
319
321
 
320
322
  if (!isValid) {
321
323
  const msg = 'Conditional update failed';
@@ -326,7 +328,6 @@ class IndexedDBAdapter implements Adapter {
326
328
  }
327
329
 
328
330
  const result: [T, OpType.INSERT | OpType.UPDATE][] = [];
329
-
330
331
  for await (const resItem of connectionStoreNames) {
331
332
  const { storeName, item, instance, keys } = resItem;
332
333
  const store = tx.objectStore(storeName);
@@ -351,7 +352,6 @@ class IndexedDBAdapter implements Adapter {
351
352
  .index('byPk')
352
353
  .getKey(this.canonicalKeyPath(itemKeyValues));
353
354
  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: string,
365
+ namespaceName: NAMESPACES,
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[srcModelName].relationTypes;
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,116 +382,6 @@ 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.map(
461
- targetName => recordItem[targetName]
462
- );
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
-
495
385
  return records.map(record =>
496
386
  this.modelInstanceCreator(modelConstructor, record)
497
387
  );
@@ -504,7 +394,9 @@ class IndexedDBAdapter implements Adapter {
504
394
  ): Promise<T[]> {
505
395
  await this.checkPrivate();
506
396
  const storeName = this.getStorenameForModel(modelConstructor);
507
- const namespaceName = this.namespaceResolver(modelConstructor);
397
+ const namespaceName = this.namespaceResolver(
398
+ modelConstructor
399
+ ) as NAMESPACES;
508
400
 
509
401
  const predicates =
510
402
  predicate && ModelPredicateCreator.getPredicates(predicate);
@@ -518,7 +410,7 @@ class IndexedDBAdapter implements Adapter {
518
410
  const hasSort = pagination && pagination.sort;
519
411
  const hasPagination = pagination && pagination.limit;
520
412
 
521
- const records: T[] = await (async () => {
413
+ const records: T[] = (await (async () => {
522
414
  if (queryByKey) {
523
415
  const record = await this.getByKey(storeName, queryByKey);
524
416
  return record ? [record] : [];
@@ -539,7 +431,7 @@ class IndexedDBAdapter implements Adapter {
539
431
  }
540
432
 
541
433
  return this.getAll(storeName);
542
- })();
434
+ })()) as T[];
543
435
 
544
436
  return await this.load(namespaceName, modelConstructor.name, records);
545
437
  }
@@ -548,8 +440,7 @@ class IndexedDBAdapter implements Adapter {
548
440
  storeName: string,
549
441
  keyValue: string[]
550
442
  ): Promise<T> {
551
- const record = <T>await this._get(storeName, keyValue);
552
- return record;
443
+ return <T>await this._get(storeName, keyValue);
553
444
  }
554
445
 
555
446
  private async getAll<T extends PersistentModel>(
@@ -568,7 +459,7 @@ class IndexedDBAdapter implements Adapter {
568
459
  return;
569
460
  }
570
461
 
571
- const keyValues = [];
462
+ const keyValues = [] as any[];
572
463
 
573
464
  for (const key of keyPath) {
574
465
  const predicateObj = predicateObjs.find(
@@ -581,17 +472,116 @@ class IndexedDBAdapter implements Adapter {
581
472
  return keyValues.length === keyPath.length ? keyValues : undefined;
582
473
  }
583
474
 
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
+
584
489
  private async filterOnPredicate<T extends PersistentModel>(
585
490
  storeName: string,
586
491
  predicates: PredicatesGroup<T>
587
492
  ) {
588
- const { predicates: predicateObjs, type } = predicates;
493
+ let { predicates: predicateObjs, type } = predicates;
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
+ }
589
503
 
590
- const all = <T[]>await this.getAll(storeName);
504
+ // where we'll accumulate candidate results, which will be filtered at the end.
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
+ }
591
581
 
592
582
  const filtered = predicateObjs
593
- ? all.filter(m => validatePredicate(m, type, predicateObjs))
594
- : all;
583
+ ? candidateResults.filter(m => validatePredicate(m, type, predicateObjs))
584
+ : candidateResults;
595
585
 
596
586
  return filtered;
597
587
  }
@@ -600,26 +590,7 @@ class IndexedDBAdapter implements Adapter {
600
590
  records: T[],
601
591
  pagination?: PaginationInput<T>
602
592
  ): T[] {
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;
593
+ return inMemoryPagination(records, pagination);
623
594
  }
624
595
 
625
596
  private async enginePagination<T extends PersistentModel>(
@@ -687,14 +658,15 @@ class IndexedDBAdapter implements Adapter {
687
658
  const deleteQueue: { storeName: string; items: T[] }[] = [];
688
659
 
689
660
  if (isModelConstructor(modelOrModelConstructor)) {
690
- const modelConstructor = modelOrModelConstructor;
691
- const nameSpace = this.namespaceResolver(modelConstructor);
661
+ const modelConstructor =
662
+ modelOrModelConstructor as PersistentModelConstructor<T>;
663
+ const nameSpace = this.namespaceResolver(modelConstructor) as NAMESPACES;
692
664
 
693
665
  const storeName = this.getStorenameForModel(modelConstructor);
694
666
 
695
667
  const models = await this.query(modelConstructor, condition);
696
668
  const relations =
697
- this.schema.namespaces[nameSpace].relationships[modelConstructor.name]
669
+ this.schema.namespaces![nameSpace].relationships![modelConstructor.name]
698
670
  .relationTypes;
699
671
 
700
672
  if (condition !== undefined) {
@@ -737,11 +709,13 @@ class IndexedDBAdapter implements Adapter {
737
709
  return [models, deletedModels];
738
710
  }
739
711
  } else {
740
- const model = modelOrModelConstructor;
712
+ const model = modelOrModelConstructor as T;
741
713
 
742
714
  const modelConstructor = Object.getPrototypeOf(model)
743
715
  .constructor as PersistentModelConstructor<T>;
744
- const namespaceName = this.namespaceResolver(modelConstructor);
716
+ const namespaceName = this.namespaceResolver(
717
+ modelConstructor
718
+ ) as NAMESPACES;
745
719
 
746
720
  const storeName = this.getStorenameForModel(modelConstructor);
747
721
 
@@ -760,9 +734,10 @@ class IndexedDBAdapter implements Adapter {
760
734
  }
761
735
 
762
736
  const predicates = ModelPredicateCreator.getPredicates(condition);
763
- const { predicates: predicateObjs, type } = predicates;
737
+ const { predicates: predicateObjs, type } =
738
+ predicates as PredicatesGroup<T>;
764
739
 
765
- const isValid = validatePredicate(fromDB, type, predicateObjs);
740
+ const isValid = validatePredicate(fromDB as T, type, predicateObjs);
766
741
 
767
742
  if (!isValid) {
768
743
  const msg = 'Conditional update failed';
@@ -773,7 +748,7 @@ class IndexedDBAdapter implements Adapter {
773
748
  await tx.done;
774
749
 
775
750
  const relations =
776
- this.schema.namespaces[namespaceName].relationships[
751
+ this.schema.namespaces[namespaceName].relationships![
777
752
  modelConstructor.name
778
753
  ].relationTypes;
779
754
 
@@ -786,7 +761,7 @@ class IndexedDBAdapter implements Adapter {
786
761
  );
787
762
  } else {
788
763
  const relations =
789
- this.schema.namespaces[namespaceName].relationships[
764
+ this.schema.namespaces[namespaceName].relationships![
790
765
  modelConstructor.name
791
766
  ].relationTypes;
792
767
 
@@ -816,18 +791,18 @@ class IndexedDBAdapter implements Adapter {
816
791
  items: T[] | IDBValidKey[];
817
792
  }[]
818
793
  ) {
819
- const connectionStoreNames = deleteQueue.map(({ storeName }) => {
794
+ const connectionStoreNames = deleteQueue!.map(({ storeName }) => {
820
795
  return storeName;
821
796
  });
822
797
 
823
798
  const tx = this.db.transaction([...connectionStoreNames], 'readwrite');
824
- for await (const deleteItem of deleteQueue) {
799
+ for await (const deleteItem of deleteQueue!) {
825
800
  const { storeName, items } = deleteItem;
826
801
  const store = tx.objectStore(storeName);
827
802
 
828
803
  for await (const item of items) {
829
804
  if (item) {
830
- let key: IDBValidKey;
805
+ let key: IDBValidKey | undefined;
831
806
 
832
807
  if (typeof item === 'object') {
833
808
  const keyValues = this.getIndexKeyValuesFromModel(item as T);
@@ -851,7 +826,7 @@ class IndexedDBAdapter implements Adapter {
851
826
  relations: RelationType[],
852
827
  models: T[],
853
828
  srcModel: string,
854
- nameSpace: string,
829
+ nameSpace: NAMESPACES,
855
830
  deleteQueue: { storeName: string; items: T[] }[]
856
831
  ): Promise<void> {
857
832
  for await (const rel of relations) {
@@ -885,7 +860,7 @@ class IndexedDBAdapter implements Adapter {
885
860
  );
886
861
 
887
862
  await this.deleteTraverse(
888
- this.schema.namespaces[nameSpace].relationships[modelName]
863
+ this.schema.namespaces[nameSpace].relationships![modelName]
889
864
  .relationTypes,
890
865
  recordToDelete ? [recordToDelete] : [],
891
866
  modelName,
@@ -908,7 +883,7 @@ class IndexedDBAdapter implements Adapter {
908
883
  // If we deprecate, we'll need to re-gen the MIPR in __tests__/schema.ts > newSchema
909
884
  // otherwise some unit tests will fail
910
885
  index = getIndex(
911
- this.schema.namespaces[nameSpace].relationships[modelName]
886
+ this.schema.namespaces[nameSpace].relationships![modelName]
912
887
  .relationTypes,
913
888
  srcModel
914
889
  );
@@ -926,7 +901,7 @@ class IndexedDBAdapter implements Adapter {
926
901
  );
927
902
 
928
903
  await this.deleteTraverse(
929
- this.schema.namespaces[nameSpace].relationships[modelName]
904
+ this.schema.namespaces[nameSpace].relationships![modelName]
930
905
  .relationTypes,
931
906
  recordToDelete ? [recordToDelete] : [],
932
907
  modelName,
@@ -941,15 +916,15 @@ class IndexedDBAdapter implements Adapter {
941
916
  const index =
942
917
  // explicit bi-directional @hasMany and @manyToMany
943
918
  getIndex(
944
- this.schema.namespaces[nameSpace].relationships[modelName]
919
+ this.schema.namespaces[nameSpace].relationships![modelName]
945
920
  .relationTypes,
946
921
  srcModel
947
922
  ) ||
948
923
  // uni and/or implicit @hasMany
949
924
  getIndexFromAssociation(
950
- this.schema.namespaces[nameSpace].relationships[modelName]
925
+ this.schema.namespaces[nameSpace].relationships![modelName]
951
926
  .indexes,
952
- associatedWith
927
+ associatedWith!
953
928
  );
954
929
  const keyValues = this.getIndexKeyValuesFromModel(model);
955
930
 
@@ -960,7 +935,7 @@ class IndexedDBAdapter implements Adapter {
960
935
  .getAll(this.canonicalKeyPath(keyValues));
961
936
 
962
937
  await this.deleteTraverse(
963
- this.schema.namespaces[nameSpace].relationships[modelName]
938
+ this.schema.namespaces[nameSpace].relationships![modelName]
964
939
  .relationTypes,
965
940
  childrenArray,
966
941
  modelName,
@@ -973,7 +948,7 @@ class IndexedDBAdapter implements Adapter {
973
948
  // Intentionally blank
974
949
  break;
975
950
  default:
976
- exhaustiveCheck(relationType);
951
+ throw new Error(`Invalid relation type ${relationType}`);
977
952
  break;
978
953
  }
979
954
  }
@@ -982,7 +957,7 @@ class IndexedDBAdapter implements Adapter {
982
957
  storeName: getStorename(nameSpace, srcModel),
983
958
  items: models.map(record =>
984
959
  this.modelInstanceCreator(
985
- this.getModelConstructorByModelName(nameSpace, srcModel),
960
+ this.getModelConstructorByModelName!(nameSpace, srcModel),
986
961
  record
987
962
  )
988
963
  ),
@@ -996,8 +971,8 @@ class IndexedDBAdapter implements Adapter {
996
971
 
997
972
  await idb.deleteDB(this.dbName);
998
973
 
999
- this.db = undefined;
1000
- this.initPromise = undefined;
974
+ this.db = undefined!;
975
+ this.initPromise = undefined!;
1001
976
  }
1002
977
 
1003
978
  async batchSave<T extends PersistentModel>(
@@ -1027,7 +1002,7 @@ class IndexedDBAdapter implements Adapter {
1027
1002
  model,
1028
1003
  this.schema.namespaces[namespaceName],
1029
1004
  this.modelInstanceCreator,
1030
- this.getModelConstructorByModelName
1005
+ this.getModelConstructorByModelName!
1031
1006
  );
1032
1007
 
1033
1008
  const keyValues = this.getIndexKeyValuesFromModel(model);
@@ -1041,7 +1016,7 @@ class IndexedDBAdapter implements Adapter {
1041
1016
  const { instance } = connectedModels.find(({ instance }) => {
1042
1017
  const instanceKeyValues = this.getIndexKeyValuesFromModel(instance);
1043
1018
  return keysEqual(instanceKeyValues, keyValues);
1044
- });
1019
+ })!;
1045
1020
 
1046
1021
  result.push([
1047
1022
  <T>(<unknown>instance),
@@ -1073,7 +1048,7 @@ class IndexedDBAdapter implements Adapter {
1073
1048
  });
1074
1049
 
1075
1050
  const { indexes } =
1076
- this.schema.namespaces[namespaceName].relationships[modelName];
1051
+ this.schema.namespaces[namespaceName].relationships![modelName];
1077
1052
 
1078
1053
  indexes.forEach(([idxName, keyPath, options]) => {
1079
1054
  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 as Adapter;
11
11
  }
12
12
 
13
- return AsyncStorageAdapter;
13
+ return AsyncStorageAdapter as Adapter;
14
14
  };
15
15
 
16
16
  export default getDefaultAdapter;