@aws-amplify/datastore 3.4.8 → 3.4.9-in-app-messaging.48

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 (48) hide show
  1. package/CHANGELOG.md +104 -445
  2. package/dist/aws-amplify-datastore.js +28167 -13268
  3. package/dist/aws-amplify-datastore.js.map +1 -1
  4. package/dist/aws-amplify-datastore.min.js +8 -8
  5. package/dist/aws-amplify-datastore.min.js.map +1 -1
  6. package/lib/.tsbuildinfo +1 -1
  7. package/lib/datastore/datastore.d.ts +4 -1
  8. package/lib/datastore/datastore.js +129 -4
  9. package/lib/datastore/datastore.js.map +1 -1
  10. package/lib/sync/index.d.ts +3 -1
  11. package/lib/sync/index.js +10 -1
  12. package/lib/sync/index.js.map +1 -1
  13. package/lib/sync/processors/subscription.js +2 -2
  14. package/lib/sync/processors/subscription.js.map +1 -1
  15. package/lib/sync/processors/sync.d.ts +1 -3
  16. package/lib/sync/processors/sync.js +8 -19
  17. package/lib/sync/processors/sync.js.map +1 -1
  18. package/lib/types.d.ts +14 -0
  19. package/lib/types.js +5 -0
  20. package/lib/types.js.map +1 -1
  21. package/lib/util.d.ts +23 -1
  22. package/lib/util.js +77 -0
  23. package/lib/util.js.map +1 -1
  24. package/lib-esm/.tsbuildinfo +1 -1
  25. package/lib-esm/datastore/datastore.d.ts +4 -1
  26. package/lib-esm/datastore/datastore.js +130 -5
  27. package/lib-esm/datastore/datastore.js.map +1 -1
  28. package/lib-esm/sync/index.d.ts +3 -1
  29. package/lib-esm/sync/index.js +11 -2
  30. package/lib-esm/sync/index.js.map +1 -1
  31. package/lib-esm/sync/processors/subscription.js +2 -2
  32. package/lib-esm/sync/processors/subscription.js.map +1 -1
  33. package/lib-esm/sync/processors/sync.d.ts +1 -3
  34. package/lib-esm/sync/processors/sync.js +8 -19
  35. package/lib-esm/sync/processors/sync.js.map +1 -1
  36. package/lib-esm/types.d.ts +14 -0
  37. package/lib-esm/types.js +5 -0
  38. package/lib-esm/types.js.map +1 -1
  39. package/lib-esm/util.d.ts +23 -1
  40. package/lib-esm/util.js +78 -1
  41. package/lib-esm/util.js.map +1 -1
  42. package/package.json +7 -7
  43. package/src/datastore/datastore.ts +267 -121
  44. package/src/sync/index.ts +19 -3
  45. package/src/sync/processors/subscription.ts +1 -1
  46. package/src/sync/processors/sync.ts +1 -17
  47. package/src/types.ts +31 -12
  48. package/src/util.ts +80 -0
