@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
@@ -1,97 +1,3 @@
1
- var __extends = (this && this.__extends) || (function () {
2
- var extendStatics = function (d, b) {
3
- extendStatics = Object.setPrototypeOf ||
4
- ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
5
- function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
6
- return extendStatics(d, b);
7
- };
8
- return function (d, b) {
9
- if (typeof b !== "function" && b !== null)
10
- throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
11
- extendStatics(d, b);
12
- function __() { this.constructor = d; }
13
- d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
14
- };
15
- })();
16
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
17
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
18
- return new (P || (P = Promise))(function (resolve, reject) {
19
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
20
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
21
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
22
- step((generator = generator.apply(thisArg, _arguments || [])).next());
23
- });
24
- };
25
- var __generator = (this && this.__generator) || function (thisArg, body) {
26
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
27
- return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
28
- function verb(n) { return function (v) { return step([n, v]); }; }
29
- function step(op) {
30
- if (f) throw new TypeError("Generator is already executing.");
31
- while (g && (g = 0, op[0] && (_ = 0)), _) try {
32
- 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;
33
- if (y = 0, t) op = [op[0] & 2, t.value];
34
- switch (op[0]) {
35
- case 0: case 1: t = op; break;
36
- case 4: _.label++; return { value: op[1], done: false };
37
- case 5: _.label++; y = op[1]; op = [0]; continue;
38
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
39
- default:
40
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
41
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
42
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
43
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
44
- if (t[2]) _.ops.pop();
45
- _.trys.pop(); continue;
46
- }
47
- op = body.call(thisArg, _);
48
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
49
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
50
- }
51
- };
52
- var __asyncValues = (this && this.__asyncValues) || function (o) {
53
- if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
54
- var m = o[Symbol.asyncIterator], i;
55
- return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
56
- function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
57
- function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
58
- };
59
- var __values = (this && this.__values) || function(o) {
60
- var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
61
- if (m) return m.call(o);
62
- if (o && typeof o.length === "number") return {
63
- next: function () {
64
- if (o && i >= o.length) o = void 0;
65
- return { value: o && o[i++], done: !o };
66
- }
67
- };
68
- throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
69
- };
70
- var __read = (this && this.__read) || function (o, n) {
71
- var m = typeof Symbol === "function" && o[Symbol.iterator];
72
- if (!m) return o;
73
- var i = m.call(o), r, ar = [], e;
74
- try {
75
- while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
76
- }
77
- catch (error) { e = { error: error }; }
78
- finally {
79
- try {
80
- if (r && !r.done && (m = i["return"])) m.call(i);
81
- }
82
- finally { if (e) throw e.error; }
83
- }
84
- return ar;
85
- };
86
- var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
87
- if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
88
- if (ar || !(i in from)) {
89
- if (!ar) ar = Array.prototype.slice.call(from, 0, i);
90
- ar[i] = from[i];
91
- }
92
- }
93
- return to.concat(ar || Array.prototype.slice.call(from));
94
- };
95
1
  // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
96
2
  // SPDX-License-Identifier: Apache-2.0
97
3
  import * as idb from 'idb';
@@ -99,7 +5,7 @@ import { isPredicateObj, isPredicateGroup, OpType, QueryOne, } from '../../types
99
5
  import { isPrivateMode, traverseModel, validatePredicate, inMemoryPagination, keysEqual, getStorename, isSafariCompatabilityMode, } from '../../util';
100
6
  import { StorageAdapterBase } from './StorageAdapterBase';
101
7
  import { ConsoleLogger } from '@aws-amplify/core';
102
- var logger = new ConsoleLogger('DataStore');
8
+ const logger = new ConsoleLogger('DataStore');
103
9
  /**
104
10
  * The point after which queries composed of multiple simple OR conditions
105
11
  * should scan-and-filter instead of individual queries for each condition.
@@ -121,14 +27,13 @@ var logger = new ConsoleLogger('DataStore');
121
27
  * etc...
122
28
  *
123
29
  */
124
- var MULTI_OR_CONDITION_SCAN_BREAKPOINT = 7;
30
+ const MULTI_OR_CONDITION_SCAN_BREAKPOINT = 7;
125
31
  //
