@aws-amplify/datastore 5.0.1-api-v6-models.c1977f8.0 → 5.0.1-api-v6-models.891fe0d.0

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 (70) hide show
  1. package/lib/authModeStrategies/defaultAuthStrategy.js +1 -1
  2. package/lib/authModeStrategies/multiAuthStrategy.js +29 -105
  3. package/lib/datastore/datastore.js +887 -1370
  4. package/lib/index.js +1 -1
  5. package/lib/predicates/index.js +53 -102
  6. package/lib/predicates/next.js +310 -557
  7. package/lib/predicates/sort.js +24 -27
  8. package/lib/ssr/index.js +1 -1
  9. package/lib/storage/adapter/AsyncStorageAdapter.js +115 -449
  10. package/lib/storage/adapter/AsyncStorageDatabase.js +215 -480
  11. package/lib/storage/adapter/InMemoryStore.js +27 -101
  12. package/lib/storage/adapter/InMemoryStore.native.js +1 -1
  13. package/lib/storage/adapter/IndexedDBAdapter.js +441 -1002
  14. package/lib/storage/adapter/StorageAdapterBase.js +177 -396
  15. package/lib/storage/adapter/getDefaultAdapter/index.js +5 -6
  16. package/lib/storage/adapter/getDefaultAdapter/index.native.js +2 -2
  17. package/lib/storage/relationship.js +174 -260
  18. package/lib/storage/storage.js +244 -507
  19. package/lib/sync/datastoreConnectivity.js +29 -84
  20. package/lib/sync/datastoreReachability/index.js +1 -1
  21. package/lib/sync/datastoreReachability/index.native.js +2 -2
  22. package/lib/sync/index.js +512 -885
  23. package/lib/sync/merger.js +30 -133
  24. package/lib/sync/outbox.js +147 -302
  25. package/lib/sync/processors/errorMaps.js +30 -80
  26. package/lib/sync/processors/mutation.js +331 -579
  27. package/lib/sync/processors/subscription.js +268 -428
  28. package/lib/sync/processors/sync.js +276 -464
  29. package/lib/sync/utils.js +248 -393
  30. package/lib/tsconfig.tsbuildinfo +1 -1
  31. package/lib/types.js +16 -58
  32. package/lib/util.js +335 -575
  33. package/lib-esm/authModeStrategies/defaultAuthStrategy.js +1 -1
  34. package/lib-esm/authModeStrategies/multiAuthStrategy.js +27 -103
  35. package/lib-esm/datastore/datastore.js +874 -1359
  36. package/lib-esm/index.js +6 -6
  37. package/lib-esm/predicates/index.js +54 -104
  38. package/lib-esm/predicates/next.js +306 -555
  39. package/lib-esm/predicates/sort.js +24 -27
  40. package/lib-esm/ssr/index.js +1 -1
  41. package/lib-esm/storage/adapter/AsyncStorageAdapter.js +111 -446
  42. package/lib-esm/storage/adapter/AsyncStorageDatabase.js +212 -477
  43. package/lib-esm/storage/adapter/InMemoryStore.js +27 -102
  44. package/lib-esm/storage/adapter/IndexedDBAdapter.js +436 -997
  45. package/lib-esm/storage/adapter/StorageAdapterBase.js +172 -392
  46. package/lib-esm/storage/adapter/getDefaultAdapter/index.js +2 -3
  47. package/lib-esm/storage/adapter/getDefaultAdapter/index.native.js +1 -1
  48. package/lib-esm/storage/relationship.js +173 -260
  49. package/lib-esm/storage/storage.js +236 -499
  50. package/lib-esm/sync/datastoreConnectivity.js +26 -82
  51. package/lib-esm/sync/datastoreReachability/index.js +1 -1
  52. package/lib-esm/sync/datastoreReachability/index.native.js +1 -1
  53. package/lib-esm/sync/index.js +498 -872
  54. package/lib-esm/sync/merger.js +28 -131
  55. package/lib-esm/sync/outbox.js +143 -298
  56. package/lib-esm/sync/processors/errorMaps.js +32 -82
  57. package/lib-esm/sync/processors/mutation.js +324 -572
  58. package/lib-esm/sync/processors/subscription.js +258 -418
  59. package/lib-esm/sync/processors/sync.js +269 -457
  60. package/lib-esm/sync/utils.js +245 -390
  61. package/lib-esm/tsconfig.tsbuildinfo +1 -1
  62. package/lib-esm/types.js +16 -59
  63. package/lib-esm/util.js +335 -577
  64. package/package.json +12 -12
  65. package/src/datastore/datastore.ts +4 -3
  66. package/src/storage/adapter/getDefaultAdapter/index.ts +1 -3
  67. package/src/sync/processors/mutation.ts +1 -1
  68. package/src/sync/processors/sync.ts +1 -1
  69. package/src/sync/utils.ts +1 -1
  70. package/src/util.ts +44 -6
package/lib/sync/index.js CHANGED
@@ -1,76 +1,4 @@
1
1
  "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- var __generator = (this && this.__generator) || function (thisArg, body) {
12
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
13
- return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
- function verb(n) { return function (v) { return step([n, v]); }; }
15
- function step(op) {
16
- if (f) throw new TypeError("Generator is already executing.");
17
- while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
- if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
- if (y = 0, t) op = [op[0] & 2, t.value];
20
- switch (op[0]) {
21
- case 0: case 1: t = op; break;
22
- case 4: _.label++; return { value: op[1], done: false };
23
- case 5: _.label++; y = op[1]; op = [0]; continue;
24
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
- default:
26
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
- if (t[2]) _.ops.pop();
31
- _.trys.pop(); continue;
32
- }
33
- op = body.call(thisArg, _);
34
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
- }
37
- };
38
- var __read = (this && this.__read) || function (o, n) {
39
- var m = typeof Symbol === "function" && o[Symbol.iterator];
40
- if (!m) return o;
41
- var i = m.call(o), r, ar = [], e;
42
- try {
43
- while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
44
- }
45
- catch (error) { e = { error: error }; }
46
- finally {
47
- try {
48
- if (r && !r.done && (m = i["return"])) m.call(i);
49
- }
50
- finally { if (e) throw e.error; }
51
- }
52
- return ar;
53
- };
54
- var __values = (this && this.__values) || function(o) {
55
- var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
56
- if (m) return m.call(o);
57
- if (o && typeof o.length === "number") return {
58
- next: function () {
59
- if (o && i >= o.length) o = void 0;
60
- return { value: o && o[i++], done: !o };
61
- }
62
- };
63
- throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
64
- };
65
- var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
66
- if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
67
- if (ar || !(i in from)) {
68
- if (!ar) ar = Array.prototype.slice.call(from, 0, i);
69
- ar[i] = from[i];
70
- }
71
- }
72
- return to.concat(ar || Array.prototype.slice.call(from));
73
- };
74
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
75
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
76
4
  };
@@ -78,22 +6,22 @@ Object.defineProperty(exports, "__esModule", { value: true });
78
6
  exports.SyncEngine = exports.ControlMessage = void 0;
79
7
  // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
80
8
  // SPDX-License-Identifier: Apache-2.0
81
- var utils_1 = require("@aws-amplify/core/internals/utils");
82
- var core_1 = require("@aws-amplify/core");
83
- var rxjs_1 = require("rxjs");
84
- var predicates_1 = require("../predicates");
85
- var types_1 = require("../types");
86
- var util_1 = require("../util");
87
- var datastoreConnectivity_1 = __importDefault(require("./datastoreConnectivity"));
88
- var merger_1 = require("./merger");
89
- var outbox_1 = require("./outbox");
90
- var mutation_1 = require("./processors/mutation");
91
- var subscription_1 = require("./processors/subscription");
92
- var sync_1 = require("./processors/sync");
93
- var utils_2 = require("./utils");
94
- var api_graphql_1 = require("@aws-amplify/api-graphql");
95
- var logger = new core_1.ConsoleLogger('DataStore');
96
- var ownSymbol = Symbol('sync');
9
+ const utils_1 = require("@aws-amplify/core/internals/utils");
10
+ const core_1 = require("@aws-amplify/core");
11
+ const rxjs_1 = require("rxjs");
12
+ const predicates_1 = require("../predicates");
13
+ const types_1 = require("../types");
14
+ const util_1 = require("../util");
15
+ const datastoreConnectivity_1 = __importDefault(require("./datastoreConnectivity"));
16
+ const merger_1 = require("./merger");
17
+ const outbox_1 = require("./outbox");
18
+ const mutation_1 = require("./processors/mutation");
19
+ const subscription_1 = require("./processors/subscription");
20
+ const sync_1 = require("./processors/sync");
21
+ const utils_2 = require("./utils");
22
+ const api_graphql_1 = require("@aws-amplify/api-graphql");
23
+ const logger = new core_1.ConsoleLogger('DataStore');
24
+ const ownSymbol = Symbol('sync');
97
25
  var ControlMessage;
