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

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 +3740 -1599
  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 +291 -83
  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 +361 -193
  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 +59 -28
  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 +454 -252
  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 +58 -7
  55. package/lib/sync/utils.js.map +1 -1
  56. package/lib/types.d.ts +90 -34
  57. package/lib/types.js +31 -2
  58. package/lib/types.js.map +1 -1
  59. package/lib/util.d.ts +48 -33
  60. package/lib/util.js +219 -57
  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 +293 -85
  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 +361 -193
  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 +60 -29
  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 +454 -252
  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 +58 -8
  110. package/lib-esm/sync/utils.js.map +1 -1
  111. package/lib-esm/types.d.ts +90 -34
  112. package/lib-esm/types.js +29 -3
  113. package/lib-esm/types.js.map +1 -1
  114. package/lib-esm/util.d.ts +48 -33
  115. package/lib-esm/util.js +207 -54
  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 +448 -148
  120. package/src/predicates/index.ts +32 -10
  121. package/src/storage/adapter/AsyncStorageAdapter.ts +305 -78
  122. package/src/storage/adapter/AsyncStorageDatabase.ts +65 -26
  123. package/src/storage/adapter/InMemoryStore.ts +2 -2
  124. package/src/storage/adapter/IndexedDBAdapter.ts +344 -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 +67 -16
  135. package/src/types.ts +252 -50
  136. package/src/util.ts +256 -42
  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,9 +57,17 @@ import {
54
57
  isNonModelFieldType,
55
58
  isModelFieldType,
56
59
  ObserveQueryOptions,
60
+ ManagedIdentifier,
61
+ PersistentModelMetaData,
62
+ IdentifierFieldOrIdentifierObject,
63
+ __modelMeta__,
64
+ isIdentifierObject,
65
+ AmplifyContext,
66
+ isModelAttributePrimaryKey,
57
67
  } from '../types';