@@ -44,6 +44,7 @@ import {
44
44
  SchemaNamespace,
45
45
  SchemaNonModel,
46
46
  SubscriptionMessage,
47
+ DataStoreSnapshot,
47
48
  SyncConflict,
48
49
  SyncError,
49
50
  TypeConstructorMap,
@@ -52,6 +53,7 @@ import {
52
53
  AuthModeStrategyType,
53
54
  isNonModelFieldType,
54
55
  isModelFieldType,
56
+ ObserveQueryOptions,
55
57
  } from '../types';
56
58
  import {
57
59
  DATASTORE,
@@ -65,6 +67,8 @@ import {
65
67
  USER,
66
68
  isNullOrUndefined,
67
69
  registerNonModelClass,
70
+ sortCompareFunction,
71
+ DeferredCallbackResolver,
68
72
  } from '../util';
69
73
 
70
74
  setAutoFreeze(true);
@@ -115,7 +119,7 @@ const isValidModelConstructor = <T extends PersistentModel>(
115
119
  return isModelConstructor(obj) && modelNamespaceMap.has(obj);
116
120
  };
117
121
 
118
- const namespaceResolver: NamespaceResolver = modelConstructor =>
122
+ const namespaceResolver: NamespaceResolver = (modelConstructor) =>
119
123
  modelNamespaceMap.get(modelConstructor);
120
124
 
121
125
  // exporting syncClasses for testing outbox.test.ts
@@ -160,7 +164,7 @@ const initSchema = (userSchema: Schema) => {
160
164
  version: userSchema.version,
161
165
  };
162
166
 
163
- Object.keys(schema.namespaces).forEach(namespace => {
167
+ Object.keys(schema.namespaces).forEach((namespace) => {
164
168
  const [relations, keys] = establishRelationAndKeys(
165
169
  schema.namespaces[namespace]
166
170
  );
@@ -170,17 +174,17 @@ const initSchema = (userSchema: Schema) => {
170
174
 
171
175
  const modelAssociations = new Map<string, string[]>();
172
176
 
173
- Object.values(schema.namespaces[namespace].models).forEach(model => {
177
+ Object.values(schema.namespaces[namespace].models).forEach((model) => {
174
178
  const connectedModels: string[] = [];
175
179
 
176
180
  Object.values(model.fields)
177
181
  .filter(
178
- field =>
182
+ (field) =>
179
183
  field.association &&
180
184
  field.association.connectionType === 'BELONGS_TO' &&
181
185
  (<ModelFieldType>field.type).model !== model.name
182
186
  )
183
- .forEach(field =>
187
+ .forEach((field) =>
184
188
  connectedModels.push((<ModelFieldType>field.type).model)
185
189
  );
186
190
 
@@ -204,12 +208,12 @@ const initSchema = (userSchema: Schema) => {
204
208
  for (const modelName of Array.from(modelAssociations.keys())) {
205
209
  const parents = modelAssociations.get(modelName);
206
210
 
207
- if (parents.every(x => result.has(x))) {
211
+ if (parents.every((x) => result.has(x))) {
208
212
  result.set(modelName, parents);
209
213
  }
210
214
  }
211
215
 
212
- Array.from(result.keys()).forEach(x => modelAssociations.delete(x));
216
+ Array.from(result.keys()).forEach((x) => modelAssociations.delete(x));
213
217
  }
214
218
 
215
219
  schema.namespaces[namespace].modelTopologicalOrdering = result;
@@ -218,9 +222,9 @@ const initSchema = (userSchema: Schema) => {
218
222
  return userClasses;
219
223
  };
220
224
 
221
- const createTypeClasses: (
222
- namespace: SchemaNamespace
223
- ) => TypeConstructorMap = namespace => {
225
+ const createTypeClasses: (namespace: SchemaNamespace) => TypeConstructorMap = (
226
+ namespace
227
+ ) => {
224
228
  const classes: TypeConstructorMap = {};
225
229
 
226
230
  Object.entries(namespace.models).forEach(([modelName, modelDefinition]) => {
@@ -254,108 +258,101 @@ function modelInstanceCreator<T extends PersistentModel = PersistentModel>(
254
258
  return <T>new modelConstructor(init);
255
259
  }
256
260
 
257
- const validateModelFields = (modelDefinition: SchemaModel | SchemaNonModel) => (
258
- k: string,
259
- v: any
260
- ) => {
261
- const fieldDefinition = modelDefinition.fields[k];
261
+ const validateModelFields =
262
+ (modelDefinition: SchemaModel | SchemaNonModel) => (k: string, v: any) => {
263
+ const fieldDefinition = modelDefinition.fields[k];
262
264
 
263
- if (fieldDefinition !== undefined) {
264
- const {
265
- type,
266
- isRequired,
267
- isArrayNullable,
268
- name,
269
- isArray,
270
- } = fieldDefinition;
265
+ if (fieldDefinition !== undefined) {
266
+ const { type, isRequired, isArrayNullable, name, isArray } =
267
+ fieldDefinition;
271
268
 
272
- if (
273
- ((!isArray && isRequired) || (isArray && !isArrayNullable)) &&
274
- (v === null || v === undefined)
275
- ) {
276
- throw new Error(`Field ${name} is required`);
277
- }
269
+ if (
270
+ ((!isArray && isRequired) || (isArray && !isArrayNullable)) &&
271
+ (v === null || v === undefined)
272
+ ) {
273
+ throw new Error(`Field ${name} is required`);
274
+ }
278
275
 
279
- if (isGraphQLScalarType(type)) {
280
- const jsType = GraphQLScalarType.getJSType(type);
281
- const validateScalar = GraphQLScalarType.getValidationFunction(type);
276
+ if (isGraphQLScalarType(type)) {
277
+ const jsType = GraphQLScalarType.getJSType(type);
278
+ const validateScalar = GraphQLScalarType.getValidationFunction(type);
282
279
 
283
- if (type === 'AWSJSON') {
284
- if (typeof v === jsType) {
285
- return;
286
- }
287
- if (typeof v === 'string') {
288
- try {
289
- JSON.parse(v);
280
+ if (type === 'AWSJSON') {
281
+ if (typeof v === jsType) {
290
282
  return;
291
- } catch (error) {
292
- throw new Error(`Field ${name} is an invalid JSON object. ${v}`);
283
+ }
284
+ if (typeof v === 'string') {
285
+ try {
286
+ JSON.parse(v);
287
+ return;
288
+ } catch (error) {
289
+ throw new Error(`Field ${name} is an invalid JSON object. ${v}`);
290
+ }
293
291
  }
294
292
  }
295
- }
296
293
 
297
- if (isArray) {
298
- let errorTypeText: string = jsType;
299
- if (!isRequired) {
300
- errorTypeText = `${jsType} | null | undefined`;
301
- }
294
+ if (isArray) {
295
+ let errorTypeText: string = jsType;
296
+ if (!isRequired) {
297
+ errorTypeText = `${jsType} | null | undefined`;
298
+ }
299
+
300
+ if (!Array.isArray(v) && !isArrayNullable) {
301
+ throw new Error(
302
+ `Field ${name} should be of type [${errorTypeText}], ${typeof v} received. ${v}`
303
+ );
304
+ }
305
+
306
+ if (
307
+ !isNullOrUndefined(v) &&
308
+ (<[]>v).some((e) =>
309
+ isNullOrUndefined(e) ? isRequired : typeof e !== jsType
310
+ )
311
+ ) {
312
+ const elemTypes = (<[]>v)
313
+ .map((e) => (e === null ? 'null' : typeof e))
314
+ .join(',');
302
315
 
303
- if (!Array.isArray(v) && !isArrayNullable) {
316
+ throw new Error(
317
+ `All elements in the ${name} array should be of type ${errorTypeText}, [${elemTypes}] received. ${v}`
318
+ );
319
+ }
320
+
321
+ if (validateScalar && !isNullOrUndefined(v)) {
322
+ const validationStatus = (<[]>v).map((e) => {
323
+ if (!isNullOrUndefined(e)) {
324
+ return validateScalar(e);
325
+ } else if (isNullOrUndefined(e) && !isRequired) {
326
+ return true;
327
+ } else {
328
+ return false;
329
+ }
330
+ });
331
+
332
+ if (!validationStatus.every((s) => s)) {
333
+ throw new Error(
334
+ `All elements in the ${name} array should be of type ${type}, validation failed for one or more elements. ${v}`
335
+ );
336
+ }
337
+ }
338
+ } else if (!isRequired && v === undefined) {
339
+ return;
340
+ } else if (typeof v !== jsType && v !== null) {
304
341
  throw new Error(
305
- `Field ${name} should be of type [${errorTypeText}], ${typeof v} received. ${v}`
342
+ `Field ${name} should be of type ${jsType}, ${typeof v} received. ${v}`
306
343
  );
307
- }
308
-
309
- if (
344
+ } else if (
310
345
  !isNullOrUndefined(v) &&
311
- (<[]>v).some(e =>
312
- isNullOrUndefined(e) ? isRequired : typeof e !== jsType
313
- )
346
+ validateScalar &&
347
+ !validateScalar(v)
314
348
  ) {
315
- const elemTypes = (<[]>v)
316
- .map(e => (e === null ? 'null' : typeof e))
317
- .join(',');
318
-
319
349
  throw new Error(
320
- `All elements in the ${name} array should be of type ${errorTypeText}, [${elemTypes}] received. ${v}`
350
+ `Field ${name} should be of type ${type}, validation failed. ${v}`
321
351
  );
322
352
  }
323
-
324
- if (validateScalar && !isNullOrUndefined(v)) {
325
- const validationStatus = (<[]>v).map(e => {
326
- if (!isNullOrUndefined(e)) {
327
- return validateScalar(e);
328
- } else if (isNullOrUndefined(e) && !isRequired) {
329
- return true;
330
- } else {
331
- return false;
332
- }
333
- });
334
-
335
- if (!validationStatus.every(s => s)) {
336
- throw new Error(
337
- `All elements in the ${name} array should be of type ${type}, validation failed for one or more elements. ${v}`
338
- );
339
- }
340
- }
341
- } else if (!isRequired && v === undefined) {
342
- return;
343
- } else if (typeof v !== jsType && v !== null) {
344
- throw new Error(
345
- `Field ${name} should be of type ${jsType}, ${typeof v} received. ${v}`
346
- );
347
- } else if (
348
- !isNullOrUndefined(v) &&
349
- validateScalar &&
350
- !validateScalar(v)
351
- ) {
352
- throw new Error(
353
- `Field ${name} should be of type ${type}, validation failed. ${v}`
354
- );
355
353
  }
356
354
  }
357
- }
358
- };
355
+ };
359
356
 
360
357
  const castInstanceType = (
361
358
  modelDefinition: SchemaModel | SchemaNonModel,
@@ -410,11 +407,10 @@ const createModelClass = <T extends PersistentModel>(
410
407
  (draft: Draft<T & ModelInstanceMetadata>) => {
411
408
  initializeInstance(init, modelDefinition, draft);
412
409
 
413
- const modelInstanceMetadata: ModelInstanceMetadata = instancesMetadata.has(
414
- init
415
- )
416
- ? <ModelInstanceMetadata>(<unknown>init)
417
- : <ModelInstanceMetadata>{};
410
+ const modelInstanceMetadata: ModelInstanceMetadata =
411
+ instancesMetadata.has(init)
412
+ ? <ModelInstanceMetadata>(<unknown>init)
413
+ : <ModelInstanceMetadata>{};
418
414
  const {
419
415
  id: _id,
420
416
  _version,
@@ -459,7 +455,7 @@ const createModelClass = <T extends PersistentModel>(
459
455
  let patches;
460
456
  const model = produce(
461
457
  source,
462
- draft => {
458
+ (draft) => {
463
459
  fn(<MutableModel<T>>(draft as unknown));
464
460
  draft.id = source.id;
465
461
  const modelValidator = validateModelFields(modelDefinition);
@@ -469,7 +465,7 @@ const createModelClass = <T extends PersistentModel>(
469
465
  modelValidator(k, parsedValue);
470
466
  });
471
467
  },
472
- p => (patches = p)
468
+ (p) => (patches = p)
473
469
  );
474
470
 
475
471
  if (patches.length) {
@@ -484,7 +480,7 @@ const createModelClass = <T extends PersistentModel>(
484
480
  // to gain access to `modelInstanceCreator` and `clazz` for persisting IDs from server to client.
485
481
  static fromJSON(json: T | T[]) {
486
482
  if (Array.isArray(json)) {
487
- return json.map(init => this.fromJSON(init));
483
+ return json.map((init) => this.fromJSON(init));
488
484
  }
489
485
 
490
486
  const instance = modelInstanceCreator(clazz, json);
@@ -512,7 +508,7 @@ const checkReadOnlyPropertyOnCreate = <T extends PersistentModel>(
512
508
  const modelKeys = Object.keys(draft);
513
509
  const { fields } = modelDefinition;
514
510
 
515
- modelKeys.forEach(key => {
511
+ modelKeys.forEach((key) => {
516
512
  if (fields[key] && fields[key].isReadOnly) {
517
513
  throw new Error(`${key} is read-only.`);
518
514
  }
@@ -523,7 +519,7 @@ const checkReadOnlyPropertyOnUpdate = (
523
519
  patches: Patch[],
524
520
  modelDefinition: SchemaModel
525
521
  ) => {
526
- const patchArray = patches.map(p => [p.path[0], p.value]);
522
+ const patchArray = patches.map((p) => [p.path[0], p.value]);
527
523
  const { fields } = modelDefinition;
528
524
 
529
525
  patchArray.forEach(([key, val]) => {
@@ -610,16 +606,15 @@ async function checkSchemaVersion(
610
606
  storage: Storage,
611
607
  version: string
612
608
  ): Promise<void> {
613
- const Setting = dataStoreClasses.Setting as PersistentModelConstructor<
614
- Setting
615
- >;
609
+ const Setting =
610
+ dataStoreClasses.Setting as PersistentModelConstructor<Setting>;
616
611
 
617
612
  const modelDefinition = schema.namespaces[DATASTORE].models.Setting;
618
613
 
619
- await storage.runExclusive(async s => {
614
+ await storage.runExclusive(async (s) => {
620
615
  const [schemaVersionSetting] = await s.query(
621
616
  Setting,
622
- ModelPredicateCreator.createFromExisting(modelDefinition, c =>
617
+ ModelPredicateCreator.createFromExisting(modelDefinition, (c) =>
623
618
  // @ts-ignore Argument of type '"eq"' is not assignable to parameter of type 'never'.
624
619
  c.key('eq', SETTING_SCHEMA_VERSION)
625
620
  ),
@@ -700,10 +695,8 @@ class DataStore {
700
695
  private sync: SyncEngine;
701
696
  private syncPageSize: number;
702
697
  private syncExpressions: SyncExpression[];
703
- private syncPredicates: WeakMap<
704
- SchemaModel,
705
- ModelPredicate<any>
706
- > = new WeakMap<SchemaModel, ModelPredicate<any>>();
698
+ private syncPredicates: WeakMap<SchemaModel, ModelPredicate<any>> =
699
+ new WeakMap<SchemaModel, ModelPredicate<any>>();
707
700
  private sessionId: string;
708
701
  private storageAdapter: Adapter;
709
702
 
@@ -781,7 +774,7 @@ class DataStore {
781
774
  data,
782
775
  });
783
776
  },
784
- error: err => {
777
+ error: (err) => {
785
778
  logger.warn('Sync error', err);
786
779
  this.initReject();
787
780
  },
@@ -908,7 +901,7 @@ class DataStore {
908
901
  condition
909
902
  );
910
903
 
911
- const [savedModel] = await this.storage.runExclusive(async s => {
904
+ const [savedModel] = await this.storage.runExclusive(async (s) => {
912
905
  await s.save(model, producedCondition, undefined, patchesTuple);
913
906
 
914
907
  return s.query(
@@ -1126,7 +1119,7 @@ class DataStore {
1126
1119
  );
1127
1120
  }
1128
1121
 
1129
- return new Observable<SubscriptionMessage<T>>(observer => {
1122
+ return new Observable<SubscriptionMessage<T>>((observer) => {
1130
1123
  let handle: ZenObservable.Subscription;
1131
1124
 
1132
1125
  (async () => {
@@ -1146,6 +1139,150 @@ class DataStore {
1146
1139
  });
1147
1140
  };
1148
1141
 
1142
+ observeQuery: {
1143
+ <T extends PersistentModel>(
1144
+ modelConstructor: PersistentModelConstructor<T>,
1145
+ criteria?: ProducerModelPredicate<T> | typeof PredicateAll,
1146
+ paginationProducer?: ObserveQueryOptions<T>
1147
+ ): Observable<DataStoreSnapshot<T>>;
1148
+ } = <T extends PersistentModel = PersistentModel>(
1149
+ model: PersistentModelConstructor<T>,
1150
+ criteria?: ProducerModelPredicate<T> | typeof PredicateAll,
1151
+ options?: ObserveQueryOptions<T>
1152
+ ): Observable<DataStoreSnapshot<T>> => {
1153
+ return new Observable<DataStoreSnapshot<T>>((observer) => {
1154
+ const items = new Map<string, T>();
1155
+ const itemsChanged = new Map<string, T>();
1156
+ let deletedItemIds: string[] = [];
1157
+ let handle: ZenObservable.Subscription;
1158
+
1159
+ const generateAndEmitSnapshot = (): void => {
1160
+ const snapshot = generateSnapshot();
1161
+ emitSnapshot(snapshot);
1162
+ };
1163
+
1164
+ // a mechanism to return data after X amount of seconds OR after the
1165
+ // "limit" (itemsChanged >= this.syncPageSize) has been reached, whichever comes first
1166
+ const limitTimerRace = new DeferredCallbackResolver({
1167
+ callback: generateAndEmitSnapshot,
1168
+ errorHandler: observer.error,
1169
+ maxInterval: 2000,
1170
+ });
1171
+
1172
+ const { sort } = options || {};
1173
+ const sortOptions = sort ? { sort } : undefined;
1174
+
1175
+ (async () => {
1176
+ try {
1177
+ // 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);
1196
+ }
1197
+
1198
+ const isSynced = this.sync.getModelSyncedStatus(model);
1199
+
1200
+ const limit =
1201
+ itemsChanged.size - deletedItemIds.length >= this.syncPageSize;
1202
+
1203
+ if (limit || isSynced) {
1204
+ limitTimerRace.resolve();
1205
+ }
1206
+
1207
+ // kicks off every subsequent race as results sync down
1208
+ limitTimerRace.start();
1209
+ });
1210
+
1211
+ // returns a set of initial/locally-available results
1212
+ generateAndEmitSnapshot();
1213
+ } catch (err) {
1214
+ observer.error(err);
1215
+ }
1216
+ })();
1217
+
1218
+ // TODO: abstract this function into a util file to be able to write better unit tests
1219
+ const generateSnapshot = (): DataStoreSnapshot<T> => {
1220
+ const isSynced = this.sync.getModelSyncedStatus(model);
1221
+ const itemsArray = [
1222
+ ...Array.from(items.values()),
1223
+ ...Array.from(itemsChanged.values()),
1224
+ ];
1225
+
1226
+ if (options?.sort) {
1227
+ sortItems(itemsArray);
1228
+ }
1229
+
1230
+ items.clear();
1231
+ itemsArray.forEach((item) => items.set(item.id, item));
1232
+
1233
+ // remove deleted items from the final result set
1234
+ deletedItemIds.forEach((id) => items.delete(id));
1235
+
1236
+ return {
1237
+ items: Array.from(items.values()),
1238
+ isSynced,
1239
+ };
1240
+ };
1241
+
1242
+ const emitSnapshot = (snapshot: DataStoreSnapshot<T>): void => {
1243
+ // send the generated snapshot to the primary subscription
1244
+ observer.next(snapshot);
1245
+
1246
+ // reset the changed items sets
1247
+ itemsChanged.clear();
1248
+ deletedItemIds = [];
1249
+ };
1250
+
1251
+ const sortItems = (itemsToSort: T[]): void => {
1252
+ const modelDefinition = getModelDefinition(model);
1253
+ const pagination = this.processPagination(modelDefinition, options);
1254
+
1255
+ const sortPredicates = ModelSortPredicateCreator.getPredicates(
1256
+ pagination.sort
1257
+ );
1258
+
1259
+ if (sortPredicates.length) {
1260
+ const compareFn = sortCompareFunction(sortPredicates);
1261
+ itemsToSort.sort(compareFn);
1262
+ }
1263
+ };
1264
+
1265
+ // send one last snapshot when the model is fully synced
1266
+ const hubCallback = ({ payload }): void => {
1267
+ const { event, data } = payload;
1268
+ if (
1269
+ event === ControlMessage.SYNC_ENGINE_MODEL_SYNCED &&
1270
+ data?.model?.name === model.name
1271
+ ) {
1272
+ generateAndEmitSnapshot();
1273
+ Hub.remove('api', hubCallback);
1274
+ }
1275
+ };
1276
+ Hub.listen('datastore', hubCallback);
1277
+
1278
+ return () => {
1279
+ if (handle) {
1280
+ handle.unsubscribe();
1281
+ }
1282
+ };
1283
+ });
1284
+ };
1285
+
1149
1286
  configure = (config: DataStoreConfig = {}) => {
1150
1287
  const {
1151
1288
  DataStore: configDataStore,
@@ -1161,7 +1298,10 @@ class DataStore {
1161
1298
  ...configFromAmplify
1162
1299
  } = config;
1163
1300
 
1164
- this.amplifyConfig = { ...configFromAmplify, ...this.amplifyConfig };
1301
+ this.amplifyConfig = {
1302
+ ...configFromAmplify,
1303
+ ...this.amplifyConfig,
1304
+ };
1165
1305
 
1166
1306
  this.conflictHandler = this.setConflictHandler(config);
1167
1307
  this.errorHandler = this.setErrorHandler(config);
@@ -1194,13 +1334,19 @@ class DataStore {
1194
1334
 
1195
1335
  this.maxRecordsToSync =
1196
1336
  (configDataStore && configDataStore.maxRecordsToSync) ||
1197
- this.maxRecordsToSync ||
1198
- configMaxRecordsToSync;
1337
+ configMaxRecordsToSync ||
1338
+ 10000;
1339
+
1340
+ // store on config object, so that Sync, Subscription, and Mutation processors can have access
1341
+ this.amplifyConfig.maxRecordsToSync = this.maxRecordsToSync;
1199
1342
 
1200
1343
  this.syncPageSize =
1201
1344
  (configDataStore && configDataStore.syncPageSize) ||
1202
- this.syncPageSize ||
1203
- configSyncPageSize;
1345
+ configSyncPageSize ||
1346
+ 1000;
1347
+
1348
+ // store on config object, so that Sync, Subscription, and Mutation processors can have access
1349
+ this.amplifyConfig.syncPageSize = this.syncPageSize;
1204
1350
 
1205
1351
  this.fullSyncInterval =
1206
1352
  (configDataStore && configDataStore.fullSyncInterval) ||
package/src/sync/index.ts CHANGED
@@ -22,7 +22,7 @@ import {
22
22
  ModelPredicate,
23
23
  AuthModeStrategy,
24
24
  } from '../types';
25
- import { exhaustiveCheck, getNow, SYNC } from '../util';
25
+ import { exhaustiveCheck, getNow, SYNC, USER } from '../util';
26
26
  import DataStoreConnectivity from './datastoreConnectivity';
27
27
  import { ModelMerger } from './merger';
28
28
  import { MutationEventOutbox } from './outbox';
@@ -95,6 +95,16 @@ export class SyncEngine {
95
95
  private readonly modelMerger: ModelMerger;
96
96
  private readonly outbox: MutationEventOutbox;
97
97
  private readonly datastoreConnectivity: DataStoreConnectivity;
98
+ private readonly modelSyncedStatus: WeakMap<
99
+ PersistentModelConstructor<any>,
100
+ boolean
101
+ > = new WeakMap();
102
+
103
+ public getModelSyncedStatus(
104
+ modelConstructor: PersistentModelConstructor<any>
105
+ ): boolean {
106
+ return this.modelSyncedStatus.get(modelConstructor);
107
+ }
98
108
 
99
109
  constructor(
100
110
  private readonly schema: InternalSchema,
@@ -126,8 +136,6 @@ export class SyncEngine {
126
136
 
127
137
  this.syncQueriesProcessor = new SyncProcessor(
128
138
  this.schema,
129
- this.maxRecordsToSync,
130
- this.syncPageSize,
131
139
  this.syncPredicates,
132
140
  this.amplifyConfig,
133
141
  this.authModeStrategy
@@ -615,6 +623,8 @@ export class SyncEngine {
615
623
 
616
624
  const counts = count.get(modelConstructor);
617
625
 
626
+ this.modelSyncedStatus.set(modelConstructor, true);
627
+
618
628
  observer.next({
619
629
  type: ControlMessage.SYNC_ENGINE_MODEL_SYNCED,
620
630
  data: {
@@ -708,6 +718,12 @@ export class SyncEngine {
708
718
  .filter(({ syncable }) => syncable)
709
719
  .forEach(model => {
710
720
  models.push([namespace.name, model]);
721
+ if (namespace.name === USER) {
722
+ const modelConstructor = this.userModelClasses[
723
+ model.name
724
+ ] as PersistentModelConstructor<any>;
725
+ this.modelSyncedStatus.set(modelConstructor, false);
726
+ }
711
727
  });
712
728
  });
713
729
 
@@ -533,7 +533,7 @@ class SubscriptionProcessor {
533
533
  })();
534
534
 
535
535
  return () => {
536
- Object.keys(subscriptions).map(modelName => {
536
+ Object.keys(subscriptions).forEach(modelName => {
537
537
  subscriptions[modelName][
538
538
  TransformerMutationType.CREATE
539
539
  ].forEach(subscription => subscription.unsubscribe());
@@ -25,9 +25,6 @@ import {
25
25
  } from '@aws-amplify/core';
26
26
  import { ModelPredicateCreator } from '../../predicates';
27
27
 
28
- const DEFAULT_PAGINATION_LIMIT = 1000;
29
- const DEFAULT_MAX_RECORDS_TO_SYNC = 10000;
30
-
31
28
  const opResultDefaults = {
32
29
  items: [],
33
30
  nextToken: null,
@@ -41,8 +38,6 @@ class SyncProcessor {
41
38
 
42
39
  constructor(
43
40
  private readonly schema: InternalSchema,
44
- private readonly maxRecordsToSync: number = DEFAULT_MAX_RECORDS_TO_SYNC,
45
- private readonly syncPageSize: number = DEFAULT_PAGINATION_LIMIT,
46
41
  private readonly syncPredicates: WeakMap<SchemaModel, ModelPredicate<any>>,
47
42
  private readonly amplifyConfig: Record<string, any> = {},
48
43
  private readonly authModeStrategy: AuthModeStrategy
@@ -289,19 +284,8 @@ class SyncProcessor {
289
284
  typesLastSync: Map<SchemaModel, [string, number]>
290
285
  ): Observable<SyncModelPage> {
291
286
  let processing = true;
292
-
293
- const maxRecordsToSync =
294
- this.maxRecordsToSync !== undefined
295
- ? this.maxRecordsToSync
296
- : DEFAULT_MAX_RECORDS_TO_SYNC;
297
-
298
- const syncPageSize =
299
- this.syncPageSize !== undefined
300
- ? this.syncPageSize
301
- : DEFAULT_PAGINATION_LIMIT;
302
-
287
+ const { maxRecordsToSync, syncPageSize } = this.amplifyConfig;
303
288
  const parentPromises = new Map<string, Promise<void>>();
304
-
305
289
  const observable = new Observable<SyncModelPage>(observer => {
306
290
  const sortedTypesLastSyncs = Object.values(this.schema.namespaces).reduce(
307
291
  (map, namespace) => {