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

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 (144) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/lib/authModeStrategies/multiAuthStrategy.js +13 -2
  3. package/lib/authModeStrategies/multiAuthStrategy.js.map +1 -1
  4. package/lib/datastore/datastore.js +648 -344
  5. package/lib/datastore/datastore.js.map +1 -1
  6. package/lib/predicates/index.js +12 -2
  7. package/lib/predicates/index.js.map +1 -1
  8. package/lib/storage/adapter/AsyncStorageAdapter.js +354 -203
  9. package/lib/storage/adapter/AsyncStorageAdapter.js.map +1 -1
  10. package/lib/storage/adapter/AsyncStorageDatabase.js +65 -28
  11. package/lib/storage/adapter/AsyncStorageDatabase.js.map +1 -1
  12. package/lib/storage/adapter/IndexedDBAdapter.js +444 -271
  13. package/lib/storage/adapter/IndexedDBAdapter.js.map +1 -1
  14. package/lib/storage/storage.js +93 -28
  15. package/lib/storage/storage.js.map +1 -1
  16. package/lib/sync/datastoreConnectivity.js +9 -0
  17. package/lib/sync/datastoreConnectivity.js.map +1 -1
  18. package/lib/sync/index.js +522 -397
  19. package/lib/sync/index.js.map +1 -1
  20. package/lib/sync/merger.js +13 -6
  21. package/lib/sync/merger.js.map +1 -1
  22. package/lib/sync/outbox.js +77 -71
  23. package/lib/sync/outbox.js.map +1 -1
  24. package/lib/sync/processors/mutation.js +269 -209
  25. package/lib/sync/processors/mutation.js.map +1 -1
  26. package/lib/sync/processors/subscription.js +213 -178
  27. package/lib/sync/processors/subscription.js.map +1 -1
  28. package/lib/sync/processors/sync.js +126 -121
  29. package/lib/sync/processors/sync.js.map +1 -1
  30. package/lib/sync/utils.js +43 -8
  31. package/lib/sync/utils.js.map +1 -1
  32. package/lib/types.js +10 -1
  33. package/lib/types.js.map +1 -1
  34. package/lib/util.js +419 -166
  35. package/lib/util.js.map +1 -1
  36. package/lib-esm/authModeStrategies/multiAuthStrategy.d.ts +11 -0
  37. package/lib-esm/authModeStrategies/multiAuthStrategy.js +12 -1
  38. package/lib-esm/authModeStrategies/multiAuthStrategy.js.map +1 -1
  39. package/lib-esm/datastore/datastore.d.ts +107 -17
  40. package/lib-esm/datastore/datastore.js +648 -344
  41. package/lib-esm/datastore/datastore.js.map +1 -1
  42. package/lib-esm/index.d.ts +3 -19
  43. package/lib-esm/predicates/index.d.ts +3 -2
  44. package/lib-esm/predicates/index.js +13 -3
  45. package/lib-esm/predicates/index.js.map +1 -1
  46. package/lib-esm/storage/adapter/AsyncStorageAdapter.d.ts +4 -3
  47. package/lib-esm/storage/adapter/AsyncStorageAdapter.js +355 -204
  48. package/lib-esm/storage/adapter/AsyncStorageAdapter.js.map +1 -1
  49. package/lib-esm/storage/adapter/AsyncStorageDatabase.d.ts +14 -4
  50. package/lib-esm/storage/adapter/AsyncStorageDatabase.js +66 -29
  51. package/lib-esm/storage/adapter/AsyncStorageDatabase.js.map +1 -1
  52. package/lib-esm/storage/adapter/IndexedDBAdapter.d.ts +26 -4
  53. package/lib-esm/storage/adapter/IndexedDBAdapter.js +445 -272
  54. package/lib-esm/storage/adapter/IndexedDBAdapter.js.map +1 -1
  55. package/lib-esm/storage/adapter/index.d.ts +1 -1
  56. package/lib-esm/storage/storage.d.ts +1 -1
  57. package/lib-esm/storage/storage.js +93 -28
  58. package/lib-esm/storage/storage.js.map +1 -1
  59. package/lib-esm/sync/datastoreConnectivity.d.ts +1 -0
  60. package/lib-esm/sync/datastoreConnectivity.js +10 -1
  61. package/lib-esm/sync/datastoreConnectivity.js.map +1 -1
  62. package/lib-esm/sync/index.d.ts +31 -5
  63. package/lib-esm/sync/index.js +524 -399
  64. package/lib-esm/sync/index.js.map +1 -1
  65. package/lib-esm/sync/merger.d.ts +9 -3
  66. package/lib-esm/sync/merger.js +13 -6
  67. package/lib-esm/sync/merger.js.map +1 -1
  68. package/lib-esm/sync/outbox.d.ts +2 -2
  69. package/lib-esm/sync/outbox.js +78 -72
  70. package/lib-esm/sync/outbox.js.map +1 -1
  71. package/lib-esm/sync/processors/mutation.d.ts +2 -0
  72. package/lib-esm/sync/processors/mutation.js +270 -210
  73. package/lib-esm/sync/processors/mutation.js.map +1 -1
  74. package/lib-esm/sync/processors/subscription.d.ts +2 -0
  75. package/lib-esm/sync/processors/subscription.js +213 -178
  76. package/lib-esm/sync/processors/subscription.js.map +1 -1
  77. package/lib-esm/sync/processors/sync.d.ts +2 -1
  78. package/lib-esm/sync/processors/sync.js +126 -121
  79. package/lib-esm/sync/processors/sync.js.map +1 -1
  80. package/lib-esm/sync/utils.d.ts +3 -2
  81. package/lib-esm/sync/utils.js +45 -11
  82. package/lib-esm/sync/utils.js.map +1 -1
  83. package/lib-esm/types.d.ts +65 -26
  84. package/lib-esm/types.js +9 -2
  85. package/lib-esm/types.js.map +1 -1
  86. package/lib-esm/util.d.ts +67 -24
  87. package/lib-esm/util.js +419 -166
  88. package/lib-esm/util.js.map +1 -1
  89. package/package.json +13 -7
  90. package/src/authModeStrategies/multiAuthStrategy.ts +12 -1
  91. package/src/datastore/datastore.ts +798 -397
  92. package/src/predicates/index.ts +32 -10
  93. package/src/storage/adapter/AsyncStorageAdapter.ts +309 -93
  94. package/src/storage/adapter/AsyncStorageDatabase.ts +74 -26
  95. package/src/storage/adapter/IndexedDBAdapter.ts +358 -134
  96. package/src/storage/adapter/index.ts +1 -1
  97. package/src/storage/storage.ts +69 -22
  98. package/src/sync/datastoreConnectivity.ts +6 -0
  99. package/src/sync/index.ts +521 -412
  100. package/src/sync/merger.ts +20 -4
  101. package/src/sync/outbox.ts +22 -9
  102. package/src/sync/processors/mutation.ts +188 -150
  103. package/src/sync/processors/subscription.ts +289 -253
  104. package/src/sync/processors/sync.ts +151 -138
  105. package/src/sync/utils.ts +67 -12
  106. package/src/types.ts +182 -30
  107. package/src/util.ts +505 -176
  108. package/build.js +0 -5
  109. package/dist/aws-amplify-datastore.js +0 -83311
  110. package/dist/aws-amplify-datastore.js.map +0 -1
  111. package/dist/aws-amplify-datastore.min.js +0 -168
  112. package/dist/aws-amplify-datastore.min.js.map +0 -1
  113. package/index.js +0 -7
  114. package/lib/authModeStrategies/defaultAuthStrategy.d.ts +0 -2
  115. package/lib/authModeStrategies/index.d.ts +0 -2
  116. package/lib/authModeStrategies/multiAuthStrategy.d.ts +0 -2
  117. package/lib/datastore/datastore.d.ts +0 -66
  118. package/lib/index.d.ts +0 -31
  119. package/lib/predicates/index.d.ts +0 -15
  120. package/lib/predicates/sort.d.ts +0 -8
  121. package/lib/ssr/index.d.ts +0 -3
  122. package/lib/storage/adapter/AsyncStorageAdapter.d.ts +0 -40
  123. package/lib/storage/adapter/AsyncStorageDatabase.d.ts +0 -29
  124. package/lib/storage/adapter/InMemoryStore.d.ts +0 -11
  125. package/lib/storage/adapter/InMemoryStore.native.d.ts +0 -1
  126. package/lib/storage/adapter/IndexedDBAdapter.d.ts +0 -37
  127. package/lib/storage/adapter/getDefaultAdapter/index.d.ts +0 -3
  128. package/lib/storage/adapter/getDefaultAdapter/index.native.d.ts +0 -3
  129. package/lib/storage/adapter/index.d.ts +0 -9
  130. package/lib/storage/storage.d.ts +0 -49
  131. package/lib/sync/datastoreConnectivity.d.ts +0 -15
  132. package/lib/sync/datastoreReachability/index.d.ts +0 -3
  133. package/lib/sync/datastoreReachability/index.native.d.ts +0 -3
  134. package/lib/sync/index.d.ts +0 -63
  135. package/lib/sync/merger.d.ts +0 -11
  136. package/lib/sync/outbox.d.ts +0 -27
  137. package/lib/sync/processors/errorMaps.d.ts +0 -17
  138. package/lib/sync/processors/mutation.d.ts +0 -56
  139. package/lib/sync/processors/subscription.d.ts +0 -31
  140. package/lib/sync/processors/sync.d.ts +0 -27
  141. package/lib/sync/utils.d.ts +0 -41
  142. package/lib/types.d.ts +0 -462
  143. package/lib/util.d.ts +0 -113
  144. package/webpack.config.dev.js +0 -6
