@aws-amplify/datastore 3.7.3 → 3.7.4-custom-pk.79

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 (137) hide show
  1. package/CHANGELOG.md +144 -17
  2. package/README.md +154 -0
  3. package/dist/aws-amplify-datastore.js +3344 -1405
  4. package/dist/aws-amplify-datastore.js.map +1 -1
  5. package/dist/aws-amplify-datastore.min.js +25 -95
  6. package/dist/aws-amplify-datastore.min.js.map +1 -1
  7. package/lib/authModeStrategies/multiAuthStrategy.d.ts +2 -2
  8. package/lib/authModeStrategies/multiAuthStrategy.js +32 -26
  9. package/lib/authModeStrategies/multiAuthStrategy.js.map +1 -1
  10. package/lib/datastore/datastore.d.ts +17 -16
  11. package/lib/datastore/datastore.js +250 -78
  12. package/lib/datastore/datastore.js.map +1 -1
  13. package/lib/index.d.ts +3 -19
  14. package/lib/predicates/index.d.ts +3 -2
  15. package/lib/predicates/index.js +12 -2
  16. package/lib/predicates/index.js.map +1 -1
  17. package/lib/storage/adapter/AsyncStorageAdapter.d.ts +6 -2
  18. package/lib/storage/adapter/AsyncStorageAdapter.js +140 -66
  19. package/lib/storage/adapter/AsyncStorageAdapter.js.map +1 -1
  20. package/lib/storage/adapter/AsyncStorageDatabase.d.ts +4 -4
  21. package/lib/storage/adapter/AsyncStorageDatabase.js +57 -27
  22. package/lib/storage/adapter/AsyncStorageDatabase.js.map +1 -1
  23. package/lib/storage/adapter/InMemoryStore.js +2 -2
  24. package/lib/storage/adapter/InMemoryStore.js.map +1 -1
  25. package/lib/storage/adapter/IndexedDBAdapter.d.ts +7 -2
  26. package/lib/storage/adapter/IndexedDBAdapter.js +460 -253
  27. package/lib/storage/adapter/IndexedDBAdapter.js.map +1 -1
  28. package/lib/storage/adapter/index.d.ts +1 -1
  29. package/lib/storage/storage.d.ts +3 -3
  30. package/lib/storage/storage.js +106 -28
  31. package/lib/storage/storage.js.map +1 -1
  32. package/lib/sync/index.d.ts +23 -7
  33. package/lib/sync/index.js +18 -17
  34. package/lib/sync/index.js.map +1 -1
  35. package/lib/sync/merger.d.ts +3 -3
  36. package/lib/sync/merger.js +8 -6
  37. package/lib/sync/merger.js.map +1 -1
  38. package/lib/sync/outbox.d.ts +2 -2
  39. package/lib/sync/outbox.js +12 -9
  40. package/lib/sync/outbox.js.map +1 -1
  41. package/lib/sync/processors/errorMaps.d.ts +17 -0
  42. package/lib/sync/processors/errorMaps.js +125 -0
  43. package/lib/sync/processors/errorMaps.js.map +1 -0
  44. package/lib/sync/processors/mutation.d.ts +22 -4
  45. package/lib/sync/processors/mutation.js +119 -51
  46. package/lib/sync/processors/mutation.js.map +1 -1
  47. package/lib/sync/processors/subscription.d.ts +4 -2
  48. package/lib/sync/processors/subscription.js +76 -41
  49. package/lib/sync/processors/subscription.js.map +1 -1
  50. package/lib/sync/processors/sync.d.ts +4 -2
  51. package/lib/sync/processors/sync.js +62 -27
  52. package/lib/sync/processors/sync.js.map +1 -1
  53. package/lib/sync/utils.d.ts +3 -2
  54. package/lib/sync/utils.js +54 -7
  55. package/lib/sync/utils.js.map +1 -1
  56. package/lib/types.d.ts +85 -33
  57. package/lib/types.js +31 -2
  58. package/lib/types.js.map +1 -1
  59. package/lib/util.d.ts +43 -33
  60. package/lib/util.js +161 -21
  61. package/lib/util.js.map +1 -1
  62. package/lib-esm/authModeStrategies/multiAuthStrategy.d.ts +2 -2
  63. package/lib-esm/authModeStrategies/multiAuthStrategy.js +32 -26
  64. package/lib-esm/authModeStrategies/multiAuthStrategy.js.map +1 -1
  65. package/lib-esm/datastore/datastore.d.ts +17 -16
  66. package/lib-esm/datastore/datastore.js +252 -80
  67. package/lib-esm/datastore/datastore.js.map +1 -1
  68. package/lib-esm/index.d.ts +3 -19
  69. package/lib-esm/predicates/index.d.ts +3 -2
  70. package/lib-esm/predicates/index.js +13 -3
  71. package/lib-esm/predicates/index.js.map +1 -1
  72. package/lib-esm/storage/adapter/AsyncStorageAdapter.d.ts +6 -2
  73. package/lib-esm/storage/adapter/AsyncStorageAdapter.js +140 -66
  74. package/lib-esm/storage/adapter/AsyncStorageAdapter.js.map +1 -1
  75. package/lib-esm/storage/adapter/AsyncStorageDatabase.d.ts +4 -4
  76. package/lib-esm/storage/adapter/AsyncStorageDatabase.js +57 -27
  77. package/lib-esm/storage/adapter/AsyncStorageDatabase.js.map +1 -1
  78. package/lib-esm/storage/adapter/InMemoryStore.js +2 -2
  79. package/lib-esm/storage/adapter/InMemoryStore.js.map +1 -1
  80. package/lib-esm/storage/adapter/IndexedDBAdapter.d.ts +7 -2
  81. package/lib-esm/storage/adapter/IndexedDBAdapter.js +460 -253
  82. package/lib-esm/storage/adapter/IndexedDBAdapter.js.map +1 -1
  83. package/lib-esm/storage/adapter/index.d.ts +1 -1
  84. package/lib-esm/storage/storage.d.ts +3 -3
  85. package/lib-esm/storage/storage.js +106 -28
  86. package/lib-esm/storage/storage.js.map +1 -1
  87. package/lib-esm/sync/index.d.ts +23 -7
  88. package/lib-esm/sync/index.js +20 -19
  89. package/lib-esm/sync/index.js.map +1 -1
  90. package/lib-esm/sync/merger.d.ts +3 -3
  91. package/lib-esm/sync/merger.js +8 -6
  92. package/lib-esm/sync/merger.js.map +1 -1
  93. package/lib-esm/sync/outbox.d.ts +2 -2
  94. package/lib-esm/sync/outbox.js +13 -10
  95. package/lib-esm/sync/outbox.js.map +1 -1
  96. package/lib-esm/sync/processors/errorMaps.d.ts +17 -0
  97. package/lib-esm/sync/processors/errorMaps.js +119 -0
  98. package/lib-esm/sync/processors/errorMaps.js.map +1 -0
  99. package/lib-esm/sync/processors/mutation.d.ts +22 -4
  100. package/lib-esm/sync/processors/mutation.js +122 -54
  101. package/lib-esm/sync/processors/mutation.js.map +1 -1
  102. package/lib-esm/sync/processors/subscription.d.ts +4 -2
  103. package/lib-esm/sync/processors/subscription.js +77 -42
  104. package/lib-esm/sync/processors/subscription.js.map +1 -1
  105. package/lib-esm/sync/processors/sync.d.ts +4 -2
  106. package/lib-esm/sync/processors/sync.js +62 -27
  107. package/lib-esm/sync/processors/sync.js.map +1 -1
  108. package/lib-esm/sync/utils.d.ts +3 -2
  109. package/lib-esm/sync/utils.js +54 -8
  110. package/lib-esm/sync/utils.js.map +1 -1
  111. package/lib-esm/types.d.ts +85 -33
  112. package/lib-esm/types.js +29 -3
  113. package/lib-esm/types.js.map +1 -1
  114. package/lib-esm/util.d.ts +43 -33
  115. package/lib-esm/util.js +151 -18
  116. package/lib-esm/util.js.map +1 -1
  117. package/package.json +10 -9
  118. package/src/authModeStrategies/multiAuthStrategy.ts +24 -21
  119. package/src/datastore/datastore.ts +396 -143
  120. package/src/predicates/index.ts +32 -10
  121. package/src/storage/adapter/AsyncStorageAdapter.ts +161 -61
  122. package/src/storage/adapter/AsyncStorageDatabase.ts +62 -25
  123. package/src/storage/adapter/InMemoryStore.ts +2 -2
  124. package/src/storage/adapter/IndexedDBAdapter.ts +355 -101
  125. package/src/storage/adapter/index.ts +1 -1
  126. package/src/storage/storage.ts +85 -24
  127. package/src/sync/index.ts +74 -58
  128. package/src/sync/merger.ts +16 -4
  129. package/src/sync/outbox.ts +22 -8
  130. package/src/sync/processors/errorMaps.ts +93 -0
  131. package/src/sync/processors/mutation.ts +143 -84
  132. package/src/sync/processors/subscription.ts +67 -36
  133. package/src/sync/processors/sync.ts +37 -12
  134. package/src/sync/utils.ts +63 -15
  135. package/src/types.ts +242 -49
  136. package/src/util.ts +212 -35
  137. package/ssr/package.json +1 -1
