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