@@ -1,10 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  var tslib_1 = require("tslib");
4
- var api_1 = tslib_1.__importDefault(require("@aws-amplify/api"));
5
- var core_1 = require("@aws-amplify/core");
4
+ var api_1 = require("@aws-amplify/api");
6
5
  var auth_1 = require("@aws-amplify/auth");
7
- var cache_1 = tslib_1.__importDefault(require("@aws-amplify/cache"));
6
+ var cache_1 = require("@aws-amplify/cache");
7
+ var core_1 = require("@aws-amplify/core");
8
8
  var immer_1 = require("immer");
9
9
  var uuid_1 = require("uuid");
10
10
  var zen_observable_ts_1 = tslib_1.__importDefault(require("zen-observable-ts"));
@@ -14,11 +14,12 @@ var storage_1 = require("../storage/storage");
14
14
  var sync_1 = require("../sync");
15
15
  var types_1 = require("../types");
16
16
  var util_1 = require("../util");
17
+ var utils_1 = require("../sync/utils");
17
18
  immer_1.setAutoFreeze(true);
18
19
  immer_1.enablePatches();
19
20
  var logger = new core_1.ConsoleLogger('DataStore');
20
21
  var ulid = util_1.monotonicUlidFactory(Date.now());
21
- var isNode = core_1.JS.browserOrNode().isNode;
22
+ var isNode = core_1.browserOrNode().isNode;
22
23
  var SETTING_SCHEMA_VERSION = 'schemaVersion';
23
24
  var schema;
24
25
  var modelNamespaceMap = new WeakMap();
@@ -117,10 +118,14 @@ var initSchema = function (userSchema) {
117
118
  return userClasses;
118
119
  };
119
120
  exports.initSchema = initSchema;
120
- /* Checks if the schema has been initialized by initSchema().
121
+ /**
122
+ * Throws an exception if the schema has *not* been initialized
123
+ * by `initSchema()`.
124
+ *
125
+ * **To be called before trying to access schema.**
121
126
  *
122
- * Call this function before accessing schema.
123
- * Currently this only needs to be called in start() and clear() because all other functions will call start first.
127
+ * Currently this only needs to be called in `start()` and `clear()` because
128
+ * all other functions will call start first.
124
129
  */
125
130
  var checkSchemaInitialized = function () {
126
131
  if (schema === undefined) {
@@ -144,6 +149,11 @@ var createTypeClasses = function (namespace) {
144
149
  });
145
150
  return classes;
146
151
  };
152
+ /**
153
+ * Collection of instantiated models to allow storage of metadata apart from
154
+ * the model visible to the consuming app -- in case the app doesn't have
155
+ * metadata fields (_version, _deleted, etc.) exposed on the model itself.
156
+ */
147
157
  var instancesMetadata = new WeakSet();