58
68
  import {
59
69
  DATASTORE,
70
+ errorMessages,
60
71
  establishRelationAndKeys,
61
72
  exhaustiveCheck,
62
73
  isModelConstructor,
@@ -69,7 +80,14 @@ import {
69
80
  registerNonModelClass,
70
81
  sortCompareFunction,
71
82
  DeferredCallbackResolver,
83
+ extractPrimaryKeyFieldNames,
84
+ extractPrimaryKeysAndValues,
85
+ isIdManaged,
86
+ isIdOptionallyManaged,
87
+ validatePredicate,
88
+ mergePatches,
72
89
  } from '../util';
90
+ import { getIdentifierValue } from '../sync/utils';
73
91
 
74
92
  setAutoFreeze(true);
75
93
  enablePatches();
@@ -79,11 +97,16 @@ const logger = new Logger('DataStore');
79
97
  const ulid = monotonicUlidFactory(Date.now());
80
98
  const { isNode } = JS.browserOrNode();
81
99
 
100
+ type SettingMetaData = {
101
+ identifier: ManagedIdentifier<Setting, 'id'>;
102
+ readOnlyFields: never;
103
+ };
82
104
  declare class Setting {
83
- constructor(init: ModelInit<Setting>);
105
+ public readonly [__modelMeta__]: SettingMetaData;
106
+ constructor(init: ModelInit<Setting, SettingMetaData>);
84
107
  static copyOf(
85
108
  src: Setting,
86
- mutator: (draft: MutableModel<Setting>) => void | Setting
109
+ mutator: (draft: MutableModel<Setting, SettingMetaData>) => void | Setting
87
110
  ): Setting;
88
111
  public readonly id: string;
89
112
  public readonly key: string;
@@ -222,6 +245,20 @@ const initSchema = (userSchema: Schema) => {
222
245
  return userClasses;
223
246
  };
224
247
 
248
+ /* Checks if the schema has been initialized by initSchema().
249
+ *
250
+ * Call this function before accessing schema.
251
+ * Currently this only needs to be called in start() and clear() because all other functions will call start first.
252
+ */
253
+ const checkSchemaInitialized = () => {
254
+ if (schema === undefined) {
255
+ const message =
256
+ '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';
257
+ logger.error(message);
258
+ throw new Error(message);
259
+ }
260
+ };
261
+
225
262
  const createTypeClasses: (
226
263
  namespace: SchemaNamespace
227
264
  ) => TypeConstructorMap = namespace => {
@@ -246,16 +283,19 @@ const createTypeClasses: (
246
283
 
247
284
  export declare type ModelInstanceCreator = typeof modelInstanceCreator;
248
285
 
249
- const instancesMetadata = new WeakSet<
250
- ModelInit<PersistentModel & Partial<ModelInstanceMetadata>>
251
- >();
252
- function modelInstanceCreator<T extends PersistentModel = PersistentModel>(
286
+ const instancesMetadata = new WeakSet<ModelInit<unknown, unknown>>();
287
+
288
+ function modelInstanceCreator<T extends PersistentModel>(
253
289
  modelConstructor: PersistentModelConstructor<T>,
254
- init: ModelInit<T> & Partial<ModelInstanceMetadata>
290
+ init: Partial<T>
255
291
  ): T {
256
292
  instancesMetadata.add(init);
257
293
 
258
- return <T>new modelConstructor(init);
294
+ return new modelConstructor(<ModelInit<T, PersistentModelMetaData<T>>>init);
295
+ }
296
+
297
+ function isSchemaModel(m: SchemaModel | SchemaNonModel): m is SchemaModel {
298
+ return (m as SchemaModel).attributes !== undefined;
259
299
  }
260
300
 
261
301
  const validateModelFields =
@@ -273,6 +313,14 @@ const validateModelFields =
273
313
  throw new Error(`Field ${name} is required`);
274
314
  }
275
315
 
316
+ if (isSchemaModel(modelDefinition) && !isIdManaged(modelDefinition)) {
317
+ const keys = extractPrimaryKeyFieldNames(modelDefinition);
318
+ if (keys.includes(k) && v === '') {
319
+ logger.error(errorMessages.idEmptyString, { k, value: v });
320
+ throw new Error(errorMessages.idEmptyString);
321
+ }
322
+ }
323
+
276
324
  if (isGraphQLScalarType(type)) {
277
325
  const jsType = GraphQLScalarType.getJSType(type);
278
326
  const validateScalar = GraphQLScalarType.getValidationFunction(type);
@@ -383,7 +431,7 @@ const castInstanceType = (
383
431
  return v;
384
432
  };
385
433
 
386
- const initializeInstance = <T>(
434
+ const initializeInstance = <T extends PersistentModel>(
387
435
  init: ModelInit<T>,
388
436
  modelDefinition: SchemaModel | SchemaNonModel,
389
437
  draft: Draft<T & ModelInstanceMetadata>
@@ -407,31 +455,39 @@ const createModelClass = <T extends PersistentModel>(
407
455
  (draft: Draft<T & ModelInstanceMetadata>) => {
408
456
  initializeInstance(init, modelDefinition, draft);
409
457
 
458
+ // model is initialized inside a DataStore component (e.g. by Sync Engine, Storage Engine, etc.)
459
+ const isInternallyInitialized = instancesMetadata.has(init);
460
+
410
461
  const modelInstanceMetadata: ModelInstanceMetadata =
411
- instancesMetadata.has(init)
462
+ isInternallyInitialized
412
463
  ? <ModelInstanceMetadata>(<unknown>init)
413
464
  : <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) {
465
+
466
+ type ModelWithIDIdentifier = { id: string };
467
+
468
+ const { id: _id } =
469
+ modelInstanceMetadata as unknown as ModelWithIDIdentifier;
470
+
471
+ if (isIdManaged(modelDefinition)) {
472
+ const isInternalModel = _id !== null && _id !== undefined;
473
+
474
+ const id = isInternalModel
475
+ ? _id
476
+ : modelDefinition.syncable
477
+ ? uuid4()
478
+ : ulid();
479
+
480
+ (<ModelWithIDIdentifier>(<unknown>draft)).id = id;
481
+ } else if (isIdOptionallyManaged(modelDefinition)) {
482
+ // only auto-populate if the id was not provided
483
+ (<ModelWithIDIdentifier>(<unknown>draft)).id = draft.id || uuid4();
484
+ }
485
+
486
+ if (!isInternallyInitialized) {
431
487
  checkReadOnlyPropertyOnCreate(draft, modelDefinition);
432
488
  }
433
489
 
434
- draft.id = id;
490
+ const { _version, _lastChangedAt, _deleted } = modelInstanceMetadata;
435
491
 
436
492
  if (modelDefinition.syncable) {
437
493
  draft._version = _version;
@@ -456,8 +512,13 @@ const createModelClass = <T extends PersistentModel>(
456
512
  const model = produce(
457
513
  source,
458
514
  draft => {
459
- fn(<MutableModel<T>>(draft as unknown));
460
- draft.id = source.id;
515
+ fn(<MutableModel<T>>draft);
516
+
517
+ const keyNames = extractPrimaryKeyFieldNames(modelDefinition);
518
+ // Keys are immutable
519
+ // @ts-ignore TODO: fix type
520
+ keyNames.forEach(key => (draft[key] = source[key]));
521
+
461
522
  const modelValidator = validateModelFields(modelDefinition);
462
523
  Object.entries(draft).forEach(([k, v]) => {
463
524
  const parsedValue = castInstanceType(modelDefinition, k, v);
@@ -468,9 +529,21 @@ const createModelClass = <T extends PersistentModel>(
468
529
  p => (patches = p)
469
530
  );
470
531
 
471
- if (patches.length) {
472
- modelPatchesMap.set(model, [patches, source]);
473
- checkReadOnlyPropertyOnUpdate(patches, modelDefinition);
532
+ const hasExistingPatches = modelPatchesMap.has(source);
533
+ if (patches.length || hasExistingPatches) {
534
+ if (hasExistingPatches) {
535
+ const [existingPatches, existingSource] = modelPatchesMap.get(source);
536
+ const mergedPatches = mergePatches(
537
+ existingSource,
538
+ existingPatches,
539
+ patches
540
+ );
541
+ modelPatchesMap.set(model, [mergedPatches, existingSource]);
542
+ checkReadOnlyPropertyOnUpdate(mergedPatches, modelDefinition);
543
+ } else {
544
+ modelPatchesMap.set(model, [patches, source]);
545
+ checkReadOnlyPropertyOnUpdate(patches, modelDefinition);
546
+ }
474
547
  }
475
548
 
476
549
  return model;
@@ -484,6 +557,7 @@ const createModelClass = <T extends PersistentModel>(
484
557
  }
485
558
 
486
559
  const instance = modelInstanceCreator(clazz, json);
560
+
487
561
  const modelValidator = validateModelFields(modelDefinition);
488
562
 
489
563
  Object.entries(instance).forEach(([k, v]) => {
@@ -531,7 +605,9 @@ const checkReadOnlyPropertyOnUpdate = (
531
605
  });
532
606
  };
533
607
 
534
- const createNonModelClass = <T>(typeDefinition: SchemaNonModel) => {
608
+ const createNonModelClass = <T extends PersistentModel>(
609
+ typeDefinition: SchemaNonModel
610
+ ) => {
535
611
  const clazz = <NonModelTypeConstructor<T>>(<unknown>class Model {
536
612
  constructor(init: ModelInit<T>) {
537
613
  const instance = produce(
@@ -564,7 +640,7 @@ function defaultConflictHandler(conflictData: SyncConflict): PersistentModel {
564
640
  return modelInstanceCreator(modelConstructor, { ...localModel, _version });
565
641
  }
566
642
 
567
- function defaultErrorHandler(error: SyncError) {
643
+ function defaultErrorHandler(error: SyncError<PersistentModel>): void {
568
644
  logger.warn(error);
569
645
  }
570
646
 
@@ -615,7 +691,6 @@ async function checkSchemaVersion(
615
691
  const [schemaVersionSetting] = await s.query(
616
692
  Setting,
617
693
  ModelPredicateCreator.createFromExisting(modelDefinition, c =>
618
- // @ts-ignore Argument of type '"eq"' is not assignable to parameter of type 'never'.
619
694
  c.key('eq', SETTING_SCHEMA_VERSION)
620
695
  ),
621
696
  { page: 0, limit: 1 }
@@ -682,23 +757,34 @@ function getNamespace(): SchemaNamespace {
682
757
  }
683
758
 
684
759
  class DataStore {
760
+ // reference to configured category instances. Used for preserving SSR context
761
+ Auth = Auth;
762
+ API = API;
763
+ Cache = Cache;
764
+
685
765
  private amplifyConfig: Record<string, any> = {};
686
766
  private authModeStrategy: AuthModeStrategy;
687
767
  private conflictHandler: ConflictHandler;
688
- private errorHandler: (error: SyncError) => void;
768
+ private errorHandler: (error: SyncError<PersistentModel>) => void;
689
769
  private fullSyncInterval: number;
690
- private initialized: Promise<void>;
770
+ private initialized?: Promise<void>;
691
771
  private initReject: Function;
692
772
  private initResolve: Function;
693
773
  private maxRecordsToSync: number;
694
- private storage: Storage;
695
- private sync: SyncEngine;
774
+ private storage?: Storage;
775
+ private sync?: SyncEngine;
696
776
  private syncPageSize: number;
697
777
  private syncExpressions: SyncExpression[];
698
778
  private syncPredicates: WeakMap<SchemaModel, ModelPredicate<any>> =
699
779
  new WeakMap<SchemaModel, ModelPredicate<any>>();
700
780
  private sessionId: string;
701
781
  private storageAdapter: Adapter;
782
+ // object that gets passed to descendent classes. Allows us to pass these down by reference
783
+ private amplifyContext: AmplifyContext = {
784
+ Auth: this.Auth,
785
+ API: this.API,
786
+ Cache: this.Cache,
787
+ };
702
788
 
703
789
  getModuleName() {
704
790
  return 'DataStore';
@@ -728,6 +814,7 @@ class DataStore {
728
814
 
729
815
  await this.storage.init();
730
816
 
817
+ checkSchemaInitialized();
731
818
  await checkSchemaVersion(this.storage, schema.version);
732
819
 
733
820
  const { aws_appsync_graphqlEndpoint } = this.amplifyConfig;
@@ -744,13 +831,12 @@ class DataStore {
744
831
  userClasses,
745
832
  this.storage,
746
833
  modelInstanceCreator,
747
- this.maxRecordsToSync,
748
- this.syncPageSize,
749
834
  this.conflictHandler,
750
835
  this.errorHandler,
751
836
  this.syncPredicates,
752
837
  this.amplifyConfig,
753
- this.authModeStrategy
838
+ this.authModeStrategy,
839
+ this.amplifyContext
754
840
  );
755
841
 
756
842
  // tslint:disable-next-line:max-line-length
@@ -796,7 +882,10 @@ class DataStore {
796
882
  query: {
797
883
  <T extends PersistentModel>(
798
884
  modelConstructor: PersistentModelConstructor<T>,
799
- id: string
885
+ identifier: IdentifierFieldOrIdentifierObject<
886
+ T,
887
+ PersistentModelMetaData<T>
888
+ >
800
889
  ): Promise<T | undefined>;
801
890
  <T extends PersistentModel>(
802
891
  modelConstructor: PersistentModelConstructor<T>,
@@ -805,7 +894,10 @@ class DataStore {
805
894
  ): Promise<T[]>;
806
895
  } = async <T extends PersistentModel>(
807
896
  modelConstructor: PersistentModelConstructor<T>,
808
- idOrCriteria?: string | ProducerModelPredicate<T> | typeof PredicateAll,
897
+ identifierOrCriteria?:
898
+ | IdentifierFieldOrIdentifierObject<T, PersistentModelMetaData<T>>
899
+ | ProducerModelPredicate<T>
900
+ | typeof PredicateAll,
809
901
  paginationProducer?: ProducerPaginationInput<T>
810
902
  ): Promise<T | T[] | undefined> => {
811
903
  await this.start();
@@ -819,28 +911,37 @@ class DataStore {
819
911
  throw new Error(msg);
820
912
  }
821
913
 
822
- if (typeof idOrCriteria === 'string') {
914
+ if (typeof identifierOrCriteria === 'string') {
823
915
  if (paginationProducer !== undefined) {
824
916
  logger.warn('Pagination is ignored when querying by id');
825
917
  }
826
918
  }
827
919
 
828
920
  const modelDefinition = getModelDefinition(modelConstructor);
921
+ const keyFields = extractPrimaryKeyFieldNames(modelDefinition);
922
+
829
923
  let predicate: ModelPredicate<T>;
830
924
 
831
- if (isQueryOne(idOrCriteria)) {
832
- predicate = ModelPredicateCreator.createForId<T>(
925
+ if (isQueryOne(identifierOrCriteria)) {
926
+ predicate = ModelPredicateCreator.createForSingleField<T>(
833
927
  modelDefinition,
834
- idOrCriteria
928
+ keyFields[0],
929
+ identifierOrCriteria
835
930
  );
836
931
  } else {
837
- if (isPredicatesAll(idOrCriteria)) {
932
+ // Object is being queried using object literal syntax
933
+ if (isIdentifierObject(<T>identifierOrCriteria, modelDefinition)) {
934
+ predicate = ModelPredicateCreator.createForPk<T>(
935
+ modelDefinition,
936
+ <T>identifierOrCriteria
937
+ );
938
+ } else if (isPredicatesAll(identifierOrCriteria)) {
838
939
  // Predicates.ALL means "all records", so no predicate (undefined)
839
940
  predicate = undefined;
840
941
  } else {
841
942
  predicate = ModelPredicateCreator.createFromExisting(
842
943
  modelDefinition,
843
- idOrCriteria
944
+ <any>identifierOrCriteria
844
945
  );
845
946
  }
846
947
  }
@@ -870,7 +971,13 @@ class DataStore {
870
971
  pagination
871
972
  );
872
973
 
873
- return isQueryOne(idOrCriteria) ? result[0] : result;
974
+ const isSingleFieldIdentifier = keyFields.length === 1;
975
+ const returnOne =
976
+ isSingleFieldIdentifier &&
977
+ (isQueryOne(identifierOrCriteria) ||
978
+ isIdentifierObject(identifierOrCriteria, modelDefinition));
979
+
980
+ return returnOne ? result[0] : result;
874
981
  };
875
982
 
876
983
  save = async <T extends PersistentModel>(
@@ -883,7 +990,7 @@ class DataStore {
883
990
  // Allows us to only include changed fields for updates
884
991
  const patchesTuple = modelPatchesMap.get(model);
885
992
 
886
- const modelConstructor: PersistentModelConstructor<T> = model
993
+ const modelConstructor: PersistentModelConstructor<T> | undefined = model
887
994
  ? <PersistentModelConstructor<T>>model.constructor
888
995
  : undefined;
889
996
 
@@ -898,15 +1005,15 @@ class DataStore {
898
1005
 
899
1006
  const producedCondition = ModelPredicateCreator.createFromExisting(
900
1007
  modelDefinition,
901
- condition
1008
+ condition!
902
1009
  );
903
1010
 
904
1011
  const [savedModel] = await this.storage.runExclusive(async s => {
905
1012
  await s.save(model, producedCondition, undefined, patchesTuple);
906
1013
 
907
- return s.query(
1014
+ return s.query<T>(
908
1015
  modelConstructor,
909
- ModelPredicateCreator.createForId(modelDefinition, model.id)
1016
+ ModelPredicateCreator.createForPk(modelDefinition, model)
910
1017
  );
911
1018
  });
912
1019
 
@@ -946,22 +1053,28 @@ class DataStore {
946
1053
  };
947
1054
 
948
1055
  delete: {
949
- <T extends PersistentModel>(
950
- model: T,
951
- condition?: ProducerModelPredicate<T>
952
- ): Promise<T>;
953
1056
  <T extends PersistentModel>(
954
1057
  modelConstructor: PersistentModelConstructor<T>,
955
- id: string
1058
+ identifier: IdentifierFieldOrIdentifierObject<
1059
+ T,
1060
+ PersistentModelMetaData<T>
1061
+ >
956
1062
  ): Promise<T[]>;
957
1063
  <T extends PersistentModel>(
958
1064
  modelConstructor: PersistentModelConstructor<T>,
959
1065
  condition: ProducerModelPredicate<T> | typeof PredicateAll
960
1066
  ): Promise<T[]>;
1067
+ <T extends PersistentModel>(
1068
+ model: T,
1069
+ condition?: ProducerModelPredicate<T>
1070
+ ): Promise<T>;
961
1071
  } = async <T extends PersistentModel>(
962
1072
  modelOrConstructor: T | PersistentModelConstructor<T>,
963
- idOrCriteria?: string | ProducerModelPredicate<T> | typeof PredicateAll
964
- ) => {
1073
+ identifierOrCriteria?:
1074
+ | IdentifierFieldOrIdentifierObject<T, PersistentModelMetaData<T>>
1075
+ | ProducerModelPredicate<T>
1076
+ | typeof PredicateAll
1077
+ ): Promise<T | T[]> => {
965
1078
  await this.start();
966
1079
 
967
1080
  let condition: ModelPredicate<T>;
@@ -973,31 +1086,43 @@ class DataStore {
973
1086
  throw new Error(msg);
974
1087
  }
975
1088
 
976
- if (isValidModelConstructor(modelOrConstructor)) {
1089
+ if (isValidModelConstructor<T>(modelOrConstructor)) {
977
1090
  const modelConstructor = modelOrConstructor;
978
1091
 
979
- if (!idOrCriteria) {
1092
+ if (!identifierOrCriteria) {
980
1093
  const msg =
981
1094
  'Id to delete or criteria required. Do you want to delete all? Pass Predicates.ALL';
982
- logger.error(msg, { idOrCriteria });
1095
+ logger.error(msg, { identifierOrCriteria });
983
1096
 
984
1097
  throw new Error(msg);
985
1098
  }
986
1099
 
987
- if (typeof idOrCriteria === 'string') {
988
- condition = ModelPredicateCreator.createForId<T>(
1100
+ const modelDefinition = getModelDefinition(modelConstructor);
1101
+
1102
+ if (typeof identifierOrCriteria === 'string') {
1103
+ const [keyField] = extractPrimaryKeyFieldNames(modelDefinition);
1104
+
1105
+ condition = ModelPredicateCreator.createForSingleField<T>(
989
1106
  getModelDefinition(modelConstructor),
990
- idOrCriteria
1107
+ keyField,
1108
+ identifierOrCriteria
991
1109
  );
992
1110
  } 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
- );
1111
+ if (isIdentifierObject(identifierOrCriteria, modelDefinition)) {
1112
+ condition = ModelPredicateCreator.createForPk<T>(
1113
+ modelDefinition,
1114
+ <T>identifierOrCriteria
1115
+ );
1116
+ } else {
1117
+ condition = ModelPredicateCreator.createFromExisting(
1118
+ modelDefinition,
1119
+ /**
1120
+ * idOrCriteria is always a ProducerModelPredicate<T>, never a symbol.
1121
+ * The symbol is used only for typing purposes. e.g. see Predicates.ALL
1122
+ */
1123
+ identifierOrCriteria as ProducerModelPredicate<T>
1124
+ );
1125
+ }
1001
1126
 
1002
1127
  if (!condition || !ModelPredicateCreator.isValidPredicate(condition)) {
1003
1128
  const msg =
@@ -1025,22 +1150,24 @@ class DataStore {
1025
1150
 
1026
1151
  const modelDefinition = getModelDefinition(modelConstructor);
1027
1152
 
1028
- const idPredicate = ModelPredicateCreator.createForId<T>(
1153
+ const pkPredicate = ModelPredicateCreator.createForPk<T>(
1029
1154
  modelDefinition,
1030
- model.id
1155
+ model
1031
1156
  );
1032
1157
 
1033
- if (idOrCriteria) {
1034
- if (typeof idOrCriteria !== 'function') {
1158
+ if (identifierOrCriteria) {
1159
+ if (typeof identifierOrCriteria !== 'function') {
1035
1160
  const msg = 'Invalid criteria';
1036
- logger.error(msg, { idOrCriteria });
1161
+ logger.error(msg, { identifierOrCriteria });
1037
1162
 
1038
1163
  throw new Error(msg);
1039
1164
  }
1040
1165
 
1041
- condition = idOrCriteria(idPredicate);
1166
+ condition = (<ProducerModelPredicate<T>>identifierOrCriteria)(
1167
+ pkPredicate
1168
+ );
1042
1169
  } else {
1043
- condition = idPredicate;
1170
+ condition = pkPredicate;
1044
1171
  }
1045
1172
 
1046
1173
  const [[deleted]] = await this.storage.delete(model, condition);
@@ -1052,20 +1179,28 @@ class DataStore {
1052
1179
  observe: {
1053
1180
  (): Observable<SubscriptionMessage<PersistentModel>>;
1054
1181
 
1055
- <T extends PersistentModel>(model: T): Observable<SubscriptionMessage<T>>;
1182
+ <T extends PersistentModel>(
1183
+ modelConstructor: PersistentModelConstructor<T>,
1184
+ identifier: string
1185
+ ): Observable<SubscriptionMessage<T>>;
1056
1186
 
1057
1187
  <T extends PersistentModel>(
1058
1188
  modelConstructor: PersistentModelConstructor<T>,
1059
- criteria?: string | ProducerModelPredicate<T>
1189
+ criteria?: ProducerModelPredicate<T> | typeof PredicateAll
1060
1190
  ): Observable<SubscriptionMessage<T>>;
1061
- } = <T extends PersistentModel = PersistentModel>(
1191
+
1192
+ <T extends PersistentModel>(model: T): Observable<SubscriptionMessage<T>>;
1193
+ } = <T extends PersistentModel>(
1062
1194
  modelOrConstructor?: T | PersistentModelConstructor<T>,
1063
- idOrCriteria?: string | ProducerModelPredicate<T>
1195
+ identifierOrCriteria?:
1196
+ | string
1197
+ | ProducerModelPredicate<T>
1198
+ | typeof PredicateAll
1064
1199
  ): Observable<SubscriptionMessage<T>> => {
1065
1200
  let predicate: ModelPredicate<T>;
1066
1201
 
1067
- const modelConstructor: PersistentModelConstructor<T> =
1068
- modelOrConstructor && isValidModelConstructor(modelOrConstructor)
1202
+ const modelConstructor: PersistentModelConstructor<T> | undefined =
1203
+ modelOrConstructor && isValidModelConstructor<T>(modelOrConstructor)
1069
1204
  ? modelOrConstructor
1070
1205
  : undefined;
1071
1206
 
@@ -1075,10 +1210,10 @@ class DataStore {
1075
1210
  model && (<Object>Object.getPrototypeOf(model)).constructor;
1076
1211
 
1077
1212
  if (isValidModelConstructor<T>(modelConstructor)) {
1078
- if (idOrCriteria) {
1213
+ if (identifierOrCriteria) {
1079
1214
  logger.warn('idOrCriteria is ignored when using a model instance', {
1080
1215
  model,
1081
- idOrCriteria,
1216
+ identifierOrCriteria,
1082
1217
  });
1083
1218
  }
1084
1219
 
@@ -1092,9 +1227,9 @@ class DataStore {
1092
1227
  }
1093
1228
  }
1094
1229
 
1095
- if (idOrCriteria !== undefined && modelConstructor === undefined) {
1230
+ if (identifierOrCriteria !== undefined && modelConstructor === undefined) {
1096
1231
  const msg = 'Cannot provide criteria without a modelConstructor';
1097
- logger.error(msg, idOrCriteria);
1232
+ logger.error(msg, identifierOrCriteria);
1098
1233
  throw new Error(msg);
1099
1234
  }
1100
1235
 
@@ -1105,18 +1240,26 @@ class DataStore {
1105
1240
  throw new Error(msg);
1106
1241
  }
1107
1242
 
1108
- if (typeof idOrCriteria === 'string') {
1109
- predicate = ModelPredicateCreator.createForId<T>(
1243
+ if (typeof identifierOrCriteria === 'string') {
1244
+ const modelDefinition = getModelDefinition(modelConstructor);
1245
+ const [keyField] = extractPrimaryKeyFieldNames(modelDefinition);
1246
+
1247
+ predicate = ModelPredicateCreator.createForSingleField<T>(
1110
1248
  getModelDefinition(modelConstructor),
1111
- idOrCriteria
1249
+ keyField,
1250
+ identifierOrCriteria
1112
1251
  );
1113
1252
  } else {
1114
- predicate =
1115
- modelConstructor &&
1116
- ModelPredicateCreator.createFromExisting<T>(
1117
- getModelDefinition(modelConstructor),
1118
- idOrCriteria
1119
- );
1253
+ if (isPredicatesAll(identifierOrCriteria)) {
1254
+ predicate = undefined;
1255
+ } else {
1256
+ predicate =
1257
+ modelConstructor &&
1258
+ ModelPredicateCreator.createFromExisting<T>(
1259
+ getModelDefinition(modelConstructor),
1260
+ identifierOrCriteria
1261
+ );
1262
+ }
1120
1263
  }
1121
1264
 
1122
1265
  return new Observable<SubscriptionMessage<T>>(observer => {
@@ -1125,10 +1268,43 @@ class DataStore {
1125
1268
  (async () => {
1126
1269
  await this.start();
1127
1270
 
1271
+ // Filter the events returned by Storage according to namespace,
1272
+ // append original element data, and subscribe to the observable
1128
1273
  handle = this.storage
1129
1274
  .observe(modelConstructor, predicate)
1130
1275
  .filter(({ model }) => namespaceResolver(model) === USER)
1131
- .subscribe(observer);
1276
+ .subscribe({
1277
+ next: async item => {
1278
+ // the `element` doesn't necessarily contain all item details or
1279
+ // have related records attached consistently with that of a query()
1280
+ // result item. for consistency, we attach them here.
1281
+
1282
+ let message = item;
1283
+
1284
+ // as long as we're not dealing with a DELETE, we need to fetch a fresh
1285
+ // item from storage to ensure it's fully populated.
1286
+ if (item.opType !== 'DELETE') {
1287
+ const modelDefinition = getModelDefinition(item.model);
1288
+ const keyFields = extractPrimaryKeyFieldNames(modelDefinition);
1289
+ const primaryKeysAndValues = extractPrimaryKeysAndValues(
1290
+ item.element,
1291
+ keyFields
1292
+ );
1293
+ const freshElement = await this.query(
1294
+ item.model,
1295
+ primaryKeysAndValues
1296
+ );
1297
+ message = {
1298
+ ...message,
1299
+ element: freshElement as T,
1300
+ };
1301
+ }
1302
+
1303
+ observer.next(message as SubscriptionMessage<T>);
1304
+ },
1305
+ error: err => observer.error(err),
1306
+ complete: () => observer.complete(),
1307
+ });
1132
1308
  })();
1133
1309
 
1134
1310
  return () => {
@@ -1145,7 +1321,7 @@ class DataStore {
1145
1321
  criteria?: ProducerModelPredicate<T> | typeof PredicateAll,
1146
1322
  paginationProducer?: ObserveQueryOptions<T>
1147
1323
  ): Observable<DataStoreSnapshot<T>>;
1148
- } = <T extends PersistentModel = PersistentModel>(
1324
+ } = <T extends PersistentModel>(
1149
1325
  model: PersistentModelConstructor<T>,
1150
1326
  criteria?: ProducerModelPredicate<T> | typeof PredicateAll,
1151
1327
  options?: ObserveQueryOptions<T>
@@ -1155,7 +1331,18 @@ class DataStore {
1155
1331
  const itemsChanged = new Map<string, T>();
1156
1332
  let deletedItemIds: string[] = [];
1157
1333
  let handle: ZenObservable.Subscription;
1158
-
1334
+ let predicate: ModelPredicate<T>;
1335
+
1336
+ /**
1337
+ * As the name suggests, this geneates a snapshot in the form of
1338
+ * `{items: T[], isSynced: boolean}`
1339
+ * and sends it to the observer.
1340
+ *
1341
+ * SIDE EFFECT: The underlying generation and emission methods may touch:
1342
+ * `items`, `itemsChanged`, and `deletedItemIds`.
1343
+ *
1344
+ * Refer to `generateSnapshot` and `emitSnapshot` for more details.
1345
+ */
1159
1346
  const generateAndEmitSnapshot = (): void => {
1160
1347
  const snapshot = generateSnapshot();
1161
1348
  emitSnapshot(snapshot);
@@ -1172,41 +1359,101 @@ class DataStore {
1172
1359
  const { sort } = options || {};
1173
1360
  const sortOptions = sort ? { sort } : undefined;
1174
1361
 
1362
+ const modelDefinition = getModelDefinition(model);
1363
+ const keyFields = extractPrimaryKeyFieldNames(modelDefinition);
1364
+
1365
+ if (isQueryOne(criteria)) {
1366
+ predicate = ModelPredicateCreator.createForSingleField<T>(
1367
+ modelDefinition,
1368
+ keyFields[0],
1369
+ criteria
1370
+ );
1371
+ } else {
1372
+ if (isPredicatesAll(criteria)) {
1373
+ // Predicates.ALL means "all records", so no predicate (undefined)
1374
+ predicate = undefined;
1375
+ } else {
1376
+ predicate = ModelPredicateCreator.createFromExisting(
1377
+ modelDefinition,
1378
+ criteria
1379
+ );
1380
+ }
1381
+ }
1382
+
1383
+ const { predicates, type: predicateGroupType } =
1384
+ ModelPredicateCreator.getPredicates(predicate, false) || {};
1385
+ const hasPredicate = !!predicates;
1386
+
1175
1387
  (async () => {
1176
1388
  try {
1177
1389
  // first, query and return any locally-available records
1178
- (await this.query(model, criteria, sortOptions)).forEach(item =>
1179
- items.set(item.id, item)
1180
- );
1181
-
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);
1390
+ (await this.query(model, criteria, sortOptions)).forEach(item => {
1391
+ let record = item;
1392
+ // TODO: fix query
1393
+ if (Array.isArray(item)) {
1394
+ record = item[0];
1196
1395
  }
1396
+ const itemModelDefinition = getModelDefinition(model);
1397
+ const idOrPk = getIdentifierValue(itemModelDefinition, record);
1398
+ items.set(idOrPk, record);
1399
+ });
1400
+
1401
+ // Observe the model and send a stream of updates (debounced).
1402
+ // We need to post-filter results instead of passing criteria through
1403
+ // to have visibility into items that move from in-set to out-of-set.
1404
+ // We need to explicitly remove those items from the existing snapshot.
1405
+ handle = this.observe(model).subscribe(
1406
+ ({ element, model, opType }) => {
1407
+ let record = element;
1408
+
1409
+ // TODO: fix query
1410
+ if (Array.isArray(element)) {
1411
+ record = element[0];
1412
+ }
1413
+ const itemModelDefinition = getModelDefinition(model);
1414
+ const idOrPk = getIdentifierValue(itemModelDefinition, record);
1415
+ if (
1416
+ hasPredicate &&
1417
+ !validatePredicate(record, predicateGroupType, predicates)
1418
+ ) {
1419
+ if (
1420
+ opType === 'UPDATE' &&
1421
+ (items.has(idOrPk) || itemsChanged.has(idOrPk))
1422
+ ) {
1423
+ // tracking as a "deleted item" will include the item in
1424
+ // page limit calculations and ensure it is removed from the
1425
+ // final items collection, regardless of which collection(s)
1426
+ // it is currently in. (I mean, it could be in both, right!?)
1427
+ deletedItemIds.push(idOrPk);
1428
+ } else {
1429
+ // ignore updates for irrelevant/filtered items.
1430
+ return;
1431
+ }
1432
+ }
1197
1433
 
1198
- const isSynced = this.sync?.getModelSyncedStatus(model) ?? false;
1434
+ // Flag items which have been recently deleted
1435
+ // NOTE: Merging of separate operations to the same model instance is handled upstream
1436
+ // in the `mergePage` method within src/sync/merger.ts. The final state of a model instance
1437
+ // depends on the LATEST record (for a given id).
1438
+ if (opType === 'DELETE') {
1439
+ deletedItemIds.push(idOrPk);
1440
+ } else {
1441
+ itemsChanged.set(idOrPk, record);
1442
+ }
1199
1443
 
1200
- const limit =
1201
- itemsChanged.size - deletedItemIds.length >= this.syncPageSize;
1444
+ const isSynced = this.sync?.getModelSyncedStatus(model) ?? false;
1202
1445
 
1203
- if (limit || isSynced) {
1204
- limitTimerRace.resolve();
1205
- }
1446
+ const limit =
1447
+ itemsChanged.size - deletedItemIds.length >= this.syncPageSize;
1206
1448
 
1207
- // kicks off every subsequent race as results sync down
1208
- limitTimerRace.start();
1209
- });
1449
+ if (limit || isSynced) {
1450
+ limitTimerRace.resolve();
1451
+ }
1452
+
1453
+ // kicks off every subsequent race as results sync down
1454
+ limitTimerRace.start();
1455
+ }
1456
+ );
1210
1457
 
1211
1458
  // returns a set of initial/locally-available results
1212
1459
  generateAndEmitSnapshot();
@@ -1215,7 +1462,12 @@ class DataStore {
1215
1462
  }
1216
1463
  })();
1217
1464
 
1218
- // TODO: abstract this function into a util file to be able to write better unit tests
1465
+ /**
1466
+ * Combines the `items`, `itemsChanged`, and `deletedItemIds` collections into
1467
+ * a snapshot in the form of `{ items: T[], isSynced: boolean}`.
1468
+ *
1469
+ * SIDE EFFECT: The shared `items` collection is recreated.
1470
+ */
1219
1471
  const generateSnapshot = (): DataStoreSnapshot<T> => {
1220
1472
  const isSynced = this.sync?.getModelSyncedStatus(model) ?? false;
1221
1473
  const itemsArray = [
@@ -1228,10 +1480,21 @@ class DataStore {
1228
1480
  }
1229
1481
 
1230
1482
  items.clear();
1231
- itemsArray.forEach(item => items.set(item.id, item));
1483
+ itemsArray.forEach(item => {
1484
+ // CPK TODO: fix query
1485
+ let record = item;
1486
+
1487
+ if (Array.isArray(item)) {
1488
+ record = item[0];
1489
+ }
1490
+
1491
+ const itemModelDefinition = getModelDefinition(model);
1492
+ const idOrPk = getIdentifierValue(itemModelDefinition, record);
1493
+ items.set(idOrPk, record);
1494
+ });
1232
1495
 
1233
1496
  // remove deleted items from the final result set
1234
- deletedItemIds.forEach(id => items.delete(id));
1497
+ deletedItemIds.forEach(idOrPk => items.delete(idOrPk));
1235
1498
 
1236
1499
  return {
1237
1500
  items: Array.from(items.values()),
@@ -1239,6 +1502,14 @@ class DataStore {
1239
1502
  };
1240
1503
  };
1241
1504
 
1505
+ /**
1506
+ * Emits the list of items to the observer.
1507
+ *
1508
+ * SIDE EFFECT: `itemsChanged` and `deletedItemIds` are cleared to prepare
1509
+ * for the next snapshot.
1510
+ *
1511
+ * @param snapshot The generated items data to emit.
1512
+ */
1242
1513
  const emitSnapshot = (snapshot: DataStoreSnapshot<T>): void => {
1243
1514
  // send the generated snapshot to the primary subscription
1244
1515
  observer.next(snapshot);
@@ -1248,6 +1519,12 @@ class DataStore {
1248
1519
  deletedItemIds = [];
1249
1520
  };
1250
1521
 
1522
+ /**
1523
+ * Sorts an `Array` of `T` according to the sort instructions given in the
1524
+ * original `observeQuery()` call.
1525
+ *
1526
+ * @param itemsToSort A array of model type.
1527
+ */
1251
1528
  const sortItems = (itemsToSort: T[]): void => {
1252
1529
  const modelDefinition = getModelDefinition(model);
1253
1530
  const pagination = this.processPagination(modelDefinition, options);
@@ -1262,7 +1539,14 @@ class DataStore {
1262
1539
  }
1263
1540
  };
1264
1541
 
1265
- // send one last snapshot when the model is fully synced
1542
+ /**
1543
+ * Force one last snapshot when the model is fully synced.
1544
+ *
1545
+ * This reduces latency for that last snapshot, which will otherwise
1546
+ * wait for the configured timeout.
1547
+ *
1548
+ * @param payload The payload from the Hub event.
1549
+ */
1266
1550
  const hubCallback = ({ payload }): void => {
1267
1551
  const { event, data } = payload;
1268
1552
  if (
@@ -1284,6 +1568,10 @@ class DataStore {
1284
1568
  };
1285
1569
 
1286
1570
  configure = (config: DataStoreConfig = {}) => {
1571
+ this.amplifyContext.Auth = this.Auth;
1572
+ this.amplifyContext.API = this.API;
1573
+ this.amplifyContext.Cache = this.Cache;
1574
+
1287
1575
  const {
1288
1576
  DataStore: configDataStore,
1289
1577
  authModeStrategyType: configAuthModeStrategyType,
@@ -1313,7 +1601,7 @@ class DataStore {
1313
1601
 
1314
1602
  switch (authModeStrategyType) {
1315
1603
  case AuthModeStrategyType.MULTI_AUTH:
1316
- this.authModeStrategy = multiAuthStrategy;
1604
+ this.authModeStrategy = multiAuthStrategy(this.amplifyContext);
1317
1605
  break;
1318
1606
  case AuthModeStrategyType.DEFAULT:
1319
1607
  this.authModeStrategy = defaultAuthStrategy;
@@ -1329,12 +1617,13 @@ class DataStore {
1329
1617
 
1330
1618
  this.syncExpressions =
1331
1619
  (configDataStore && configDataStore.syncExpressions) ||
1332
- this.syncExpressions ||
1333
- configSyncExpressions;
1620
+ configSyncExpressions ||
1621
+ this.syncExpressions;
1334
1622
 
1335
1623
  this.maxRecordsToSync =
1336
1624
  (configDataStore && configDataStore.maxRecordsToSync) ||
1337
1625
  configMaxRecordsToSync ||
1626
+ this.maxRecordsToSync ||
1338
1627
  10000;
1339
1628
 
1340
1629
  // store on config object, so that Sync, Subscription, and Mutation processors can have access
@@ -1343,6 +1632,7 @@ class DataStore {
1343
1632
  this.syncPageSize =
1344
1633
  (configDataStore && configDataStore.syncPageSize) ||
1345
1634
  configSyncPageSize ||
1635
+ this.syncPageSize ||
1346
1636
  1000;
1347
1637
 
1348
1638
  // store on config object, so that Sync, Subscription, and Mutation processors can have access
@@ -1350,22 +1640,32 @@ class DataStore {
1350
1640
 
1351
1641
  this.fullSyncInterval =
1352
1642
  (configDataStore && configDataStore.fullSyncInterval) ||
1353
- this.fullSyncInterval ||
1354
1643
  configFullSyncInterval ||
1644
+ this.fullSyncInterval ||
1355
1645
  24 * 60; // 1 day
1356
1646
 
1357
1647
  this.storageAdapter =
1358
1648
  (configDataStore && configDataStore.storageAdapter) ||
1359
- this.storageAdapter ||
1360
1649
  configStorageAdapter ||
1650
+ this.storageAdapter ||
1361
1651
  undefined;
1362
1652
 
1363
- this.sessionId = this.retrieveSessionId();
1653
+ this.sessionId = this.retrieveSessionId()!;
1364
1654
  };
1365
1655
 
1366
1656
  clear = async function clear() {
1657
+ checkSchemaInitialized();
1367
1658
  if (this.storage === undefined) {
1368
- return;
1659
+ // connect to storage so that it can be cleared without fully starting DataStore
1660
+ this.storage = new Storage(
1661
+ schema,
1662
+ namespaceResolver,
1663
+ getModelConstructorByModelName,
1664
+ modelInstanceCreator,
1665
+ this.storageAdapter,
1666
+ this.sessionId
1667
+ );
1668
+ await this.storage.init();
1369
1669
  }
1370
1670
 
1371
1671
  if (syncSubscription && !syncSubscription.closed) {
@@ -1384,7 +1684,7 @@ class DataStore {
1384
1684
  this.syncPredicates = new WeakMap<SchemaModel, ModelPredicate<any>>();
1385
1685
  };
1386
1686
 
1387
- stop = async function stop() {
1687
+ stop = async function stop(this: InstanceType<typeof DataStore>) {
1388
1688
  if (this.initialized !== undefined) {
1389
1689
  await this.start();
1390
1690
  }
@@ -1548,9 +1848,9 @@ class DataStore {
1548
1848
 
1549
1849
  return `${sessionId}-${appSyncId}`;
1550
1850
  }
1551
- } catch {
1552
- return undefined;
1553
- }
1851
+ } catch {}
1852
+
1853
+ return undefined;
1554
1854
  }
1555
1855
  }
1556
1856