98
26
  (function (ControlMessage) {
99
27
  ControlMessage["SYNC_ENGINE_STORAGE_SUBSCRIBED"] = "storageSubscribed";
@@ -107,10 +35,11 @@ var ControlMessage;
107
35
  ControlMessage["SYNC_ENGINE_NETWORK_STATUS"] = "networkStatus";
108
36
  ControlMessage["SYNC_ENGINE_READY"] = "ready";
109
37
  })(ControlMessage = exports.ControlMessage || (exports.ControlMessage = {}));
110
- var SyncEngine = /** @class */ (function () {
111
- function SyncEngine(schema, namespaceResolver, modelClasses, userModelClasses, storage, modelInstanceCreator, conflictHandler, errorHandler, syncPredicates, amplifyConfig, authModeStrategy, amplifyContext, connectivityMonitor) {
112
- if (amplifyConfig === void 0) { amplifyConfig = {}; }
113
- var _this = this;
38
+ class SyncEngine {
39
+ getModelSyncedStatus(modelConstructor) {
40
+ return this.modelSyncedStatus.get(modelConstructor);
41
+ }
42
+ constructor(schema, namespaceResolver, modelClasses, userModelClasses, storage, modelInstanceCreator, conflictHandler, errorHandler, syncPredicates, amplifyConfig = {}, authModeStrategy, amplifyContext, connectivityMonitor) {
114
43
  this.schema = schema;
115
44
  this.namespaceResolver = namespaceResolver;
116
45
  this.modelClasses = modelClasses;
@@ -126,10 +55,10 @@ var SyncEngine = /** @class */ (function () {
126
55
  this.modelSyncedStatus = new WeakMap();
127
56
  this.connectionDisrupted = false;
128
57
  this.runningProcesses = new utils_1.BackgroundProcessManager();
129
- this.waitForSleepState = new Promise(function (resolve) {
130
- _this.syncQueriesObservableStartSleeping = resolve;
58
+ this.waitForSleepState = new Promise(resolve => {
59
+ this.syncQueriesObservableStartSleeping = resolve;
131
60
  });
132
- var MutationEvent = this.modelClasses['MutationEvent'];
61
+ const MutationEvent = this.modelClasses['MutationEvent'];
133
62
  this.outbox = new outbox_1.MutationEventOutbox(this.schema, MutationEvent, modelInstanceCreator, ownSymbol);
134
63
  this.modelMerger = new merger_1.ModelMerger(this.outbox, ownSymbol);
135
64
  this.syncQueriesProcessor = new sync_1.SyncProcessor(this.schema, this.syncPredicates, this.amplifyConfig, this.authModeStrategy, errorHandler, this.amplifyContext);
@@ -138,807 +67,510 @@ var SyncEngine = /** @class */ (function () {
138
67
  this.datastoreConnectivity =
139
68
  this.connectivityMonitor || new datastoreConnectivity_1.default();
140
69
  }
141
- SyncEngine.prototype.getModelSyncedStatus = function (modelConstructor) {
142
- return this.modelSyncedStatus.get(modelConstructor);
143
- };
144
- SyncEngine.prototype.start = function (params) {
145
- var _this = this;
146
- return new rxjs_1.Observable(function (observer) {
70
+ start(params) {
71
+ return new rxjs_1.Observable(observer => {
147
72
  logger.log('starting sync engine...');
148
- var subscriptions = [];
149
- _this.runningProcesses.add(function () { return __awaiter(_this, void 0, void 0, function () {
150
- var err_1, startPromise, hasMutationsInOutbox;
151
- var _this = this;
152
- return __generator(this, function (_a) {
153
- switch (_a.label) {
154
- case 0:
155
- _a.trys.push([0, 2, , 3]);
156
- return [4 /*yield*/, this.setupModels(params)];
157
- case 1:
158
- _a.sent();
159
- return [3 /*break*/, 3];
160
- case 2:
161
- err_1 = _a.sent();
162
- observer.error(err_1);
163
- return [2 /*return*/];
164
- case 3:
165
- startPromise = new Promise(function (doneStarting, failedStarting) {
166
- _this.datastoreConnectivity.status().subscribe(function (_a) {
167
- var online = _a.online;
168
- return __awaiter(_this, void 0, void 0, function () {
169
- var _this = this;
170
- return __generator(this, function (_b) {
171
- return [2 /*return*/, this.runningProcesses.isOpen &&
172
- this.runningProcesses.add(function (onTerminate) { return __awaiter(_this, void 0, void 0, function () {
173
- var ctlSubsObservable_1, dataSubsObservable, err_2, error_1;
174
- var _a;
175
- var _this = this;
176
- return __generator(this, function (_b) {
177
- switch (_b.label) {
178
- case 0:
179
- if (!(online && !this.online)) return [3 /*break*/, 9];
180
- this.online = online;
181
- observer.next({
182
- type: ControlMessage.SYNC_ENGINE_NETWORK_STATUS,
183
- data: {
184
- active: this.online,
185
- },
186
- });
187
- dataSubsObservable = void 0;
188
- this.stopDisruptionListener =
189
- this.startDisruptionListener();
190
- //#region GraphQL Subscriptions
191
- _a = __read(this.subscriptionsProcessor.start(), 2), ctlSubsObservable_1 = _a[0], dataSubsObservable = _a[1];
192
- _b.label = 1;
193
- case 1:
194
- _b.trys.push([1, 3, , 4]);
195
- return [4 /*yield*/, new Promise(function (resolve, reject) {
196
- onTerminate.then(reject);
197
- var ctlSubsSubscription = ctlSubsObservable_1.subscribe({
198
- next: function (msg) {
199
- if (msg === subscription_1.CONTROL_MSG.CONNECTED) {
200
- resolve();
201
- }
202
- },
203
- error: function (err) {
204
- reject(err);
205
- var handleDisconnect = _this.disconnectionHandler();
206
- handleDisconnect(err);
207
- },
208
- });
209
- subscriptions.push(ctlSubsSubscription);
210
- })];
211
- case 2:
212
- _b.sent();
213
- return [3 /*break*/, 4];
214
- case 3:
215
- err_2 = _b.sent();
216
- observer.error(err_2);
217
- failedStarting();
218
- return [2 /*return*/];
219
- case 4:
220
- logger.log('Realtime ready');
221
- observer.next({
222
- type: ControlMessage.SYNC_ENGINE_SUBSCRIPTIONS_ESTABLISHED,
223
- });
224
- _b.label = 5;
225
- case 5:
226
- _b.trys.push([5, 7, , 8]);
227
- return [4 /*yield*/, new Promise(function (resolve, reject) {
228
- var syncQuerySubscription = _this.syncQueriesObservable().subscribe({
229
- next: function (message) {
230
- var type = message.type;
231
- if (type ===
232
- ControlMessage.SYNC_ENGINE_SYNC_QUERIES_READY) {
233
- resolve();
234
- }
235
- observer.next(message);
236
- },
237
- complete: function () {
238
- resolve();
239
- },
240
- error: function (error) {
241
- reject(error);
242
- },
243
- });
244
- if (syncQuerySubscription) {
245
- subscriptions.push(syncQuerySubscription);
246
- }
247
- })];
248
- case 6:
249
- _b.sent();
250
- return [3 /*break*/, 8];
251
- case 7:
252
- error_1 = _b.sent();
253
- observer.error(error_1);
254
- failedStarting();
255
- return [2 /*return*/];
256
- case 8:
257
- //#endregion
258
- //#region process mutations (outbox)
259
- subscriptions.push(this.mutationsProcessor
260
- .start()
261
- .subscribe(function (_a) {
262
- var modelDefinition = _a.modelDefinition, item = _a.model, hasMore = _a.hasMore;
263
- return _this.runningProcesses.add(function () { return __awaiter(_this, void 0, void 0, function () {
264
- var modelConstructor, model;
265
- var _this = this;
266
- return __generator(this, function (_a) {
267
- switch (_a.label) {
268
- case 0:
269
- modelConstructor = this.userModelClasses[modelDefinition.name];
270
- model = this.modelInstanceCreator(modelConstructor, item);
271
- return [4 /*yield*/, this.storage.runExclusive(function (storage) {
272
- return _this.modelMerger.merge(storage, model, modelDefinition);
273
- })];
274
- case 1:
275
- _a.sent();
276
- observer.next({
277
- type: ControlMessage.SYNC_ENGINE_OUTBOX_MUTATION_PROCESSED,
278
- data: {
279
- model: modelConstructor,
280
- element: model,
281
- },
282
- });
283
- observer.next({
284
- type: ControlMessage.SYNC_ENGINE_OUTBOX_STATUS,
285
- data: {
286
- isEmpty: !hasMore,
287
- },
288
- });
289
- return [2 /*return*/];
290
- }
291
- });
292
- }); }, 'mutation processor event');
293
- }));
294
- //#endregion
295
- //#region Merge subscriptions buffer
296
- subscriptions.push(dataSubsObservable.subscribe(function (_a) {
297
- var _b = __read(_a, 3), _transformerMutationType = _b[0], modelDefinition = _b[1], item = _b[2];
298
- return _this.runningProcesses.add(function () { return __awaiter(_this, void 0, void 0, function () {
299
- var modelConstructor, model;
300
- var _this = this;
301
- return __generator(this, function (_a) {
302
- switch (_a.label) {
303
- case 0:
304
- modelConstructor = this.userModelClasses[modelDefinition.name];
305
- model = this.modelInstanceCreator(modelConstructor, item);
306
- return [4 /*yield*/, this.storage.runExclusive(function (storage) {
307
- return _this.modelMerger.merge(storage, model, modelDefinition);
308
- })];
309
- case 1:
310
- _a.sent();
311
- return [2 /*return*/];
312
- }
313
- });
314
- }); }, 'subscription dataSubsObservable event');
315
- }));
316
- return [3 /*break*/, 10];
317
- case 9:
318
- if (!online) {
319
- this.online = online;
320
- observer.next({
321
- type: ControlMessage.SYNC_ENGINE_NETWORK_STATUS,
322
- data: {
323
- active: this.online,
324
- },
325
- });
326
- subscriptions.forEach(function (sub) { return sub.unsubscribe(); });
327
- subscriptions = [];
328
- }
329
- _b.label = 10;
330
- case 10:
331
- doneStarting();
332
- return [2 /*return*/];
333
- }
334
- });
335
- }); }, 'datastore connectivity event')];
73
+ let subscriptions = [];
74
+ this.runningProcesses.add(async () => {
75
+ try {
76
+ await this.setupModels(params);
77
+ }
78
+ catch (err) {
79
+ observer.error(err);
80
+ return;
81
+ }
82
+ // this is awaited at the bottom. so, we don't need to register
83
+ // this explicitly with the context. it's already contained.
84
+ const startPromise = new Promise((doneStarting, failedStarting) => {
85
+ this.datastoreConnectivity.status().subscribe(async ({ online }) => this.runningProcesses.isOpen &&
86
+ this.runningProcesses.add(async (onTerminate) => {
87
+ // From offline to online
88
+ if (online && !this.online) {
89
+ this.online = online;
90
+ observer.next({
91
+ type: ControlMessage.SYNC_ENGINE_NETWORK_STATUS,
92
+ data: {
93
+ active: this.online,
94
+ },
95
+ });
96
+ let ctlSubsObservable;
97
+ let dataSubsObservable;
98
+ this.stopDisruptionListener =
99
+ this.startDisruptionListener();
100
+ //#region GraphQL Subscriptions
101
+ [ctlSubsObservable, dataSubsObservable] =
102
+ this.subscriptionsProcessor.start();
103
+ try {
104
+ await new Promise((resolve, reject) => {
105
+ onTerminate.then(reject);
106
+ const ctlSubsSubscription = ctlSubsObservable.subscribe({
107
+ next: msg => {
108
+ if (msg === subscription_1.CONTROL_MSG.CONNECTED) {
109
+ resolve();
110
+ }
111
+ },
112
+ error: err => {
113
+ reject(err);
114
+ const handleDisconnect = this.disconnectionHandler();
115
+ handleDisconnect(err);
116
+ },
336
117
  });
118
+ subscriptions.push(ctlSubsSubscription);
337
119
  });
120
+ }
121
+ catch (err) {
122
+ observer.error(err);
123
+ failedStarting();
124
+ return;
125
+ }
126
+ logger.log('Realtime ready');
127
+ observer.next({
128
+ type: ControlMessage.SYNC_ENGINE_SUBSCRIPTIONS_ESTABLISHED,
338
129
  });
339
- });
340
- this.storage
341
- .observe(null, null, ownSymbol)
342
- .pipe((0, rxjs_1.filter)(function (_a) {
343
- var model = _a.model;
344
- var modelDefinition = _this.getModelDefinition(model);
345
- return modelDefinition.syncable === true;
346
- }))
130
+ //#endregion
131
+ //#region Base & Sync queries
132
+ try {
133
+ await new Promise((resolve, reject) => {
134
+ const syncQuerySubscription = this.syncQueriesObservable().subscribe({
135
+ next: message => {
136
+ const { type } = message;
137
+ if (type ===
138
+ ControlMessage.SYNC_ENGINE_SYNC_QUERIES_READY) {
139
+ resolve();
140
+ }
141
+ observer.next(message);
142
+ },
143
+ complete: () => {
144
+ resolve();
145
+ },
146
+ error: error => {
147
+ reject(error);
148
+ },
149
+ });
150
+ if (syncQuerySubscription) {
151
+ subscriptions.push(syncQuerySubscription);
152
+ }
153
+ });
154
+ }
155
+ catch (error) {
156
+ observer.error(error);
157
+ failedStarting();
158
+ return;
159
+ }
160
+ //#endregion
161
+ //#region process mutations (outbox)
162
+ subscriptions.push(this.mutationsProcessor
163
+ .start()
164
+ .subscribe(({ modelDefinition, model: item, hasMore }) => this.runningProcesses.add(async () => {
165
+ const modelConstructor = this.userModelClasses[modelDefinition.name];
166
+ const model = this.modelInstanceCreator(modelConstructor, item);
167
+ await this.storage.runExclusive(storage => this.modelMerger.merge(storage, model, modelDefinition));
168
+ observer.next({
169
+ type: ControlMessage.SYNC_ENGINE_OUTBOX_MUTATION_PROCESSED,
170
+ data: {
171
+ model: modelConstructor,
172
+ element: model,
173
+ },
174
+ });
175
+ observer.next({
176
+ type: ControlMessage.SYNC_ENGINE_OUTBOX_STATUS,
177
+ data: {
178
+ isEmpty: !hasMore,
179
+ },
180
+ });
181
+ }, 'mutation processor event')));
182
+ //#endregion
183
+ //#region Merge subscriptions buffer
184
+ subscriptions.push(dataSubsObservable.subscribe(([_transformerMutationType, modelDefinition, item]) => this.runningProcesses.add(async () => {
185
+ const modelConstructor = this.userModelClasses[modelDefinition.name];
186
+ const model = this.modelInstanceCreator(modelConstructor, item);
187
+ await this.storage.runExclusive(storage => this.modelMerger.merge(storage, model, modelDefinition));
188
+ }, 'subscription dataSubsObservable event')));
189
+ //#endregion
190
+ }
191
+ else if (!online) {
192
+ this.online = online;
193
+ observer.next({
194
+ type: ControlMessage.SYNC_ENGINE_NETWORK_STATUS,
195
+ data: {
196
+ active: this.online,
197
+ },
198
+ });
199
+ subscriptions.forEach(sub => sub.unsubscribe());
200
+ subscriptions = [];
201
+ }
202
+ doneStarting();
203
+ }, 'datastore connectivity event'));
204
+ });
205
+ this.storage
206
+ .observe(null, null, ownSymbol)
207
+ .pipe((0, rxjs_1.filter)(({ model }) => {
208
+ const modelDefinition = this.getModelDefinition(model);
209
+ return modelDefinition.syncable === true;
210
+ }))
211
+ .subscribe({
212
+ next: async ({ opType, model, element, condition }) => this.runningProcesses.add(async () => {
213
+ const namespace = this.schema.namespaces[this.namespaceResolver(model)];
214
+ const MutationEventConstructor = this.modelClasses['MutationEvent'];
215
+ const modelDefinition = this.getModelDefinition(model);
216
+ const graphQLCondition = (0, utils_2.predicateToGraphQLCondition)(condition, modelDefinition);
217
+ const mutationEvent = (0, utils_2.createMutationInstanceFromModelOperation)(namespace.relationships, this.getModelDefinition(model), opType, model, element, graphQLCondition, MutationEventConstructor, this.modelInstanceCreator);
218
+ await this.outbox.enqueue(this.storage, mutationEvent);
219
+ observer.next({
220
+ type: ControlMessage.SYNC_ENGINE_OUTBOX_MUTATION_ENQUEUED,
221
+ data: {
222
+ model,
223
+ element,
224
+ },
225
+ });
226
+ observer.next({
227
+ type: ControlMessage.SYNC_ENGINE_OUTBOX_STATUS,
228
+ data: {
229
+ isEmpty: false,
230
+ },
231
+ });
232
+ await startPromise;
233
+ // Set by the this.datastoreConnectivity.status().subscribe() loop
234
+ if (this.online) {
235
+ this.mutationsProcessor.resume();
236
+ }
237
+ }, 'storage event'),
238
+ });
239
+ observer.next({
240
+ type: ControlMessage.SYNC_ENGINE_STORAGE_SUBSCRIBED,
241
+ });
242
+ const hasMutationsInOutbox = (await this.outbox.peek(this.storage)) === undefined;
243
+ observer.next({
244
+ type: ControlMessage.SYNC_ENGINE_OUTBOX_STATUS,
245
+ data: {
246
+ isEmpty: hasMutationsInOutbox,
247
+ },
248
+ });
249
+ await startPromise;
250
+ observer.next({
251
+ type: ControlMessage.SYNC_ENGINE_READY,
252
+ });
253
+ }, 'sync start');
254
+ });
255
+ }
256
+ async getModelsMetadataWithNextFullSync(currentTimeStamp) {
257
+ const modelLastSync = new Map((await this.runningProcesses.add(() => this.getModelsMetadata(), 'sync/index getModelsMetadataWithNextFullSync')).map(({ namespace, model, lastSync, lastFullSync, fullSyncInterval, lastSyncPredicate, }) => {
258
+ const nextFullSync = lastFullSync + fullSyncInterval;
259
+ const syncFrom = !lastFullSync || nextFullSync < currentTimeStamp
260
+ ? 0 // perform full sync if expired
261
+ : lastSync; // perform delta sync
262
+ return [
263
+ this.schema.namespaces[namespace].models[model],
264
+ [namespace, syncFrom],
265
+ ];
266
+ }));
267
+ return modelLastSync;
268
+ }
269
+ syncQueriesObservable() {
270
+ if (!this.online) {
271
+ return (0, rxjs_1.of)({}); // TODO(v6): fix this
272
+ }
273
+ return new rxjs_1.Observable(observer => {
274
+ let syncQueriesSubscription;
275
+ this.runningProcesses.isOpen &&
276
+ this.runningProcesses.add(async (onTerminate) => {
277
+ let terminated = false;
278
+ while (!observer.closed && !terminated) {
279
+ const count = new WeakMap();
280
+ const modelLastSync = await this.getModelsMetadataWithNextFullSync(Date.now());
281
+ const paginatingModels = new Set(modelLastSync.keys());
282
+ let lastFullSyncStartedAt;
283
+ let syncInterval;
284
+ let start;
285
+ let syncDuration;
286
+ let lastStartedAt;
287
+ await new Promise((resolve, reject) => {
288
+ if (!this.runningProcesses.isOpen)
289
+ resolve();
290
+ onTerminate.then(() => resolve());
291
+ syncQueriesSubscription = this.syncQueriesProcessor
292
+ .start(modelLastSync)
347
293
  .subscribe({
348
- next: function (_a) {
349
- var opType = _a.opType, model = _a.model, element = _a.element, condition = _a.condition;
350
- return __awaiter(_this, void 0, void 0, function () {
351
- var _this = this;
352
- return __generator(this, function (_b) {
353
- return [2 /*return*/, this.runningProcesses.add(function () { return __awaiter(_this, void 0, void 0, function () {
354
- var namespace, MutationEventConstructor, modelDefinition, graphQLCondition, mutationEvent;
355
- return __generator(this, function (_a) {
356
- switch (_a.label) {
357
- case 0:
358
- namespace = this.schema.namespaces[this.namespaceResolver(model)];
359
- MutationEventConstructor = this.modelClasses['MutationEvent'];
360
- modelDefinition = this.getModelDefinition(model);
361
- graphQLCondition = (0, utils_2.predicateToGraphQLCondition)(condition, modelDefinition);
362
- mutationEvent = (0, utils_2.createMutationInstanceFromModelOperation)(namespace.relationships, this.getModelDefinition(model), opType, model, element, graphQLCondition, MutationEventConstructor, this.modelInstanceCreator);
363
- return [4 /*yield*/, this.outbox.enqueue(this.storage, mutationEvent)];
364
- case 1:
365
- _a.sent();
366
- observer.next({
367
- type: ControlMessage.SYNC_ENGINE_OUTBOX_MUTATION_ENQUEUED,
368
- data: {
369
- model: model,
370
- element: element,
371
- },
372
- });
373
- observer.next({
374
- type: ControlMessage.SYNC_ENGINE_OUTBOX_STATUS,
375
- data: {
376
- isEmpty: false,
377
- },
378
- });
379
- return [4 /*yield*/, startPromise];
380
- case 2:
381
- _a.sent();
382
- // Set by the this.datastoreConnectivity.status().subscribe() loop
383
- if (this.online) {
384
- this.mutationsProcessor.resume();
385
- }
386
- return [2 /*return*/];
387
- }
388
- });
389
- }); }, 'storage event')];
294
+ next: async ({ namespace, modelDefinition, items, done, startedAt, isFullSync, }) => {
295
+ const modelConstructor = this.userModelClasses[modelDefinition.name];
296
+ if (!count.has(modelConstructor)) {
297
+ count.set(modelConstructor, {
298
+ new: 0,
299
+ updated: 0,
300
+ deleted: 0,
301
+ });
302
+ start = (0, util_1.getNow)();
303
+ lastStartedAt =
304
+ lastStartedAt === undefined
305
+ ? startedAt
306
+ : Math.max(lastStartedAt, startedAt);
307
+ }
308
+ /**
309
+ * If there are mutations in the outbox for a given id, those need to be
310
+ * merged individually. Otherwise, we can merge them in batches.
311
+ */
312
+ await this.storage.runExclusive(async (storage) => {
313
+ const idsInOutbox = await this.outbox.getModelIds(storage);
314
+ const oneByOne = [];
315
+ const page = items.filter(item => {
316
+ const itemId = (0, utils_2.getIdentifierValue)(modelDefinition, item);
317
+ if (!idsInOutbox.has(itemId)) {
318
+ return true;
319
+ }
320
+ oneByOne.push(item);
321
+ return false;
322
+ });
323
+ const opTypeCount = [];
324
+ for (const item of oneByOne) {
325
+ const opType = await this.modelMerger.merge(storage, item, modelDefinition);
326
+ if (opType !== undefined) {
327
+ opTypeCount.push([item, opType]);
328
+ }
329
+ }
330
+ opTypeCount.push(...(await this.modelMerger.mergePage(storage, modelConstructor, page, modelDefinition)));
331
+ const counts = count.get(modelConstructor);
332
+ opTypeCount.forEach(([, opType]) => {
333
+ switch (opType) {
334
+ case types_1.OpType.INSERT:
335
+ counts.new++;
336
+ break;
337
+ case types_1.OpType.UPDATE:
338
+ counts.updated++;
339
+ break;
340
+ case types_1.OpType.DELETE:
341
+ counts.deleted++;
342
+ break;
343
+ default:
344
+ throw new Error(`Invalid opType ${opType}`);
345
+ }
390
346
  });
391
347
  });
348
+ if (done) {
349
+ const { name: modelName } = modelDefinition;
350
+ //#region update last sync for type
351
+ let modelMetadata = await this.getModelMetadata(namespace, modelName);
352
+ const { lastFullSync, fullSyncInterval } = modelMetadata;
353
+ syncInterval = fullSyncInterval;
354
+ lastFullSyncStartedAt =
355
+ lastFullSyncStartedAt === undefined
356
+ ? lastFullSync
357
+ : Math.max(lastFullSyncStartedAt, isFullSync ? startedAt : lastFullSync);
358
+ modelMetadata = this.modelClasses
359
+ .ModelMetadata.copyOf(modelMetadata, draft => {
360
+ draft.lastSync = startedAt;
361
+ draft.lastFullSync = isFullSync
362
+ ? startedAt
363
+ : modelMetadata.lastFullSync;
364
+ });
365
+ await this.storage.save(modelMetadata, undefined, ownSymbol);
366
+ //#endregion
367
+ const counts = count.get(modelConstructor);
368
+ this.modelSyncedStatus.set(modelConstructor, true);
369
+ observer.next({
370
+ type: ControlMessage.SYNC_ENGINE_MODEL_SYNCED,
371
+ data: {
372
+ model: modelConstructor,
373
+ isFullSync,
374
+ isDeltaSync: !isFullSync,
375
+ counts,
376
+ },
377
+ });
378
+ paginatingModels.delete(modelDefinition);
379
+ if (paginatingModels.size === 0) {
380
+ syncDuration = (0, util_1.getNow)() - start;
381
+ resolve();
382
+ observer.next({
383
+ type: ControlMessage.SYNC_ENGINE_SYNC_QUERIES_READY,
384
+ });
385
+ syncQueriesSubscription.unsubscribe();
386
+ }
387
+ }
388
+ },
389
+ error: error => {
390
+ observer.error(error);
392
391
  },
393
392
  });
394
393
  observer.next({
395
- type: ControlMessage.SYNC_ENGINE_STORAGE_SUBSCRIBED,
396
- });
397
- return [4 /*yield*/, this.outbox.peek(this.storage)];
398
- case 4:
399
- hasMutationsInOutbox = (_a.sent()) === undefined;
400
- observer.next({
401
- type: ControlMessage.SYNC_ENGINE_OUTBOX_STATUS,
394
+ type: ControlMessage.SYNC_ENGINE_SYNC_QUERIES_STARTED,
402
395
  data: {
403
- isEmpty: hasMutationsInOutbox,
396
+ models: Array.from(paginatingModels).map(({ name }) => name),
404
397
  },
405
398
  });
406
- return [4 /*yield*/, startPromise];
407
- case 5:
408
- _a.sent();
409
- observer.next({
410
- type: ControlMessage.SYNC_ENGINE_READY,
399
+ });
400
+ // null is cast to 0 resulting in unexpected behavior.
401
+ // undefined in arithmetic operations results in NaN also resulting in unexpected behavior.
402
+ // If lastFullSyncStartedAt is null this is the first sync.
403
+ // Assume lastStartedAt is is also newest full sync.
404
+ let msNextFullSync;
405
+ if (!lastFullSyncStartedAt) {
406
+ msNextFullSync = syncInterval - syncDuration;
407
+ }
408
+ else {
409
+ msNextFullSync =
410
+ lastFullSyncStartedAt +
411
+ syncInterval -
412
+ (lastStartedAt + syncDuration);
413
+ }
414
+ logger.debug(`Next fullSync in ${msNextFullSync / 1000} seconds. (${new Date(Date.now() + msNextFullSync)})`);
415
+ // TODO: create `BackgroundProcessManager.sleep()` ... but, need to put
416
+ // a lot of thought into what that contract looks like to
417
+ // support possible use-cases:
418
+ //
419
+ // 1. non-cancelable
420
+ // 2. cancelable, unsleep on exit()
421
+ // 3. cancelable, throw Error on exit()
422
+ // 4. cancelable, callback first on exit()?
423
+ // 5. ... etc. ? ...
424
+ //
425
+ // TLDR; this is a lot of complexity here for a sleep(),
426
+ // but, it's not clear to me yet how to support an
427
+ // extensible, centralized cancelable `sleep()` elegantly.
428
+ await this.runningProcesses.add(async (onTerminate) => {
429
+ let sleepTimer;
430
+ let unsleep;
431
+ const sleep = new Promise(_unsleep => {
432
+ unsleep = _unsleep;
433
+ sleepTimer = setTimeout(unsleep, msNextFullSync);
411
434
  });
412
- return [2 /*return*/];
435
+ onTerminate.then(() => {
436
+ terminated = true;
437
+ this.syncQueriesObservableStartSleeping();
438
+ unsleep();
439
+ });
440
+ this.unsleepSyncQueriesObservable = unsleep;
441
+ this.syncQueriesObservableStartSleeping();
442
+ return sleep;
443
+ }, 'syncQueriesObservable sleep');
444
+ this.unsleepSyncQueriesObservable = null;
445
+ this.waitForSleepState = new Promise(resolve => {
446
+ this.syncQueriesObservableStartSleeping = resolve;
447
+ });
413
448
  }
414
- });
415
- }); }, 'sync start');
416
- });
417
- };
418
- SyncEngine.prototype.getModelsMetadataWithNextFullSync = function (currentTimeStamp) {
419
- return __awaiter(this, void 0, void 0, function () {
420
- var modelLastSync, _a;
421
- var _this = this;
422
- return __generator(this, function (_b) {
423
- switch (_b.label) {
424
- case 0:
425
- _a = Map.bind;
426
- return [4 /*yield*/, this.runningProcesses.add(function () { return _this.getModelsMetadata(); }, 'sync/index getModelsMetadataWithNextFullSync')];
427
- case 1:
428
- modelLastSync = new (_a.apply(Map, [void 0, (_b.sent()).map(function (_a) {
429
- var namespace = _a.namespace, model = _a.model, lastSync = _a.lastSync, lastFullSync = _a.lastFullSync, fullSyncInterval = _a.fullSyncInterval, lastSyncPredicate = _a.lastSyncPredicate;
430
- var nextFullSync = lastFullSync + fullSyncInterval;
431
- var syncFrom = !lastFullSync || nextFullSync < currentTimeStamp
432
- ? 0 // perform full sync if expired
433
- : lastSync; // perform delta sync
434
- return [
435
- _this.schema.namespaces[namespace].models[model],
436
- [namespace, syncFrom],
437
- ];
438
- })]))();
439
- return [2 /*return*/, modelLastSync];
440
- }
441
- });
442
- });
443
- };
444
- SyncEngine.prototype.syncQueriesObservable = function () {
445
- var _this = this;
446
- if (!this.online) {
447
- return (0, rxjs_1.of)({}); // TODO(v6): fix this
448
- }
449
- return new rxjs_1.Observable(function (observer) {
450
- var syncQueriesSubscription;
451
- _this.runningProcesses.isOpen &&
452
- _this.runningProcesses.add(function (onTerminate) { return __awaiter(_this, void 0, void 0, function () {
453
- var terminated, _loop_1, this_1;
454
- var _this = this;
455
- return __generator(this, function (_a) {
456
- switch (_a.label) {
457
- case 0:
458
- terminated = false;
459
- _loop_1 = function () {
460
- var count, modelLastSync, paginatingModels, lastFullSyncStartedAt, syncInterval, start, syncDuration, lastStartedAt, msNextFullSync;
461
- return __generator(this, function (_b) {
462
- switch (_b.label) {
463
- case 0:
464
- count = new WeakMap();
465
- return [4 /*yield*/, this_1.getModelsMetadataWithNextFullSync(Date.now())];
466
- case 1:
467
- modelLastSync = _b.sent();
468
- paginatingModels = new Set(modelLastSync.keys());
469
- return [4 /*yield*/, new Promise(function (resolve, reject) {
470
- if (!_this.runningProcesses.isOpen)
471
- resolve();
472
- onTerminate.then(function () { return resolve(); });
473
- syncQueriesSubscription = _this.syncQueriesProcessor
474
- .start(modelLastSync)
475
- .subscribe({
476
- next: function (_a) {
477
- var namespace = _a.namespace, modelDefinition = _a.modelDefinition, items = _a.items, done = _a.done, startedAt = _a.startedAt, isFullSync = _a.isFullSync;
478
- return __awaiter(_this, void 0, void 0, function () {
479
- var modelConstructor, modelName, modelMetadata_1, lastFullSync, fullSyncInterval, counts;
480
- var _this = this;
481
- return __generator(this, function (_b) {
482
- switch (_b.label) {
483
- case 0:
484
- modelConstructor = this.userModelClasses[modelDefinition.name];
485
- if (!count.has(modelConstructor)) {
486
- count.set(modelConstructor, {
487
- new: 0,
488
- updated: 0,
489
- deleted: 0,
490
- });
491
- start = (0, util_1.getNow)();
492
- lastStartedAt =
493
- lastStartedAt === undefined
494
- ? startedAt
495
- : Math.max(lastStartedAt, startedAt);
496
- }
497
- /**
498
- * If there are mutations in the outbox for a given id, those need to be
499
- * merged individually. Otherwise, we can merge them in batches.
500
- */
501
- return [4 /*yield*/, this.storage.runExclusive(function (storage) { return __awaiter(_this, void 0, void 0, function () {
502
- var idsInOutbox, oneByOne, page, opTypeCount, oneByOne_1, oneByOne_1_1, item, opType, e_1_1, _a, _b, _c, _d, counts;
503
- var e_1, _e;
504
- return __generator(this, function (_f) {
505
- switch (_f.label) {
506
- case 0: return [4 /*yield*/, this.outbox.getModelIds(storage)];
507
- case 1:
508
- idsInOutbox = _f.sent();
509
- oneByOne = [];
510
- page = items.filter(function (item) {
511
- var itemId = (0, utils_2.getIdentifierValue)(modelDefinition, item);
512
- if (!idsInOutbox.has(itemId)) {
513
- return true;
514
- }
515
- oneByOne.push(item);
516
- return false;
517
- });
518
- opTypeCount = [];
519
- _f.label = 2;
520
- case 2:
521
- _f.trys.push([2, 7, 8, 9]);
522
- oneByOne_1 = __values(oneByOne), oneByOne_1_1 = oneByOne_1.next();
523
- _f.label = 3;
524
- case 3:
525
- if (!!oneByOne_1_1.done) return [3 /*break*/, 6];
526
- item = oneByOne_1_1.value;
527
- return [4 /*yield*/, this.modelMerger.merge(storage, item, modelDefinition)];
528
- case 4:
529
- opType = _f.sent();
530
- if (opType !== undefined) {
531
- opTypeCount.push([item, opType]);
532
- }
533
- _f.label = 5;
534
- case 5:
535
- oneByOne_1_1 = oneByOne_1.next();
536
- return [3 /*break*/, 3];
537
- case 6: return [3 /*break*/, 9];
538
- case 7:
539
- e_1_1 = _f.sent();
540
- e_1 = { error: e_1_1 };
541
- return [3 /*break*/, 9];
542
- case 8:
543
- try {
544
- if (oneByOne_1_1 && !oneByOne_1_1.done && (_e = oneByOne_1.return)) _e.call(oneByOne_1);
545
- }
546
- finally { if (e_1) throw e_1.error; }
547
- return [7 /*endfinally*/];
548
- case 9:
549
- _b = (_a = opTypeCount.push).apply;
550
- _c = [opTypeCount];
551
- _d = [[]];
552
- return [4 /*yield*/, this.modelMerger.mergePage(storage, modelConstructor, page, modelDefinition)];
553
- case 10:
554
- _b.apply(_a, _c.concat([__spreadArray.apply(void 0, _d.concat([__read.apply(void 0, [(_f.sent())]), false]))]));
555
- counts = count.get(modelConstructor);
556
- opTypeCount.forEach(function (_a) {
557
- var _b = __read(_a, 2), opType = _b[1];
558
- switch (opType) {
559
- case types_1.OpType.INSERT:
560
- counts.new++;
561
- break;
562
- case types_1.OpType.UPDATE:
563
- counts.updated++;
564
- break;
565
- case types_1.OpType.DELETE:
566
- counts.deleted++;
567
- break;
568
- default:
569
- throw new Error("Invalid opType ".concat(opType));
570
- }
571
- });
572
- return [2 /*return*/];
573
- }
574
- });
575
- }); })];
576
- case 1:
577
- /**
578
- * If there are mutations in the outbox for a given id, those need to be
579
- * merged individually. Otherwise, we can merge them in batches.
580
- */
581
- _b.sent();
582
- if (!done) return [3 /*break*/, 4];
583
- modelName = modelDefinition.name;
584
- return [4 /*yield*/, this.getModelMetadata(namespace, modelName)];
585
- case 2:
586
- modelMetadata_1 = _b.sent();
587
- lastFullSync = modelMetadata_1.lastFullSync, fullSyncInterval = modelMetadata_1.fullSyncInterval;
588
- syncInterval = fullSyncInterval;
589
- lastFullSyncStartedAt =
590
- lastFullSyncStartedAt === undefined
591
- ? lastFullSync
592
- : Math.max(lastFullSyncStartedAt, isFullSync ? startedAt : lastFullSync);
593
- modelMetadata_1 = this.modelClasses
594
- .ModelMetadata.copyOf(modelMetadata_1, function (draft) {
595
- draft.lastSync = startedAt;
596
- draft.lastFullSync = isFullSync
597
- ? startedAt
598
- : modelMetadata_1.lastFullSync;
599
- });
600
- return [4 /*yield*/, this.storage.save(modelMetadata_1, undefined, ownSymbol)];
601
- case 3:
602
- _b.sent();
603
- counts = count.get(modelConstructor);
604
- this.modelSyncedStatus.set(modelConstructor, true);
605
- observer.next({
606
- type: ControlMessage.SYNC_ENGINE_MODEL_SYNCED,
607
- data: {
608
- model: modelConstructor,
609
- isFullSync: isFullSync,
610
- isDeltaSync: !isFullSync,
611
- counts: counts,
612
- },
613
- });
614
- paginatingModels.delete(modelDefinition);
615
- if (paginatingModels.size === 0) {
616
- syncDuration = (0, util_1.getNow)() - start;
617
- resolve();
618
- observer.next({
619
- type: ControlMessage.SYNC_ENGINE_SYNC_QUERIES_READY,
620
- });
621
- syncQueriesSubscription.unsubscribe();
622
- }
623
- _b.label = 4;
624
- case 4: return [2 /*return*/];
625
- }
626
- });
627
- });
628
- },
629
- error: function (error) {
630
- observer.error(error);
631
- },
632
- });
633
- observer.next({
634
- type: ControlMessage.SYNC_ENGINE_SYNC_QUERIES_STARTED,
635
- data: {
636
- models: Array.from(paginatingModels).map(function (_a) {
637
- var name = _a.name;
638
- return name;
639
- }),
640
- },
641
- });
642
- })];
643
- case 2:
644
- _b.sent();
645
- if (!lastFullSyncStartedAt) {
646
- msNextFullSync = syncInterval - syncDuration;
647
- }
648
- else {
649
- msNextFullSync =
650
- lastFullSyncStartedAt +
651
- syncInterval -
652
- (lastStartedAt + syncDuration);
653
- }
654
- logger.debug("Next fullSync in ".concat(msNextFullSync / 1000, " seconds. (").concat(new Date(Date.now() + msNextFullSync), ")"));
655
- // TODO: create `BackgroundProcessManager.sleep()` ... but, need to put
656
- // a lot of thought into what that contract looks like to
657
- // support possible use-cases:
658
- //
659
- // 1. non-cancelable
660
- // 2. cancelable, unsleep on exit()
661
- // 3. cancelable, throw Error on exit()
662
- // 4. cancelable, callback first on exit()?
663
- // 5. ... etc. ? ...
664
- //
665
- // TLDR; this is a lot of complexity here for a sleep(),
666
- // but, it's not clear to me yet how to support an
667
- // extensible, centralized cancelable `sleep()` elegantly.
668
- return [4 /*yield*/, this_1.runningProcesses.add(function (onTerminate) { return __awaiter(_this, void 0, void 0, function () {
669
- var sleepTimer, unsleep, sleep;
670
- var _this = this;
671
- return __generator(this, function (_a) {
672
- sleep = new Promise(function (_unsleep) {
673
- unsleep = _unsleep;
674
- sleepTimer = setTimeout(unsleep, msNextFullSync);
675
- });
676
- onTerminate.then(function () {
677
- terminated = true;
678
- _this.syncQueriesObservableStartSleeping();
679
- unsleep();
680
- });
681
- this.unsleepSyncQueriesObservable = unsleep;
682
- this.syncQueriesObservableStartSleeping();
683
- return [2 /*return*/, sleep];
684
- });
685
- }); }, 'syncQueriesObservable sleep')];
686
- case 3:
687
- // TODO: create `BackgroundProcessManager.sleep()` ... but, need to put
688
- // a lot of thought into what that contract looks like to
689
- // support possible use-cases:
690
- //
691
- // 1. non-cancelable
692
- // 2. cancelable, unsleep on exit()
693
- // 3. cancelable, throw Error on exit()
694
- // 4. cancelable, callback first on exit()?
695
- // 5. ... etc. ? ...
696
- //
697
- // TLDR; this is a lot of complexity here for a sleep(),
698
- // but, it's not clear to me yet how to support an
699
- // extensible, centralized cancelable `sleep()` elegantly.
700
- _b.sent();
701
- this_1.unsleepSyncQueriesObservable = null;
702
- this_1.waitForSleepState = new Promise(function (resolve) {
703
- _this.syncQueriesObservableStartSleeping = resolve;
704
- });
705
- return [2 /*return*/];
706
- }
707
- });
708
- };
709
- this_1 = this;
710
- _a.label = 1;
711
- case 1:
712
- if (!(!observer.closed && !terminated)) return [3 /*break*/, 3];
713
- return [5 /*yield**/, _loop_1()];
714
- case 2:
715
- _a.sent();
716
- return [3 /*break*/, 1];
717
- case 3: return [2 /*return*/];
718
- }
719
- });
720
- }); }, 'syncQueriesObservable main');
449
+ }, 'syncQueriesObservable main');
721
450
  });
722
- };
723
- SyncEngine.prototype.disconnectionHandler = function () {
724
- var _this = this;
725
- return function (msg) {
451
+ }
452
+ disconnectionHandler() {
453
+ return (msg) => {
726
454
  // This implementation is tied to AWSAppSyncRealTimeProvider 'Connection closed', 'Timeout disconnect' msg
727
455
  if (api_graphql_1.CONTROL_MSG.CONNECTION_CLOSED === msg ||
728
456
  api_graphql_1.CONTROL_MSG.TIMEOUT_DISCONNECT === msg) {
729
- _this.datastoreConnectivity.socketDisconnected();
457
+ this.datastoreConnectivity.socketDisconnected();
730
458
  }
731
459
  };
732
- };
733
- SyncEngine.prototype.unsubscribeConnectivity = function () {
460
+ }
461
+ unsubscribeConnectivity() {
734
462
  this.datastoreConnectivity.unsubscribe();
735
- };
463
+ }
736
464
  /**
737
465
  * Stops all subscription activities and resolves when all activies report
738
466
  * that they're disconnected, done retrying, etc..
739
467
  */
740
- SyncEngine.prototype.stop = function () {
741
- return __awaiter(this, void 0, void 0, function () {
742
- return __generator(this, function (_a) {
743
- switch (_a.label) {
744
- case 0:
745
- logger.debug('stopping sync engine');
746
- /**
747
- * Gracefully disconnecting subscribers first just prevents *more* work
748
- * from entering the pipelines.
749
- */
750
- this.unsubscribeConnectivity();
751
- /**
752
- * Stop listening for websocket connection disruption
753
- */
754
- this.stopDisruptionListener && this.stopDisruptionListener();
755
- /**
756
- * aggressively shut down any lingering background processes.
757
- * some of this might be semi-redundant with unsubscribing. however,
758
- * unsubscribing doesn't allow us to wait for settling.
759
- * (Whereas `stop()` does.)
760
- */
761
- return [4 /*yield*/, this.mutationsProcessor.stop()];
762
- case 1:
763
- /**
764
- * aggressively shut down any lingering background processes.
765
- * some of this might be semi-redundant with unsubscribing. however,
766
- * unsubscribing doesn't allow us to wait for settling.
767
- * (Whereas `stop()` does.)
768
- */
769
- _a.sent();
770
- return [4 /*yield*/, this.subscriptionsProcessor.stop()];
771
- case 2:
772
- _a.sent();
773
- return [4 /*yield*/, this.datastoreConnectivity.stop()];
774
- case 3:
775
- _a.sent();
776
- return [4 /*yield*/, this.syncQueriesProcessor.stop()];
777
- case 4:
778
- _a.sent();
779
- return [4 /*yield*/, this.runningProcesses.close()];
780
- case 5:
781
- _a.sent();
782
- return [4 /*yield*/, this.runningProcesses.open()];
783
- case 6:
784
- _a.sent();
785
- logger.debug('sync engine stopped and ready to restart');
786
- return [2 /*return*/];
787
- }
788
- });
789
- });
790
- };
791
- SyncEngine.prototype.setupModels = function (params) {
792
- return __awaiter(this, void 0, void 0, function () {
793
- var fullSyncInterval, ModelMetadataConstructor, models, savedModel, promises, result, _a, _b, modelMetadata, modelName, e_2_1;
794
- var e_2, _c;
795
- var _this = this;
796
- return __generator(this, function (_d) {
797
- switch (_d.label) {
798
- case 0:
799
- fullSyncInterval = params.fullSyncInterval;
800
- ModelMetadataConstructor = this.modelClasses
801
- .ModelMetadata;
802
- models = [];
803
- Object.values(this.schema.namespaces).forEach(function (namespace) {
804
- Object.values(namespace.models)
805
- .filter(function (_a) {
806
- var syncable = _a.syncable;
807
- return syncable;
808
- })
809
- .forEach(function (model) {
810
- models.push([namespace.name, model]);
811
- if (namespace.name === util_1.USER) {
812
- var modelConstructor = _this.userModelClasses[model.name];
813
- _this.modelSyncedStatus.set(modelConstructor, false);
814
- }
815
- });
816
- });
817
- promises = models.map(function (_a) {
818
- var _b = __read(_a, 2), namespace = _b[0], model = _b[1];
819
- return __awaiter(_this, void 0, void 0, function () {
820
- var modelMetadata, syncPredicate, lastSyncPredicate, prevSyncPredicate, syncPredicateUpdated_1;
821
- var _c, _d, _e, _f;
822
- return __generator(this, function (_g) {
823
- switch (_g.label) {
824
- case 0: return [4 /*yield*/, this.getModelMetadata(namespace, model.name)];
825
- case 1:
826
- modelMetadata = _g.sent();
827
- syncPredicate = predicates_1.ModelPredicateCreator.getPredicates(this.syncPredicates.get(model), false);
828
- lastSyncPredicate = syncPredicate
829
- ? JSON.stringify(syncPredicate)
830
- : null;
831
- if (!(modelMetadata === undefined)) return [3 /*break*/, 3];
832
- return [4 /*yield*/, this.storage.save(this.modelInstanceCreator(ModelMetadataConstructor, {
833
- model: model.name,
834
- namespace: namespace,
835
- lastSync: null,
836
- fullSyncInterval: fullSyncInterval,
837
- lastFullSync: null,
838
- lastSyncPredicate: lastSyncPredicate,
839
- }), undefined, ownSymbol)];
840
- case 2:
841
- _c = __read.apply(void 0, [_g.sent(), 1]), _d = __read(_c[0], 1), savedModel = _d[0];
842
- return [3 /*break*/, 5];
843
- case 3:
844
- prevSyncPredicate = modelMetadata.lastSyncPredicate
845
- ? modelMetadata.lastSyncPredicate
846
- : null;
847
- syncPredicateUpdated_1 = prevSyncPredicate !== lastSyncPredicate;
848
- return [4 /*yield*/, this.storage.save(ModelMetadataConstructor.copyOf(modelMetadata, function (draft) {
849
- draft.fullSyncInterval = fullSyncInterval;
850
- // perform a base sync if the syncPredicate changed in between calls to DataStore.start
851
- // ensures that the local store contains all the data specified by the syncExpression
852
- if (syncPredicateUpdated_1) {
853
- draft.lastSync = null;
854
- draft.lastFullSync = null;
855
- draft.lastSyncPredicate = lastSyncPredicate;
856
- }
857
- }))];
858
- case 4:
859
- _e = __read.apply(void 0, [_g.sent(), 1]), _f = __read(_e[0], 1), savedModel = _f[0];
860
- _g.label = 5;
861
- case 5: return [2 /*return*/, savedModel];
862
- }
863
- });
864
- });
865
- });
866
- result = {};
867
- _d.label = 1;
868
- case 1:
869
- _d.trys.push([1, 6, 7, 8]);
870
- return [4 /*yield*/, Promise.all(promises)];
871
- case 2:
872
- _a = __values.apply(void 0, [_d.sent()]), _b = _a.next();
873
- _d.label = 3;
874
- case 3:
875
- if (!!_b.done) return [3 /*break*/, 5];
876
- modelMetadata = _b.value;
877
- modelName = modelMetadata.model;
878
- result[modelName] = modelMetadata;
879
- _d.label = 4;
880
- case 4:
881
- _b = _a.next();
882
- return [3 /*break*/, 3];
883
- case 5: return [3 /*break*/, 8];
884
- case 6:
885
- e_2_1 = _d.sent();
886
- e_2 = { error: e_2_1 };
887
- return [3 /*break*/, 8];
888
- case 7:
889
- try {
890
- if (_b && !_b.done && (_c = _a.return)) _c.call(_a);
891
- }
892
- finally { if (e_2) throw e_2.error; }
893
- return [7 /*endfinally*/];
894
- case 8: return [2 /*return*/, result];
468
+ async stop() {
469
+ logger.debug('stopping sync engine');
470
+ /**
471
+ * Gracefully disconnecting subscribers first just prevents *more* work
472
+ * from entering the pipelines.
473
+ */
474
+ this.unsubscribeConnectivity();
475
+ /**
476
+ * Stop listening for websocket connection disruption
477
+ */
478
+ this.stopDisruptionListener && this.stopDisruptionListener();
479
+ /**
480
+ * aggressively shut down any lingering background processes.
481
+ * some of this might be semi-redundant with unsubscribing. however,
482
+ * unsubscribing doesn't allow us to wait for settling.
483
+ * (Whereas `stop()` does.)
484
+ */
485
+ await this.mutationsProcessor.stop();
486
+ await this.subscriptionsProcessor.stop();
487
+ await this.datastoreConnectivity.stop();
488
+ await this.syncQueriesProcessor.stop();
489
+ await this.runningProcesses.close();
490
+ await this.runningProcesses.open();
491
+ logger.debug('sync engine stopped and ready to restart');
492
+ }
493
+ async setupModels(params) {
494
+ const { fullSyncInterval } = params;
495
+ const ModelMetadataConstructor = this.modelClasses
496
+ .ModelMetadata;
497
+ const models = [];
498
+ let savedModel;
499
+ Object.values(this.schema.namespaces).forEach(namespace => {
500
+ Object.values(namespace.models)
501
+ .filter(({ syncable }) => syncable)
502
+ .forEach(model => {
503
+ models.push([namespace.name, model]);
504
+ if (namespace.name === util_1.USER) {
505
+ const modelConstructor = this.userModelClasses[model.name];
506
+ this.modelSyncedStatus.set(modelConstructor, false);
895
507
  }
896
508
  });
897
509
  });
898
- };
899
- SyncEngine.prototype.getModelsMetadata = function () {
900
- return __awaiter(this, void 0, void 0, function () {
901
- var ModelMetadata, modelsMetadata;
902
- return __generator(this, function (_a) {
903
- switch (_a.label) {
904
- case 0:
905
- ModelMetadata = this.modelClasses
906
- .ModelMetadata;
907
- return [4 /*yield*/, this.storage.query(ModelMetadata)];
908
- case 1:
909
- modelsMetadata = _a.sent();
910
- return [2 /*return*/, modelsMetadata];
911
- }
912
- });
510
+ const promises = models.map(async ([namespace, model]) => {
511
+ const modelMetadata = await this.getModelMetadata(namespace, model.name);
512
+ const syncPredicate = predicates_1.ModelPredicateCreator.getPredicates(this.syncPredicates.get(model), false);
513
+ const lastSyncPredicate = syncPredicate
514
+ ? JSON.stringify(syncPredicate)
515
+ : null;
516
+ if (modelMetadata === undefined) {
517
+ [[savedModel]] = await this.storage.save(this.modelInstanceCreator(ModelMetadataConstructor, {
518
+ model: model.name,
519
+ namespace,
520
+ lastSync: null,
521
+ fullSyncInterval,
522
+ lastFullSync: null,
523
+ lastSyncPredicate,
524
+ }), undefined, ownSymbol);
525
+ }
526
+ else {
527
+ const prevSyncPredicate = modelMetadata.lastSyncPredicate
528
+ ? modelMetadata.lastSyncPredicate
529
+ : null;
530
+ const syncPredicateUpdated = prevSyncPredicate !== lastSyncPredicate;
531
+ [[savedModel]] = await this.storage.save(ModelMetadataConstructor.copyOf(modelMetadata, draft => {
532
+ draft.fullSyncInterval = fullSyncInterval;
533
+ // perform a base sync if the syncPredicate changed in between calls to DataStore.start
534
+ // ensures that the local store contains all the data specified by the syncExpression
535
+ if (syncPredicateUpdated) {
536
+ draft.lastSync = null;
537
+ draft.lastFullSync = null;
538
+ draft.lastSyncPredicate = lastSyncPredicate;
539
+ }
540
+ }));
541
+ }
542
+ return savedModel;
913
543
  });
914
- };
915
- SyncEngine.prototype.getModelMetadata = function (namespace, model) {
916
- return __awaiter(this, void 0, void 0, function () {
917
- var ModelMetadata, predicate, _a, modelMetadata;
918
- return __generator(this, function (_b) {
919
- switch (_b.label) {
920
- case 0:
921
- ModelMetadata = this.modelClasses
922
- .ModelMetadata;
923
- predicate = predicates_1.ModelPredicateCreator.createFromAST(this.schema.namespaces[util_1.SYNC].models[ModelMetadata.name], { and: [{ namespace: { eq: namespace } }, { model: { eq: model } }] });
924
- return [4 /*yield*/, this.storage.query(ModelMetadata, predicate, {
925
- page: 0,
926
- limit: 1,
927
- })];
928
- case 1:
929
- _a = __read.apply(void 0, [_b.sent(), 1]), modelMetadata = _a[0];
930
- return [2 /*return*/, modelMetadata];
931
- }
932
- });
544
+ const result = {};
545
+ for (const modelMetadata of await Promise.all(promises)) {
546
+ const { model: modelName } = modelMetadata;
547
+ result[modelName] = modelMetadata;
548
+ }
549
+ return result;
550
+ }
551
+ async getModelsMetadata() {
552
+ const ModelMetadata = this.modelClasses
553
+ .ModelMetadata;
554
+ const modelsMetadata = await this.storage.query(ModelMetadata);
555
+ return modelsMetadata;
556
+ }
557
+ async getModelMetadata(namespace, model) {
558
+ const ModelMetadata = this.modelClasses
559
+ .ModelMetadata;
560
+ const predicate = predicates_1.ModelPredicateCreator.createFromAST(this.schema.namespaces[util_1.SYNC].models[ModelMetadata.name], { and: [{ namespace: { eq: namespace } }, { model: { eq: model } }] });
561
+ const [modelMetadata] = await this.storage.query(ModelMetadata, predicate, {
562
+ page: 0,
563
+ limit: 1,
933
564
  });
934
- };
935
- SyncEngine.prototype.getModelDefinition = function (modelConstructor) {
936
- var namespaceName = this.namespaceResolver(modelConstructor);
937
- var modelDefinition = this.schema.namespaces[namespaceName].models[modelConstructor.name];
565
+ return modelMetadata;
566
+ }
567
+ getModelDefinition(modelConstructor) {
568
+ const namespaceName = this.namespaceResolver(modelConstructor);
569
+ const modelDefinition = this.schema.namespaces[namespaceName].models[modelConstructor.name];
938
570
  return modelDefinition;
939
- };
940
- SyncEngine.getNamespace = function () {
941
- var namespace = {
571
+ }
572
+ static getNamespace() {
573
+ const namespace = {
942
574
  name: util_1.SYNC,
943
575
  relationships: {},
944
576
  enums: {
@@ -1046,7 +678,7 @@ var SyncEngine = /** @class */ (function () {
1046
678
  },
1047
679
  };
1048
680
  return namespace;
1049
- };
681
+ }
1050
682
  /**
1051
683
  * listen for websocket connection disruption
1052
684
  *
@@ -1054,43 +686,38 @@ var SyncEngine = /** @class */ (function () {
1054
686
  * from AppSync were missed. A sync needs to be triggered to
1055
687
  * retrieve the missed data.
1056
688
  */
1057
- SyncEngine.prototype.startDisruptionListener = function () {
1058
- var _this = this;
1059
- return core_1.Hub.listen('api', function (data) {
689
+ startDisruptionListener() {
690
+ return core_1.Hub.listen('api', (data) => {
1060
691
  if (data.source === 'PubSub' &&
1061
692
  data.payload.event === api_graphql_1.CONNECTION_STATE_CHANGE) {
1062
- var connectionState = data.payload.data
693
+ const connectionState = data.payload.data
1063
694
  .connectionState;
1064
695
  switch (connectionState) {
1065
696
  // Do not need to listen for ConnectionDisruptedPendingNetwork
1066
697
  // Normal network reconnection logic will handle the sync
1067
698
  case api_graphql_1.ConnectionState.ConnectionDisrupted:
1068
- _this.connectionDisrupted = true;
699
+ this.connectionDisrupted = true;
1069
700
  break;
1070
701
  case api_graphql_1.ConnectionState.Connected:
1071
- if (_this.connectionDisrupted) {
1072
- _this.scheduleSync();
702
+ if (this.connectionDisrupted) {
703
+ this.scheduleSync();
1073
704
  }
1074
- _this.connectionDisrupted = false;
705
+ this.connectionDisrupted = false;
1075
706
  break;
1076
707
  }
1077
708
  }
1078
709
  });
1079
- };
710
+ }
1080
711
  /*
1081
712
  * Schedule a sync to start when syncQueriesObservable enters sleep state
1082
713
  * Start sync immediately if syncQueriesObservable is already in sleep state
1083
714
  */
1084
- SyncEngine.prototype.scheduleSync = function () {
1085
- var _this = this;
715
+ scheduleSync() {
1086
716
  return (this.runningProcesses.isOpen &&
1087
- this.runningProcesses.add(function () {
1088
- return _this.waitForSleepState.then(function () {
1089
- // unsleepSyncQueriesObservable will be set if waitForSleepState has resolved
1090
- _this.unsleepSyncQueriesObservable();
1091
- });
1092
- }));
1093
- };
1094
- return SyncEngine;
1095
- }());
717
+ this.runningProcesses.add(() => this.waitForSleepState.then(() => {
718
+ // unsleepSyncQueriesObservable will be set if waitForSleepState has resolved
719
+ this.unsleepSyncQueriesObservable();
720
+ })));
721
+ }
722
+ }
1096
723
  exports.SyncEngine = SyncEngine;