148
158
  function modelInstanceCreator(modelConstructor, init) {
149
159
  instancesMetadata.add(init);
@@ -152,15 +162,23 @@ function modelInstanceCreator(modelConstructor, init) {
152
162
  var validateModelFields = function (modelDefinition) { return function (k, v) {
153
163
  var fieldDefinition = modelDefinition.fields[k];
154
164
  if (fieldDefinition !== undefined) {
155
- var type = fieldDefinition.type, isRequired_1 = fieldDefinition.isRequired, isArrayNullable = fieldDefinition.isArrayNullable, name_1 = fieldDefinition.name, isArray = fieldDefinition.isArray;
165
+ var type_1 = fieldDefinition.type, isRequired_1 = fieldDefinition.isRequired, isArrayNullable = fieldDefinition.isArrayNullable, name_1 = fieldDefinition.name, isArray = fieldDefinition.isArray;
156
166
  if (((!isArray && isRequired_1) || (isArray && !isArrayNullable)) &&
157
167
  (v === null || v === undefined)) {
158
168
  throw new Error("Field " + name_1 + " is required");
159
169
  }
160
- if (types_1.isGraphQLScalarType(type)) {
161
- var jsType_1 = types_1.GraphQLScalarType.getJSType(type);
162
- var validateScalar_1 = types_1.GraphQLScalarType.getValidationFunction(type);
163
- if (type === 'AWSJSON') {
170
+ if (types_1.isSchemaModelWithAttributes(modelDefinition) &&
171
+ !util_1.isIdManaged(modelDefinition)) {
172
+ var keys = util_1.extractPrimaryKeyFieldNames(modelDefinition);
173
+ if (keys.includes(k) && v === '') {
174
+ logger.error(util_1.errorMessages.idEmptyString, { k: k, value: v });
175
+ throw new Error(util_1.errorMessages.idEmptyString);
176
+ }
177
+ }
178
+ if (types_1.isGraphQLScalarType(type_1)) {
179
+ var jsType_1 = types_1.GraphQLScalarType.getJSType(type_1);
180
+ var validateScalar_1 = types_1.GraphQLScalarType.getValidationFunction(type_1);
181
+ if (type_1 === 'AWSJSON') {
164
182
  if (typeof v === jsType_1) {
165
183
  return;
166
184
  }
@@ -204,7 +222,7 @@ var validateModelFields = function (modelDefinition) { return function (k, v) {
204
222
  }
205
223
  });
206
224
  if (!validationStatus.every(function (s) { return s; })) {
207
- throw new Error("All elements in the " + name_1 + " array should be of type " + type + ", validation failed for one or more elements. " + v);
225
+ throw new Error("All elements in the " + name_1 + " array should be of type " + type_1 + ", validation failed for one or more elements. " + v);
208
226
  }
209
227
  }
210
228
  }
@@ -217,7 +235,42 @@ var validateModelFields = function (modelDefinition) { return function (k, v) {
217
235
  else if (!util_1.isNullOrUndefined(v) &&
218
236
  validateScalar_1 &&
219
237
  !validateScalar_1(v)) {
220
- throw new Error("Field " + name_1 + " should be of type " + type + ", validation failed. " + v);
238
+ throw new Error("Field " + name_1 + " should be of type " + type_1 + ", validation failed. " + v);
239
+ }
240
+ }
241
+ else if (types_1.isNonModelFieldType(type_1)) {
242
+ // do not check non model fields if undefined or null
243
+ if (!util_1.isNullOrUndefined(v)) {
244
+ var subNonModelDefinition_1 = schema.namespaces.user.nonModels[type_1.nonModel];
245
+ var modelValidator_1 = validateModelFields(subNonModelDefinition_1);
246
+ if (isArray) {
247
+ var errorTypeText = type_1.nonModel;
248
+ if (!isRequired_1) {
249
+ errorTypeText = type_1.nonModel + " | null | undefined";
250
+ }
251
+ if (!Array.isArray(v)) {
252
+ throw new Error("Field " + name_1 + " should be of type [" + errorTypeText + "], " + typeof v + " received. " + v);
253
+ }
254
+ v.forEach(function (item) {
255
+ if ((util_1.isNullOrUndefined(item) && isRequired_1) ||
256
+ (typeof item !== 'object' && typeof item !== 'undefined')) {
257
+ throw new Error("All elements in the " + name_1 + " array should be of type " + type_1.nonModel + ", [" + typeof item + "] received. " + item);
258
+ }
259
+ if (!util_1.isNullOrUndefined(item)) {
260
+ Object.keys(subNonModelDefinition_1.fields).forEach(function (subKey) {
261
+ modelValidator_1(subKey, item[subKey]);
262
+ });
263
+ }
264
+ });
265
+ }
266
+ else {
267
+ if (typeof v !== 'object') {
268
+ throw new Error("Field " + name_1 + " should be of type " + type_1.nonModel + ", " + typeof v + " recieved. " + v);
269
+ }
270
+ Object.keys(subNonModelDefinition_1.fields).forEach(function (subKey) {
271
+ modelValidator_1(subKey, v[subKey]);
272
+ });
273
+ }
221
274
  }
222
275
  }
223
276
  }
@@ -257,21 +310,29 @@ var createModelClass = function (modelDefinition) {
257
310
  function Model(init) {
258
311
  var instance = immer_1.produce(this, function (draft) {
259
312
  initializeInstance(init, modelDefinition, draft);
260
- var modelInstanceMetadata = instancesMetadata.has(init)
313
+ // model is initialized inside a DataStore component (e.g. by Sync Engine, Storage Engine, etc.)
314
+ var isInternallyInitialized = instancesMetadata.has(init);
315
+ var modelInstanceMetadata = isInternallyInitialized
261
316
  ? init
262
317
  : {};
263
- var _id = modelInstanceMetadata.id, _version = modelInstanceMetadata._version, _lastChangedAt = modelInstanceMetadata._lastChangedAt, _deleted = modelInstanceMetadata._deleted;
264
- // instancesIds are set by modelInstanceCreator, it is accessible only internally
265
- var isInternal = _id !== null && _id !== undefined;
266
- var id = isInternal
267
- ? _id
268
- : modelDefinition.syncable
269
- ? uuid_1.v4()
270
- : ulid();
271
- if (!isInternal) {
318
+ var _id = modelInstanceMetadata.id;
319
+ if (util_1.isIdManaged(modelDefinition)) {
320
+ var isInternalModel = _id !== null && _id !== undefined;
321
+ var id = isInternalModel
322
+ ? _id
323
+ : modelDefinition.syncable
324
+ ? uuid_1.v4()
325
+ : ulid();
326
+ draft.id = id;
327
+ }
328
+ else if (util_1.isIdOptionallyManaged(modelDefinition)) {
329
+ // only auto-populate if the id was not provided
330
+ draft.id = draft.id || uuid_1.v4();
331
+ }
332
+ if (!isInternallyInitialized) {
272
333
  checkReadOnlyPropertyOnCreate(draft, modelDefinition);
273
334
  }
274
- draft.id = id;
335
+ var _version = modelInstanceMetadata._version, _lastChangedAt = modelInstanceMetadata._lastChangedAt, _deleted = modelInstanceMetadata._deleted;
275
336
  if (modelDefinition.syncable) {
276
337
  draft._version = _version;
277
338
  draft._lastChangedAt = _lastChangedAt;
@@ -290,7 +351,9 @@ var createModelClass = function (modelDefinition) {
290
351
  var patches;
291
352
  var model = immer_1.produce(source, function (draft) {
292
353
  fn(draft);
293
- draft.id = source.id;
354
+ var keyNames = util_1.extractPrimaryKeyFieldNames(modelDefinition);
355
+ // Keys are immutable
356
+ keyNames.forEach(function (key) { return (draft[key] = source[key]); });
294
357
  var modelValidator = validateModelFields(modelDefinition);
295
358
  Object.entries(draft).forEach(function (_a) {
296
359
  var _b = tslib_1.__read(_a, 2), k = _b[0], v = _b[1];
@@ -409,6 +472,18 @@ function getModelConstructorByModelName(namespaceName, modelName) {
409
472
  throw new Error(msg);
410
473
  }
411
474
  }
475
+ /**
476
+ * Queries the DataStore metadata tables to see if they are the expected
477
+ * version. If not, clobbers the whole DB. If so, leaves them alone.
478
+ * Otherwise, simply writes the schema version.
479
+ *
480
+ * SIDE EFFECT:
481
+ * 1. Creates a transaction
482
+ * 1. Updates data.
483
+ *
484
+ * @param storage Storage adapter containing the metadata.
485
+ * @param version The expected schema version.
486
+ */
412
487
  function checkSchemaVersion(storage, version) {
413
488
  return tslib_1.__awaiter(this, void 0, void 0, function () {
414
489
  var Setting, modelDefinition;
@@ -423,7 +498,6 @@ function checkSchemaVersion(storage, version) {
423
498
  return tslib_1.__generator(this, function (_b) {
424
499
  switch (_b.label) {
425
500
  case 0: return [4 /*yield*/, s.query(Setting, predicates_1.ModelPredicateCreator.createFromExisting(modelDefinition, function (c) {
426
- // @ts-ignore Argument of type '"eq"' is not assignable to parameter of type 'never'.
427
501
  return c.key('eq', SETTING_SCHEMA_VERSION);
428
502
  }), { page: 0, limit: 1 })];
429
503
  case 1:
@@ -492,13 +566,21 @@ function getNamespace() {
492
566
  };
493
567
  return namespace;
494
568
  }
569
+ var DataStoreState;
570
+ (function (DataStoreState) {
571
+ DataStoreState["NotRunning"] = "Not Running";
572
+ DataStoreState["Starting"] = "Starting";
573
+ DataStoreState["Running"] = "Running";
574
+ DataStoreState["Stopping"] = "Stopping";
575
+ DataStoreState["Clearing"] = "Clearing";
576
+ })(DataStoreState || (DataStoreState = {}));
495
577
  var DataStore = /** @class */ (function () {
496
578
  function DataStore() {
497
579
  var _this = this;
498
580
  // reference to configured category instances. Used for preserving SSR context
499
581
  this.Auth = auth_1.Auth;
500
- this.API = api_1.default;
501
- this.Cache = cache_1.default;
582
+ this.API = api_1.API;
583
+ this.Cache = cache_1.BrowserStorageCache;
502
584
  this.amplifyConfig = {};
503
585
  this.syncPredicates = new WeakMap();
504
586
  // object that gets passed to descendent classes. Allows us to pass these down by reference
@@ -507,156 +589,235 @@ var DataStore = /** @class */ (function () {
507
589
  API: this.API,
508
590
  Cache: this.Cache,
509
591
  };
592
+ /**
593
+ * **IMPORTANT!**
594
+ *
595
+ * Accumulator for background things that can **and MUST** be called when
596
+ * DataStore stops.
597
+ *
598
+ * These jobs **MUST** be *idempotent promises* that resolve ONLY
599
+ * once the intended jobs are completely finished and/or otherwise destroyed
600
+ * and cleaned up with ZERO outstanding:
601
+ *
602
+ * 1. side effects (e.g., state changes)
603
+ * 1. callbacks
604
+ * 1. subscriptions
605
+ * 1. calls to storage
606
+ * 1. *etc.*
607
+ *
608
+ * Methods that create pending promises, subscriptions, callbacks, or any
609
+ * type of side effect **MUST** be registered with the manager. And, a new
610
+ * manager must be created after each `exit()`.
611
+ *
612
+ * Failure to comply will put DataStore into a highly unpredictable state
613
+ * when it needs to stop or clear -- which occurs when restarting with new
614
+ * sync expressions, during testing, and potentially during app code
615
+ * recovery handling, etc..
616
+ *
617
+ * It is up to the discretion of each disposer whether to wait for job
618
+ * completion or to cancel operations and issue failures *as long as the
619
+ * disposer returns in a reasonable amount of time.*
620
+ *
621
+ * (Reasonable = *seconds*, not minutes.)
622
+ */
623
+ this.runningProcesses = new core_1.BackgroundProcessManager();
624
+ /**
625
+ * Indicates what state DataStore is in.
626
+ *
627
+ * Not [yet?] used for actual state management; but for messaging
628
+ * when errors occur, to help troubleshoot.
629
+ */
630
+ this.state = DataStoreState.NotRunning;
631
+ /**
632
+ * If not already done:
633
+ * 1. Attaches and initializes storage.
634
+ * 1. Loads the schema and records metadata.
635
+ * 1. If `this.amplifyConfig.aws_appsync_graphqlEndpoint` contains a URL,
636
+ * attaches a sync engine, starts it, and subscribes.
637
+ */
510
638
  this.start = function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
511
- var aws_appsync_graphqlEndpoint, _a, fullSyncIntervalInMilliseconds;
512
639
  var _this = this;
513
- return tslib_1.__generator(this, function (_b) {
514
- switch (_b.label) {
515
- case 0:
516
- if (!(this.initialized === undefined)) return [3 /*break*/, 1];
517
- logger.debug('Starting DataStore');
518
- this.initialized = new Promise(function (res, rej) {
519
- _this.initResolve = res;
520
- _this.initReject = rej;
521
- });
522
- return [3 /*break*/, 3];
523
- case 1: return [4 /*yield*/, this.initialized];
524
- case 2:
525
- _b.sent();
526
- return [2 /*return*/];
527
- case 3:
528
- this.storage = new storage_1.ExclusiveStorage(schema, namespaceResolver, getModelConstructorByModelName, modelInstanceCreator, this.storageAdapter, this.sessionId);
529
- return [4 /*yield*/, this.storage.init()];
530
- case 4:
531
- _b.sent();
532
- checkSchemaInitialized();
533
- return [4 /*yield*/, checkSchemaVersion(this.storage, schema.version)];
534
- case 5:
535
- _b.sent();
536
- aws_appsync_graphqlEndpoint = this.amplifyConfig.aws_appsync_graphqlEndpoint;
537
- if (!aws_appsync_graphqlEndpoint) return [3 /*break*/, 7];
538
- logger.debug('GraphQL endpoint available', aws_appsync_graphqlEndpoint);
539
- _a = this;
540
- return [4 /*yield*/, this.processSyncExpressions()];
541
- case 6:
542
- _a.syncPredicates = _b.sent();
543
- this.sync = new sync_1.SyncEngine(schema, namespaceResolver, exports.syncClasses, userClasses, this.storage, modelInstanceCreator, this.conflictHandler, this.errorHandler, this.syncPredicates, this.amplifyConfig, this.authModeStrategy, this.amplifyContext);
544
- fullSyncIntervalInMilliseconds = this.fullSyncInterval * 1000 * 60;
545
- syncSubscription = this.sync
546
- .start({ fullSyncInterval: fullSyncIntervalInMilliseconds })
547
- .subscribe({
548
- next: function (_a) {
549
- var type = _a.type, data = _a.data;
550
- // In Node, we need to wait for queries to be synced to prevent returning empty arrays.
551
- // In the Browser, we can begin returning data once subscriptions are in place.
552
- var readyType = isNode
553
- ? sync_1.ControlMessage.SYNC_ENGINE_SYNC_QUERIES_READY
554
- : sync_1.ControlMessage.SYNC_ENGINE_STORAGE_SUBSCRIBED;
555
- if (type === readyType) {
556
- _this.initResolve();
557
- }
558
- core_1.Hub.dispatch('datastore', {
559
- event: type,
560
- data: data,
561
- });
562
- },
563
- error: function (err) {
564
- logger.warn('Sync error', err);
565
- _this.initReject();
566
- },
567
- });
568
- return [3 /*break*/, 8];
569
- case 7:
570
- logger.warn("Data won't be synchronized. No GraphQL endpoint configured. Did you forget `Amplify.configure(awsconfig)`?", {
571
- config: this.amplifyConfig,
640
+ return tslib_1.__generator(this, function (_a) {
641
+ return [2 /*return*/, this.runningProcesses
642
+ .add(function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
643
+ var aws_appsync_graphqlEndpoint, _a, fullSyncIntervalInMilliseconds;
644
+ var _this = this;
645
+ return tslib_1.__generator(this, function (_b) {
646
+ switch (_b.label) {
647
+ case 0:
648
+ this.state = DataStoreState.Starting;
649
+ if (!(this.initialized === undefined)) return [3 /*break*/, 1];
650
+ logger.debug('Starting DataStore');
651
+ this.initialized = new Promise(function (res, rej) {
652
+ _this.initResolve = res;
653
+ _this.initReject = rej;
654
+ });
655
+ return [3 /*break*/, 3];
656
+ case 1: return [4 /*yield*/, this.initialized];
657
+ case 2:
658
+ _b.sent();
659
+ return [2 /*return*/];
660
+ case 3:
661
+ this.storage = new storage_1.ExclusiveStorage(schema, namespaceResolver, getModelConstructorByModelName, modelInstanceCreator, this.storageAdapter, this.sessionId);
662
+ return [4 /*yield*/, this.storage.init()];
663
+ case 4:
664
+ _b.sent();
665
+ checkSchemaInitialized();
666
+ return [4 /*yield*/, checkSchemaVersion(this.storage, schema.version)];
667
+ case 5:
668
+ _b.sent();
669
+ aws_appsync_graphqlEndpoint = this.amplifyConfig.aws_appsync_graphqlEndpoint;
670
+ if (!aws_appsync_graphqlEndpoint) return [3 /*break*/, 7];
671
+ logger.debug('GraphQL endpoint available', aws_appsync_graphqlEndpoint);
672
+ _a = this;
673
+ return [4 /*yield*/, this.processSyncExpressions()];
674
+ case 6:
675
+ _a.syncPredicates = _b.sent();
676
+ this.sync = new sync_1.SyncEngine(schema, namespaceResolver, exports.syncClasses, userClasses, this.storage, modelInstanceCreator, this.conflictHandler, this.errorHandler, this.syncPredicates, this.amplifyConfig, this.authModeStrategy, this.amplifyContext, this.connectivityMonitor);
677
+ fullSyncIntervalInMilliseconds = this.fullSyncInterval * 1000 * 60;
678
+ syncSubscription = this.sync
679
+ .start({ fullSyncInterval: fullSyncIntervalInMilliseconds })
680
+ .subscribe({
681
+ next: function (_a) {
682
+ var type = _a.type, data = _a.data;
683
+ // In Node, we need to wait for queries to be synced to prevent returning empty arrays.
684
+ // In the Browser, we can begin returning data once subscriptions are in place.
685
+ var readyType = isNode
686
+ ? sync_1.ControlMessage.SYNC_ENGINE_SYNC_QUERIES_READY
687
+ : sync_1.ControlMessage.SYNC_ENGINE_STORAGE_SUBSCRIBED;
688
+ if (type === readyType) {
689
+ _this.initResolve();
690
+ }
691
+ core_1.Hub.dispatch('datastore', {
692
+ event: type,
693
+ data: data,
694
+ });
695
+ },
696
+ error: function (err) {
697
+ logger.warn('Sync error', err);
698
+ _this.initReject();
699
+ },
700
+ });
701
+ return [3 /*break*/, 8];
702
+ case 7:
703
+ logger.warn("Data won't be synchronized. No GraphQL endpoint configured. Did you forget `Amplify.configure(awsconfig)`?", {
704
+ config: this.amplifyConfig,
705
+ });
706
+ this.initResolve();
707
+ _b.label = 8;
708
+ case 8: return [4 /*yield*/, this.initialized];
709
+ case 9:
710
+ _b.sent();
711
+ this.state = DataStoreState.Running;
712
+ return [2 /*return*/];
713
+ }
572
714
  });
573
- this.initResolve();
574
- _b.label = 8;
575
- case 8: return [4 /*yield*/, this.initialized];
576
- case 9:
577
- _b.sent();
578
- return [2 /*return*/];
579
- }
715
+ }); }, 'datastore start')
716
+ .catch(this.handleAddProcError('DataStore.start()'))];
580
717
  });
581
718
  }); };
582
- this.query = function (modelConstructor, idOrCriteria, paginationProducer) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
583
- var msg, modelDefinition, predicate, pagination, result;
719
+ this.query = function (modelConstructor, identifierOrCriteria, paginationProducer) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
720
+ var _this = this;
584
721
  return tslib_1.__generator(this, function (_a) {
585
- switch (_a.label) {
586
- case 0: return [4 /*yield*/, this.start()];
587
- case 1:
588
- _a.sent();
589
- //#region Input validation
590
- if (!isValidModelConstructor(modelConstructor)) {
591
- msg = 'Constructor is not for a valid model';
592
- logger.error(msg, { modelConstructor: modelConstructor });
593
- throw new Error(msg);
594
- }
595
- if (typeof idOrCriteria === 'string') {
596
- if (paginationProducer !== undefined) {
597
- logger.warn('Pagination is ignored when querying by id');
598
- }
599
- }
600
- modelDefinition = getModelDefinition(modelConstructor);
601
- if (isQueryOne(idOrCriteria)) {
602
- predicate = predicates_1.ModelPredicateCreator.createForId(modelDefinition, idOrCriteria);
603
- }
604
- else {
605
- if (predicates_1.isPredicatesAll(idOrCriteria)) {
606
- // Predicates.ALL means "all records", so no predicate (undefined)
607
- predicate = undefined;
608
- }
609
- else {
610
- predicate = predicates_1.ModelPredicateCreator.createFromExisting(modelDefinition, idOrCriteria);
722
+ return [2 /*return*/, this.runningProcesses
723
+ .add(function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
724
+ var msg, modelDefinition, keyFields, predicate, msg, pagination, result, returnOne;
725
+ return tslib_1.__generator(this, function (_a) {
726
+ switch (_a.label) {
727
+ case 0: return [4 /*yield*/, this.start()];
728
+ case 1:
729
+ _a.sent();
730
+ //#region Input validation
731
+ if (!isValidModelConstructor(modelConstructor)) {
732
+ msg = 'Constructor is not for a valid model';
733
+ logger.error(msg, { modelConstructor: modelConstructor });
734
+ throw new Error(msg);
735
+ }
736
+ if (typeof identifierOrCriteria === 'string') {
737
+ if (paginationProducer !== undefined) {
738
+ logger.warn('Pagination is ignored when querying by id');
739
+ }
740
+ }
741
+ modelDefinition = getModelDefinition(modelConstructor);
742
+ keyFields = util_1.extractPrimaryKeyFieldNames(modelDefinition);
743
+ if (isQueryOne(identifierOrCriteria)) {
744
+ if (keyFields.length > 1) {
745
+ msg = util_1.errorMessages.queryByPkWithCompositeKeyPresent;
746
+ logger.error(msg, { keyFields: keyFields });
747
+ throw new Error(msg);
748
+ }
749
+ predicate = predicates_1.ModelPredicateCreator.createForSingleField(modelDefinition, keyFields[0], identifierOrCriteria);
750
+ }
751
+ else {
752
+ // Object is being queried using object literal syntax
753
+ if (types_1.isIdentifierObject(identifierOrCriteria, modelDefinition)) {
754
+ predicate = predicates_1.ModelPredicateCreator.createForPk(modelDefinition, identifierOrCriteria);
755
+ }
756
+ else if (predicates_1.isPredicatesAll(identifierOrCriteria)) {
757
+ // Predicates.ALL means "all records", so no predicate (undefined)
758
+ predicate = undefined;
759
+ }
760
+ else {
761
+ predicate = predicates_1.ModelPredicateCreator.createFromExisting(modelDefinition, identifierOrCriteria);
762
+ }
763
+ }
764
+ pagination = this.processPagination(modelDefinition, paginationProducer);
765
+ //#endregion
766
+ logger.debug('params ready', {
767
+ modelConstructor: modelConstructor,
768
+ predicate: predicates_1.ModelPredicateCreator.getPredicates(predicate, false),
769
+ pagination: tslib_1.__assign(tslib_1.__assign({}, pagination), { sort: predicates_1.ModelSortPredicateCreator.getPredicates(pagination && pagination.sort, false) }),
770
+ });
771
+ return [4 /*yield*/, this.storage.query(modelConstructor, predicate, pagination)];
772
+ case 2:
773
+ result = _a.sent();
774
+ returnOne = isQueryOne(identifierOrCriteria) ||
775
+ types_1.isIdentifierObject(identifierOrCriteria, modelDefinition);
776
+ return [2 /*return*/, returnOne ? result[0] : result];
611
777
  }
612
- }
613
- pagination = this.processPagination(modelDefinition, paginationProducer);
614
- //#endregion
615
- logger.debug('params ready', {
616
- modelConstructor: modelConstructor,
617
- predicate: predicates_1.ModelPredicateCreator.getPredicates(predicate, false),
618
- pagination: tslib_1.__assign(tslib_1.__assign({}, pagination), { sort: predicates_1.ModelSortPredicateCreator.getPredicates(pagination && pagination.sort, false) }),
619
778
  });
620
- return [4 /*yield*/, this.storage.query(modelConstructor, predicate, pagination)];
621
- case 2:
622
- result = _a.sent();
623
- return [2 /*return*/, isQueryOne(idOrCriteria) ? result[0] : result];
624
- }
779
+ }); }, 'datastore query')
780
+ .catch(this.handleAddProcError('DataStore.query()'))];
625
781
  });
626
782
  }); };
627
783
  this.save = function (model, condition) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
628
- var patchesTuple, modelConstructor, msg, modelDefinition, producedCondition, _a, savedModel;
629
784
  var _this = this;
630
- return tslib_1.__generator(this, function (_b) {
631
- switch (_b.label) {
632
- case 0: return [4 /*yield*/, this.start()];
633
- case 1:
634
- _b.sent();
635
- patchesTuple = modelPatchesMap.get(model);
636
- modelConstructor = model
637
- ? model.constructor
638
- : undefined;
639
- if (!isValidModelConstructor(modelConstructor)) {
640
- msg = 'Object is not an instance of a valid model';
641
- logger.error(msg, { model: model });
642
- throw new Error(msg);
643
- }
644
- modelDefinition = getModelDefinition(modelConstructor);
645
- producedCondition = predicates_1.ModelPredicateCreator.createFromExisting(modelDefinition, condition);
646
- return [4 /*yield*/, this.storage.runExclusive(function (s) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
647
- return tslib_1.__generator(this, function (_a) {
648
- switch (_a.label) {
649
- case 0: return [4 /*yield*/, s.save(model, producedCondition, undefined, patchesTuple)];
650
- case 1:
651
- _a.sent();
652
- return [2 /*return*/, s.query(modelConstructor, predicates_1.ModelPredicateCreator.createForId(modelDefinition, model.id))];
785
+ return tslib_1.__generator(this, function (_a) {
786
+ return [2 /*return*/, this.runningProcesses
787
+ .add(function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
788
+ var patchesTuple, modelConstructor, msg, modelDefinition, producedCondition, _a, savedModel;
789
+ var _this = this;
790
+ return tslib_1.__generator(this, function (_b) {
791
+ switch (_b.label) {
792
+ case 0: return [4 /*yield*/, this.start()];
793
+ case 1:
794
+ _b.sent();
795
+ patchesTuple = modelPatchesMap.get(model);
796
+ modelConstructor = model ? model.constructor : undefined;
797
+ if (!isValidModelConstructor(modelConstructor)) {
798
+ msg = 'Object is not an instance of a valid model';
799
+ logger.error(msg, { model: model });
800
+ throw new Error(msg);
653
801
  }
654
- });
655
- }); })];
656
- case 2:
657
- _a = tslib_1.__read.apply(void 0, [_b.sent(), 1]), savedModel = _a[0];
658
- return [2 /*return*/, savedModel];
659
- }
802
+ modelDefinition = getModelDefinition(modelConstructor);
803
+ producedCondition = predicates_1.ModelPredicateCreator.createFromExisting(modelDefinition, condition);
804
+ return [4 /*yield*/, this.storage.runExclusive(function (s) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
805
+ return tslib_1.__generator(this, function (_a) {
806
+ switch (_a.label) {
807
+ case 0: return [4 /*yield*/, s.save(model, producedCondition, undefined, patchesTuple)];
808
+ case 1:
809
+ _a.sent();
810
+ return [2 /*return*/, s.query(modelConstructor, predicates_1.ModelPredicateCreator.createForPk(modelDefinition, model))];
811
+ }
812
+ });
813
+ }); })];
814
+ case 2:
815
+ _a = tslib_1.__read.apply(void 0, [_b.sent(), 1]), savedModel = _a[0];
816
+ return [2 /*return*/, savedModel];
817
+ }
818
+ });
819
+ }); }, 'datastore save')
820
+ .catch(this.handleAddProcError('DataStore.save()'))];
660
821
  });
661
822
  }); };
662
823
  this.setConflictHandler = function (config) {
@@ -685,75 +846,95 @@ var DataStore = /** @class */ (function () {
685
846
  }
686
847
  return _this.errorHandler || defaultErrorHandler;
687
848
  };
688
- this.delete = function (modelOrConstructor, idOrCriteria) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
689
- var condition, msg, modelConstructor, msg, msg, _a, deleted, model, modelConstructor, msg, modelDefinition, idPredicate, msg, _b, _c, deleted;
690
- return tslib_1.__generator(this, function (_d) {
691
- switch (_d.label) {
692
- case 0: return [4 /*yield*/, this.start()];
693
- case 1:
694
- _d.sent();
695
- if (!modelOrConstructor) {
696
- msg = 'Model or Model Constructor required';
697
- logger.error(msg, { modelOrConstructor: modelOrConstructor });
698
- throw new Error(msg);
699
- }
700
- if (!isValidModelConstructor(modelOrConstructor)) return [3 /*break*/, 3];
701
- modelConstructor = modelOrConstructor;
702
- if (!idOrCriteria) {
703
- msg = 'Id to delete or criteria required. Do you want to delete all? Pass Predicates.ALL';
704
- logger.error(msg, { idOrCriteria: idOrCriteria });
705
- throw new Error(msg);
706
- }
707
- if (typeof idOrCriteria === 'string') {
708
- condition = predicates_1.ModelPredicateCreator.createForId(getModelDefinition(modelConstructor), idOrCriteria);
709
- }
710
- else {
711
- condition = predicates_1.ModelPredicateCreator.createFromExisting(getModelDefinition(modelConstructor),
712
- /**
713
- * idOrCriteria is always a ProducerModelPredicate<T>, never a symbol.
714
- * The symbol is used only for typing purposes. e.g. see Predicates.ALL
715
- */
716
- idOrCriteria);
717
- if (!condition || !predicates_1.ModelPredicateCreator.isValidPredicate(condition)) {
718
- msg = 'Criteria required. Do you want to delete all? Pass Predicates.ALL';
719
- logger.error(msg, { condition: condition });
720
- throw new Error(msg);
721
- }
722
- }
723
- return [4 /*yield*/, this.storage.delete(modelConstructor, condition)];
724
- case 2:
725
- _a = tslib_1.__read.apply(void 0, [_d.sent(), 1]), deleted = _a[0];
726
- return [2 /*return*/, deleted];
727
- case 3:
728
- model = modelOrConstructor;
729
- modelConstructor = Object.getPrototypeOf(model || {})
730
- .constructor;
731
- if (!isValidModelConstructor(modelConstructor)) {
732
- msg = 'Object is not an instance of a valid model';
733
- logger.error(msg, { model: model });
734
- throw new Error(msg);
735
- }
736
- modelDefinition = getModelDefinition(modelConstructor);
737
- idPredicate = predicates_1.ModelPredicateCreator.createForId(modelDefinition, model.id);
738
- if (idOrCriteria) {
739
- if (typeof idOrCriteria !== 'function') {
740
- msg = 'Invalid criteria';
741
- logger.error(msg, { idOrCriteria: idOrCriteria });
742
- throw new Error(msg);
849
+ this.delete = function (modelOrConstructor, identifierOrCriteria) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
850
+ var _this = this;
851
+ return tslib_1.__generator(this, function (_a) {
852
+ return [2 /*return*/, this.runningProcesses
853
+ .add(function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
854
+ var condition, msg, modelConstructor, msg, modelDefinition, keyFields, msg, msg, _a, deleted, model, modelConstructor, msg, modelDefinition, pkPredicate, msg, _b, _c, deleted;
855
+ return tslib_1.__generator(this, function (_d) {
856
+ switch (_d.label) {
857
+ case 0: return [4 /*yield*/, this.start()];
858
+ case 1:
859
+ _d.sent();
860
+ if (!modelOrConstructor) {
861
+ msg = 'Model or Model Constructor required';
862
+ logger.error(msg, { modelOrConstructor: modelOrConstructor });
863
+ throw new Error(msg);
864
+ }
865
+ if (!isValidModelConstructor(modelOrConstructor)) return [3 /*break*/, 3];
866
+ modelConstructor = modelOrConstructor;
867
+ if (!identifierOrCriteria) {
868
+ msg = 'Id to delete or criteria required. Do you want to delete all? Pass Predicates.ALL';
869
+ logger.error(msg, { identifierOrCriteria: identifierOrCriteria });
870
+ throw new Error(msg);
871
+ }
872
+ modelDefinition = getModelDefinition(modelConstructor);
873
+ if (typeof identifierOrCriteria === 'string') {
874
+ keyFields = util_1.extractPrimaryKeyFieldNames(modelDefinition);
875
+ if (keyFields.length > 1) {
876
+ msg = util_1.errorMessages.deleteByPkWithCompositeKeyPresent;
877
+ logger.error(msg, { keyFields: keyFields });
878
+ throw new Error(msg);
879
+ }
880
+ condition = predicates_1.ModelPredicateCreator.createForSingleField(getModelDefinition(modelConstructor), keyFields[0], identifierOrCriteria);
881
+ }
882
+ else {
883
+ if (types_1.isIdentifierObject(identifierOrCriteria, modelDefinition)) {
884
+ condition = predicates_1.ModelPredicateCreator.createForPk(modelDefinition, identifierOrCriteria);
885
+ }
886
+ else {
887
+ condition = predicates_1.ModelPredicateCreator.createFromExisting(modelDefinition,
888
+ /**
889
+ * idOrCriteria is always a ProducerModelPredicate<T>, never a symbol.
890
+ * The symbol is used only for typing purposes. e.g. see Predicates.ALL
891
+ */
892
+ identifierOrCriteria);
893
+ }
894
+ if (!condition ||
895
+ !predicates_1.ModelPredicateCreator.isValidPredicate(condition)) {
896
+ msg = 'Criteria required. Do you want to delete all? Pass Predicates.ALL';
897
+ logger.error(msg, { condition: condition });
898
+ throw new Error(msg);
899
+ }
900
+ }
901
+ return [4 /*yield*/, this.storage.delete(modelConstructor, condition)];
902
+ case 2:
903
+ _a = tslib_1.__read.apply(void 0, [_d.sent(), 1]), deleted = _a[0];
904
+ return [2 /*return*/, deleted];
905
+ case 3:
906
+ model = modelOrConstructor;
907
+ modelConstructor = Object.getPrototypeOf(model || {})
908
+ .constructor;
909
+ if (!isValidModelConstructor(modelConstructor)) {
910
+ msg = 'Object is not an instance of a valid model';
911
+ logger.error(msg, { model: model });
912
+ throw new Error(msg);
913
+ }
914
+ modelDefinition = getModelDefinition(modelConstructor);
915
+ pkPredicate = predicates_1.ModelPredicateCreator.createForPk(modelDefinition, model);
916
+ if (identifierOrCriteria) {
917
+ if (typeof identifierOrCriteria !== 'function') {
918
+ msg = 'Invalid criteria';
919
+ logger.error(msg, { identifierOrCriteria: identifierOrCriteria });
920
+ throw new Error(msg);
921
+ }
922
+ condition = identifierOrCriteria(pkPredicate);
923
+ }
924
+ else {
925
+ condition = pkPredicate;
926
+ }
927
+ return [4 /*yield*/, this.storage.delete(model, condition)];
928
+ case 4:
929
+ _b = tslib_1.__read.apply(void 0, [_d.sent(), 1]), _c = tslib_1.__read(_b[0], 1), deleted = _c[0];
930
+ return [2 /*return*/, deleted];
743
931
  }
744
- condition = idOrCriteria(idPredicate);
745
- }
746
- else {
747
- condition = idPredicate;
748
- }
749
- return [4 /*yield*/, this.storage.delete(model, condition)];
750
- case 4:
751
- _b = tslib_1.__read.apply(void 0, [_d.sent(), 1]), _c = tslib_1.__read(_b[0], 1), deleted = _c[0];
752
- return [2 /*return*/, deleted];
753
- }
932
+ });
933
+ }); }, 'datastore delete')
934
+ .catch(this.handleAddProcError('DataStore.delete()'))];
754
935
  });
755
936
  }); };
756
- this.observe = function (modelOrConstructor, idOrCriteria) {
937
+ this.observe = function (modelOrConstructor, identifierOrCriteria) {
757
938
  var predicate;
758
939
  var modelConstructor = modelOrConstructor && isValidModelConstructor(modelOrConstructor)
759
940
  ? modelOrConstructor
@@ -762,10 +943,10 @@ var DataStore = /** @class */ (function () {
762
943
  var model = modelOrConstructor;
763
944
  var modelConstructor_1 = model && Object.getPrototypeOf(model).constructor;
764
945
  if (isValidModelConstructor(modelConstructor_1)) {
765
- if (idOrCriteria) {
946
+ if (identifierOrCriteria) {
766
947
  logger.warn('idOrCriteria is ignored when using a model instance', {
767
948
  model: model,
768
- idOrCriteria: idOrCriteria,
949
+ identifierOrCriteria: identifierOrCriteria,
769
950
  });
770
951
  }
771
952
  return _this.observe(modelConstructor_1, model.id);
@@ -776,9 +957,17 @@ var DataStore = /** @class */ (function () {
776
957
  throw new Error(msg);
777
958
  }
778
959
  }
779
- if (idOrCriteria !== undefined && modelConstructor === undefined) {
960
+ // observe should not accept object literal syntax
961
+ if (identifierOrCriteria &&
962
+ modelConstructor &&
963
+ types_1.isIdentifierObject(identifierOrCriteria, getModelDefinition(modelConstructor))) {
964
+ var msg = util_1.errorMessages.observeWithObjectLiteral;
965
+ logger.error(msg, { objectLiteral: identifierOrCriteria });
966
+ throw new Error(msg);
967
+ }
968
+ if (identifierOrCriteria !== undefined && modelConstructor === undefined) {
780
969
  var msg = 'Cannot provide criteria without a modelConstructor';
781
- logger.error(msg, idOrCriteria);
970
+ logger.error(msg, identifierOrCriteria);
782
971
  throw new Error(msg);
783
972
  }
784
973
  if (modelConstructor && !isValidModelConstructor(modelConstructor)) {
@@ -786,17 +975,25 @@ var DataStore = /** @class */ (function () {
786
975
  logger.error(msg, { modelConstructor: modelConstructor });
787
976
  throw new Error(msg);
788
977
  }
789
- if (typeof idOrCriteria === 'string') {
790
- predicate = predicates_1.ModelPredicateCreator.createForId(getModelDefinition(modelConstructor), idOrCriteria);
978
+ if (typeof identifierOrCriteria === 'string') {
979
+ var modelDefinition = getModelDefinition(modelConstructor);
980
+ var _a = tslib_1.__read(util_1.extractPrimaryKeyFieldNames(modelDefinition), 1), keyField = _a[0];
981
+ predicate = predicates_1.ModelPredicateCreator.createForSingleField(getModelDefinition(modelConstructor), keyField, identifierOrCriteria);
791
982
  }
792
983
  else {
793
- predicate =
794
- modelConstructor &&
795
- predicates_1.ModelPredicateCreator.createFromExisting(getModelDefinition(modelConstructor), idOrCriteria);
984
+ if (predicates_1.isPredicatesAll(identifierOrCriteria)) {
985
+ predicate = undefined;
986
+ }
987
+ else {
988
+ predicate =
989
+ modelConstructor &&
990
+ predicates_1.ModelPredicateCreator.createFromExisting(getModelDefinition(modelConstructor), identifierOrCriteria);
991
+ }
796
992
  }
797
993
  return new zen_observable_ts_1.default(function (observer) {
798
994
  var handle;
799
- (function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
995
+ _this.runningProcesses
996
+ .add(function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
800
997
  var _this = this;
801
998
  return tslib_1.__generator(this, function (_a) {
802
999
  switch (_a.label) {
@@ -812,36 +1009,52 @@ var DataStore = /** @class */ (function () {
812
1009
  return namespaceResolver(model) === util_1.USER;
813
1010
  })
814
1011
  .subscribe({
815
- next: function (item) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
816
- var message, freshElement;
817
- return tslib_1.__generator(this, function (_a) {
818
- switch (_a.label) {
819
- case 0:
820
- message = item;
821
- if (!(item.opType !== 'DELETE')) return [3 /*break*/, 2];
822
- return [4 /*yield*/, this.query(item.model, item.element.id)];
823
- case 1:
824
- freshElement = _a.sent();
825
- message = tslib_1.__assign(tslib_1.__assign({}, message), { element: freshElement });
826
- _a.label = 2;
827
- case 2:
828
- observer.next(message);
829
- return [2 /*return*/];
830
- }
831
- });
832
- }); },
1012
+ next: function (item) {
1013
+ return _this.runningProcesses.isOpen &&
1014
+ _this.runningProcesses.add(function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
1015
+ var message, modelDefinition, keyFields, primaryKeysAndValues, freshElement;
1016
+ return tslib_1.__generator(this, function (_a) {
1017
+ switch (_a.label) {
1018
+ case 0:
1019
+ message = item;
1020
+ if (!(item.opType !== 'DELETE')) return [3 /*break*/, 2];
1021
+ modelDefinition = getModelDefinition(item.model);
1022
+ keyFields = util_1.extractPrimaryKeyFieldNames(modelDefinition);
1023
+ primaryKeysAndValues = util_1.extractPrimaryKeysAndValues(item.element, keyFields);
1024
+ return [4 /*yield*/, this.query(item.model, primaryKeysAndValues)];
1025
+ case 1:
1026
+ freshElement = _a.sent();
1027
+ message = tslib_1.__assign(tslib_1.__assign({}, message), { element: freshElement });
1028
+ _a.label = 2;
1029
+ case 2:
1030
+ observer.next(message);
1031
+ return [2 /*return*/];
1032
+ }
1033
+ });
1034
+ }); }, 'datastore observe message handler');
1035
+ },
833
1036
  error: function (err) { return observer.error(err); },
834
1037
  complete: function () { return observer.complete(); },
835
1038
  });
836
1039
  return [2 /*return*/];
837
1040
  }
838
1041
  });
839
- }); })();
840
- return function () {
841
- if (handle) {
842
- handle.unsubscribe();
843
- }
844
- };
1042
+ }); }, 'datastore observe observable initialization')
1043
+ .catch(_this.handleAddProcError('DataStore.observe()'))
1044
+ .catch(function (error) {
1045
+ observer.error(error);
1046
+ });
1047
+ // better than no cleaner, but if the subscriber is handling the
1048
+ // complete() message async and not registering with the context,
1049
+ // this will still be problematic.
1050
+ return _this.runningProcesses.addCleaner(function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
1051
+ return tslib_1.__generator(this, function (_a) {
1052
+ if (handle) {
1053
+ handle.unsubscribe();
1054
+ }
1055
+ return [2 /*return*/];
1056
+ });
1057
+ }); }, 'DataStore.observe() cleanup');
845
1058
  });
846
1059
  };
847
1060
  this.observeQuery = function (model, criteria, options) {
@@ -875,8 +1088,9 @@ var DataStore = /** @class */ (function () {
875
1088
  var sort = (options || {}).sort;
876
1089
  var sortOptions = sort ? { sort: sort } : undefined;
877
1090
  var modelDefinition = getModelDefinition(model);
1091
+ var keyFields = util_1.extractPrimaryKeyFieldNames(modelDefinition);
878
1092
  if (isQueryOne(criteria)) {
879
- predicate = predicates_1.ModelPredicateCreator.createForId(modelDefinition, criteria);
1093
+ predicate = predicates_1.ModelPredicateCreator.createForSingleField(modelDefinition, keyFields[0], criteria);
880
1094
  }
881
1095
  else {
882
1096
  if (predicates_1.isPredicatesAll(criteria)) {
@@ -889,7 +1103,8 @@ var DataStore = /** @class */ (function () {
889
1103
  }
890
1104
  var _a = predicates_1.ModelPredicateCreator.getPredicates(predicate, false) || {}, predicates = _a.predicates, predicateGroupType = _a.type;
891
1105
  var hasPredicate = !!predicates;
892
- (function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
1106
+ _this.runningProcesses
1107
+ .add(function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
893
1108
  var err_1;
894
1109
  var _this = this;
895
1110
  return tslib_1.__generator(this, function (_a) {
@@ -900,7 +1115,9 @@ var DataStore = /** @class */ (function () {
900
1115
  case 1:
901
1116
  // first, query and return any locally-available records
902
1117
  (_a.sent()).forEach(function (item) {
903
- return items.set(item.id, item);
1118
+ var itemModelDefinition = getModelDefinition(model);
1119
+ var idOrPk = utils_1.getIdentifierValue(itemModelDefinition, item);
1120
+ items.set(idOrPk, item);
904
1121
  });
905
1122
  // Observe the model and send a stream of updates (debounced).
906
1123
  // We need to post-filter results instead of passing criteria through
@@ -909,15 +1126,17 @@ var DataStore = /** @class */ (function () {
909
1126
  handle = this.observe(model).subscribe(function (_a) {
910
1127
  var element = _a.element, model = _a.model, opType = _a.opType;
911
1128
  var _b, _c;
1129
+ var itemModelDefinition = getModelDefinition(model);
1130
+ var idOrPk = utils_1.getIdentifierValue(itemModelDefinition, element);
912
1131
  if (hasPredicate &&
913
1132
  !util_1.validatePredicate(element, predicateGroupType, predicates)) {
914
1133
  if (opType === 'UPDATE' &&
915
- (items.has(element.id) || itemsChanged.has(element.id))) {
1134
+ (items.has(idOrPk) || itemsChanged.has(idOrPk))) {
916
1135
  // tracking as a "deleted item" will include the item in
917
1136
  // page limit calculations and ensure it is removed from the
918
1137
  // final items collection, regardless of which collection(s)
919
1138
  // it is currently in. (I mean, it could be in both, right!?)
920
- deletedItemIds.push(element.id);
1139
+ deletedItemIds.push(idOrPk);
921
1140
  }
922
1141
  else {
923
1142
  // ignore updates for irrelevant/filtered items.
@@ -929,13 +1148,14 @@ var DataStore = /** @class */ (function () {
929
1148
  // in the `mergePage` method within src/sync/merger.ts. The final state of a model instance
930
1149
  // depends on the LATEST record (for a given id).
931
1150
  if (opType === 'DELETE') {
932
- deletedItemIds.push(element.id);
1151
+ deletedItemIds.push(idOrPk);
933
1152
  }
934
1153
  else {
935
- itemsChanged.set(element.id, element);
1154
+ itemsChanged.set(idOrPk, element);
936
1155
  }
937
1156
  var isSynced = (_c = (_b = _this.sync) === null || _b === void 0 ? void 0 : _b.getModelSyncedStatus(model)) !== null && _c !== void 0 ? _c : false;
938
- var limit = itemsChanged.size - deletedItemIds.length >= _this.syncPageSize;
1157
+ var limit = itemsChanged.size - deletedItemIds.length >=
1158
+ _this.syncPageSize;
939
1159
  if (limit || isSynced) {
940
1160
  limitTimerRace.resolve();
941
1161
  }
@@ -952,7 +1172,11 @@ var DataStore = /** @class */ (function () {
952
1172
  case 3: return [2 /*return*/];
953
1173
  }
954
1174
  });
955
- }); })();
1175
+ }); }, 'datastore observequery startup')
1176
+ .catch(_this.handleAddProcError('DataStore.observeQuery()'))
1177
+ .catch(function (error) {
1178
+ observer.error(error);
1179
+ });
956
1180
  /**
957
1181
  * Combines the `items`, `itemsChanged`, and `deletedItemIds` collections into
958
1182
  * a snapshot in the form of `{ items: T[], isSynced: boolean}`.
@@ -967,9 +1191,13 @@ var DataStore = /** @class */ (function () {
967
1191
  sortItems(itemsArray);
968
1192
  }
969
1193
  items.clear();
970
- itemsArray.forEach(function (item) { return items.set(item.id, item); });
1194
+ itemsArray.forEach(function (item) {
1195
+ var itemModelDefinition = getModelDefinition(model);
1196
+ var idOrPk = utils_1.getIdentifierValue(itemModelDefinition, item);
1197
+ items.set(idOrPk, item);
1198
+ });
971
1199
  // remove deleted items from the final result set
972
- deletedItemIds.forEach(function (id) { return items.delete(id); });
1200
+ deletedItemIds.forEach(function (idOrPk) { return items.delete(idOrPk); });
973
1201
  return {
974
1202
  items: Array.from(items.values()),
975
1203
  isSynced: isSynced,
@@ -984,7 +1212,8 @@ var DataStore = /** @class */ (function () {
984
1212
  * @param snapshot The generated items data to emit.
985
1213
  */
986
1214
  var emitSnapshot = function (snapshot) {
987
- // send the generated snapshot to the primary subscription
1215
+ // send the generated snapshot to the primary subscription.
1216
+ // NOTE: This observer's handler *could* be async ...
988
1217
  observer.next(snapshot);
989
1218
  // reset the changed items sets
990
1219
  itemsChanged.clear();
@@ -1020,15 +1249,18 @@ var DataStore = /** @class */ (function () {
1020
1249
  if (event === sync_1.ControlMessage.SYNC_ENGINE_MODEL_SYNCED &&
1021
1250
  ((_b = data === null || data === void 0 ? void 0 : data.model) === null || _b === void 0 ? void 0 : _b.name) === model.name) {
1022
1251
  generateAndEmitSnapshot();
1023
- core_1.Hub.remove('api', hubCallback);
1252
+ core_1.Hub.remove('datastore', hubCallback);
1024
1253
  }
1025
1254
  };
1026
1255
  core_1.Hub.listen('datastore', hubCallback);
1027
- return function () {
1028
- if (handle) {
1029
- handle.unsubscribe();
1030
- }
1031
- };
1256
+ return _this.runningProcesses.addCleaner(function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
1257
+ return tslib_1.__generator(this, function (_a) {
1258
+ if (handle) {
1259
+ handle.unsubscribe();
1260
+ }
1261
+ return [2 /*return*/];
1262
+ });
1263
+ }); }, 'datastore observequery cleaner');
1032
1264
  });
1033
1265
  };
1034
1266
  this.configure = function (config) {
@@ -1087,66 +1319,132 @@ var DataStore = /** @class */ (function () {
1087
1319
  undefined;
1088
1320
  _this.sessionId = _this.retrieveSessionId();
1089
1321
  };
1090
- this.clear = function clear() {
1091
- return tslib_1.__awaiter(this, void 0, void 0, function () {
1092
- return tslib_1.__generator(this, function (_a) {
1093
- switch (_a.label) {
1094
- case 0:
1095
- checkSchemaInitialized();
1096
- if (!(this.storage === undefined)) return [3 /*break*/, 2];
1097
- // connect to storage so that it can be cleared without fully starting DataStore
1098
- this.storage = new storage_1.ExclusiveStorage(schema, namespaceResolver, getModelConstructorByModelName, modelInstanceCreator, this.storageAdapter, this.sessionId);
1099
- return [4 /*yield*/, this.storage.init()];
1100
- case 1:
1101
- _a.sent();
1102
- _a.label = 2;
1103
- case 2:
1104
- if (syncSubscription && !syncSubscription.closed) {
1105
- syncSubscription.unsubscribe();
1106
- }
1107
- return [4 /*yield*/, this.storage.clear()];
1108
- case 3:
1109
- _a.sent();
1110
- if (this.sync) {
1111
- this.sync.unsubscribeConnectivity();
1112
- }
1113
- this.initialized = undefined; // Should re-initialize when start() is called.
1114
- this.storage = undefined;
1115
- this.sync = undefined;
1116
- this.syncPredicates = new WeakMap();
1117
- return [2 /*return*/];
1118
- }
1119
- });
1120
- });
1121
- };
1122
- this.stop = function stop() {
1123
- return tslib_1.__awaiter(this, void 0, void 0, function () {
1124
- return tslib_1.__generator(this, function (_a) {
1125
- switch (_a.label) {
1126
- case 0:
1127
- if (!(this.initialized !== undefined)) return [3 /*break*/, 2];
1128
- return [4 /*yield*/, this.start()];
1129
- case 1:
1130
- _a.sent();
1131
- _a.label = 2;
1132
- case 2:
1133
- if (syncSubscription && !syncSubscription.closed) {
1134
- syncSubscription.unsubscribe();
1135
- }
1136
- if (this.sync) {
1137
- this.sync.unsubscribeConnectivity();
1138
- }
1139
- this.initialized = undefined; // Should re-initialize when start() is called.
1140
- this.sync = undefined;
1141
- return [2 /*return*/];
1142
- }
1143
- });
1144
- });
1145
- };
1146
1322
  }
1147
1323
  DataStore.prototype.getModuleName = function () {
1148
1324
  return 'DataStore';
1149
1325
  };
1326
+ /**
1327
+ * Builds a function to capture `BackgroundManagerNotOpenError`'s to produce friendlier,
1328
+ * more instructive errors for customers.
1329
+ *
1330
+ * @param operation The name of the operation (usually a Datastore method) the customer
1331
+ * tried to call.
1332
+ */
1333
+ DataStore.prototype.handleAddProcError = function (operation) {
1334
+ var _this = this;
1335
+ /**
1336
+ * If the tested error is a `BackgroundManagerNotOpenError`, it will be captured
1337
+ * and replaced with a friendlier message that instructs the App Developer.
1338
+ *
1339
+ * @param err An error to test.
1340
+ */
1341
+ var handler = function (err) {
1342
+ if (err.message.startsWith('BackgroundManagerNotOpenError')) {
1343
+ throw new Error([
1344
+ "DataStoreStateError: Tried to execute `" + operation + "` while DataStore was \"" + _this.state + "\".",
1345
+ "This can only be done while DataStore is \"Started\" or \"Stopped\". To remedy:",
1346
+ 'Ensure all calls to `stop()` and `clear()` have completed first.',
1347
+ 'If this is not possible, retry the operation until it succeeds.',
1348
+ ].join('\n'));
1349
+ }
1350
+ else {
1351
+ throw err;
1352
+ }
1353
+ };
1354
+ return handler;
1355
+ };
1356
+ /**
1357
+ * Clears all data from storage and removes all data, schema info, other
1358
+ * initialization details, and then stops DataStore.
1359
+ *
1360
+ * That said, reinitialization is required after clearing. This can be done
1361
+ * by explicitiliy calling `start()` or any method that implicitly starts
1362
+ * DataStore, such as `query()`, `save()`, or `delete()`.
1363
+ */
1364
+ DataStore.prototype.clear = function () {
1365
+ return tslib_1.__awaiter(this, void 0, void 0, function () {
1366
+ return tslib_1.__generator(this, function (_a) {
1367
+ switch (_a.label) {
1368
+ case 0:
1369
+ checkSchemaInitialized();
1370
+ this.state = DataStoreState.Clearing;
1371
+ return [4 /*yield*/, this.runningProcesses.close()];
1372
+ case 1:
1373
+ _a.sent();
1374
+ if (!(this.storage === undefined)) return [3 /*break*/, 3];
1375
+ // connect to storage so that it can be cleared without fully starting DataStore
1376
+ this.storage = new storage_1.ExclusiveStorage(schema, namespaceResolver, getModelConstructorByModelName, modelInstanceCreator, this.storageAdapter, this.sessionId);
1377
+ return [4 /*yield*/, this.storage.init()];
1378
+ case 2:
1379
+ _a.sent();
1380
+ _a.label = 3;
1381
+ case 3:
1382
+ if (syncSubscription && !syncSubscription.closed) {
1383
+ syncSubscription.unsubscribe();
1384
+ }
1385
+ if (!this.sync) return [3 /*break*/, 5];
1386
+ return [4 /*yield*/, this.sync.stop()];
1387
+ case 4:
1388
+ _a.sent();
1389
+ _a.label = 5;
1390
+ case 5: return [4 /*yield*/, this.storage.clear()];
1391
+ case 6:
1392
+ _a.sent();
1393
+ this.initialized = undefined; // Should re-initialize when start() is called.
1394
+ this.storage = undefined;
1395
+ this.sync = undefined;
1396
+ this.syncPredicates = new WeakMap();
1397
+ return [4 /*yield*/, this.runningProcesses.open()];
1398
+ case 7:
1399
+ _a.sent();
1400
+ this.state = DataStoreState.NotRunning;
1401
+ return [2 /*return*/];
1402
+ }
1403
+ });
1404
+ });
1405
+ };
1406
+ /**
1407
+ * Stops all DataStore sync activities.
1408
+ *
1409
+ * TODO: "Waits for graceful termination of
1410
+ * running queries and terminates subscriptions."
1411
+ */
1412
+ DataStore.prototype.stop = function () {
1413
+ return tslib_1.__awaiter(this, void 0, void 0, function () {
1414
+ return tslib_1.__generator(this, function (_a) {
1415
+ switch (_a.label) {
1416
+ case 0:
1417
+ this.state = DataStoreState.Stopping;
1418
+ return [4 /*yield*/, this.runningProcesses.close()];
1419
+ case 1:
1420
+ _a.sent();
1421
+ if (syncSubscription && !syncSubscription.closed) {
1422
+ syncSubscription.unsubscribe();
1423
+ }
1424
+ if (!this.sync) return [3 /*break*/, 3];
1425
+ return [4 /*yield*/, this.sync.stop()];
1426
+ case 2:
1427
+ _a.sent();
1428
+ _a.label = 3;
1429
+ case 3:
1430
+ this.initialized = undefined; // Should re-initialize when start() is called.
1431
+ this.sync = undefined;
1432
+ return [4 /*yield*/, this.runningProcesses.open()];
1433
+ case 4:
1434
+ _a.sent();
1435
+ this.state = DataStoreState.NotRunning;
1436
+ return [2 /*return*/];
1437
+ }
1438
+ });
1439
+ });
1440
+ };
1441
+ /**
1442
+ * Validates given pagination input from a query and creates a pagination
1443
+ * argument for use against the storage layer.
1444
+ *
1445
+ * @param modelDefinition
1446
+ * @param paginationProducer
1447
+ */
1150
1448
  DataStore.prototype.processPagination = function (modelDefinition, paginationProducer) {
1151
1449
  var sortPredicate;
1152
1450
  var _a = paginationProducer || {}, limit = _a.limit, page = _a.page, sort = _a.sort;
@@ -1181,6 +1479,10 @@ var DataStore = /** @class */ (function () {
1181
1479
  sort: sortPredicate,
1182
1480
  };
1183
1481
  };
1482
+ /**
1483
+ * Examines the configured `syncExpressions` and produces a WeakMap of
1484
+ * SchemaModel -> predicate to use during sync.
1485
+ */
1184
1486
  DataStore.prototype.processSyncExpressions = function () {
1185
1487
  return tslib_1.__awaiter(this, void 0, void 0, function () {
1186
1488
  var syncPredicates;
@@ -1262,7 +1564,10 @@ var DataStore = /** @class */ (function () {
1262
1564
  return map;
1263
1565
  }, new WeakMap());
1264
1566
  };
1265
- // database separation for Amplify Console. Not a public API
1567
+ /**
1568
+ * A session ID to allow CMS to open databases against multiple apps.
1569
+ * This session ID is only expected be set by AWS Amplify Studio.
1570
+ */
1266
1571
  DataStore.prototype.retrieveSessionId = function () {
1267
1572
  try {
1268
1573
  var sessionId = sessionStorage.getItem('datastoreSessionId');
@@ -1273,9 +1578,8 @@ var DataStore = /** @class */ (function () {
1273
1578
  return sessionId + "-" + appSyncId;
1274
1579
  }
1275
1580
  }
1276
- catch (_b) {
1277
- return undefined;
1278
- }
1581
+ catch (_b) { }
1582
+ return undefined;
1279
1583
  };
1280
1584
  return DataStore;
1281
1585
  }());