@aws-amplify/datastore 4.0.17 → 4.0.18-unstable-v5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/lib/datastore/datastore.d.ts +29 -2
  2. package/lib/datastore/datastore.js +129 -45
  3. package/lib/datastore/datastore.js.map +1 -1
  4. package/lib/predicates/index.d.ts +48 -3
  5. package/lib/predicates/index.js +91 -51
  6. package/lib/predicates/index.js.map +1 -1
  7. package/lib/predicates/next.d.ts +0 -6
  8. package/lib/predicates/next.js +0 -6
  9. package/lib/predicates/next.js.map +1 -1
  10. package/lib/sync/processors/subscription.d.ts +6 -0
  11. package/lib/sync/processors/subscription.js +195 -135
  12. package/lib/sync/processors/subscription.js.map +1 -1
  13. package/lib/sync/utils.d.ts +65 -2
  14. package/lib/sync/utils.js +260 -16
  15. package/lib/sync/utils.js.map +1 -1
  16. package/lib/types.d.ts +1 -0
  17. package/lib/types.js.map +1 -1
  18. package/lib-esm/datastore/datastore.d.ts +29 -2
  19. package/lib-esm/datastore/datastore.js +130 -46
  20. package/lib-esm/datastore/datastore.js.map +1 -1
  21. package/lib-esm/predicates/index.d.ts +48 -3
  22. package/lib-esm/predicates/index.js +92 -52
  23. package/lib-esm/predicates/index.js.map +1 -1
  24. package/lib-esm/predicates/next.d.ts +0 -6
  25. package/lib-esm/predicates/next.js +0 -6
  26. package/lib-esm/predicates/next.js.map +1 -1
  27. package/lib-esm/sync/processors/subscription.d.ts +6 -0
  28. package/lib-esm/sync/processors/subscription.js +196 -136
  29. package/lib-esm/sync/processors/subscription.js.map +1 -1
  30. package/lib-esm/sync/utils.d.ts +65 -2
  31. package/lib-esm/sync/utils.js +257 -18
  32. package/lib-esm/sync/utils.js.map +1 -1
  33. package/lib-esm/types.d.ts +1 -0
  34. package/lib-esm/types.js.map +1 -1
  35. package/package.json +8 -8
  36. package/src/datastore/datastore.ts +110 -62
  37. package/src/predicates/index.ts +92 -43
  38. package/src/predicates/next.ts +0 -6
  39. package/src/sync/processors/subscription.ts +118 -9
  40. package/src/sync/utils.ts +281 -15
  41. package/src/types.ts +1 -0
@@ -54,12 +54,39 @@ declare const initSchema: (userSchema: Schema) => Record<string, PersistentModel
54
54
  */
55
55
  export declare type ModelInstanceCreator = typeof modelInstanceCreator;
56
56
  declare function modelInstanceCreator<T extends PersistentModel>(modelConstructor: PersistentModelConstructor<T>, init: Partial<T>): T;
57
+ /**
58
+ * An eventually loaded related model instance.
59
+ */
57
60
  export declare class AsyncItem<T> extends Promise<T> {
58
61
  }