126
- var DB_VERSION = 3;
127
- var IndexedDBAdapter = /** @class */ (function (_super) {
128
- __extends(IndexedDBAdapter, _super);
129
- function IndexedDBAdapter() {
130
- var _this = _super !== null && _super.apply(this, arguments) || this;
131
- _this.safariCompatabilityMode = false;
32
+ const DB_VERSION = 3;
33
+ class IndexedDBAdapter extends StorageAdapterBase {
34
+ constructor() {
35
+ super(...arguments);
36
+ this.safariCompatabilityMode = false;
132
37
  /**
133
38
  * Checks the given path against the browser's IndexedDB implementation for
134
39
  * necessary compatibility transformations, applying those transforms if needed.
@@ -137,43 +42,22 @@ var IndexedDBAdapter = /** @class */ (function (_super) {
137
42
  * @returns An array or string, depending on and given key,
138
43
  * that is ensured to be compatible with the IndexedDB implementation's nuances.
139
44
  */
140
- _this.canonicalKeyPath = function (keyArr) {
141
- if (_this.safariCompatabilityMode) {
45
+ this.canonicalKeyPath = (keyArr) => {
46
+ if (this.safariCompatabilityMode) {
142
47
  return keyArr.length > 1 ? keyArr : keyArr[0];
143
48
  }
144
49
  return keyArr;
145
50
  };
146
- return _this;
147
51
  //#endregion
148
52
  }
149
53
  // checks are called by StorageAdapterBase class
150
- IndexedDBAdapter.prototype.preSetUpChecks = function () {
151
- return __awaiter(this, void 0, void 0, function () {
152
- return __generator(this, function (_a) {
153
- switch (_a.label) {
154
- case 0: return [4 /*yield*/, this.checkPrivate()];
155
- case 1:
156
- _a.sent();
157
- return [4 /*yield*/, this.setSafariCompatabilityMode()];
158
- case 2:
159
- _a.sent();
160
- return [2 /*return*/];
161
- }
162
- });
163
- });
164
- };
165
- IndexedDBAdapter.prototype.preOpCheck = function () {
166
- return __awaiter(this, void 0, void 0, function () {
167
- return __generator(this, function (_a) {
168
- switch (_a.label) {
169
- case 0: return [4 /*yield*/, this.checkPrivate()];
170
- case 1:
171
- _a.sent();
172
- return [2 /*return*/];
173
- }
174
- });
175
- });
176
- };
54
+ async preSetUpChecks() {
55
+ await this.checkPrivate();
56
+ await this.setSafariCompatabilityMode();
57
+ }
58
+ async preOpCheck() {
59
+ await this.checkPrivate();
60
+ }
177
61
  /**
178
62
  * Initialize IndexedDB database
179
63
  * Create new DB if one doesn't exist
@@ -183,559 +67,245 @@ var IndexedDBAdapter = /** @class */ (function (_super) {
183
67
  *
184
68
  * @returns IDB Database instance
185
69
  */
186
- IndexedDBAdapter.prototype.initDb = function () {
187
- return __awaiter(this, void 0, void 0, function () {
188
- var _this = this;
189
- return __generator(this, function (_a) {
190
- switch (_a.label) {
191
- case 0: return [4 /*yield*/, idb.openDB(this.dbName, DB_VERSION, {
192
- upgrade: function (db, oldVersion, newVersion, txn) { return __awaiter(_this, void 0, void 0, function () {
193
- var _a, _b, storeName, origStore, tmpName, _c, namespaceName, modelName, modelInCurrentSchema, newStore, cursor, count, e_1_1, error_1;
194
- var e_1, _d;
195
- var _this = this;
196
- return __generator(this, function (_e) {
197
- switch (_e.label) {
198
- case 0:
199
- // create new database
200
- if (oldVersion === 0) {
201
- Object.keys(this.schema.namespaces).forEach(function (namespaceName) {
202
- var namespace = _this.schema.namespaces[namespaceName];
203
- Object.keys(namespace.models).forEach(function (modelName) {
204
- var storeName = getStorename(namespaceName, modelName);
205
- _this.createObjectStoreForModel(db, namespaceName, storeName, modelName);
206
- });
207
- });
208
- return [2 /*return*/];
209
- }
210
- if (!((oldVersion === 1 || oldVersion === 2) && newVersion === 3)) return [3 /*break*/, 16];
211
- _e.label = 1;
212
- case 1:
213
- _e.trys.push([1, 14, , 15]);
214
- _e.label = 2;
215
- case 2:
216
- _e.trys.push([2, 11, 12, 13]);
217
- _a = __values(txn.objectStoreNames), _b = _a.next();
218
- _e.label = 3;
219
- case 3:
220
- if (!!_b.done) return [3 /*break*/, 10];
221
- storeName = _b.value;
222
- origStore = txn.objectStore(storeName);
223
- tmpName = "tmp_".concat(storeName);
224
- origStore.name = tmpName;
225
- _c = this.getNamespaceAndModelFromStorename(storeName), namespaceName = _c.namespaceName, modelName = _c.modelName;
226
- modelInCurrentSchema = modelName in this.schema.namespaces[namespaceName].models;
227
- if (!modelInCurrentSchema) {
228
- // delete original
229
- db.deleteObjectStore(tmpName);
230
- return [3 /*break*/, 9];
231
- }
232
- newStore = this.createObjectStoreForModel(db, namespaceName, storeName, modelName);
233
- return [4 /*yield*/, origStore.openCursor()];
234
- case 4:
235
- cursor = _e.sent();
236
- count = 0;
237
- _e.label = 5;
238
- case 5:
239
- if (!(cursor && cursor.value)) return [3 /*break*/, 8];
240
- // we don't pass key, since they are all new entries in the new store
241
- return [4 /*yield*/, newStore.put(cursor.value)];
242
- case 6:
243
- // we don't pass key, since they are all new entries in the new store
244
- _e.sent();
245
- return [4 /*yield*/, cursor.continue()];
246
- case 7:
247
- cursor = _e.sent();
248
- count++;
249
- return [3 /*break*/, 5];
250
- case 8:
251
- // delete original
252
- db.deleteObjectStore(tmpName);
253
- logger.debug("".concat(count, " ").concat(storeName, " records migrated"));
254
- _e.label = 9;
255
- case 9:
256
- _b = _a.next();
257
- return [3 /*break*/, 3];
258
- case 10: return [3 /*break*/, 13];
259
- case 11:
260
- e_1_1 = _e.sent();
261
- e_1 = { error: e_1_1 };
262
- return [3 /*break*/, 13];
263
- case 12:
264
- try {
265
- if (_b && !_b.done && (_d = _a.return)) _d.call(_a);
266
- }
267
- finally { if (e_1) throw e_1.error; }
268
- return [7 /*endfinally*/];
269
- case 13:
270
- // add new models created after IndexedDB, but before migration
271
- // this case may happen when a user has not opened an app for
272
- // some time and a new model is added during that time
273
- Object.keys(this.schema.namespaces).forEach(function (namespaceName) {
274
- var namespace = _this.schema.namespaces[namespaceName];
275
- var objectStoreNames = new Set(txn.objectStoreNames);
276
- Object.keys(namespace.models)
277
- .map(function (modelName) {
278
- return [modelName, getStorename(namespaceName, modelName)];
279
- })
280
- .filter(function (_a) {
281
- var _b = __read(_a, 2), storeName = _b[1];
282
- return !objectStoreNames.has(storeName);
283
- })
284
- .forEach(function (_a) {
285
- var _b = __read(_a, 2), modelName = _b[0], storeName = _b[1];
286
- _this.createObjectStoreForModel(db, namespaceName, storeName, modelName);
287
- });
288
- });
289
- return [3 /*break*/, 15];
290
- case 14:
291
- error_1 = _e.sent();
292
- logger.error('Error migrating IndexedDB data', error_1);
293
- txn.abort();
294
- throw error_1;
295
- case 15: return [2 /*return*/];
296
- case 16: return [2 /*return*/];
297
- }
298
- });
299
- }); },
300
- })];
301
- case 1: return [2 /*return*/, _a.sent()];
70
+ async initDb() {
71
+ return await idb.openDB(this.dbName, DB_VERSION, {
72
+ upgrade: async (db, oldVersion, newVersion, txn) => {
73
+ // create new database
74
+ if (oldVersion === 0) {
75
+ Object.keys(this.schema.namespaces).forEach(namespaceName => {
76
+ const namespace = this.schema.namespaces[namespaceName];
77
+ Object.keys(namespace.models).forEach(modelName => {
78
+ const storeName = getStorename(namespaceName, modelName);
79
+ this.createObjectStoreForModel(db, namespaceName, storeName, modelName);
80
+ });
81
+ });
82
+ return;
302
83
  }
303
- });
304
- });
305
- };
306
- IndexedDBAdapter.prototype._get = function (storeOrStoreName, keyArr) {
307
- return __awaiter(this, void 0, void 0, function () {
308
- var index, storeName, store, result;
309
- return __generator(this, function (_a) {
310
- switch (_a.label) {
311
- case 0:
312
- if (typeof storeOrStoreName === 'string') {
313
- storeName = storeOrStoreName;
314
- index = this.db.transaction(storeName, 'readonly').store.index('byPk');
315
- }
316
- else {
317
- store = storeOrStoreName;
318
- index = store.index('byPk');
84
+ // migrate existing database to latest schema
85
+ if ((oldVersion === 1 || oldVersion === 2) && newVersion === 3) {
86
+ try {
87
+ for (const storeName of txn.objectStoreNames) {
88
+ const origStore = txn.objectStore(storeName);
89
+ // rename original store
90
+ const tmpName = `tmp_${storeName}`;
91
+ origStore.name = tmpName;
92
+ const { namespaceName, modelName } = this.getNamespaceAndModelFromStorename(storeName);
93
+ const modelInCurrentSchema = modelName in this.schema.namespaces[namespaceName].models;
94
+ if (!modelInCurrentSchema) {
95
+ // delete original
96
+ db.deleteObjectStore(tmpName);
97
+ continue;
98
+ }
99
+ const newStore = this.createObjectStoreForModel(db, namespaceName, storeName, modelName);
100
+ let cursor = await origStore.openCursor();
101
+ let count = 0;
102
+ // Copy data from original to new
103
+ while (cursor && cursor.value) {
104
+ // we don't pass key, since they are all new entries in the new store
105
+ await newStore.put(cursor.value);
106
+ cursor = await cursor.continue();
107
+ count++;
108
+ }
109
+ // delete original
110
+ db.deleteObjectStore(tmpName);
111
+ logger.debug(`${count} ${storeName} records migrated`);
319
112
  }
320
- return [4 /*yield*/, index.get(this.canonicalKeyPath(keyArr))];
321
- case 1:
322
- result = _a.sent();
323
- return [2 /*return*/, result];
324
- }
325
- });
326
- });
327
- };
328
- IndexedDBAdapter.prototype.clear = function () {
329
- var _a;
330
- return __awaiter(this, void 0, void 0, function () {
331
- return __generator(this, function (_b) {
332
- switch (_b.label) {
333
- case 0: return [4 /*yield*/, this.checkPrivate()];
334
- case 1:
335
- _b.sent();
336
- (_a = this.db) === null || _a === void 0 ? void 0 : _a.close();
337
- return [4 /*yield*/, idb.deleteDB(this.dbName)];
338
- case 2:
339
- _b.sent();
340
- this.db = undefined;
341
- this.initPromise = undefined;
342
- return [2 /*return*/];
343
- }
344
- });
345
- });
346
- };
347
- IndexedDBAdapter.prototype.save = function (model, condition) {
348
- var _a, e_2, _b, _c;
349
- return __awaiter(this, void 0, void 0, function () {
350
- var _d, storeName, set, connectionStoreNames, modelKeyValues, tx, store, fromDB, result, _loop_1, this_1, _e, connectionStoreNames_1, connectionStoreNames_1_1, e_2_1;
351
- return __generator(this, function (_f) {
352
- switch (_f.label) {
353
- case 0: return [4 /*yield*/, this.checkPrivate()];
354
- case 1:
355
- _f.sent();
356
- _d = this.saveMetadata(model), storeName = _d.storeName, set = _d.set, connectionStoreNames = _d.connectionStoreNames, modelKeyValues = _d.modelKeyValues;
357
- tx = this.db.transaction(__spreadArray([storeName], __read(Array.from(set.values())), false), 'readwrite');
358
- store = tx.objectStore(storeName);
359
- return [4 /*yield*/, this._get(store, modelKeyValues)];
360
- case 2:
361
- fromDB = _f.sent();
362
- this.validateSaveCondition(condition, fromDB);
363
- result = [];
364
- _f.label = 3;
365
- case 3:
366
- _f.trys.push([3, 9, 10, 15]);
367
- _loop_1 = function () {
368
- var resItem, storeName_1, item, instance, keys, store_1, itemKeyValues, fromDB_1, opType, key;
369
- return __generator(this, function (_g) {
370
- switch (_g.label) {
371
- case 0:
372
- _c = connectionStoreNames_1_1.value;
373
- _e = false;
374
- _g.label = 1;
375
- case 1:
376
- _g.trys.push([1, , 6, 7]);
377
- resItem = _c;
378
- storeName_1 = resItem.storeName, item = resItem.item, instance = resItem.instance, keys = resItem.keys;
379
- store_1 = tx.objectStore(storeName_1);
380
- itemKeyValues = keys.map(function (key) { return item[key]; });
381
- return [4 /*yield*/, this_1._get(store_1, itemKeyValues)];
382
- case 2:
383
- fromDB_1 = _g.sent();
384
- opType = fromDB_1 ? OpType.UPDATE : OpType.INSERT;
385
- if (!(keysEqual(itemKeyValues, modelKeyValues) ||
386
- opType === OpType.INSERT)) return [3 /*break*/, 5];
387
- return [4 /*yield*/, store_1
388
- .index('byPk')
389
- .getKey(this_1.canonicalKeyPath(itemKeyValues))];
390
- case 3:
391
- key = _g.sent();
392
- return [4 /*yield*/, store_1.put(item, key)];
393
- case 4:
394
- _g.sent();
395
- result.push([instance, opType]);
396
- _g.label = 5;
397
- case 5: return [3 /*break*/, 7];
398
- case 6:
399
- _e = true;
400
- return [7 /*endfinally*/];
401
- case 7: return [2 /*return*/];
402
- }
113
+ // add new models created after IndexedDB, but before migration
114
+ // this case may happen when a user has not opened an app for
115
+ // some time and a new model is added during that time
116
+ Object.keys(this.schema.namespaces).forEach(namespaceName => {
117
+ const namespace = this.schema.namespaces[namespaceName];
118
+ const objectStoreNames = new Set(txn.objectStoreNames);
119
+ Object.keys(namespace.models)
120
+ .map(modelName => {
121
+ return [modelName, getStorename(namespaceName, modelName)];
122
+ })
123
+ .filter(([, storeName]) => !objectStoreNames.has(storeName))
124
+ .forEach(([modelName, storeName]) => {
125
+ this.createObjectStoreForModel(db, namespaceName, storeName, modelName);
403
126
  });
404
- };
405
- this_1 = this;
406
- _e = true, connectionStoreNames_1 = __asyncValues(connectionStoreNames);
407
- _f.label = 4;
408
- case 4: return [4 /*yield*/, connectionStoreNames_1.next()];
409
- case 5:
410
- if (!(connectionStoreNames_1_1 = _f.sent(), _a = connectionStoreNames_1_1.done, !_a)) return [3 /*break*/, 8];
411
- return [5 /*yield**/, _loop_1()];
412
- case 6:
413
- _f.sent();
414
- _f.label = 7;
415
- case 7: return [3 /*break*/, 4];
416
- case 8: return [3 /*break*/, 15];
417
- case 9:
418
- e_2_1 = _f.sent();
419
- e_2 = { error: e_2_1 };
420
- return [3 /*break*/, 15];
421
- case 10:
422
- _f.trys.push([10, , 13, 14]);
423
- if (!(!_e && !_a && (_b = connectionStoreNames_1.return))) return [3 /*break*/, 12];
424
- return [4 /*yield*/, _b.call(connectionStoreNames_1)];
425
- case 11:
426
- _f.sent();
427
- _f.label = 12;
428
- case 12: return [3 /*break*/, 14];
429
- case 13:
430
- if (e_2) throw e_2.error;
431
- return [7 /*endfinally*/];
432
- case 14: return [7 /*endfinally*/];
433
- case 15: return [4 /*yield*/, tx.done];
434
- case 16:
435
- _f.sent();
436
- return [2 /*return*/, result];
437
- }
438
- });
439
- });
440
- };
441
- IndexedDBAdapter.prototype.query = function (modelConstructor, predicate, pagination) {
442
- return __awaiter(this, void 0, void 0, function () {
443
- var _a, storeName, namespaceName, queryByKey, predicates, hasSort, hasPagination, records;
444
- var _this = this;
445
- return __generator(this, function (_b) {
446
- switch (_b.label) {
447
- case 0: return [4 /*yield*/, this.checkPrivate()];
448
- case 1:
449
- _b.sent();
450
- _a = this.queryMetadata(modelConstructor, predicate, pagination), storeName = _a.storeName, namespaceName = _a.namespaceName, queryByKey = _a.queryByKey, predicates = _a.predicates, hasSort = _a.hasSort, hasPagination = _a.hasPagination;
451
- return [4 /*yield*/, (function () { return __awaiter(_this, void 0, void 0, function () {
452
- var record, filtered, all;
453
- return __generator(this, function (_a) {
454
- switch (_a.label) {
455
- case 0:
456
- if (!queryByKey) return [3 /*break*/, 2];
457
- return [4 /*yield*/, this.getByKey(storeName, queryByKey)];
458
- case 1:
459
- record = _a.sent();
460
- return [2 /*return*/, record ? [record] : []];
461
- case 2:
462
- if (!predicates) return [3 /*break*/, 4];
463
- return [4 /*yield*/, this.filterOnPredicate(storeName, predicates)];
464
- case 3:
465
- filtered = _a.sent();
466
- return [2 /*return*/, this.inMemoryPagination(filtered, pagination)];
467
- case 4:
468
- if (!hasSort) return [3 /*break*/, 6];
469
- return [4 /*yield*/, this.getAll(storeName)];
470
- case 5:
471
- all = _a.sent();
472
- return [2 /*return*/, this.inMemoryPagination(all, pagination)];
473
- case 6:
474
- if (hasPagination) {
475
- return [2 /*return*/, this.enginePagination(storeName, pagination)];
476
- }
477
- return [2 /*return*/, this.getAll(storeName)];
478
- }
479
- });
480
- }); })()];
481
- case 2:
482
- records = (_b.sent());
483
- return [4 /*yield*/, this.load(namespaceName, modelConstructor.name, records)];
484
- case 3: return [2 /*return*/, _b.sent()];
485
- }
486
- });
487
- });
488
- };
489
- IndexedDBAdapter.prototype.queryOne = function (modelConstructor, firstOrLast) {
490
- if (firstOrLast === void 0) { firstOrLast = QueryOne.FIRST; }
491
- return __awaiter(this, void 0, void 0, function () {
492
- var storeName, cursor, result;
493
- return __generator(this, function (_a) {
494
- switch (_a.label) {
495
- case 0: return [4 /*yield*/, this.checkPrivate()];
496
- case 1:
497
- _a.sent();
498
- storeName = this.getStorenameForModel(modelConstructor);
499
- return [4 /*yield*/, this.db
500
- .transaction([storeName], 'readonly')
501
- .objectStore(storeName)
502
- .openCursor(undefined, firstOrLast === QueryOne.FIRST ? 'next' : 'prev')];
503
- case 2:
504
- cursor = _a.sent();
505
- result = cursor ? cursor.value : undefined;
506
- return [2 /*return*/, result && this.modelInstanceCreator(modelConstructor, result)];
127
+ });
128
+ }
129
+ catch (error) {
130
+ logger.error('Error migrating IndexedDB data', error);
131
+ txn.abort();
132
+ throw error;
133
+ }
134
+ return;
507
135
  }
508
- });
136
+ },
509
137
  });
510
- };
511
- IndexedDBAdapter.prototype.batchSave = function (modelConstructor, items) {
512
- return __awaiter(this, void 0, void 0, function () {
513
- var modelName, namespaceName, storeName, result, txn, store, _loop_2, this_2, items_1, items_1_1, item, e_3_1;
514
- var e_3, _a;
515
- var _this = this;
516
- return __generator(this, function (_b) {
517
- switch (_b.label) {
518
- case 0: return [4 /*yield*/, this.checkPrivate()];
519
- case 1:
520
- _b.sent();
521
- if (items.length === 0) {
522
- return [2 /*return*/, []];
523
- }
524
- modelName = modelConstructor.name;
525
- namespaceName = this.namespaceResolver(modelConstructor);
526
- storeName = this.getStorenameForModel(modelConstructor);
527
- result = [];
528
- txn = this.db.transaction(storeName, 'readwrite');
529
- store = txn.store;
530
- _loop_2 = function (item) {
531
- var model, connectedModels, keyValues, _deleted, index, key, instance;
532
- return __generator(this, function (_c) {
533
- switch (_c.label) {
534
- case 0:
535
- model = this_2.modelInstanceCreator(modelConstructor, item);
536
- connectedModels = traverseModel(modelName, model, this_2.schema.namespaces[namespaceName], this_2.modelInstanceCreator, this_2.getModelConstructorByModelName);
537
- keyValues = this_2.getIndexKeyValuesFromModel(model);
538
- _deleted = item._deleted;
539
- index = store.index('byPk');
540
- return [4 /*yield*/, index.getKey(this_2.canonicalKeyPath(keyValues))];
541
- case 1:
542
- key = _c.sent();
543
- if (!!_deleted) return [3 /*break*/, 3];
544
- instance = connectedModels.find(function (_a) {
545
- var instance = _a.instance;
546
- var instanceKeyValues = _this.getIndexKeyValuesFromModel(instance);
547
- return keysEqual(instanceKeyValues, keyValues);
548
- }).instance;
549
- result.push([
550
- instance,
551
- key ? OpType.UPDATE : OpType.INSERT,
552
- ]);
553
- return [4 /*yield*/, store.put(instance, key)];
554
- case 2:
555
- _c.sent();
556
- return [3 /*break*/, 5];
557
- case 3:
558
- result.push([item, OpType.DELETE]);
559
- if (!key) return [3 /*break*/, 5];
560
- return [4 /*yield*/, store.delete(key)];
561
- case 4:
562
- _c.sent();
563
- _c.label = 5;
564
- case 5: return [2 /*return*/];
565
- }
566
- });
567
- };
568
- this_2 = this;
569
- _b.label = 2;
570
- case 2:
571
- _b.trys.push([2, 7, 8, 9]);
572
- items_1 = __values(items), items_1_1 = items_1.next();
573
- _b.label = 3;
574
- case 3:
575
- if (!!items_1_1.done) return [3 /*break*/, 6];
576
- item = items_1_1.value;
577
- return [5 /*yield**/, _loop_2(item)];
578
- case 4:
579
- _b.sent();
580
- _b.label = 5;
581
- case 5:
582
- items_1_1 = items_1.next();
583
- return [3 /*break*/, 3];
584
- case 6: return [3 /*break*/, 9];
585
- case 7:
586
- e_3_1 = _b.sent();
587
- e_3 = { error: e_3_1 };
588
- return [3 /*break*/, 9];
589
- case 8:
590
- try {
591
- if (items_1_1 && !items_1_1.done && (_a = items_1.return)) _a.call(items_1);
592
- }
593
- finally { if (e_3) throw e_3.error; }
594
- return [7 /*endfinally*/];
595
- case 9: return [4 /*yield*/, txn.done];
596
- case 10:
597
- _b.sent();
598
- return [2 /*return*/, result];
138
+ }
139
+ async _get(storeOrStoreName, keyArr) {
140
+ let index;
141
+ if (typeof storeOrStoreName === 'string') {
142
+ const storeName = storeOrStoreName;
143
+ index = this.db.transaction(storeName, 'readonly').store.index('byPk');
144
+ }
145
+ else {
146
+ const store = storeOrStoreName;
147
+ index = store.index('byPk');
148
+ }
149
+ const result = await index.get(this.canonicalKeyPath(keyArr));
150
+ return result;
151
+ }
152
+ async clear() {
153
+ await this.checkPrivate();
154
+ this.db?.close();
155
+ await idb.deleteDB(this.dbName);
156
+ this.db = undefined;
157
+ this.initPromise = undefined;
158
+ }
159
+ async save(model, condition) {
160
+ await this.checkPrivate();
161
+ const { storeName, set, connectionStoreNames, modelKeyValues } = this.saveMetadata(model);
162
+ const tx = this.db.transaction([storeName, ...Array.from(set.values())], 'readwrite');
163
+ const store = tx.objectStore(storeName);
164
+ const fromDB = await this._get(store, modelKeyValues);
165
+ this.validateSaveCondition(condition, fromDB);
166
+ const result = [];
167
+ for await (const resItem of connectionStoreNames) {
168
+ const { storeName, item, instance, keys } = resItem;
169
+ const store = tx.objectStore(storeName);
170
+ const itemKeyValues = keys.map(key => item[key]);
171
+ const fromDB = await this._get(store, itemKeyValues);
172
+ const opType = fromDB ? OpType.UPDATE : OpType.INSERT;
173
+ if (keysEqual(itemKeyValues, modelKeyValues) ||
174
+ opType === OpType.INSERT) {
175
+ const key = await store
176
+ .index('byPk')
177
+ .getKey(this.canonicalKeyPath(itemKeyValues));
178
+ await store.put(item, key);
179
+ result.push([instance, opType]);
180
+ }
181
+ }
182
+ await tx.done;
183
+ return result;
184
+ }
185
+ async query(modelConstructor, predicate, pagination) {
186
+ await this.checkPrivate();
187
+ const { storeName, namespaceName, queryByKey, predicates, hasSort, hasPagination, } = this.queryMetadata(modelConstructor, predicate, pagination);
188
+ const records = (await (async () => {
189
+ //
190
+ // NOTE: @svidgen explored removing this and letting query() take care of automatic
191
+ // index leveraging. This would eliminate some amount of very similar code.
192
+ // But, getAll is slightly slower than get()
193
+ //
194
+ // On Chrome:
195
+ // ~700ms vs ~1175ms per 10k reads.
196
+ //
197
+ // You can (and should) check my work here:
198
+ // https://gist.github.com/svidgen/74e55d573b19c3e5432b1b5bdf0f4d96
199
+ //
200
+ if (queryByKey) {
201
+ const record = await this.getByKey(storeName, queryByKey);
202
+ return record ? [record] : [];
203
+ }
204
+ if (predicates) {
205
+ const filtered = await this.filterOnPredicate(storeName, predicates);
206
+ return this.inMemoryPagination(filtered, pagination);
207
+ }
208
+ if (hasSort) {
209
+ const all = await this.getAll(storeName);
210
+ return this.inMemoryPagination(all, pagination);
211
+ }
212
+ if (hasPagination) {
213
+ return this.enginePagination(storeName, pagination);
214
+ }
215
+ return this.getAll(storeName);
216
+ })());
217
+ return await this.load(namespaceName, modelConstructor.name, records);
218
+ }
219
+ async queryOne(modelConstructor, firstOrLast = QueryOne.FIRST) {
220
+ await this.checkPrivate();
221
+ const storeName = this.getStorenameForModel(modelConstructor);
222
+ const cursor = await this.db
223
+ .transaction([storeName], 'readonly')
224
+ .objectStore(storeName)
225
+ .openCursor(undefined, firstOrLast === QueryOne.FIRST ? 'next' : 'prev');
226
+ const result = cursor ? cursor.value : undefined;
227
+ return result && this.modelInstanceCreator(modelConstructor, result);
228
+ }
229
+ async batchSave(modelConstructor, items) {
230
+ await this.checkPrivate();
231
+ if (items.length === 0) {
232
+ return [];
233
+ }
234
+ const modelName = modelConstructor.name;
235
+ const namespaceName = this.namespaceResolver(modelConstructor);
236
+ const storeName = this.getStorenameForModel(modelConstructor);
237
+ const result = [];
238
+ const txn = this.db.transaction(storeName, 'readwrite');
239
+ const store = txn.store;
240
+ for (const item of items) {
241
+ const model = this.modelInstanceCreator(modelConstructor, item);
242
+ const connectedModels = traverseModel(modelName, model, this.schema.namespaces[namespaceName], this.modelInstanceCreator, this.getModelConstructorByModelName);
243
+ const keyValues = this.getIndexKeyValuesFromModel(model);
244
+ const { _deleted } = item;
245
+ const index = store.index('byPk');
246
+ const key = await index.getKey(this.canonicalKeyPath(keyValues));
247
+ if (!_deleted) {
248
+ const { instance } = connectedModels.find(({ instance }) => {
249
+ const instanceKeyValues = this.getIndexKeyValuesFromModel(instance);
250
+ return keysEqual(instanceKeyValues, keyValues);
251
+ });
252
+ result.push([
253
+ instance,
254
+ key ? OpType.UPDATE : OpType.INSERT,
255
+ ]);
256
+ await store.put(instance, key);
257
+ }
258
+ else {
259
+ result.push([item, OpType.DELETE]);
260
+ if (key) {
261
+ await store.delete(key);
599
262
  }
600
- });
263
+ }
264
+ }
265
+ await txn.done;
266
+ return result;
267
+ }
268
+ async deleteItem(deleteQueue) {
269
+ const connectionStoreNames = deleteQueue.map(({ storeName }) => {
270
+ return storeName;
601
271
  });
602
- };
603
- IndexedDBAdapter.prototype.deleteItem = function (deleteQueue) {
604
- var _a, e_4, _b, _c, _d, e_5, _e, _f;
605
- return __awaiter(this, void 0, void 0, function () {
606
- var connectionStoreNames, tx, _g, _h, _j, deleteItem, storeName, items, store, _k, items_2, items_2_1, item, key, keyValues, itemKey, e_5_1, e_4_1;
607
- return __generator(this, function (_l) {
608
- switch (_l.label) {
609
- case 0:
610
- connectionStoreNames = deleteQueue.map(function (_a) {
611
- var storeName = _a.storeName;
612
- return storeName;
613
- });
614
- tx = this.db.transaction(__spreadArray([], __read(connectionStoreNames), false), 'readwrite');
615
- _l.label = 1;
616
- case 1:
617
- _l.trys.push([1, 28, 29, 34]);
618
- _g = true, _h = __asyncValues(deleteQueue);
619
- _l.label = 2;
620
- case 2: return [4 /*yield*/, _h.next()];
621
- case 3:
622
- if (!(_j = _l.sent(), _a = _j.done, !_a)) return [3 /*break*/, 27];
623
- _c = _j.value;
624
- _g = false;
625
- _l.label = 4;
626
- case 4:
627
- _l.trys.push([4, , 25, 26]);
628
- deleteItem = _c;
629
- storeName = deleteItem.storeName, items = deleteItem.items;
630
- store = tx.objectStore(storeName);
631
- _l.label = 5;
632
- case 5:
633
- _l.trys.push([5, 18, 19, 24]);
634
- _k = true, items_2 = (e_5 = void 0, __asyncValues(items));
635
- _l.label = 6;
636
- case 6: return [4 /*yield*/, items_2.next()];
637
- case 7:
638
- if (!(items_2_1 = _l.sent(), _d = items_2_1.done, !_d)) return [3 /*break*/, 17];
639
- _f = items_2_1.value;
640
- _k = false;
641
- _l.label = 8;
642
- case 8:
643
- _l.trys.push([8, , 15, 16]);
644
- item = _f;
645
- if (!item) return [3 /*break*/, 14];
646
- key = void 0;
647
- if (!(typeof item === 'object')) return [3 /*break*/, 10];
648
- keyValues = this.getIndexKeyValuesFromModel(item);
649
- return [4 /*yield*/, store
650
- .index('byPk')
651
- .getKey(this.canonicalKeyPath(keyValues))];
652
- case 9:
653
- key = _l.sent();
654
- return [3 /*break*/, 12];
655
- case 10:
656
- itemKey = item.toString();
657
- return [4 /*yield*/, store.index('byPk').getKey(itemKey)];
658
- case 11:
659
- key = _l.sent();
660
- _l.label = 12;
661
- case 12:
662
- if (!(key !== undefined)) return [3 /*break*/, 14];
663
- return [4 /*yield*/, store.delete(key)];
664
- case 13:
665
- _l.sent();
666
- _l.label = 14;
667
- case 14: return [3 /*break*/, 16];
668
- case 15:
669
- _k = true;
670
- return [7 /*endfinally*/];
671
- case 16: return [3 /*break*/, 6];
672
- case 17: return [3 /*break*/, 24];
673
- case 18:
674
- e_5_1 = _l.sent();
675
- e_5 = { error: e_5_1 };
676
- return [3 /*break*/, 24];
677
- case 19:
678
- _l.trys.push([19, , 22, 23]);
679
- if (!(!_k && !_d && (_e = items_2.return))) return [3 /*break*/, 21];
680
- return [4 /*yield*/, _e.call(items_2)];
681
- case 20:
682
- _l.sent();
683
- _l.label = 21;
684
- case 21: return [3 /*break*/, 23];
685
- case 22:
686
- if (e_5) throw e_5.error;
687
- return [7 /*endfinally*/];
688
- case 23: return [7 /*endfinally*/];
689
- case 24: return [3 /*break*/, 26];
690
- case 25:
691
- _g = true;
692
- return [7 /*endfinally*/];
693
- case 26: return [3 /*break*/, 2];
694
- case 27: return [3 /*break*/, 34];
695
- case 28:
696
- e_4_1 = _l.sent();
697
- e_4 = { error: e_4_1 };
698
- return [3 /*break*/, 34];
699
- case 29:
700
- _l.trys.push([29, , 32, 33]);
701
- if (!(!_g && !_a && (_b = _h.return))) return [3 /*break*/, 31];
702
- return [4 /*yield*/, _b.call(_h)];
703
- case 30:
704
- _l.sent();
705
- _l.label = 31;
706
- case 31: return [3 /*break*/, 33];
707
- case 32:
708
- if (e_4) throw e_4.error;
709
- return [7 /*endfinally*/];
710
- case 33: return [7 /*endfinally*/];
711
- case 34: return [2 /*return*/];
272
+ const tx = this.db.transaction([...connectionStoreNames], 'readwrite');
273
+ for await (const deleteItem of deleteQueue) {
274
+ const { storeName, items } = deleteItem;
275
+ const store = tx.objectStore(storeName);
276
+ for await (const item of items) {
277
+ if (item) {
278
+ let key;
279
+ if (typeof item === 'object') {
280
+ const keyValues = this.getIndexKeyValuesFromModel(item);
281
+ key = await store
282
+ .index('byPk')
283
+ .getKey(this.canonicalKeyPath(keyValues));
284
+ }
285
+ else {
286
+ const itemKey = item.toString();
287
+ key = await store.index('byPk').getKey(itemKey);
288
+ }
289
+ if (key !== undefined) {
290
+ await store.delete(key);
291
+ }
712
292
  }
713
- });
714
- });
715
- };
293
+ }
294
+ }
295
+ }
716
296
  //#region platform-specific helper methods
717
- IndexedDBAdapter.prototype.checkPrivate = function () {
718
- return __awaiter(this, void 0, void 0, function () {
719
- var isPrivate;
720
- return __generator(this, function (_a) {
721
- switch (_a.label) {
722
- case 0: return [4 /*yield*/, isPrivateMode().then(function (isPrivate) {
723
- return isPrivate;
724
- })];
725
- case 1:
726
- isPrivate = _a.sent();
727
- if (isPrivate) {
728
- logger.error("IndexedDB not supported in this browser's private mode");
729
- return [2 /*return*/, Promise.reject("IndexedDB not supported in this browser's private mode")];
730
- }
731
- else {
732
- return [2 /*return*/, Promise.resolve()];
733
- }
734
- return [2 /*return*/];
735
- }
736
- });
297
+ async checkPrivate() {
298
+ const isPrivate = await isPrivateMode().then(isPrivate => {
299
+ return isPrivate;
737
300
  });
738
- };
301
+ if (isPrivate) {
302
+ logger.error("IndexedDB not supported in this browser's private mode");
303
+ return Promise.reject("IndexedDB not supported in this browser's private mode");
304
+ }
305
+ else {
306
+ return Promise.resolve();
307
+ }
308
+ }
739
309
  /**
740
310
  * Whether the browser's implementation of IndexedDB is coercing single-field
741
311
  * indexes to a scalar key.
@@ -746,62 +316,35 @@ var IndexedDBAdapter = /** @class */ (function (_super) {
746
316
  * See PR description for reference:
747
317
  * https://github.com/aws-amplify/amplify-js/pull/10527
748
318
  */
749
- IndexedDBAdapter.prototype.setSafariCompatabilityMode = function () {
750
- return __awaiter(this, void 0, void 0, function () {
751
- var _a;
752
- return __generator(this, function (_b) {
753
- switch (_b.label) {
754
- case 0:
755
- _a = this;
756
- return [4 /*yield*/, isSafariCompatabilityMode()];
757
- case 1:
758
- _a.safariCompatabilityMode = _b.sent();
759
- if (this.safariCompatabilityMode === true) {
760
- logger.debug('IndexedDB Adapter is running in Safari Compatability Mode');
761
- }
762
- return [2 /*return*/];
763
- }
764
- });
765
- });
766
- };
767
- IndexedDBAdapter.prototype.getNamespaceAndModelFromStorename = function (storeName) {
768
- var _a = __read(storeName.split('_')), namespaceName = _a[0], modelNameArr = _a.slice(1);
319
+ async setSafariCompatabilityMode() {
320
+ this.safariCompatabilityMode = await isSafariCompatabilityMode();
321
+ if (this.safariCompatabilityMode === true) {
322
+ logger.debug('IndexedDB Adapter is running in Safari Compatability Mode');
323
+ }
324
+ }
325
+ getNamespaceAndModelFromStorename(storeName) {
326
+ const [namespaceName, ...modelNameArr] = storeName.split('_');
769
327
  return {
770
- namespaceName: namespaceName,
328
+ namespaceName,
771
329
  modelName: modelNameArr.join('_'),
772
330
  };
773
- };
774
- IndexedDBAdapter.prototype.createObjectStoreForModel = function (db, namespaceName, storeName, modelName) {
775
- var store = db.createObjectStore(storeName, {
331
+ }
332
+ createObjectStoreForModel(db, namespaceName, storeName, modelName) {
333
+ const store = db.createObjectStore(storeName, {
776
334
  autoIncrement: true,
777
335
  });
778
- var indexes = this.schema.namespaces[namespaceName].relationships[modelName].indexes;
779
- indexes.forEach(function (_a) {
780
- var _b = __read(_a, 3), idxName = _b[0], keyPath = _b[1], options = _b[2];
336
+ const { indexes } = this.schema.namespaces[namespaceName].relationships[modelName];
337
+ indexes.forEach(([idxName, keyPath, options]) => {
781
338
  store.createIndex(idxName, keyPath, options);
782
339
  });
783
340
  return store;
784
- };
785
- IndexedDBAdapter.prototype.getByKey = function (storeName, keyValue) {
786
- return __awaiter(this, void 0, void 0, function () {
787
- return __generator(this, function (_a) {
788
- switch (_a.label) {
789
- case 0: return [4 /*yield*/, this._get(storeName, keyValue)];
790
- case 1: return [2 /*return*/, _a.sent()];
791
- }
792
- });
793
- });
794
- };
795
- IndexedDBAdapter.prototype.getAll = function (storeName) {
796
- return __awaiter(this, void 0, void 0, function () {
797
- return __generator(this, function (_a) {
798
- switch (_a.label) {
799
- case 0: return [4 /*yield*/, this.db.getAll(storeName)];
800
- case 1: return [2 /*return*/, _a.sent()];
801
- }
802
- });
803
- });
804
- };
341
+ }
342
+ async getByKey(storeName, keyValue) {
343
+ return await this._get(storeName, keyValue);
344
+ }
345
+ async getAll(storeName) {
346
+ return await this.db.getAll(storeName);
347
+ }
805
348
  /**
806
349
  * Tries to generate an index fetcher for the given predicates. Assumes
807
350
  * that the given predicate conditions are contained by an AND group and
@@ -811,295 +354,191 @@ var IndexedDBAdapter = /** @class */ (function (_super) {
811
354
  * @param predicates The predicates to try to AND together.
812
355
  * @param transaction
813
356
  */
814
- IndexedDBAdapter.prototype.matchingIndexQueries = function (storeName, predicates, transaction) {
815
- var e_6, _a, e_7, _b;
816
- var _this = this;
357
+ matchingIndexQueries(storeName, predicates, transaction) {
817
358
  // could be expanded later to include `exec()` and a `cardinality` estimate?
818
- var queries = [];
819
- var predicateIndex = new Map();
820
- try {
821
- for (var predicates_1 = __values(predicates), predicates_1_1 = predicates_1.next(); !predicates_1_1.done; predicates_1_1 = predicates_1.next()) {
822
- var predicate = predicates_1_1.value;
823
- predicateIndex.set(String(predicate.field), predicate);
824
- }
825
- }
826
- catch (e_6_1) { e_6 = { error: e_6_1 }; }
827
- finally {
828
- try {
829
- if (predicates_1_1 && !predicates_1_1.done && (_a = predicates_1.return)) _a.call(predicates_1);
830
- }
831
- finally { if (e_6) throw e_6.error; }
359
+ const queries = [];
360
+ const predicateIndex = new Map();
361
+ for (const predicate of predicates) {
362
+ predicateIndex.set(String(predicate.field), predicate);
832
363
  }
833
- var store = transaction.objectStore(storeName);
834
- var _loop_3 = function (name_1) {
835
- var e_8, _e;
836
- var idx = store.index(name_1);
837
- var keypath = Array.isArray(idx.keyPath) ? idx.keyPath : [idx.keyPath];
838
- var matchingPredicateValues = [];
839
- try {
840
- for (var keypath_1 = (e_8 = void 0, __values(keypath)), keypath_1_1 = keypath_1.next(); !keypath_1_1.done; keypath_1_1 = keypath_1.next()) {
841
- var field = keypath_1_1.value;
842
- var p = predicateIndex.get(field);
843
- if (p && p.operand !== null && p.operand !== undefined) {
844
- matchingPredicateValues.push(p.operand);
845
- }
846
- else {
847
- break;
848
- }
364
+ const store = transaction.objectStore(storeName);
365
+ for (const name of store.indexNames) {
366
+ const idx = store.index(name);
367
+ const keypath = Array.isArray(idx.keyPath) ? idx.keyPath : [idx.keyPath];
368
+ const matchingPredicateValues = [];
369
+ for (const field of keypath) {
370
+ const p = predicateIndex.get(field);
371
+ if (p && p.operand !== null && p.operand !== undefined) {
372
+ matchingPredicateValues.push(p.operand);
849
373
  }
850
- }
851
- catch (e_8_1) { e_8 = { error: e_8_1 }; }
852
- finally {
853
- try {
854
- if (keypath_1_1 && !keypath_1_1.done && (_e = keypath_1.return)) _e.call(keypath_1);
374
+ else {
375
+ break;
855
376
  }
856
- finally { if (e_8) throw e_8.error; }
857
377
  }
858
378
  // if we have a matching predicate field for each component of this index,
859
379
  // we can build a query for it. otherwise, we can't.
860
380
  if (matchingPredicateValues.length === keypath.length) {
861
381
  // re-create a transaction, because the transaction used to fetch the
862
382
  // indexes may no longer be active.
863
- queries.push(function () {
864
- return _this.db
865
- .transaction(storeName)
866
- .objectStore(storeName)
867
- .index(name_1)
868
- .getAll(_this.canonicalKeyPath(matchingPredicateValues));
869
- });
870
- }
871
- };
872
- try {
873
- for (var _c = __values(store.indexNames), _d = _c.next(); !_d.done; _d = _c.next()) {
874
- var name_1 = _d.value;
875
- _loop_3(name_1);
383
+ queries.push(() => this.db
384
+ .transaction(storeName)
385
+ .objectStore(storeName)
386
+ .index(name)
387
+ .getAll(this.canonicalKeyPath(matchingPredicateValues)));
876
388
  }
877
389
  }
878
- catch (e_7_1) { e_7 = { error: e_7_1 }; }
879
- finally {
880
- try {
881
- if (_d && !_d.done && (_b = _c.return)) _b.call(_c);
390
+ return queries;
391
+ }
392
+ async baseQueryIndex(storeName, predicates, transaction) {
393
+ let { predicates: predicateObjs, type } = predicates;
394
+ // the predicate objects we care about tend to be nested at least
395
+ // one level down: `{and: {or: {and: { <the predicates we want> }}}}`
396
+ // so, we unpack and/or groups until we find a group with more than 1
397
+ // child OR a child that is not a group (and is therefore a predicate "object").
398
+ while (predicateObjs.length === 1 &&
399
+ isPredicateGroup(predicateObjs[0]) &&
400
+ predicateObjs[0].type !== 'not') {
401
+ type = predicateObjs[0].type;
402
+ predicateObjs = predicateObjs[0].predicates;
403
+ }
404
+ const fieldPredicates = predicateObjs.filter(p => isPredicateObj(p) && p.operator === 'eq');
405
+ // several sub-queries could occur here. explicitly start a txn here to avoid
406
+ // opening/closing multiple txns.
407
+ const txn = transaction || this.db.transaction(storeName);
408
+ let result = {};
409
+ // `or` conditions, if usable, need to generate multiple queries. this is unlike
410
+ // `and` conditions, which should just be combined.
411
+ if (type === 'or') {
412
+ /**
413
+ * Base queries for each child group.
414
+ *
415
+ * For each child group, if it's an AND condition that results in a single
416
+ * subordinate "base query", we can use it. if it's any more complicated
417
+ * than that, it's not a simple join condition we want to use.
418
+ */
419
+ const groupQueries = await Promise.all(predicateObjs
420
+ .filter(o => isPredicateGroup(o) && o.type === 'and')
421
+ .map(o => this.baseQueryIndex(storeName, o, txn))).then(queries => queries
422
+ .filter(q => q.indexedQueries.length === 1)
423
+ .map(i => i.indexedQueries));
424
+ /**
425
+ * Base queries for each simple child "object" (field condition).
426
+ */
427
+ const objectQueries = predicateObjs
428
+ .filter(o => isPredicateObj(o))
429
+ .map(o => this.matchingIndexQueries(storeName, [o], txn));
430
+ const indexedQueries = [...groupQueries, ...objectQueries]
431
+ .map(q => q[0])
432
+ .filter(i => i);
433
+ // if, after hunting for base queries, we don't have exactly 1 base query
434
+ // for each child group + object, stop trying to optimize. we're not dealing
435
+ // with a simple query that fits the intended optimization path.
436
+ if (predicateObjs.length > indexedQueries.length) {
437
+ result = {
438
+ groupType: null,
439
+ indexedQueries: [],
440
+ };
441
+ }
442
+ else {
443
+ result = {
444
+ groupType: 'or',
445
+ indexedQueries,
446
+ };
882
447
  }
883
- finally { if (e_7) throw e_7.error; }
884
448
  }
885
- return queries;
886
- };
887
- IndexedDBAdapter.prototype.baseQueryIndex = function (storeName, predicates, transaction) {
888
- return __awaiter(this, void 0, void 0, function () {
889
- var predicateObjs, type, fieldPredicates, txn, result, groupQueries, objectQueries, indexedQueries;
890
- var _this = this;
891
- return __generator(this, function (_a) {
892
- switch (_a.label) {
893
- case 0:
894
- predicateObjs = predicates.predicates, type = predicates.type;
895
- // the predicate objects we care about tend to be nested at least
896
- // one level down: `{and: {or: {and: { <the predicates we want> }}}}`
897
- // so, we unpack and/or groups until we find a group with more than 1
898
- // child OR a child that is not a group (and is therefore a predicate "object").
899
- while (predicateObjs.length === 1 &&
900
- isPredicateGroup(predicateObjs[0]) &&
901
- predicateObjs[0].type !== 'not') {
902
- type = predicateObjs[0].type;
903
- predicateObjs = predicateObjs[0].predicates;
904
- }
905
- fieldPredicates = predicateObjs.filter(function (p) { return isPredicateObj(p) && p.operator === 'eq'; });
906
- txn = transaction || this.db.transaction(storeName);
907
- result = {};
908
- if (!(type === 'or')) return [3 /*break*/, 2];
909
- return [4 /*yield*/, Promise.all(predicateObjs
910
- .filter(function (o) { return isPredicateGroup(o) && o.type === 'and'; })
911
- .map(function (o) {
912
- return _this.baseQueryIndex(storeName, o, txn);
913
- })).then(function (queries) {
914
- return queries
915
- .filter(function (q) { return q.indexedQueries.length === 1; })
916
- .map(function (i) { return i.indexedQueries; });
917
- })];
918
- case 1:
919
- groupQueries = _a.sent();
920
- objectQueries = predicateObjs
921
- .filter(function (o) { return isPredicateObj(o); })
922
- .map(function (o) {
923
- return _this.matchingIndexQueries(storeName, [o], txn);
924
- });
925
- indexedQueries = __spreadArray(__spreadArray([], __read(groupQueries), false), __read(objectQueries), false).map(function (q) { return q[0]; })
926
- .filter(function (i) { return i; });
927
- // if, after hunting for base queries, we don't have exactly 1 base query
928
- // for each child group + object, stop trying to optimize. we're not dealing
929
- // with a simple query that fits the intended optimization path.
930
- if (predicateObjs.length > indexedQueries.length) {
931
- result = {
932
- groupType: null,
933
- indexedQueries: [],
934
- };
935
- }
936
- else {
937
- result = {
938
- groupType: 'or',
939
- indexedQueries: indexedQueries,
940
- };
941
- }
942
- return [3 /*break*/, 3];
943
- case 2:
944
- if (type === 'and') {
945
- // our potential indexes or lacks thereof.
946
- // note that we're only optimizing for `eq` right now.
947
- result = {
948
- groupType: type,
949
- indexedQueries: this.matchingIndexQueries(storeName, fieldPredicates, txn),
950
- };
951
- }
952
- else {
953
- result = {
954
- groupType: null,
955
- indexedQueries: [],
956
- };
957
- }
958
- _a.label = 3;
959
- case 3:
960
- if (!!transaction) return [3 /*break*/, 5];
961
- return [4 /*yield*/, txn.done];
962
- case 4:
963
- _a.sent();
964
- _a.label = 5;
965
- case 5: return [2 /*return*/, result];
966
- }
967
- });
968
- });
969
- };
970
- IndexedDBAdapter.prototype.filterOnPredicate = function (storeName, predicates) {
971
- return __awaiter(this, void 0, void 0, function () {
972
- var predicateObjs, type, _a, groupType, indexedQueries, candidateResults, distinctResults, indexedQueries_1, indexedQueries_1_1, query, resultGroup, resultGroup_1, resultGroup_1_1, item, distinctificationString, e_9_1, filtered;
973
- var e_9, _b, e_10, _c;
974
- return __generator(this, function (_d) {
975
- switch (_d.label) {
976
- case 0:
977
- predicateObjs = predicates.predicates, type = predicates.type;
978
- return [4 /*yield*/, this.baseQueryIndex(storeName, predicates)];
979
- case 1:
980
- _a = _d.sent(), groupType = _a.groupType, indexedQueries = _a.indexedQueries;
981
- if (!(groupType === 'and' && indexedQueries.length > 0)) return [3 /*break*/, 3];
982
- return [4 /*yield*/, indexedQueries[0]()];
983
- case 2:
984
- // each condition must be satsified, we can form a base set with any
985
- // ONE of those conditions and then filter.
986
- candidateResults = _d.sent();
987
- return [3 /*break*/, 14];
988
- case 3:
989
- if (!(groupType === 'or' &&
990
- indexedQueries.length > 0 &&
991
- indexedQueries.length <= MULTI_OR_CONDITION_SCAN_BREAKPOINT)) return [3 /*break*/, 12];
992
- distinctResults = new Map();
993
- _d.label = 4;
994
- case 4:
995
- _d.trys.push([4, 9, 10, 11]);
996
- indexedQueries_1 = __values(indexedQueries), indexedQueries_1_1 = indexedQueries_1.next();
997
- _d.label = 5;
998
- case 5:
999
- if (!!indexedQueries_1_1.done) return [3 /*break*/, 8];
1000
- query = indexedQueries_1_1.value;
1001
- return [4 /*yield*/, query()];
1002
- case 6:
1003
- resultGroup = _d.sent();
1004
- try {
1005
- for (resultGroup_1 = (e_10 = void 0, __values(resultGroup)), resultGroup_1_1 = resultGroup_1.next(); !resultGroup_1_1.done; resultGroup_1_1 = resultGroup_1.next()) {
1006
- item = resultGroup_1_1.value;
1007
- distinctificationString = JSON.stringify(item);
1008
- distinctResults.set(distinctificationString, item);
1009
- }
1010
- }
1011
- catch (e_10_1) { e_10 = { error: e_10_1 }; }
1012
- finally {
1013
- try {
1014
- if (resultGroup_1_1 && !resultGroup_1_1.done && (_c = resultGroup_1.return)) _c.call(resultGroup_1);
1015
- }
1016
- finally { if (e_10) throw e_10.error; }
1017
- }
1018
- _d.label = 7;
1019
- case 7:
1020
- indexedQueries_1_1 = indexedQueries_1.next();
1021
- return [3 /*break*/, 5];
1022
- case 8: return [3 /*break*/, 11];
1023
- case 9:
1024
- e_9_1 = _d.sent();
1025
- e_9 = { error: e_9_1 };
1026
- return [3 /*break*/, 11];
1027
- case 10:
1028
- try {
1029
- if (indexedQueries_1_1 && !indexedQueries_1_1.done && (_b = indexedQueries_1.return)) _b.call(indexedQueries_1);
1030
- }
1031
- finally { if (e_9) throw e_9.error; }
1032
- return [7 /*endfinally*/];
1033
- case 11:
1034
- // we could conceivably check for special conditions and return early here.
1035
- // but, this is simpler and has not yet had a measurable performance impact.
1036
- candidateResults = Array.from(distinctResults.values());
1037
- return [3 /*break*/, 14];
1038
- case 12: return [4 /*yield*/, this.getAll(storeName)];
1039
- case 13:
1040
- // nothing intelligent we can do with `not` groups unless or until we start
1041
- // smashing comparison operators against indexes -- at which point we could
1042
- // perform some reversal here.
1043
- candidateResults = (_d.sent());
1044
- _d.label = 14;
1045
- case 14:
1046
- filtered = predicateObjs
1047
- ? candidateResults.filter(function (m) { return validatePredicate(m, type, predicateObjs); })
1048
- : candidateResults;
1049
- return [2 /*return*/, filtered];
449
+ else if (type === 'and') {
450
+ // our potential indexes or lacks thereof.
451
+ // note that we're only optimizing for `eq` right now.
452
+ result = {
453
+ groupType: type,
454
+ indexedQueries: this.matchingIndexQueries(storeName, fieldPredicates, txn),
455
+ };
456
+ }
457
+ else {
458
+ result = {
459
+ groupType: null,
460
+ indexedQueries: [],
461
+ };
462
+ }
463
+ // Explicitly wait for txns from index queries to complete before proceding.
464
+ // This helps ensure IndexedDB is in a stable, ready state. Else, subseqeuent
465
+ // qeuries can sometimes appear to deadlock (at least in FakeIndexedDB).
466
+ // (Unless we were *given* the transaction -- we'll assume the parent handles it.)
467
+ if (!transaction)
468
+ await txn.done;
469
+ return result;
470
+ }
471
+ async filterOnPredicate(storeName, predicates) {
472
+ const { predicates: predicateObjs, type } = predicates;
473
+ const { groupType, indexedQueries } = await this.baseQueryIndex(storeName, predicates);
474
+ // where we'll accumulate candidate results, which will be filtered at the end.
475
+ let candidateResults;
476
+ // semi-naive implementation:
477
+ if (groupType === 'and' && indexedQueries.length > 0) {
478
+ // each condition must be satsified, we can form a base set with any
479
+ // ONE of those conditions and then filter.
480
+ candidateResults = await indexedQueries[0]();
481
+ }
482
+ else if (groupType === 'or' &&
483
+ indexedQueries.length > 0 &&
484
+ indexedQueries.length <= MULTI_OR_CONDITION_SCAN_BREAKPOINT) {
485
+ // NOTE: each condition implies a potentially distinct set. we only benefit
486
+ // from using indexes here if EVERY condition uses an index. if any one
487
+ // index requires a table scan, we gain nothing from the indexes.
488
+ // NOTE: results must be DISTINCT-ified if we leverage indexes.
489
+ const distinctResults = new Map();
490
+ for (const query of indexedQueries) {
491
+ const resultGroup = await query();
492
+ for (const item of resultGroup) {
493
+ const distinctificationString = JSON.stringify(item);
494
+ distinctResults.set(distinctificationString, item);
1050
495
  }
1051
- });
1052
- });
1053
- };
1054
- IndexedDBAdapter.prototype.inMemoryPagination = function (records, pagination) {
496
+ }
497
+ // we could conceivably check for special conditions and return early here.
498
+ // but, this is simpler and has not yet had a measurable performance impact.
499
+ candidateResults = Array.from(distinctResults.values());
500
+ }
501
+ else {
502
+ // nothing intelligent we can do with `not` groups unless or until we start
503
+ // smashing comparison operators against indexes -- at which point we could
504
+ // perform some reversal here.
505
+ candidateResults = await this.getAll(storeName);
506
+ }
507
+ const filtered = predicateObjs
508
+ ? candidateResults.filter(m => validatePredicate(m, type, predicateObjs))
509
+ : candidateResults;
510
+ return filtered;
511
+ }
512
+ inMemoryPagination(records, pagination) {
1055
513
  return inMemoryPagination(records, pagination);
1056
- };
1057
- IndexedDBAdapter.prototype.enginePagination = function (storeName, pagination) {
1058
- return __awaiter(this, void 0, void 0, function () {
1059
- var result, _a, page, _b, limit, initialRecord, cursor, pageResults, hasLimit;
1060
- return __generator(this, function (_c) {
1061
- switch (_c.label) {
1062
- case 0:
1063
- if (!pagination) return [3 /*break*/, 7];
1064
- _a = pagination.page, page = _a === void 0 ? 0 : _a, _b = pagination.limit, limit = _b === void 0 ? 0 : _b;
1065
- initialRecord = Math.max(0, page * limit) || 0;
1066
- return [4 /*yield*/, this.db
1067
- .transaction(storeName)
1068
- .objectStore(storeName)
1069
- .openCursor()];
1070
- case 1:
1071
- cursor = _c.sent();
1072
- if (!(cursor && initialRecord > 0)) return [3 /*break*/, 3];
1073
- return [4 /*yield*/, cursor.advance(initialRecord)];
1074
- case 2:
1075
- _c.sent();
1076
- _c.label = 3;
1077
- case 3:
1078
- pageResults = [];
1079
- hasLimit = typeof limit === 'number' && limit > 0;
1080
- _c.label = 4;
1081
- case 4:
1082
- if (!(cursor && cursor.value)) return [3 /*break*/, 6];
1083
- pageResults.push(cursor.value);
1084
- if (hasLimit && pageResults.length === limit) {
1085
- return [3 /*break*/, 6];
1086
- }
1087
- return [4 /*yield*/, cursor.continue()];
1088
- case 5:
1089
- cursor = _c.sent();
1090
- return [3 /*break*/, 4];
1091
- case 6:
1092
- result = pageResults;
1093
- return [3 /*break*/, 9];
1094
- case 7: return [4 /*yield*/, this.db.getAll(storeName)];
1095
- case 8:
1096
- result = (_c.sent());
1097
- _c.label = 9;
1098
- case 9: return [2 /*return*/, result];
514
+ }
515
+ async enginePagination(storeName, pagination) {
516
+ let result;
517
+ if (pagination) {
518
+ const { page = 0, limit = 0 } = pagination;
519
+ const initialRecord = Math.max(0, page * limit) || 0;
520
+ let cursor = await this.db
521
+ .transaction(storeName)
522
+ .objectStore(storeName)
523
+ .openCursor();
524
+ if (cursor && initialRecord > 0) {
525
+ await cursor.advance(initialRecord);
526
+ }
527
+ const pageResults = [];
528
+ const hasLimit = typeof limit === 'number' && limit > 0;
529
+ while (cursor && cursor.value) {
530
+ pageResults.push(cursor.value);
531
+ if (hasLimit && pageResults.length === limit) {
532
+ break;
1099
533
  }
1100
- });
1101
- });
1102
- };
1103
- return IndexedDBAdapter;
1104
- }(StorageAdapterBase));
534
+ cursor = await cursor.continue();
535
+ }
536
+ result = pageResults;
537
+ }
538
+ else {
539
+ result = await this.db.getAll(storeName);
540
+ }
541
+ return result;
542
+ }
543
+ }
1105
544
  export default new IndexedDBAdapter();