@@ -1,4 +1,7 @@
1
+ import API from '@aws-amplify/api';
1
2
  import { Amplify, ConsoleLogger as Logger, Hub, JS } from '@aws-amplify/core';
3
+ import { Auth } from '@aws-amplify/auth';
4
+ import Cache from '@aws-amplify/cache';
2
5
  import {
3
6
  Draft,
4
7
  immerable,
@@ -54,6 +57,12 @@ import {
54
57
  isNonModelFieldType,
55
58
  isModelFieldType,
56
59
  ObserveQueryOptions,
60
+ ManagedIdentifier,
61
+ PersistentModelMetaData,
62
+ IdentifierFieldOrIdentifierObject,
63
+ __modelMeta__,
64
+ isIdentifierObject,
65
+ AmplifyContext,
57
66
  } from '../types';
58
67
  import {
59
68
  DATASTORE,
@@ -69,6 +78,11 @@ import {
69
78
  registerNonModelClass,
70
79
  sortCompareFunction,
71
80
  DeferredCallbackResolver,
81
+ extractPrimaryKeyFieldNames,
82
+ isIdManaged,
83
+ isIdOptionallyManaged,
84
+ validatePredicate,
85
+ mergePatches,
72
86
  } from '../util';
73
87
 
74
88
  setAutoFreeze(true);
@@ -79,11 +93,16 @@ const logger = new Logger('DataStore');
79
93
  const ulid = monotonicUlidFactory(Date.now());
80
94
  const { isNode } = JS.browserOrNode();
81
95
 
96
+ type SettingMetaData = {
97
+ identifier: ManagedIdentifier<Setting, 'id'>;
98
+ readOnlyFields: never;
99
+ };
82
100
  declare class Setting {
83
- constructor(init: ModelInit<Setting>);
101
+ public readonly [__modelMeta__]: SettingMetaData;
102
+ constructor(init: ModelInit<Setting, SettingMetaData>);
84
103
  static copyOf(
85
104
  src: Setting,
86
- mutator: (draft: MutableModel<Setting>) => void | Setting
105
+ mutator: (draft: MutableModel<Setting, SettingMetaData>) => void | Setting
87
106
  ): Setting;
88
107
  public readonly id: string;
89
108
  public readonly key: string;
@@ -222,6 +241,20 @@ const initSchema = (userSchema: Schema) => {
222
241
  return userClasses;
223
242
  };
224
243
 
244
+ /* Checks if the schema has been initialized by initSchema().
245
+ *
246
+ * Call this function before accessing schema.
247
+ * Currently this only needs to be called in start() and clear() because all other functions will call start first.
248
+ */
249
+ const checkSchemaInitialized = () => {
250
+ if (schema === undefined) {
251
+ const message =
252
+ 'Schema is not initialized. DataStore will not function as expected. This could happen if you have multiple versions of DataStore installed. Please see https://docs.amplify.aws/lib/troubleshooting/upgrading/q/platform/js/#check-for-duplicate-versions';
253
+ logger.error(message);
254
+ throw new Error(message);
255
+ }
256
+ };
257
+
225
258
  const createTypeClasses: (
226
259
  namespace: SchemaNamespace
227
260
  ) => TypeConstructorMap = namespace => {
@@ -246,16 +279,15 @@ const createTypeClasses: (
246
279
 
247
280
  export declare type ModelInstanceCreator = typeof modelInstanceCreator;
248
281
 
249
- const instancesMetadata = new WeakSet<
250
- ModelInit<PersistentModel & Partial<ModelInstanceMetadata>>
251
- >();
252
- function modelInstanceCreator<T extends PersistentModel = PersistentModel>(
282
+ const instancesMetadata = new WeakSet<ModelInit<unknown, unknown>>();
283
+
284
+ function modelInstanceCreator<T extends PersistentModel>(
253
285
  modelConstructor: PersistentModelConstructor<T>,
254
- init: ModelInit<T> & Partial<ModelInstanceMetadata>
286
+ init: Partial<T>
255
287
  ): T {
256
288
  instancesMetadata.add(init);
257
289
 
258
- return <T>new modelConstructor(init);
290
+ return new modelConstructor(<ModelInit<T, PersistentModelMetaData<T>>>init);
259
291
  }
260
292
 
261
293
  const validateModelFields =
@@ -383,7 +415,7 @@ const castInstanceType = (
383
415
  return v;
384
416
  };
385
417
 
386
- const initializeInstance = <T>(
418
+ const initializeInstance = <T extends PersistentModel>(
387
419
  init: ModelInit<T>,
388
420
  modelDefinition: SchemaModel | SchemaNonModel,
389
421
  draft: Draft<T & ModelInstanceMetadata>
@@ -407,31 +439,39 @@ const createModelClass = <T extends PersistentModel>(
407
439
  (draft: Draft<T & ModelInstanceMetadata>) => {
408
440
  initializeInstance(init, modelDefinition, draft);
409
441
 
442
+ // model is initialized inside a DataStore component (e.g. by Sync Engine, Storage Engine, etc.)
443
+ const isInternallyInitialized = instancesMetadata.has(init);
444
+
410
445
  const modelInstanceMetadata: ModelInstanceMetadata =
411
- instancesMetadata.has(init)
446
+ isInternallyInitialized
412
447
  ? <ModelInstanceMetadata>(<unknown>init)
413
448
  : <ModelInstanceMetadata>{};
414
- const {
415
- id: _id,
416
- _version,
417
- _lastChangedAt,
418
- _deleted,
419
- } = modelInstanceMetadata;
420
-
421
- // instancesIds are set by modelInstanceCreator, it is accessible only internally
422
- const isInternal = _id !== null && _id !== undefined;
423
-
424
- const id = isInternal
425
- ? _id
426
- : modelDefinition.syncable
427
- ? uuid4()
428
- : ulid();
429
-
430
- if (!isInternal) {
449
+
450
+ type ModelWithIDIdentifier = { id: string };
451
+
452
+ const { id: _id } =
453
+ modelInstanceMetadata as unknown as ModelWithIDIdentifier;
454
+
455
+ if (isIdManaged(modelDefinition)) {
456
+ const isInternalModel = _id !== null && _id !== undefined;
457
+
458
+ const id = isInternalModel
459
+ ? _id
460
+ : modelDefinition.syncable
461
+ ? uuid4()
462
+ : ulid();
463
+
464
+ (<ModelWithIDIdentifier>(<unknown>draft)).id = id;
465
+ } else if (isIdOptionallyManaged(modelDefinition)) {
466
+ // only auto-populate if the id was not provided
467
+ (<ModelWithIDIdentifier>(<unknown>draft)).id = draft.id || uuid4();
468
+ }
469
+
470
+ if (!isInternallyInitialized) {
431
471
  checkReadOnlyPropertyOnCreate(draft, modelDefinition);
432
472
  }
433
473
 
434
- draft.id = id;
474
+ const { _version, _lastChangedAt, _deleted } = modelInstanceMetadata;
435
475
 
436
476
  if (modelDefinition.syncable) {
437
477
  draft._version = _version;
@@ -456,8 +496,14 @@ const createModelClass = <T extends PersistentModel>(
456
496
  const model = produce(
457
497
  source,
458
498
  draft => {
459
- fn(<MutableModel<T>>(draft as unknown));
460
- draft.id = source.id;
499
+ fn(<MutableModel<T>>draft);
500
+
501
+ const keyNames = extractPrimaryKeyFieldNames(modelDefinition);
502
+ // Keys are immutable
503
+ // CPK TODO: Previous comment from Ivan:
504
+ // @ts-ignore TODO: fix type
505
+ keyNames.forEach(key => (draft[key] = source[key]));
506
+
461
507
  const modelValidator = validateModelFields(modelDefinition);
462
508
  Object.entries(draft).forEach(([k, v]) => {
463
509
  const parsedValue = castInstanceType(modelDefinition, k, v);
@@ -468,9 +514,21 @@ const createModelClass = <T extends PersistentModel>(
468
514
  p => (patches = p)
469
515
  );
470
516
 
471
- if (patches.length) {
472
- modelPatchesMap.set(model, [patches, source]);
473
- checkReadOnlyPropertyOnUpdate(patches, modelDefinition);
517
+ const hasExistingPatches = modelPatchesMap.has(source);
518
+ if (patches.length || hasExistingPatches) {
519
+ if (hasExistingPatches) {
520
+ const [existingPatches, existingSource] = modelPatchesMap.get(source);
521
+ const mergedPatches = mergePatches(
522
+ existingSource,
523
+ existingPatches,
524
+ patches
525
+ );
526
+ modelPatchesMap.set(model, [mergedPatches, existingSource]);
527
+ checkReadOnlyPropertyOnUpdate(mergedPatches, modelDefinition);
528
+ } else {
529
+ modelPatchesMap.set(model, [patches, source]);
530
+ checkReadOnlyPropertyOnUpdate(patches, modelDefinition);
531
+ }
474
532
  }
475
533
 
476
534
  return model;
@@ -484,6 +542,7 @@ const createModelClass = <T extends PersistentModel>(
484
542
  }
485
543
 
486
544
  const instance = modelInstanceCreator(clazz, json);
545
+
487
546
  const modelValidator = validateModelFields(modelDefinition);
488
547
 
489
548
  Object.entries(instance).forEach(([k, v]) => {
@@ -531,7 +590,9 @@ const checkReadOnlyPropertyOnUpdate = (
531
590
  });
532
591
  };
533
592
 
534
- const createNonModelClass = <T>(typeDefinition: SchemaNonModel) => {
593
+ const createNonModelClass = <T extends PersistentModel>(
594
+ typeDefinition: SchemaNonModel
595
+ ) => {
535
596
  const clazz = <NonModelTypeConstructor<T>>(<unknown>class Model {
536
597
  constructor(init: ModelInit<T>) {
537
598
  const instance = produce(
@@ -564,7 +625,7 @@ function defaultConflictHandler(conflictData: SyncConflict): PersistentModel {
564
625
  return modelInstanceCreator(modelConstructor, { ...localModel, _version });
565
626
  }
566
627
 
567
- function defaultErrorHandler(error: SyncError) {
628
+ function defaultErrorHandler(error: SyncError<PersistentModel>): void {
568
629
  logger.warn(error);
569
630
  }
570
631
 
@@ -615,7 +676,6 @@ async function checkSchemaVersion(
615
676
  const [schemaVersionSetting] = await s.query(
616
677
  Setting,
617
678
  ModelPredicateCreator.createFromExisting(modelDefinition, c =>
618
- // @ts-ignore Argument of type '"eq"' is not assignable to parameter of type 'never'.
619
679
  c.key('eq', SETTING_SCHEMA_VERSION)
620
680
  ),
621
681
  { page: 0, limit: 1 }
@@ -682,23 +742,34 @@ function getNamespace(): SchemaNamespace {
682
742
  }
683
743
 
684
744
  class DataStore {
745
+ // reference to configured category instances. Used for preserving SSR context
746
+ Auth = Auth;
747
+ API = API;
748
+ Cache = Cache;
749
+
685
750
  private amplifyConfig: Record<string, any> = {};
686
751
  private authModeStrategy: AuthModeStrategy;
687
752
  private conflictHandler: ConflictHandler;
688
- private errorHandler: (error: SyncError) => void;
753
+ private errorHandler: (error: SyncError<PersistentModel>) => void;
689
754
  private fullSyncInterval: number;
690
- private initialized: Promise<void>;
755
+ private initialized?: Promise<void>;
691
756
  private initReject: Function;
692
757
  private initResolve: Function;
693
758
  private maxRecordsToSync: number;
694
- private storage: Storage;
695
- private sync: SyncEngine;
759
+ private storage?: Storage;
760
+ private sync?: SyncEngine;
696
761
  private syncPageSize: number;
697
762
  private syncExpressions: SyncExpression[];
698
763
  private syncPredicates: WeakMap<SchemaModel, ModelPredicate<any>> =
699
764
  new WeakMap<SchemaModel, ModelPredicate<any>>();
700
765
  private sessionId: string;
701
766
  private storageAdapter: Adapter;
767
+ // object that gets passed to descendent classes. Allows us to pass these down by reference
768
+ private amplifyContext: AmplifyContext = {
769
+ Auth: this.Auth,
770
+ API: this.API,
771
+ Cache: this.Cache,
772
+ };
702
773
 
703
774
  getModuleName() {
704
775
  return 'DataStore';
@@ -728,6 +799,7 @@ class DataStore {
728
799
 
729
800
  await this.storage.init();
730
801
 
802
+ checkSchemaInitialized();
731
803
  await checkSchemaVersion(this.storage, schema.version);
732
804
 
733
805
  const { aws_appsync_graphqlEndpoint } = this.amplifyConfig;
@@ -744,13 +816,12 @@ class DataStore {
744
816
  userClasses,
745
817
  this.storage,
746
818
  modelInstanceCreator,
747
- this.maxRecordsToSync,
748
- this.syncPageSize,
749
819
  this.conflictHandler,
750
820
  this.errorHandler,
751
821
  this.syncPredicates,
752
822
  this.amplifyConfig,
753
- this.authModeStrategy
823
+ this.authModeStrategy,
824
+ this.amplifyContext
754
825
  );
755
826
 
756
827
  // tslint:disable-next-line:max-line-length
@@ -796,7 +867,10 @@ class DataStore {
796
867
  query: {
797
868
  <T extends PersistentModel>(
798
869
  modelConstructor: PersistentModelConstructor<T>,
799
- id: string
870
+ identifier: IdentifierFieldOrIdentifierObject<
871
+ T,
872
+ PersistentModelMetaData<T>
873
+ >
800
874
  ): Promise<T | undefined>;
801
875
  <T extends PersistentModel>(
802
876
  modelConstructor: PersistentModelConstructor<T>,
@@ -805,7 +879,10 @@ class DataStore {
805
879
  ): Promise<T[]>;
806
880
  } = async <T extends PersistentModel>(
807
881
  modelConstructor: PersistentModelConstructor<T>,
808
- idOrCriteria?: string | ProducerModelPredicate<T> | typeof PredicateAll,
882
+ identifierOrCriteria?:
883
+ | IdentifierFieldOrIdentifierObject<T, PersistentModelMetaData<T>>
884
+ | ProducerModelPredicate<T>
885
+ | typeof PredicateAll,
809
886
  paginationProducer?: ProducerPaginationInput<T>
810
887
  ): Promise<T | T[] | undefined> => {
811
888
  await this.start();
@@ -819,28 +896,37 @@ class DataStore {
819
896
  throw new Error(msg);
820
897
  }
821
898
 
822
- if (typeof idOrCriteria === 'string') {
899
+ if (typeof identifierOrCriteria === 'string') {
823
900
  if (paginationProducer !== undefined) {
824
901
  logger.warn('Pagination is ignored when querying by id');
825
902
  }
826
903
  }
827
904
 
828
905
  const modelDefinition = getModelDefinition(modelConstructor);
906
+ const keyFields = extractPrimaryKeyFieldNames(modelDefinition);
907
+
829
908
  let predicate: ModelPredicate<T>;
830
909
 
831
- if (isQueryOne(idOrCriteria)) {
832
- predicate = ModelPredicateCreator.createForId<T>(
910
+ if (isQueryOne(identifierOrCriteria)) {
911
+ predicate = ModelPredicateCreator.createForSingleField<T>(
833
912
  modelDefinition,
834
- idOrCriteria
913
+ keyFields[0],
914
+ identifierOrCriteria
835
915
  );
836
916
  } else {
837
- if (isPredicatesAll(idOrCriteria)) {
917
+ // Object is being queried using object literal syntax
918
+ if (isIdentifierObject(<T>identifierOrCriteria, modelDefinition)) {
919
+ predicate = ModelPredicateCreator.createForPk<T>(
920
+ modelDefinition,
921
+ <T>identifierOrCriteria
922
+ );
923
+ } else if (isPredicatesAll(identifierOrCriteria)) {
838
924
  // Predicates.ALL means "all records", so no predicate (undefined)
839
925
  predicate = undefined;
840
926
  } else {
841
927
  predicate = ModelPredicateCreator.createFromExisting(
842
928
  modelDefinition,
843
- idOrCriteria
929
+ <any>identifierOrCriteria
844
930
  );
845
931
  }
846
932
  }
@@ -870,7 +956,13 @@ class DataStore {
870
956
  pagination
871
957
  );
872
958
 
873
- return isQueryOne(idOrCriteria) ? result[0] : result;
959
+ const isSingleFieldIdentifier = keyFields.length === 1;
960
+ const returnOne =
961
+ isSingleFieldIdentifier &&
962
+ (isQueryOne(identifierOrCriteria) ||
963
+ isIdentifierObject(identifierOrCriteria, modelDefinition));
964
+
965
+ return returnOne ? result[0] : result;
874
966
  };
875
967
 
876
968
  save = async <T extends PersistentModel>(
@@ -883,7 +975,7 @@ class DataStore {
883
975
  // Allows us to only include changed fields for updates
884
976
  const patchesTuple = modelPatchesMap.get(model);
885
977
 
886
- const modelConstructor: PersistentModelConstructor<T> = model
978
+ const modelConstructor: PersistentModelConstructor<T> | undefined = model
887
979
  ? <PersistentModelConstructor<T>>model.constructor
888
980
  : undefined;
889
981
 
@@ -898,15 +990,15 @@ class DataStore {
898
990
 
899
991
  const producedCondition = ModelPredicateCreator.createFromExisting(
900
992
  modelDefinition,
901
- condition
993
+ condition!
902
994
  );
903
995
 
904
996
  const [savedModel] = await this.storage.runExclusive(async s => {
905
997
  await s.save(model, producedCondition, undefined, patchesTuple);
906
998
 
907
- return s.query(
999
+ return s.query<T>(
908
1000
  modelConstructor,
909
- ModelPredicateCreator.createForId(modelDefinition, model.id)
1001
+ ModelPredicateCreator.createForPk(modelDefinition, model)
910
1002
  );
911
1003
  });
912
1004
 
@@ -946,22 +1038,28 @@ class DataStore {
946
1038
  };
947
1039
 
948
1040
  delete: {
949
- <T extends PersistentModel>(
950
- model: T,
951
- condition?: ProducerModelPredicate<T>
952
- ): Promise<T>;
953
1041
  <T extends PersistentModel>(
954
1042
  modelConstructor: PersistentModelConstructor<T>,
955
- id: string
1043
+ identifier: IdentifierFieldOrIdentifierObject<
1044
+ T,
1045
+ PersistentModelMetaData<T>
1046
+ >
956
1047
  ): Promise<T[]>;
957
1048
  <T extends PersistentModel>(
958
1049
  modelConstructor: PersistentModelConstructor<T>,
959
1050
  condition: ProducerModelPredicate<T> | typeof PredicateAll
960
1051
  ): Promise<T[]>;
1052
+ <T extends PersistentModel>(
1053
+ model: T,
1054
+ condition?: ProducerModelPredicate<T>
1055
+ ): Promise<T>;
961
1056
  } = async <T extends PersistentModel>(
962
1057
  modelOrConstructor: T | PersistentModelConstructor<T>,
963
- idOrCriteria?: string | ProducerModelPredicate<T> | typeof PredicateAll
964
- ) => {
1058
+ identifierOrCriteria?:
1059
+ | IdentifierFieldOrIdentifierObject<T, PersistentModelMetaData<T>>
1060
+ | ProducerModelPredicate<T>
1061
+ | typeof PredicateAll
1062
+ ): Promise<T | T[]> => {
965
1063
  await this.start();
966
1064
 
967
1065
  let condition: ModelPredicate<T>;
@@ -973,31 +1071,43 @@ class DataStore {
973
1071
  throw new Error(msg);
974
1072
  }
975
1073
 
976
- if (isValidModelConstructor(modelOrConstructor)) {
1074
+ if (isValidModelConstructor<T>(modelOrConstructor)) {
977
1075
  const modelConstructor = modelOrConstructor;
978
1076
 
979
- if (!idOrCriteria) {
1077
+ if (!identifierOrCriteria) {
980
1078
  const msg =
981
1079
  'Id to delete or criteria required. Do you want to delete all? Pass Predicates.ALL';
982
- logger.error(msg, { idOrCriteria });
1080
+ logger.error(msg, { identifierOrCriteria });
983
1081
 
984
1082
  throw new Error(msg);
985
1083
  }
986
1084
 
987
- if (typeof idOrCriteria === 'string') {
988
- condition = ModelPredicateCreator.createForId<T>(
1085
+ const modelDefinition = getModelDefinition(modelConstructor);
1086
+
1087
+ if (typeof identifierOrCriteria === 'string') {
1088
+ const [keyField] = extractPrimaryKeyFieldNames(modelDefinition);
1089
+
1090
+ condition = ModelPredicateCreator.createForSingleField<T>(
989
1091
  getModelDefinition(modelConstructor),
990
- idOrCriteria
1092
+ keyField,
1093
+ identifierOrCriteria
991
1094
  );
992
1095
  } else {
993
- condition = ModelPredicateCreator.createFromExisting(
994
- getModelDefinition(modelConstructor),
995
- /**
996
- * idOrCriteria is always a ProducerModelPredicate<T>, never a symbol.
997
- * The symbol is used only for typing purposes. e.g. see Predicates.ALL
998
- */
999
- idOrCriteria as ProducerModelPredicate<T>
1000
- );
1096
+ if (isIdentifierObject(identifierOrCriteria, modelDefinition)) {
1097
+ condition = ModelPredicateCreator.createForPk<T>(
1098
+ modelDefinition,
1099
+ <T>identifierOrCriteria
1100
+ );
1101
+ } else {
1102
+ condition = ModelPredicateCreator.createFromExisting(
1103
+ modelDefinition,
1104
+ /**
1105
+ * idOrCriteria is always a ProducerModelPredicate<T>, never a symbol.
1106
+ * The symbol is used only for typing purposes. e.g. see Predicates.ALL
1107
+ */
1108
+ identifierOrCriteria as ProducerModelPredicate<T>
1109
+ );
1110
+ }
1001
1111
 
1002
1112
  if (!condition || !ModelPredicateCreator.isValidPredicate(condition)) {
1003
1113
  const msg =
@@ -1025,22 +1135,24 @@ class DataStore {
1025
1135
 
1026
1136
  const modelDefinition = getModelDefinition(modelConstructor);
1027
1137
 
1028
- const idPredicate = ModelPredicateCreator.createForId<T>(
1138
+ const pkPredicate = ModelPredicateCreator.createForPk<T>(
1029
1139
  modelDefinition,
1030
- model.id
1140
+ model
1031
1141
  );
1032
1142
 
1033
- if (idOrCriteria) {
1034
- if (typeof idOrCriteria !== 'function') {
1143
+ if (identifierOrCriteria) {
1144
+ if (typeof identifierOrCriteria !== 'function') {
1035
1145
  const msg = 'Invalid criteria';
1036
- logger.error(msg, { idOrCriteria });
1146
+ logger.error(msg, { identifierOrCriteria });
1037
1147
 
1038
1148
  throw new Error(msg);
1039
1149
  }
1040
1150
 
1041
- condition = idOrCriteria(idPredicate);
1151
+ condition = (<ProducerModelPredicate<T>>identifierOrCriteria)(
1152
+ pkPredicate
1153
+ );
1042
1154
  } else {
1043
- condition = idPredicate;
1155
+ condition = pkPredicate;
1044
1156
  }
1045
1157
 
1046
1158
  const [[deleted]] = await this.storage.delete(model, condition);
@@ -1052,20 +1164,28 @@ class DataStore {
1052
1164
  observe: {
1053
1165
  (): Observable<SubscriptionMessage<PersistentModel>>;
1054
1166
 
1055
- <T extends PersistentModel>(model: T): Observable<SubscriptionMessage<T>>;
1167
+ <T extends PersistentModel>(
1168
+ modelConstructor: PersistentModelConstructor<T>,
1169
+ identifier: string
1170
+ ): Observable<SubscriptionMessage<T>>;
1056
1171
 
1057
1172
  <T extends PersistentModel>(
1058
1173
  modelConstructor: PersistentModelConstructor<T>,
1059
- criteria?: string | ProducerModelPredicate<T>
1174
+ criteria?: ProducerModelPredicate<T> | typeof PredicateAll
1060
1175
  ): Observable<SubscriptionMessage<T>>;
1061
- } = <T extends PersistentModel = PersistentModel>(
1176
+
1177
+ <T extends PersistentModel>(model: T): Observable<SubscriptionMessage<T>>;
1178
+ } = <T extends PersistentModel>(
1062
1179
  modelOrConstructor?: T | PersistentModelConstructor<T>,
1063
- idOrCriteria?: string | ProducerModelPredicate<T>
1180
+ identifierOrCriteria?:
1181
+ | string
1182
+ | ProducerModelPredicate<T>
1183
+ | typeof PredicateAll
1064
1184
  ): Observable<SubscriptionMessage<T>> => {
1065
1185
  let predicate: ModelPredicate<T>;
1066
1186
 
1067
- const modelConstructor: PersistentModelConstructor<T> =
1068
- modelOrConstructor && isValidModelConstructor(modelOrConstructor)
1187
+ const modelConstructor: PersistentModelConstructor<T> | undefined =
1188
+ modelOrConstructor && isValidModelConstructor<T>(modelOrConstructor)
1069
1189
  ? modelOrConstructor
1070
1190
  : undefined;
1071
1191
 
@@ -1075,10 +1195,10 @@ class DataStore {
1075
1195
  model && (<Object>Object.getPrototypeOf(model)).constructor;
1076
1196
 
1077
1197
  if (isValidModelConstructor<T>(modelConstructor)) {
1078
- if (idOrCriteria) {
1198
+ if (identifierOrCriteria) {
1079
1199
  logger.warn('idOrCriteria is ignored when using a model instance', {
1080
1200
  model,
1081
- idOrCriteria,
1201
+ identifierOrCriteria,
1082
1202
  });
1083
1203
  }
1084
1204
 
@@ -1092,9 +1212,9 @@ class DataStore {
1092
1212
  }
1093
1213
  }
1094
1214
 
1095
- if (idOrCriteria !== undefined && modelConstructor === undefined) {
1215
+ if (identifierOrCriteria !== undefined && modelConstructor === undefined) {
1096
1216
  const msg = 'Cannot provide criteria without a modelConstructor';
1097
- logger.error(msg, idOrCriteria);
1217
+ logger.error(msg, identifierOrCriteria);
1098
1218
  throw new Error(msg);
1099
1219
  }
1100
1220
 
@@ -1105,18 +1225,26 @@ class DataStore {
1105
1225
  throw new Error(msg);
1106
1226
  }
1107
1227
 
1108
- if (typeof idOrCriteria === 'string') {
1109
- predicate = ModelPredicateCreator.createForId<T>(
1228
+ if (typeof identifierOrCriteria === 'string') {
1229
+ const modelDefinition = getModelDefinition(modelConstructor);
1230
+ const [keyField] = extractPrimaryKeyFieldNames(modelDefinition);
1231
+
1232
+ predicate = ModelPredicateCreator.createForSingleField<T>(
1110
1233
  getModelDefinition(modelConstructor),
1111
- idOrCriteria
1234
+ keyField,
1235
+ identifierOrCriteria
1112
1236
  );
1113
1237
  } else {
1114
- predicate =
1115
- modelConstructor &&
1116
- ModelPredicateCreator.createFromExisting<T>(
1117
- getModelDefinition(modelConstructor),
1118
- idOrCriteria
1119
- );
1238
+ if (isPredicatesAll(identifierOrCriteria)) {
1239
+ predicate = undefined;
1240
+ } else {
1241
+ predicate =
1242
+ modelConstructor &&
1243
+ ModelPredicateCreator.createFromExisting<T>(
1244
+ getModelDefinition(modelConstructor),
1245
+ identifierOrCriteria
1246
+ );
1247
+ }
1120
1248
  }
1121
1249
 
1122
1250
  return new Observable<SubscriptionMessage<T>>(observer => {
@@ -1125,10 +1253,37 @@ class DataStore {
1125
1253
  (async () => {
1126
1254
  await this.start();
1127
1255
 
1256
+ // Filter the events returned by Storage according to namespace,
1257
+ // append original element data, and subscribe to the observable
1128
1258
  handle = this.storage
1129
1259
  .observe(modelConstructor, predicate)
1130
1260
  .filter(({ model }) => namespaceResolver(model) === USER)
1131
- .subscribe(observer);
1261
+ .subscribe({
1262
+ next: async item => {
1263
+ // the `element` doesn't necessarily contain all item details or
1264
+ // have related records attached consistently with that of a query()
1265
+ // result item. for consistency, we attach them here.
1266
+
1267
+ let message = item;
1268
+
1269
+ // as lnog as we're not dealing with a DELETE, we need to fetch a fresh
1270
+ // item from storage to ensure it's fully populated.
1271
+ if (item.opType !== 'DELETE') {
1272
+ const freshElement = await this.query(
1273
+ item.model,
1274
+ item.element.id
1275
+ );
1276
+ message = {
1277
+ ...message,
1278
+ element: freshElement as T,
1279
+ };
1280
+ }
1281
+
1282
+ observer.next(message as SubscriptionMessage<T>);
1283
+ },
1284
+ error: err => observer.error(err),
1285
+ complete: () => observer.complete(),
1286
+ });
1132
1287
  })();
1133
1288
 
1134
1289
  return () => {
@@ -1145,7 +1300,7 @@ class DataStore {
1145
1300
  criteria?: ProducerModelPredicate<T> | typeof PredicateAll,
1146
1301
  paginationProducer?: ObserveQueryOptions<T>
1147
1302
  ): Observable<DataStoreSnapshot<T>>;
1148
- } = <T extends PersistentModel = PersistentModel>(
1303
+ } = <T extends PersistentModel>(
1149
1304
  model: PersistentModelConstructor<T>,
1150
1305
  criteria?: ProducerModelPredicate<T> | typeof PredicateAll,
1151
1306
  options?: ObserveQueryOptions<T>
@@ -1155,7 +1310,18 @@ class DataStore {
1155
1310
  const itemsChanged = new Map<string, T>();
1156
1311
  let deletedItemIds: string[] = [];
1157
1312
  let handle: ZenObservable.Subscription;
1158
-
1313
+ let predicate: ModelPredicate<T>;
1314
+
1315
+ /**
1316
+ * As the name suggests, this geneates a snapshot in the form of
1317
+ * `{items: T[], isSynced: boolean}`
1318
+ * and sends it to the observer.
1319
+ *
1320
+ * SIDE EFFECT: The underlying generation and emission methods may touch:
1321
+ * `items`, `itemsChanged`, and `deletedItemIds`.
1322
+ *
1323
+ * Refer to `generateSnapshot` and `emitSnapshot` for more details.
1324
+ */
1159
1325
  const generateAndEmitSnapshot = (): void => {
1160
1326
  const snapshot = generateSnapshot();
1161
1327
  emitSnapshot(snapshot);
@@ -1172,6 +1338,31 @@ class DataStore {
1172
1338
  const { sort } = options || {};
1173
1339
  const sortOptions = sort ? { sort } : undefined;
1174
1340
 
1341
+ const modelDefinition = getModelDefinition(model);
1342
+ const keyFields = extractPrimaryKeyFieldNames(modelDefinition);
1343
+
1344
+ if (isQueryOne(criteria)) {
1345
+ predicate = ModelPredicateCreator.createForSingleField<T>(
1346
+ modelDefinition,
1347
+ keyFields[0],
1348
+ criteria
1349
+ );
1350
+ } else {
1351
+ if (isPredicatesAll(criteria)) {
1352
+ // Predicates.ALL means "all records", so no predicate (undefined)
1353
+ predicate = undefined;
1354
+ } else {
1355
+ predicate = ModelPredicateCreator.createFromExisting(
1356
+ modelDefinition,
1357
+ criteria
1358
+ );
1359
+ }
1360
+ }
1361
+
1362
+ const { predicates, type: predicateGroupType } =
1363
+ ModelPredicateCreator.getPredicates(predicate, false) || {};
1364
+ const hasPredicate = !!predicates;
1365
+
1175
1366
  (async () => {
1176
1367
  try {
1177
1368
  // first, query and return any locally-available records
@@ -1179,34 +1370,54 @@ class DataStore {
1179
1370
  items.set(item.id, item)
1180
1371
  );
1181
1372
 
1182
- // observe the model and send a stream of updates (debounced)
1183
- handle = this.observe(
1184
- model,
1185
- // @ts-ignore TODO: fix this TSlint error
1186
- criteria
1187
- ).subscribe(({ element, model, opType }) => {
1188
- // Flag items which have been recently deleted
1189
- // NOTE: Merging of separate operations to the same model instance is handled upstream
1190
- // in the `mergePage` method within src/sync/merger.ts. The final state of a model instance
1191
- // depends on the LATEST record (for a given id).
1192
- if (opType === 'DELETE') {
1193
- deletedItemIds.push(element.id);
1194
- } else {
1195
- itemsChanged.set(element.id, element);
1196
- }
1373
+ // Observe the model and send a stream of updates (debounced).
1374
+ // We need to post-filter results instead of passing criteria through
1375
+ // to have visibility into items that move from in-set to out-of-set.
1376
+ // We need to explicitly remove those items from the existing snapshot.
1377
+ handle = this.observe(model).subscribe(
1378
+ ({ element, model, opType }) => {
1379
+ if (
1380
+ hasPredicate &&
1381
+ !validatePredicate(element, predicateGroupType, predicates)
1382
+ ) {
1383
+ if (
1384
+ opType === 'UPDATE' &&
1385
+ (items.has(element.id) || itemsChanged.has(element.id))
1386
+ ) {
1387
+ // tracking as a "deleted item" will include the item in
1388
+ // page limit calculations and ensure it is removed from the
1389
+ // final items collection, regardless of which collection(s)
1390
+ // it is currently in. (I mean, it could be in both, right!?)
1391
+ deletedItemIds.push(element.id);
1392
+ } else {
1393
+ // ignore updates for irrelevant/filtered items.
1394
+ return;
1395
+ }
1396
+ }
1197
1397
 
1198
- const isSynced = this.sync?.getModelSyncedStatus(model) ?? false;
1398
+ // Flag items which have been recently deleted
1399
+ // NOTE: Merging of separate operations to the same model instance is handled upstream
1400
+ // in the `mergePage` method within src/sync/merger.ts. The final state of a model instance
1401
+ // depends on the LATEST record (for a given id).
1402
+ if (opType === 'DELETE') {
1403
+ deletedItemIds.push(element.id);
1404
+ } else {
1405
+ itemsChanged.set(element.id, element);
1406
+ }
1199
1407
 
1200
- const limit =
1201
- itemsChanged.size - deletedItemIds.length >= this.syncPageSize;
1408
+ const isSynced = this.sync?.getModelSyncedStatus(model) ?? false;
1202
1409
 
1203
- if (limit || isSynced) {
1204
- limitTimerRace.resolve();
1205
- }
1410
+ const limit =
1411
+ itemsChanged.size - deletedItemIds.length >= this.syncPageSize;
1206
1412
 
1207
- // kicks off every subsequent race as results sync down
1208
- limitTimerRace.start();
1209
- });
1413
+ if (limit || isSynced) {
1414
+ limitTimerRace.resolve();
1415
+ }
1416
+
1417
+ // kicks off every subsequent race as results sync down
1418
+ limitTimerRace.start();
1419
+ }
1420
+ );
1210
1421
 
1211
1422
  // returns a set of initial/locally-available results
1212
1423
  generateAndEmitSnapshot();
@@ -1215,7 +1426,12 @@ class DataStore {
1215
1426
  }
1216
1427
  })();
1217
1428
 
1218
- // TODO: abstract this function into a util file to be able to write better unit tests
1429
+ /**
1430
+ * Combines the `items`, `itemsChanged`, and `deletedItemIds` collections into
1431
+ * a snapshot in the form of `{ items: T[], isSynced: boolean}`.
1432
+ *
1433
+ * SIDE EFFECT: The shared `items` collection is recreated.
1434
+ */
1219
1435
  const generateSnapshot = (): DataStoreSnapshot<T> => {
1220
1436
  const isSynced = this.sync?.getModelSyncedStatus(model) ?? false;
1221
1437
  const itemsArray = [
@@ -1239,6 +1455,14 @@ class DataStore {
1239
1455
  };
1240
1456
  };
1241
1457
 
1458
+ /**
1459
+ * Emits the list of items to the observer.
1460
+ *
1461
+ * SIDE EFFECT: `itemsChanged` and `deletedItemIds` are cleared to prepare
1462
+ * for the next snapshot.
1463
+ *
1464
+ * @param snapshot The generated items data to emit.
1465
+ */
1242
1466
  const emitSnapshot = (snapshot: DataStoreSnapshot<T>): void => {
1243
1467
  // send the generated snapshot to the primary subscription
1244
1468
  observer.next(snapshot);
@@ -1248,6 +1472,12 @@ class DataStore {
1248
1472
  deletedItemIds = [];
1249
1473
  };
1250
1474
 
1475
+ /**
1476
+ * Sorts an `Array` of `T` according to the sort instructions given in the
1477
+ * original `observeQuery()` call.
1478
+ *
1479
+ * @param itemsToSort A array of model type.
1480
+ */
1251
1481
  const sortItems = (itemsToSort: T[]): void => {
1252
1482
  const modelDefinition = getModelDefinition(model);
1253
1483
  const pagination = this.processPagination(modelDefinition, options);
@@ -1262,7 +1492,14 @@ class DataStore {
1262
1492
  }
1263
1493
  };
1264
1494
 
1265
- // send one last snapshot when the model is fully synced
1495
+ /**
1496
+ * Force one last snapshot when the model is fully synced.
1497
+ *
1498
+ * This reduces latency for that last snapshot, which will otherwise
1499
+ * wait for the configured timeout.
1500
+ *
1501
+ * @param payload The payload from the Hub event.
1502
+ */
1266
1503
  const hubCallback = ({ payload }): void => {
1267
1504
  const { event, data } = payload;
1268
1505
  if (
@@ -1284,6 +1521,10 @@ class DataStore {
1284
1521
  };
1285
1522
 
1286
1523
  configure = (config: DataStoreConfig = {}) => {
1524
+ this.amplifyContext.Auth = this.Auth;
1525
+ this.amplifyContext.API = this.API;
1526
+ this.amplifyContext.Cache = this.Cache;
1527
+
1287
1528
  const {
1288
1529
  DataStore: configDataStore,
1289
1530
  authModeStrategyType: configAuthModeStrategyType,
@@ -1313,7 +1554,7 @@ class DataStore {
1313
1554
 
1314
1555
  switch (authModeStrategyType) {
1315
1556
  case AuthModeStrategyType.MULTI_AUTH:
1316
- this.authModeStrategy = multiAuthStrategy;
1557
+ this.authModeStrategy = multiAuthStrategy(this.amplifyContext);
1317
1558
  break;
1318
1559
  case AuthModeStrategyType.DEFAULT:
1319
1560
  this.authModeStrategy = defaultAuthStrategy;
@@ -1329,12 +1570,13 @@ class DataStore {
1329
1570
 
1330
1571
  this.syncExpressions =
1331
1572
  (configDataStore && configDataStore.syncExpressions) ||
1332
- this.syncExpressions ||
1333
- configSyncExpressions;
1573
+ configSyncExpressions ||
1574
+ this.syncExpressions;
1334
1575
 
1335
1576
  this.maxRecordsToSync =
1336
1577
  (configDataStore && configDataStore.maxRecordsToSync) ||
1337
1578
  configMaxRecordsToSync ||
1579
+ this.maxRecordsToSync ||
1338
1580
  10000;
1339
1581
 
1340
1582
  // store on config object, so that Sync, Subscription, and Mutation processors can have access
@@ -1343,6 +1585,7 @@ class DataStore {
1343
1585
  this.syncPageSize =
1344
1586
  (configDataStore && configDataStore.syncPageSize) ||
1345
1587
  configSyncPageSize ||
1588
+ this.syncPageSize ||
1346
1589
  1000;
1347
1590
 
1348
1591
  // store on config object, so that Sync, Subscription, and Mutation processors can have access
@@ -1350,22 +1593,32 @@ class DataStore {
1350
1593
 
1351
1594
  this.fullSyncInterval =
1352
1595
  (configDataStore && configDataStore.fullSyncInterval) ||
1353
- this.fullSyncInterval ||
1354
1596
  configFullSyncInterval ||
1597
+ this.fullSyncInterval ||
1355
1598
  24 * 60; // 1 day
1356
1599
 
1357
1600
  this.storageAdapter =
1358
1601
  (configDataStore && configDataStore.storageAdapter) ||
1359
- this.storageAdapter ||
1360
1602
  configStorageAdapter ||
1603
+ this.storageAdapter ||
1361
1604
  undefined;
1362
1605
 
1363
- this.sessionId = this.retrieveSessionId();
1606
+ this.sessionId = this.retrieveSessionId()!;
1364
1607
  };
1365
1608
 
1366
1609
  clear = async function clear() {
1610
+ checkSchemaInitialized();
1367
1611
  if (this.storage === undefined) {
1368
- return;
1612
+ // connect to storage so that it can be cleared without fully starting DataStore
1613
+ this.storage = new Storage(
1614
+ schema,
1615
+ namespaceResolver,
1616
+ getModelConstructorByModelName,
1617
+ modelInstanceCreator,
1618
+ this.storageAdapter,
1619
+ this.sessionId
1620
+ );
1621
+ await this.storage.init();
1369
1622
  }
1370
1623
 
1371
1624
  if (syncSubscription && !syncSubscription.closed) {
@@ -1384,7 +1637,7 @@ class DataStore {
1384
1637
  this.syncPredicates = new WeakMap<SchemaModel, ModelPredicate<any>>();
1385
1638
  };
1386
1639
 
1387
- stop = async function stop() {
1640
+ stop = async function stop(this: InstanceType<typeof DataStore>) {
1388
1641
  if (this.initialized !== undefined) {
1389
1642
  await this.start();
1390
1643
  }
@@ -1548,9 +1801,9 @@ class DataStore {
1548
1801
 
1549
1802
  return `${sessionId}-${appSyncId}`;
1550
1803
  }
1551
- } catch {
1552
- return undefined;
1553
- }
1804
+ } catch {}
1805
+
1806
+ return undefined;
1554
1807
  }
1555
1808
  }
1556
1809