62
+ /**
63
+ * A collection of related model instances.
64
+ *
65
+ * This collection can be async-iterated or turned directly into an array using `toArray()`.
66
+ */
59
67
  export declare class AsyncCollection<T> implements AsyncIterable<T> {
60
68
  private values;
61
69
  constructor(values: Array<any> | Promise<Array<any>>);
70
+ /**
71
+ * Facilitates async iteration.
72
+ *
73
+ * ```ts
74
+ * for await (const item of collection) {
75
+ * handle(item)
76
+ * }
77
+ * ```
78
+ *
79
+ * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of
80
+ */
62
81
  [Symbol.asyncIterator](): AsyncIterator<T>;
82
+ /**
83
+ * Turns the collection into an array, up to the amount specified in `max` param.
84
+ *
85
+ * ```ts
86
+ * const all = await collection.toArray();
87
+ * const first100 = await collection.toArray({max: 100});
88
+ * ```
89
+ */
63
90
  toArray({ max, }?: {
64
91
  max?: number;
65
92
  }): Promise<T[]>;
@@ -137,8 +164,8 @@ declare class DataStore {
137
164
  /**
138
165
  * If not already done:
139
166
  * 1. Attaches and initializes storage.
140
- * 1. Loads the schema and records metadata.
141
- * 1. If `this.amplifyConfig.aws_appsync_graphqlEndpoint` contains a URL,
167
+ * 2. Loads the schema and records metadata.
168
+ * 3. If `this.amplifyConfig.aws_appsync_graphqlEndpoint` contains a URL,
142
169
  * attaches a sync engine, starts it, and subscribes.
143
170
  */
144
171
  start: () => Promise<void>;
@@ -25,9 +25,12 @@ var isNode = core_1.browserOrNode().isNode;
25
25
  var SETTING_SCHEMA_VERSION = 'schemaVersion';
26
26
  var schema;
27
27
  var modelNamespaceMap = new WeakMap();
28
- // stores data for crafting the correct update mutation input for a model
29
- // Patch[] - array of changed fields and metadata
30
- // PersistentModel - the source model, used for diffing object-type fields
28
+ /**
29
+ * Stores data for crafting the correct update mutation input for a model.
30
+ *
31
+ * - `Patch[]` - array of changed fields and metadata.
32
+ * - `PersistentModel` - the source model, used for diffing object-type fields.
33
+ */
31
34
  var modelPatchesMap = new WeakMap();
32
35
  var getModelDefinition = function (modelConstructor) {
33
36
  var namespace = modelNamespaceMap.get(modelConstructor);
@@ -36,11 +39,6 @@ var getModelDefinition = function (modelConstructor) {
36
39
  : undefined;
37
40
  return definition;
38
41
  };
39
- var getModelPKFieldName = function (modelConstructor) {
40
- var _a, _b, _c;
41
- var namespace = modelNamespaceMap.get(modelConstructor);
42
- return ((namespace && ((_c = (_b = (_a = schema.namespaces) === null || _a === void 0 ? void 0 : _a[namespace]) === null || _b === void 0 ? void 0 : _b.keys) === null || _c === void 0 ? void 0 : _c[modelConstructor.name].primaryKey)) || ['id']);
43
- };
44
42
  /**
45
43
  * Determine what the managed timestamp field names are for the given model definition
46
44
  * and return the mapping.
@@ -64,13 +62,14 @@ var getTimestampFields = function (definition) {
64
62
  var customFields = timestampFieldsMap || {};
65
63
  return tslib_1.__assign(tslib_1.__assign({}, defaultFields), customFields);
66
64
  };
65
+ /**
66
+ * Determines whether the given object is a Model Constructor that DataStore can
67
+ * safely use to construct objects and discover related metadata.
68
+ *
69
+ * @param obj The object to test.
70
+ */
67
71
  var isValidModelConstructor = function (obj) {
68
- if (util_1.isModelConstructor(obj) && modelNamespaceMap.has(obj)) {
69
- return true;
70
- }
71
- else {
72
- return false;
73
- }
72
+ return util_1.isModelConstructor(obj) && modelNamespaceMap.has(obj);
74
73
  };
75
74
  var namespaceResolver = function (modelConstructor) {
76
75
  var resolver = modelNamespaceMap.get(modelConstructor);
@@ -79,13 +78,32 @@ var namespaceResolver = function (modelConstructor) {
79
78
  }
80
79
  return resolver;
81
80
  };
81
+ /**
82
+ * Creates a predicate without any conditions that can be passed to customer
83
+ * code to have conditions added to it.
84
+ *
85
+ * For example, in this query:
86
+ *
87
+ * ```ts
88
+ * await DataStore.query(
89
+ * Model,
90
+ * item => item.field.eq('value')
91
+ * );
92
+ * ```
93
+ *
94
+ * `buildSeedPredicate(Model)` is used to create `item`, which is passed to the
95
+ * predicate function, which in turn uses that "seed" predicate (`item`) to build
96
+ * a predicate tree.
97
+ *
98
+ * @param modelConstructor The model the predicate will query.
99
+ */
82
100
  var buildSeedPredicate = function (modelConstructor) {
83
101
  if (!modelConstructor)
84
102
  throw new Error('Missing modelConstructor');
85
103
  var modelSchema = getModelDefinition(modelConstructor);
86
104
  if (!modelSchema)
87
105
  throw new Error('Missing modelSchema');
88
- var pks = getModelPKFieldName(modelConstructor);
106
+ var pks = util_1.extractPrimaryKeyFieldNames(modelSchema);
89
107
  if (!pks)
90
108
  throw new Error('Could not determine PK');
91
109
  return next_1.recursivePredicateFor({
@@ -209,15 +227,21 @@ var initSchema = function (userSchema) {
209
227
  return connectedModels.push(field.type.model);
210
228
  });
211
229
  modelAssociations.set(model.name, connectedModels);
230
+ // Precompute model info (such as pk fields) so that downstream schema consumers
231
+ // (such as predicate builders) don't have to reach back into "DataStore" space
232
+ // to go looking for it.
212
233
  Object.values(model.fields).forEach(function (field) {
213
- if (typeof field.type === 'object' &&
214
- !Object.getOwnPropertyDescriptor(field.type, 'modelConstructor')) {
234
+ var relatedModel = userClasses[field.type.model];
235
+ if (util_1.isModelConstructor(relatedModel)) {
215
236
  Object.defineProperty(field.type, 'modelConstructor', {
216
237
  get: function () {
238
+ var relatedModelDefinition = getModelDefinition(relatedModel);
239
+ if (!relatedModelDefinition)
240
+ throw new Error("Could not find model definition for " + relatedModel.name);
217
241
  return {
218
- builder: userClasses[field.type.model],
219
- schema: schema.namespaces[namespace].models[field.type.model],
220
- pkField: getModelPKFieldName(userClasses[field.type.model]),
242
+ builder: relatedModel,
243
+ schema: relatedModelDefinition,
244
+ pkField: util_1.extractPrimaryKeyFieldNames(relatedModelDefinition),
221
245
  };
222
246
  },
223
247
  });
@@ -570,6 +594,7 @@ var normalize = function (modelDefinition, draft) {
570
594
  }
571
595
  };
572
596
  var createModelClass = function (modelDefinition) {
597
+ var e_5, _a;
573
598
  var clazz = /** @class */ (function () {
574
599
  function Model(init) {
575
600
  var instance = immer_1.produce(this, function (draft) {
@@ -666,16 +691,16 @@ var createModelClass = function (modelDefinition) {
666
691
  }());
667
692
  clazz[immer_1.immerable] = true;
668
693
  Object.defineProperty(clazz, 'name', { value: modelDefinition.name });
669
- var _loop_1 = function (field) {
670
- if (!types_1.isFieldAssociation(modelDefinition, field)) {
671
- return "continue";
672
- }
673
- var _a = modelDefinition.fields[field], type = _a.type, localAssociation = _a.association, _b = _a.association, targetName = _b.targetName, targetNames = _b.targetNames;
674
- var relationship = new relationship_1.ModelRelationship({
675
- builder: clazz,
676
- schema: modelDefinition,
677
- pkField: util_1.extractPrimaryKeyFieldNames(modelDefinition),
678
- }, field);
694
+ // Add getters/setters for relationship fields.
695
+ // getter - for lazy loading
696
+ // setter - for FK management
697
+ var allModelRelationships = relationship_1.ModelRelationship.allFrom({
698
+ builder: clazz,
699
+ schema: modelDefinition,
700
+ pkField: util_1.extractPrimaryKeyFieldNames(modelDefinition),
701
+ });
702
+ var _loop_1 = function (relationship) {
703
+ var field = relationship.field;
679
704
  Object.defineProperty(clazz.prototype, modelDefinition.fields[field].name, {
680
705
  set: function (model) {
681
706
  if (!(typeof model === 'object' || typeof model === 'undefined'))
@@ -701,6 +726,7 @@ var createModelClass = function (modelDefinition) {
701
726
  }
702
727
  }
703
728
  }
729
+ // if the relationship can be managed automagically, set the FK's
704
730
  if (relationship.isComplete) {
705
731
  for (var i = 0; i < relationship.localJoinFields.length; i++) {
706
732
  this[relationship.localJoinFields[i]] = model === null || model === void 0 ? void 0 : model[relationship.remoteJoinFields[i]];
@@ -713,22 +739,41 @@ var createModelClass = function (modelDefinition) {
713
739
  },
714
740
  get: function () {
715
741
  var _this = this;
742
+ /**
743
+ * Bucket for holding related models instances specific to `this` instance.
744
+ */
716
745
  var instanceMemos = modelInstanceAssociationsMap.has(this)
717
746
  ? modelInstanceAssociationsMap.get(this)
718
747
  : modelInstanceAssociationsMap.set(this, {}).get(this);
748
+ // if the memos already has a result for this field, we'll use it.
749
+ // there is no "cache" invalidation of any kind; memos are permanent to
750
+ // keep an immutable perception of the instance.
719
751
  if (!instanceMemos.hasOwnProperty(field)) {
752
+ // before we populate the memo, we need to know where to look for relatives.
753
+ // today, this only supports DataStore. Models aren't managed elsewhere in Amplify.
720
754
  if (exports.getAttachment(this) === ModelAttachment.DataStore) {
755
+ // when we fetch the results using a query constructed under the guidance
756
+ // of the relationship metadata, we DO NOT AWAIT resolution. we want to
757
+ // drop the promise into the memo's synchronously, eliminating the chance
758
+ // for a race.
721
759
  var resultPromise = instance.query(relationship.remoteModelConstructor, function (base) {
722
760
  return base.and(function (q) {
723
761
  return relationship.remoteJoinFields.map(function (field, index) {
762
+ // TODO: anything we can use instead of `any` here?
724
763
  return q[field].eq(_this[relationship.localJoinFields[index]]);
725
764
  });
726
765
  });
727
766
  });
767
+ // results in hand, how we return them to the caller depends on the relationship type.
728
768
  if (relationship.type === 'HAS_MANY') {
769
+ // collections should support async iteration, even though we don't
770
+ // leverage it fully [yet].
729
771
  instanceMemos[field] = new AsyncCollection(resultPromise);
730
772
  }
731
773
  else {
774
+ // non-collections should only ever return 1 value *or nothing*.
775
+ // if we have more than 1 record, something's amiss. it's not our job
776
+ // pick a result for the customer. it's our job to say "something's wrong."
732
777
  instanceMemos[field] = resultPromise.then(function (rows) {
733
778
  if (rows.length > 1) {
734
779
  // should never happen for a HAS_ONE or BELONGS_TO.
@@ -758,11 +803,24 @@ var createModelClass = function (modelDefinition) {
758
803
  },
759
804
  });
760
805
  };
761
- for (var field in modelDefinition.fields) {
762
- _loop_1(field);
806
+ try {
807
+ for (var allModelRelationships_1 = tslib_1.__values(allModelRelationships), allModelRelationships_1_1 = allModelRelationships_1.next(); !allModelRelationships_1_1.done; allModelRelationships_1_1 = allModelRelationships_1.next()) {
808
+ var relationship = allModelRelationships_1_1.value;
809
+ _loop_1(relationship);
810
+ }
811
+ }
812
+ catch (e_5_1) { e_5 = { error: e_5_1 }; }
813
+ finally {
814
+ try {
815
+ if (allModelRelationships_1_1 && !allModelRelationships_1_1.done && (_a = allModelRelationships_1.return)) _a.call(allModelRelationships_1);
816
+ }
817
+ finally { if (e_5) throw e_5.error; }
763
818
  }
764
819
  return clazz;
765
820
  };
821
+ /**
822
+ * An eventually loaded related model instance.
823
+ */
766
824
  var AsyncItem = /** @class */ (function (_super) {
767
825
  tslib_1.__extends(AsyncItem, _super);
768
826
  function AsyncItem() {
@@ -771,10 +829,26 @@ var AsyncItem = /** @class */ (function (_super) {
771
829
  return AsyncItem;
772
830
  }(Promise));
773
831
  exports.AsyncItem = AsyncItem;
832
+ /**
833
+ * A collection of related model instances.
834
+ *
835
+ * This collection can be async-iterated or turned directly into an array using `toArray()`.
836
+ */
774
837
  var AsyncCollection = /** @class */ (function () {
775
838
  function AsyncCollection(values) {
776
839
  this.values = values;
777
840
  }
841
+ /**
842
+ * Facilitates async iteration.
843
+ *
844
+ * ```ts
845
+ * for await (const item of collection) {
846
+ * handle(item)
847
+ * }
848
+ * ```
849
+ *
850
+ * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of
851
+ */
778
852
  AsyncCollection.prototype[Symbol.asyncIterator] = function () {
779
853
  var _this = this;
780
854
  var values;
@@ -808,11 +882,19 @@ var AsyncCollection = /** @class */ (function () {
808
882
  }); },
809
883
  };
810
884
  };
885
+ /**
886
+ * Turns the collection into an array, up to the amount specified in `max` param.
887
+ *
888
+ * ```ts
889
+ * const all = await collection.toArray();
890
+ * const first100 = await collection.toArray({max: 100});
891
+ * ```
892
+ */
811
893
  AsyncCollection.prototype.toArray = function (_a) {
812
894
  var _b = (_a === void 0 ? {} : _a).max, max = _b === void 0 ? Number.MAX_SAFE_INTEGER : _b;
813
- var e_5, _c;
895
+ var e_6, _c;
814
896
  return tslib_1.__awaiter(this, void 0, void 0, function () {
815
- var output, i, _d, _e, element, e_5_1;
897
+ var output, i, _d, _e, element, e_6_1;
816
898
  return tslib_1.__generator(this, function (_f) {
817
899
  switch (_f.label) {
818
900
  case 0:
@@ -838,8 +920,8 @@ var AsyncCollection = /** @class */ (function () {
838
920
  case 4: return [3 /*break*/, 2];
839
921
  case 5: return [3 /*break*/, 12];
840
922
  case 6:
841
- e_5_1 = _f.sent();
842
- e_5 = { error: e_5_1 };
923
+ e_6_1 = _f.sent();
924
+ e_6 = { error: e_6_1 };
843
925
  return [3 /*break*/, 12];
844
926
  case 7:
845
927
  _f.trys.push([7, , 10, 11]);
@@ -850,7 +932,7 @@ var AsyncCollection = /** @class */ (function () {
850
932
  _f.label = 9;
851
933
  case 9: return [3 /*break*/, 11];
852
934
  case 10:
853
- if (e_5) throw e_5.error;
935
+ if (e_6) throw e_6.error;
854
936
  return [7 /*endfinally*/];
855
937
  case 11: return [7 /*endfinally*/];
856
938
  case 12: return [2 /*return*/, output];
@@ -1037,6 +1119,8 @@ var DataStoreState;
1037
1119
  DataStoreState["Stopping"] = "Stopping";
1038
1120
  DataStoreState["Clearing"] = "Clearing";
1039
1121
  })(DataStoreState || (DataStoreState = {}));
1122
+ // TODO: How can we get rid of the non-null assertions?
1123
+ // https://github.com/aws-amplify/amplify-js/pull/10477/files#r1007363485
1040
1124
  var DataStore = /** @class */ (function () {
1041
1125
  function DataStore() {
1042
1126
  var _this = this;
@@ -1100,8 +1184,8 @@ var DataStore = /** @class */ (function () {
1100
1184
  /**
1101
1185
  * If not already done:
1102
1186
  * 1. Attaches and initializes storage.
1103
- * 1. Loads the schema and records metadata.
1104
- * 1. If `this.amplifyConfig.aws_appsync_graphqlEndpoint` contains a URL,
1187
+ * 2. Loads the schema and records metadata.
1188
+ * 3. If `this.amplifyConfig.aws_appsync_graphqlEndpoint` contains a URL,
1105
1189
  * attaches a sync engine, starts it, and subscribes.
1106
1190
  */
1107
1191
  this.start = function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
@@ -1246,7 +1330,7 @@ var DataStore = /** @class */ (function () {
1246
1330
  seedPredicate = next_1.recursivePredicateFor({
1247
1331
  builder: modelConstructor,
1248
1332
  schema: modelDefinition,
1249
- pkField: getModelPKFieldName(modelConstructor),
1333
+ pkField: util_1.extractPrimaryKeyFieldNames(modelDefinition),
1250
1334
  });
1251
1335
  predicate = next_1.internals(identifierOrCriteria(seedPredicate));
1252
1336
  return [4 /*yield*/, predicate.fetch(this.storage)];
@@ -1296,8 +1380,8 @@ var DataStore = /** @class */ (function () {
1296
1380
  pkField: util_1.extractPrimaryKeyFieldNames(modelDefinition),
1297
1381
  };
1298
1382
  return [4 /*yield*/, this.storage.runExclusive(function (s) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
1299
- var nonHasManyRelationships, nonHasManyRelationships_1, nonHasManyRelationships_1_1, relationship, queryObject, related, e_6_1;
1300
- var e_6, _a;
1383
+ var nonHasManyRelationships, nonHasManyRelationships_1, nonHasManyRelationships_1_1, relationship, queryObject, related, e_7_1;
1384
+ var e_7, _a;
1301
1385
  var _b;
1302
1386
  return tslib_1.__generator(this, function (_c) {
1303
1387
  switch (_c.label) {
@@ -1330,14 +1414,14 @@ var DataStore = /** @class */ (function () {
1330
1414
  return [3 /*break*/, 2];
1331
1415
  case 5: return [3 /*break*/, 8];
1332
1416
  case 6:
1333
- e_6_1 = _c.sent();
1334
- e_6 = { error: e_6_1 };
1417
+ e_7_1 = _c.sent();
1418
+ e_7 = { error: e_7_1 };
1335
1419
  return [3 /*break*/, 8];
1336
1420
  case 7:
1337
1421
  try {
1338
1422
  if (nonHasManyRelationships_1_1 && !nonHasManyRelationships_1_1.done && (_a = nonHasManyRelationships_1.return)) _a.call(nonHasManyRelationships_1);
1339
1423
  }
1340
- finally { if (e_6) throw e_6.error; }
1424
+ finally { if (e_7) throw e_7.error; }
1341
1425
  return [7 /*endfinally*/];
1342
1426
  case 8: return [2 /*return*/];
1343
1427
  }