@aws-amplify/datastore 3.12.6-next.20 → 3.12.6-next.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +58 -0
- package/lib/authModeStrategies/multiAuthStrategy.js +13 -2
- package/lib/authModeStrategies/multiAuthStrategy.js.map +1 -1
- package/lib/datastore/datastore.js +648 -344
- package/lib/datastore/datastore.js.map +1 -1
- package/lib/predicates/index.js +12 -2
- package/lib/predicates/index.js.map +1 -1
- package/lib/storage/adapter/AsyncStorageAdapter.js +354 -203
- package/lib/storage/adapter/AsyncStorageAdapter.js.map +1 -1
- package/lib/storage/adapter/AsyncStorageDatabase.js +65 -28
- package/lib/storage/adapter/AsyncStorageDatabase.js.map +1 -1
- package/lib/storage/adapter/IndexedDBAdapter.js +444 -271
- package/lib/storage/adapter/IndexedDBAdapter.js.map +1 -1
- package/lib/storage/storage.js +93 -28
- package/lib/storage/storage.js.map +1 -1
- package/lib/sync/datastoreConnectivity.js +9 -0
- package/lib/sync/datastoreConnectivity.js.map +1 -1
- package/lib/sync/index.js +522 -397
- package/lib/sync/index.js.map +1 -1
- package/lib/sync/merger.js +13 -6
- package/lib/sync/merger.js.map +1 -1
- package/lib/sync/outbox.js +77 -71
- package/lib/sync/outbox.js.map +1 -1
- package/lib/sync/processors/mutation.js +269 -209
- package/lib/sync/processors/mutation.js.map +1 -1
- package/lib/sync/processors/subscription.js +213 -178
- package/lib/sync/processors/subscription.js.map +1 -1
- package/lib/sync/processors/sync.js +126 -121
- package/lib/sync/processors/sync.js.map +1 -1
- package/lib/sync/utils.js +43 -8
- package/lib/sync/utils.js.map +1 -1
- package/lib/types.js +10 -1
- package/lib/types.js.map +1 -1
- package/lib/util.js +419 -166
- package/lib/util.js.map +1 -1
- package/lib-esm/authModeStrategies/multiAuthStrategy.d.ts +11 -0
- package/lib-esm/authModeStrategies/multiAuthStrategy.js +12 -1
- package/lib-esm/authModeStrategies/multiAuthStrategy.js.map +1 -1
- package/lib-esm/datastore/datastore.d.ts +107 -17
- package/lib-esm/datastore/datastore.js +648 -344
- package/lib-esm/datastore/datastore.js.map +1 -1
- package/lib-esm/index.d.ts +3 -19
- package/lib-esm/predicates/index.d.ts +3 -2
- package/lib-esm/predicates/index.js +13 -3
- package/lib-esm/predicates/index.js.map +1 -1
- package/lib-esm/storage/adapter/AsyncStorageAdapter.d.ts +4 -3
- package/lib-esm/storage/adapter/AsyncStorageAdapter.js +355 -204
- package/lib-esm/storage/adapter/AsyncStorageAdapter.js.map +1 -1
- package/lib-esm/storage/adapter/AsyncStorageDatabase.d.ts +14 -4
- package/lib-esm/storage/adapter/AsyncStorageDatabase.js +66 -29
- package/lib-esm/storage/adapter/AsyncStorageDatabase.js.map +1 -1
- package/lib-esm/storage/adapter/IndexedDBAdapter.d.ts +26 -4
- package/lib-esm/storage/adapter/IndexedDBAdapter.js +445 -272
- package/lib-esm/storage/adapter/IndexedDBAdapter.js.map +1 -1
- package/lib-esm/storage/adapter/index.d.ts +1 -1
- package/lib-esm/storage/storage.d.ts +1 -1
- package/lib-esm/storage/storage.js +93 -28
- package/lib-esm/storage/storage.js.map +1 -1
- package/lib-esm/sync/datastoreConnectivity.d.ts +1 -0
- package/lib-esm/sync/datastoreConnectivity.js +10 -1
- package/lib-esm/sync/datastoreConnectivity.js.map +1 -1
- package/lib-esm/sync/index.d.ts +31 -5
- package/lib-esm/sync/index.js +524 -399
- package/lib-esm/sync/index.js.map +1 -1
- package/lib-esm/sync/merger.d.ts +9 -3
- package/lib-esm/sync/merger.js +13 -6
- package/lib-esm/sync/merger.js.map +1 -1
- package/lib-esm/sync/outbox.d.ts +2 -2
- package/lib-esm/sync/outbox.js +78 -72
- package/lib-esm/sync/outbox.js.map +1 -1
- package/lib-esm/sync/processors/mutation.d.ts +2 -0
- package/lib-esm/sync/processors/mutation.js +270 -210
- package/lib-esm/sync/processors/mutation.js.map +1 -1
- package/lib-esm/sync/processors/subscription.d.ts +2 -0
- package/lib-esm/sync/processors/subscription.js +213 -178
- package/lib-esm/sync/processors/subscription.js.map +1 -1
- package/lib-esm/sync/processors/sync.d.ts +2 -1
- package/lib-esm/sync/processors/sync.js +126 -121
- package/lib-esm/sync/processors/sync.js.map +1 -1
- package/lib-esm/sync/utils.d.ts +3 -2
- package/lib-esm/sync/utils.js +45 -11
- package/lib-esm/sync/utils.js.map +1 -1
- package/lib-esm/types.d.ts +65 -26
- package/lib-esm/types.js +9 -2
- package/lib-esm/types.js.map +1 -1
- package/lib-esm/util.d.ts +67 -24
- package/lib-esm/util.js +419 -166
- package/lib-esm/util.js.map +1 -1
- package/package.json +13 -7
- package/src/authModeStrategies/multiAuthStrategy.ts +12 -1
- package/src/datastore/datastore.ts +798 -397
- package/src/predicates/index.ts +32 -10
- package/src/storage/adapter/AsyncStorageAdapter.ts +309 -93
- package/src/storage/adapter/AsyncStorageDatabase.ts +74 -26
- package/src/storage/adapter/IndexedDBAdapter.ts +358 -134
- package/src/storage/adapter/index.ts +1 -1
- package/src/storage/storage.ts +69 -22
- package/src/sync/datastoreConnectivity.ts +6 -0
- package/src/sync/index.ts +521 -412
- package/src/sync/merger.ts +20 -4
- package/src/sync/outbox.ts +22 -9
- package/src/sync/processors/mutation.ts +188 -150
- package/src/sync/processors/subscription.ts +289 -253
- package/src/sync/processors/sync.ts +151 -138
- package/src/sync/utils.ts +67 -12
- package/src/types.ts +182 -30
- package/src/util.ts +505 -176
- package/build.js +0 -5
- package/dist/aws-amplify-datastore.js +0 -83311
- package/dist/aws-amplify-datastore.js.map +0 -1
- package/dist/aws-amplify-datastore.min.js +0 -168
- package/dist/aws-amplify-datastore.min.js.map +0 -1
- package/index.js +0 -7
- package/lib/authModeStrategies/defaultAuthStrategy.d.ts +0 -2
- package/lib/authModeStrategies/index.d.ts +0 -2
- package/lib/authModeStrategies/multiAuthStrategy.d.ts +0 -2
- package/lib/datastore/datastore.d.ts +0 -66
- package/lib/index.d.ts +0 -31
- package/lib/predicates/index.d.ts +0 -15
- package/lib/predicates/sort.d.ts +0 -8
- package/lib/ssr/index.d.ts +0 -3
- package/lib/storage/adapter/AsyncStorageAdapter.d.ts +0 -40
- package/lib/storage/adapter/AsyncStorageDatabase.d.ts +0 -29
- package/lib/storage/adapter/InMemoryStore.d.ts +0 -11
- package/lib/storage/adapter/InMemoryStore.native.d.ts +0 -1
- package/lib/storage/adapter/IndexedDBAdapter.d.ts +0 -37
- package/lib/storage/adapter/getDefaultAdapter/index.d.ts +0 -3
- package/lib/storage/adapter/getDefaultAdapter/index.native.d.ts +0 -3
- package/lib/storage/adapter/index.d.ts +0 -9
- package/lib/storage/storage.d.ts +0 -49
- package/lib/sync/datastoreConnectivity.d.ts +0 -15
- package/lib/sync/datastoreReachability/index.d.ts +0 -3
- package/lib/sync/datastoreReachability/index.native.d.ts +0 -3
- package/lib/sync/index.d.ts +0 -63
- package/lib/sync/merger.d.ts +0 -11
- package/lib/sync/outbox.d.ts +0 -27
- package/lib/sync/processors/errorMaps.d.ts +0 -17
- package/lib/sync/processors/mutation.d.ts +0 -56
- package/lib/sync/processors/subscription.d.ts +0 -31
- package/lib/sync/processors/sync.d.ts +0 -27
- package/lib/sync/utils.d.ts +0 -41
- package/lib/types.d.ts +0 -462
- package/lib/util.d.ts +0 -113
- package/webpack.config.dev.js +0 -6
|
@@ -29,13 +29,17 @@ import {
|
|
|
29
29
|
traverseModel,
|
|
30
30
|
validatePredicate,
|
|
31
31
|
sortCompareFunction,
|
|
32
|
+
keysEqual,
|
|
33
|
+
getStorename,
|
|
34
|
+
getIndexKeys,
|
|
35
|
+
extractPrimaryKeyValues,
|
|
36
|
+
isSafariCompatabilityMode,
|
|
32
37
|
} from '../../util';
|
|
33
38
|
import { Adapter } from './index';
|
|
34
39
|
|
|
35
40
|
const logger = new Logger('DataStore');
|
|
36
41
|
|
|
37
42
|
const DB_NAME = 'amplify-datastore';
|
|
38
|
-
|
|
39
43
|
class IndexedDBAdapter implements Adapter {
|
|
40
44
|
private schema: InternalSchema;
|
|
41
45
|
private namespaceResolver: NamespaceResolver;
|
|
@@ -49,6 +53,32 @@ class IndexedDBAdapter implements Adapter {
|
|
|
49
53
|
private resolve: (value?: any) => void;
|
|
50
54
|
private reject: (value?: any) => void;
|
|
51
55
|
private dbName: string = DB_NAME;
|
|
56
|
+
private safariCompatabilityMode: boolean = false;
|
|
57
|
+
|
|
58
|
+
private getStorenameForModel(
|
|
59
|
+
modelConstructor: PersistentModelConstructor<any>
|
|
60
|
+
) {
|
|
61
|
+
const namespace = this.namespaceResolver(modelConstructor);
|
|
62
|
+
const { name: modelName } = modelConstructor;
|
|
63
|
+
|
|
64
|
+
return getStorename(namespace, modelName);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Retrieves primary key values from a model
|
|
68
|
+
private getIndexKeyValuesFromModel<T extends PersistentModel>(
|
|
69
|
+
model: T
|
|
70
|
+
): string[] {
|
|
71
|
+
const modelConstructor = Object.getPrototypeOf(model)
|
|
72
|
+
.constructor as PersistentModelConstructor<T>;
|
|
73
|
+
const namespaceName = this.namespaceResolver(modelConstructor);
|
|
74
|
+
|
|
75
|
+
const keys = getIndexKeys(
|
|
76
|
+
this.schema.namespaces[namespaceName],
|
|
77
|
+
modelConstructor.name
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
return extractPrimaryKeyValues(model, keys);
|
|
81
|
+
}
|
|
52
82
|
|
|
53
83
|
private async checkPrivate() {
|
|
54
84
|
const isPrivate = await isPrivateMode().then(isPrivate => {
|
|
@@ -64,19 +94,30 @@ class IndexedDBAdapter implements Adapter {
|
|
|
64
94
|
}
|
|
65
95
|
}
|
|
66
96
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
97
|
+
/**
|
|
98
|
+
* Whether the browser's implementation of IndexedDB is coercing single-field
|
|
99
|
+
* indexes to a scalar key.
|
|
100
|
+
*
|
|
101
|
+
* If this returns `true`, we need to treat indexes containing a single field
|
|
102
|
+
* as scalars.
|
|
103
|
+
*
|
|
104
|
+
* See PR description for reference:
|
|
105
|
+
* https://github.com/aws-amplify/amplify-js/pull/10527
|
|
106
|
+
*/
|
|
107
|
+
private async setSafariCompatabilityMode() {
|
|
108
|
+
this.safariCompatabilityMode = await isSafariCompatabilityMode();
|
|
109
|
+
|
|
110
|
+
if (this.safariCompatabilityMode === true) {
|
|
111
|
+
logger.debug('IndexedDB Adapter is running in Safari Compatability Mode');
|
|
112
|
+
}
|
|
74
113
|
}
|
|
75
114
|
|
|
76
|
-
private
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
115
|
+
private getNamespaceAndModelFromStorename(storeName: string) {
|
|
116
|
+
const [namespaceName, ...modelNameArr] = storeName.split('_');
|
|
117
|
+
return {
|
|
118
|
+
namespaceName,
|
|
119
|
+
modelName: modelNameArr.join('_'),
|
|
120
|
+
};
|
|
80
121
|
}
|
|
81
122
|
|
|
82
123
|
async setUp(
|
|
@@ -90,6 +131,8 @@ class IndexedDBAdapter implements Adapter {
|
|
|
90
131
|
sessionId?: string
|
|
91
132
|
) {
|
|
92
133
|
await this.checkPrivate();
|
|
134
|
+
await this.setSafariCompatabilityMode();
|
|
135
|
+
|
|
93
136
|
if (!this.initPromise) {
|
|
94
137
|
this.initPromise = new Promise((res, rej) => {
|
|
95
138
|
this.resolve = res;
|
|
@@ -108,7 +151,7 @@ class IndexedDBAdapter implements Adapter {
|
|
|
108
151
|
|
|
109
152
|
try {
|
|
110
153
|
if (!this.db) {
|
|
111
|
-
const VERSION =
|
|
154
|
+
const VERSION = 3;
|
|
112
155
|
this.db = await idb.openDB(this.dbName, VERSION, {
|
|
113
156
|
upgrade: async (db, oldVersion, newVersion, txn) => {
|
|
114
157
|
if (oldVersion === 0) {
|
|
@@ -116,7 +159,7 @@ class IndexedDBAdapter implements Adapter {
|
|
|
116
159
|
const namespace = theSchema.namespaces[namespaceName];
|
|
117
160
|
|
|
118
161
|
Object.keys(namespace.models).forEach(modelName => {
|
|
119
|
-
const storeName =
|
|
162
|
+
const storeName = getStorename(namespaceName, modelName);
|
|
120
163
|
this.createObjectStoreForModel(
|
|
121
164
|
db,
|
|
122
165
|
namespaceName,
|
|
@@ -129,7 +172,7 @@ class IndexedDBAdapter implements Adapter {
|
|
|
129
172
|
return;
|
|
130
173
|
}
|
|
131
174
|
|
|
132
|
-
if (oldVersion === 1 && newVersion ===
|
|
175
|
+
if ((oldVersion === 1 || oldVersion === 2) && newVersion === 3) {
|
|
133
176
|
try {
|
|
134
177
|
for (const storeName of txn.objectStoreNames) {
|
|
135
178
|
const origStore = txn.objectStore(storeName);
|
|
@@ -138,13 +181,15 @@ class IndexedDBAdapter implements Adapter {
|
|
|
138
181
|
const tmpName = `tmp_${storeName}`;
|
|
139
182
|
origStore.name = tmpName;
|
|
140
183
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
keyPath: undefined,
|
|
144
|
-
autoIncrement: true,
|
|
145
|
-
});
|
|
184
|
+
const { namespaceName, modelName } =
|
|
185
|
+
this.getNamespaceAndModelFromStorename(storeName);
|
|
146
186
|
|
|
147
|
-
newStore.
|
|
187
|
+
const newStore = this.createObjectStoreForModel(
|
|
188
|
+
db,
|
|
189
|
+
namespaceName,
|
|
190
|
+
storeName,
|
|
191
|
+
modelName
|
|
192
|
+
);
|
|
148
193
|
|
|
149
194
|
let cursor = await origStore.openCursor();
|
|
150
195
|
let count = 0;
|
|
@@ -175,7 +220,7 @@ class IndexedDBAdapter implements Adapter {
|
|
|
175
220
|
.map(modelName => {
|
|
176
221
|
return [
|
|
177
222
|
modelName,
|
|
178
|
-
|
|
223
|
+
getStorename(namespaceName, modelName),
|
|
179
224
|
];
|
|
180
225
|
})
|
|
181
226
|
.filter(([, storeName]) => !objectStoreNames.has(storeName))
|
|
@@ -208,19 +253,19 @@ class IndexedDBAdapter implements Adapter {
|
|
|
208
253
|
|
|
209
254
|
private async _get<T>(
|
|
210
255
|
storeOrStoreName: idb.IDBPObjectStore | string,
|
|
211
|
-
|
|
256
|
+
keyArr: string[]
|
|
212
257
|
): Promise<T> {
|
|
213
258
|
let index: idb.IDBPIndex;
|
|
214
259
|
|
|
215
260
|
if (typeof storeOrStoreName === 'string') {
|
|
216
261
|
const storeName = storeOrStoreName;
|
|
217
|
-
index = this.db.transaction(storeName, 'readonly').store.index('
|
|
262
|
+
index = this.db.transaction(storeName, 'readonly').store.index('byPk');
|
|
218
263
|
} else {
|
|
219
264
|
const store = storeOrStoreName;
|
|
220
|
-
index = store.index('
|
|
265
|
+
index = store.index('byPk');
|
|
221
266
|
}
|
|
222
267
|
|
|
223
|
-
const result = await index.get(
|
|
268
|
+
const result = await index.get(this.canonicalKeyPath(keyArr));
|
|
224
269
|
|
|
225
270
|
return result;
|
|
226
271
|
}
|
|
@@ -233,21 +278,26 @@ class IndexedDBAdapter implements Adapter {
|
|
|
233
278
|
const modelConstructor = Object.getPrototypeOf(model)
|
|
234
279
|
.constructor as PersistentModelConstructor<T>;
|
|
235
280
|
const storeName = this.getStorenameForModel(modelConstructor);
|
|
281
|
+
const namespaceName = this.namespaceResolver(modelConstructor);
|
|
282
|
+
|
|
236
283
|
const connectedModels = traverseModel(
|
|
237
284
|
modelConstructor.name,
|
|
238
285
|
model,
|
|
239
|
-
this.schema.namespaces[
|
|
286
|
+
this.schema.namespaces[namespaceName],
|
|
240
287
|
this.modelInstanceCreator,
|
|
241
288
|
this.getModelConstructorByModelName
|
|
242
289
|
);
|
|
243
|
-
const namespaceName = this.namespaceResolver(modelConstructor);
|
|
244
290
|
|
|
245
291
|
const set = new Set<string>();
|
|
246
292
|
const connectionStoreNames = Object.values(connectedModels).map(
|
|
247
293
|
({ modelName, item, instance }) => {
|
|
248
|
-
const storeName =
|
|
294
|
+
const storeName = getStorename(namespaceName, modelName);
|
|
249
295
|
set.add(storeName);
|
|
250
|
-
|
|
296
|
+
const keys = getIndexKeys(
|
|
297
|
+
this.schema.namespaces[namespaceName],
|
|
298
|
+
modelName
|
|
299
|
+
);
|
|
300
|
+
return { storeName, item, instance, keys };
|
|
251
301
|
}
|
|
252
302
|
);
|
|
253
303
|
|
|
@@ -257,7 +307,9 @@ class IndexedDBAdapter implements Adapter {
|
|
|
257
307
|
);
|
|
258
308
|
const store = tx.objectStore(storeName);
|
|
259
309
|
|
|
260
|
-
const
|
|
310
|
+
const keyValues = this.getIndexKeyValuesFromModel(model);
|
|
311
|
+
|
|
312
|
+
const fromDB = await this._get(store, keyValues);
|
|
261
313
|
|
|
262
314
|
if (condition && fromDB) {
|
|
263
315
|
const predicates = ModelPredicateCreator.getPredicates(condition);
|
|
@@ -276,17 +328,28 @@ class IndexedDBAdapter implements Adapter {
|
|
|
276
328
|
const result: [T, OpType.INSERT | OpType.UPDATE][] = [];
|
|
277
329
|
|
|
278
330
|
for await (const resItem of connectionStoreNames) {
|
|
279
|
-
const { storeName, item, instance } = resItem;
|
|
331
|
+
const { storeName, item, instance, keys } = resItem;
|
|
280
332
|
const store = tx.objectStore(storeName);
|
|
281
|
-
const { id } = item;
|
|
282
333
|
|
|
283
|
-
const
|
|
334
|
+
const itemKeyValues = keys.map(key => {
|
|
335
|
+
const value = item[key];
|
|
336
|
+
return value;
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
const fromDB = <T>await this._get(store, itemKeyValues);
|
|
284
340
|
const opType: OpType =
|
|
285
341
|
fromDB === undefined ? OpType.INSERT : OpType.UPDATE;
|
|
286
342
|
|
|
343
|
+
const modelKeyValues = this.getIndexKeyValuesFromModel(model);
|
|
344
|
+
|
|
287
345
|
// Even if the parent is an INSERT, the child might not be, so we need to get its key
|
|
288
|
-
if (
|
|
289
|
-
|
|
346
|
+
if (
|
|
347
|
+
keysEqual(itemKeyValues, modelKeyValues) ||
|
|
348
|
+
opType === OpType.INSERT
|
|
349
|
+
) {
|
|
350
|
+
const key = await store
|
|
351
|
+
.index('byPk')
|
|
352
|
+
.getKey(this.canonicalKeyPath(itemKeyValues));
|
|
290
353
|
await store.put(item, key);
|
|
291
354
|
|
|
292
355
|
result.push([instance, opType]);
|
|
@@ -306,7 +369,7 @@ class IndexedDBAdapter implements Adapter {
|
|
|
306
369
|
const namespace = this.schema.namespaces[namespaceName];
|
|
307
370
|
const relations = namespace.relationships[srcModelName].relationTypes;
|
|
308
371
|
const connectionStoreNames = relations.map(({ modelName }) => {
|
|
309
|
-
return
|
|
372
|
+
return getStorename(namespaceName, modelName);
|
|
310
373
|
});
|
|
311
374
|
const modelConstructor = this.getModelConstructorByModelName(
|
|
312
375
|
namespaceName,
|
|
@@ -322,8 +385,9 @@ class IndexedDBAdapter implements Adapter {
|
|
|
322
385
|
const tx = this.db.transaction([...connectionStoreNames], 'readonly');
|
|
323
386
|
|
|
324
387
|
for await (const relation of relations) {
|
|
325
|
-
|
|
326
|
-
const
|
|
388
|
+
// target name, metadata, set by init
|
|
389
|
+
const { fieldName, modelName, targetName, targetNames } = relation;
|
|
390
|
+
const storeName = getStorename(namespaceName, modelName);
|
|
327
391
|
const store = tx.objectStore(storeName);
|
|
328
392
|
const modelConstructor = this.getModelConstructorByModelName(
|
|
329
393
|
namespaceName,
|
|
@@ -333,35 +397,91 @@ class IndexedDBAdapter implements Adapter {
|
|
|
333
397
|
switch (relation.relationType) {
|
|
334
398
|
case 'HAS_ONE':
|
|
335
399
|
for await (const recordItem of records) {
|
|
336
|
-
|
|
337
|
-
if (
|
|
400
|
+
// POST CPK codegen changes:
|
|
401
|
+
if (targetNames?.length) {
|
|
402
|
+
let getByFields = [];
|
|
403
|
+
let allPresent;
|
|
404
|
+
// iterate through all targetnames to make sure they are all present in the recordItem
|
|
405
|
+
allPresent = targetNames.every(targetName => {
|
|
406
|
+
return recordItem[targetName] != null;
|
|
407
|
+
});
|
|
338
408
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
);
|
|
409
|
+
if (!allPresent) {
|
|
410
|
+
break;
|
|
411
|
+
}
|
|
343
412
|
|
|
344
|
-
|
|
345
|
-
connectionRecord &&
|
|
346
|
-
this.modelInstanceCreator(modelConstructor, connectionRecord);
|
|
347
|
-
}
|
|
413
|
+
getByFields = targetNames as any;
|
|
348
414
|
|
|
415
|
+
// keys are the key values
|
|
416
|
+
const keys = getByFields.map(
|
|
417
|
+
getByField => recordItem[getByField]
|
|
418
|
+
);
|
|
419
|
+
|
|
420
|
+
const connectionRecord = await this._get(store, keys);
|
|
421
|
+
|
|
422
|
+
recordItem[fieldName] =
|
|
423
|
+
connectionRecord &&
|
|
424
|
+
this.modelInstanceCreator(modelConstructor, connectionRecord);
|
|
425
|
+
} else {
|
|
426
|
+
// If single target name, using old codegen
|
|
427
|
+
const getByfield = recordItem[targetName]
|
|
428
|
+
? targetName
|
|
429
|
+
: fieldName;
|
|
430
|
+
|
|
431
|
+
// We break here, because the recordItem does not have 'team', the `getByField`
|
|
432
|
+
// extract the keys on the related model.
|
|
433
|
+
if (!recordItem[getByfield]) break;
|
|
434
|
+
|
|
435
|
+
const key = [recordItem[getByfield]];
|
|
436
|
+
|
|
437
|
+
const connectionRecord = await this._get(store, key);
|
|
438
|
+
|
|
439
|
+
recordItem[fieldName] =
|
|
440
|
+
connectionRecord &&
|
|
441
|
+
this.modelInstanceCreator(modelConstructor, connectionRecord);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
349
444
|
break;
|
|
350
445
|
case 'BELONGS_TO':
|
|
351
446
|
for await (const recordItem of records) {
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
447
|
+
// POST CPK codegen changes:
|
|
448
|
+
if (targetNames?.length) {
|
|
449
|
+
let allPresent;
|
|
450
|
+
// iterate through all targetnames to make sure they are all present in the recordItem
|
|
451
|
+
allPresent = targetNames.every(targetName => {
|
|
452
|
+
return recordItem[targetName] != null;
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
// If not present, there is not yet a connected record
|
|
456
|
+
if (!allPresent) {
|
|
457
|
+
break;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
const keys = targetNames.map(
|
|
461
|
+
targetName => recordItem[targetName]
|
|
356
462
|
);
|
|
357
463
|
|
|
464
|
+
// Retrieve the connected record
|
|
465
|
+
const connectionRecord = await this._get(store, keys);
|
|
466
|
+
|
|
467
|
+
recordItem[fieldName] =
|
|
468
|
+
connectionRecord &&
|
|
469
|
+
this.modelInstanceCreator(modelConstructor, connectionRecord);
|
|
470
|
+
|
|
471
|
+
targetNames?.map(targetName => {
|
|
472
|
+
delete recordItem[targetName];
|
|
473
|
+
});
|
|
474
|
+
} else if (recordItem[targetName]) {
|
|
475
|
+
const key = [recordItem[targetName]];
|
|
476
|
+
|
|
477
|
+
const connectionRecord = await this._get(store, key);
|
|
478
|
+
|
|
358
479
|
recordItem[fieldName] =
|
|
359
480
|
connectionRecord &&
|
|
360
481
|
this.modelInstanceCreator(modelConstructor, connectionRecord);
|
|
361
482
|
delete recordItem[targetName];
|
|
362
483
|
}
|
|
363
484
|
}
|
|
364
|
-
|
|
365
485
|
break;
|
|
366
486
|
case 'HAS_MANY':
|
|
367
487
|
// TODO: Lazy loading
|
|
@@ -388,13 +508,19 @@ class IndexedDBAdapter implements Adapter {
|
|
|
388
508
|
|
|
389
509
|
const predicates =
|
|
390
510
|
predicate && ModelPredicateCreator.getPredicates(predicate);
|
|
391
|
-
const
|
|
511
|
+
const keyPath = getIndexKeys(
|
|
512
|
+
this.schema.namespaces[namespaceName],
|
|
513
|
+
modelConstructor.name
|
|
514
|
+
);
|
|
515
|
+
const queryByKey =
|
|
516
|
+
predicates && this.keyValueFromPredicate(predicates, keyPath);
|
|
517
|
+
|
|
392
518
|
const hasSort = pagination && pagination.sort;
|
|
393
519
|
const hasPagination = pagination && pagination.limit;
|
|
394
520
|
|
|
395
521
|
const records: T[] = await (async () => {
|
|
396
|
-
if (
|
|
397
|
-
const record = await this.
|
|
522
|
+
if (queryByKey) {
|
|
523
|
+
const record = await this.getByKey(storeName, queryByKey);
|
|
398
524
|
return record ? [record] : [];
|
|
399
525
|
}
|
|
400
526
|
|
|
@@ -418,11 +544,11 @@ class IndexedDBAdapter implements Adapter {
|
|
|
418
544
|
return await this.load(namespaceName, modelConstructor.name, records);
|
|
419
545
|
}
|
|
420
546
|
|
|
421
|
-
private async
|
|
547
|
+
private async getByKey<T extends PersistentModel>(
|
|
422
548
|
storeName: string,
|
|
423
|
-
|
|
549
|
+
keyValue: string[]
|
|
424
550
|
): Promise<T> {
|
|
425
|
-
const record = <T>await this._get(storeName,
|
|
551
|
+
const record = <T>await this._get(storeName, keyValue);
|
|
426
552
|
return record;
|
|
427
553
|
}
|
|
428
554
|
|
|
@@ -432,17 +558,27 @@ class IndexedDBAdapter implements Adapter {
|
|
|
432
558
|
return await this.db.getAll(storeName);
|
|
433
559
|
}
|
|
434
560
|
|
|
435
|
-
private
|
|
436
|
-
predicates: PredicatesGroup<T
|
|
437
|
-
|
|
561
|
+
private keyValueFromPredicate<T extends PersistentModel>(
|
|
562
|
+
predicates: PredicatesGroup<T>,
|
|
563
|
+
keyPath: string[]
|
|
564
|
+
): string[] | undefined {
|
|
438
565
|
const { predicates: predicateObjs } = predicates;
|
|
439
|
-
const idPredicate =
|
|
440
|
-
predicateObjs.length === 1 &&
|
|
441
|
-
(predicateObjs.find(
|
|
442
|
-
p => isPredicateObj(p) && p.field === 'id' && p.operator === 'eq'
|
|
443
|
-
) as PredicateObject<T>);
|
|
444
566
|
|
|
445
|
-
|
|
567
|
+
if (predicateObjs.length !== keyPath.length) {
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
const keyValues = [];
|
|
572
|
+
|
|
573
|
+
for (const key of keyPath) {
|
|
574
|
+
const predicateObj = predicateObjs.find(
|
|
575
|
+
p => isPredicateObj(p) && p.field === key && p.operator === 'eq'
|
|
576
|
+
) as PredicateObject<T>;
|
|
577
|
+
|
|
578
|
+
predicateObj && keyValues.push(predicateObj.operand);
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
return keyValues.length === keyPath.length ? keyValues : undefined;
|
|
446
582
|
}
|
|
447
583
|
|
|
448
584
|
private async filterOnPredicate<T extends PersistentModel>(
|
|
@@ -605,15 +741,16 @@ class IndexedDBAdapter implements Adapter {
|
|
|
605
741
|
|
|
606
742
|
const modelConstructor = Object.getPrototypeOf(model)
|
|
607
743
|
.constructor as PersistentModelConstructor<T>;
|
|
608
|
-
const
|
|
744
|
+
const namespaceName = this.namespaceResolver(modelConstructor);
|
|
609
745
|
|
|
610
746
|
const storeName = this.getStorenameForModel(modelConstructor);
|
|
611
747
|
|
|
612
748
|
if (condition) {
|
|
613
749
|
const tx = this.db.transaction([storeName], 'readwrite');
|
|
614
750
|
const store = tx.objectStore(storeName);
|
|
751
|
+
const keyValues = this.getIndexKeyValuesFromModel(model);
|
|
615
752
|
|
|
616
|
-
const fromDB = await this._get(store,
|
|
753
|
+
const fromDB = await this._get(store, keyValues);
|
|
617
754
|
|
|
618
755
|
if (fromDB === undefined) {
|
|
619
756
|
const msg = 'Model instance not found in storage';
|
|
@@ -636,26 +773,28 @@ class IndexedDBAdapter implements Adapter {
|
|
|
636
773
|
await tx.done;
|
|
637
774
|
|
|
638
775
|
const relations =
|
|
639
|
-
this.schema.namespaces[
|
|
640
|
-
.
|
|
776
|
+
this.schema.namespaces[namespaceName].relationships[
|
|
777
|
+
modelConstructor.name
|
|
778
|
+
].relationTypes;
|
|
641
779
|
|
|
642
780
|
await this.deleteTraverse(
|
|
643
781
|
relations,
|
|
644
782
|
[model],
|
|
645
783
|
modelConstructor.name,
|
|
646
|
-
|
|
784
|
+
namespaceName,
|
|
647
785
|
deleteQueue
|
|
648
786
|
);
|
|
649
787
|
} else {
|
|
650
788
|
const relations =
|
|
651
|
-
this.schema.namespaces[
|
|
652
|
-
.
|
|
789
|
+
this.schema.namespaces[namespaceName].relationships[
|
|
790
|
+
modelConstructor.name
|
|
791
|
+
].relationTypes;
|
|
653
792
|
|
|
654
793
|
await this.deleteTraverse(
|
|
655
794
|
relations,
|
|
656
795
|
[model],
|
|
657
796
|
modelConstructor.name,
|
|
658
|
-
|
|
797
|
+
namespaceName,
|
|
659
798
|
deleteQueue
|
|
660
799
|
);
|
|
661
800
|
}
|
|
@@ -672,7 +811,10 @@ class IndexedDBAdapter implements Adapter {
|
|
|
672
811
|
}
|
|
673
812
|
|
|
674
813
|
private async deleteItem<T extends PersistentModel>(
|
|
675
|
-
deleteQueue?: {
|
|
814
|
+
deleteQueue?: {
|
|
815
|
+
storeName: string;
|
|
816
|
+
items: T[] | IDBValidKey[];
|
|
817
|
+
}[]
|
|
676
818
|
) {
|
|
677
819
|
const connectionStoreNames = deleteQueue.map(({ storeName }) => {
|
|
678
820
|
return storeName;
|
|
@@ -688,9 +830,13 @@ class IndexedDBAdapter implements Adapter {
|
|
|
688
830
|
let key: IDBValidKey;
|
|
689
831
|
|
|
690
832
|
if (typeof item === 'object') {
|
|
691
|
-
|
|
833
|
+
const keyValues = this.getIndexKeyValuesFromModel(item as T);
|
|
834
|
+
key = await store
|
|
835
|
+
.index('byPk')
|
|
836
|
+
.getKey(this.canonicalKeyPath(keyValues));
|
|
692
837
|
} else {
|
|
693
|
-
|
|
838
|
+
const itemKey = item.toString();
|
|
839
|
+
key = await store.index('byPk').getKey(itemKey);
|
|
694
840
|
}
|
|
695
841
|
|
|
696
842
|
if (key !== undefined) {
|
|
@@ -709,57 +855,109 @@ class IndexedDBAdapter implements Adapter {
|
|
|
709
855
|
deleteQueue: { storeName: string; items: T[] }[]
|
|
710
856
|
): Promise<void> {
|
|
711
857
|
for await (const rel of relations) {
|
|
712
|
-
const {
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
// if we were unable to find an index via relationTypes
|
|
722
|
-
// i.e. for keyName connections, attempt to find one by the
|
|
723
|
-
// associatedWith property
|
|
724
|
-
getIndexFromAssociation(
|
|
725
|
-
this.schema.namespaces[nameSpace].relationships[modelName].indexes,
|
|
726
|
-
rel.associatedWith
|
|
727
|
-
);
|
|
858
|
+
const {
|
|
859
|
+
relationType,
|
|
860
|
+
modelName,
|
|
861
|
+
targetName,
|
|
862
|
+
targetNames,
|
|
863
|
+
associatedWith,
|
|
864
|
+
} = rel;
|
|
865
|
+
|
|
866
|
+
const storeName = getStorename(nameSpace, modelName);
|
|
728
867
|
|
|
729
868
|
switch (relationType) {
|
|
730
869
|
case 'HAS_ONE':
|
|
731
870
|
for await (const model of models) {
|
|
732
|
-
const hasOneIndex =
|
|
733
|
-
|
|
734
|
-
const hasOneCustomField = targetName in model;
|
|
735
|
-
const value = hasOneCustomField ? model[targetName] : model.id;
|
|
736
|
-
if (!value) break;
|
|
737
|
-
|
|
738
|
-
const recordToDelete = <T>(
|
|
739
|
-
await this.db
|
|
740
|
-
.transaction(storeName, 'readwrite')
|
|
741
|
-
.objectStore(storeName)
|
|
742
|
-
.index(hasOneIndex)
|
|
743
|
-
.get(value)
|
|
744
|
-
);
|
|
871
|
+
const hasOneIndex = 'byPk';
|
|
745
872
|
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
873
|
+
if (targetNames?.length) {
|
|
874
|
+
// CPK codegen
|
|
875
|
+
const values = targetNames.map(targetName => model[targetName]);
|
|
876
|
+
|
|
877
|
+
if (values.length === 0) break;
|
|
878
|
+
|
|
879
|
+
const recordToDelete = <T>(
|
|
880
|
+
await this.db
|
|
881
|
+
.transaction(storeName, 'readwrite')
|
|
882
|
+
.objectStore(storeName)
|
|
883
|
+
.index(hasOneIndex)
|
|
884
|
+
.get(this.canonicalKeyPath(values))
|
|
885
|
+
);
|
|
886
|
+
|
|
887
|
+
await this.deleteTraverse(
|
|
888
|
+
this.schema.namespaces[nameSpace].relationships[modelName]
|
|
889
|
+
.relationTypes,
|
|
890
|
+
recordToDelete ? [recordToDelete] : [],
|
|
891
|
+
modelName,
|
|
892
|
+
nameSpace,
|
|
893
|
+
deleteQueue
|
|
894
|
+
);
|
|
895
|
+
break;
|
|
896
|
+
} else {
|
|
897
|
+
// PRE-CPK codegen
|
|
898
|
+
let index;
|
|
899
|
+
let values: string[];
|
|
900
|
+
|
|
901
|
+
if (targetName && targetName in model) {
|
|
902
|
+
index = hasOneIndex;
|
|
903
|
+
const value = model[targetName];
|
|
904
|
+
values = [value];
|
|
905
|
+
} else {
|
|
906
|
+
// backwards compatability for older versions of codegen that did not emit targetName for HAS_ONE relations
|
|
907
|
+
// TODO: can we deprecate this? it's been ~2 years since codegen started including targetName for HAS_ONE
|
|
908
|
+
// If we deprecate, we'll need to re-gen the MIPR in __tests__/schema.ts > newSchema
|
|
909
|
+
// otherwise some unit tests will fail
|
|
910
|
+
index = getIndex(
|
|
911
|
+
this.schema.namespaces[nameSpace].relationships[modelName]
|
|
912
|
+
.relationTypes,
|
|
913
|
+
srcModel
|
|
914
|
+
);
|
|
915
|
+
values = this.getIndexKeyValuesFromModel(model);
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
if (!values || !index) break;
|
|
919
|
+
|
|
920
|
+
const recordToDelete = <T>(
|
|
921
|
+
await this.db
|
|
922
|
+
.transaction(storeName, 'readwrite')
|
|
923
|
+
.objectStore(storeName)
|
|
924
|
+
.index(index)
|
|
925
|
+
.get(this.canonicalKeyPath(values))
|
|
926
|
+
);
|
|
927
|
+
|
|
928
|
+
await this.deleteTraverse(
|
|
929
|
+
this.schema.namespaces[nameSpace].relationships[modelName]
|
|
930
|
+
.relationTypes,
|
|
931
|
+
recordToDelete ? [recordToDelete] : [],
|
|
932
|
+
modelName,
|
|
933
|
+
nameSpace,
|
|
934
|
+
deleteQueue
|
|
935
|
+
);
|
|
936
|
+
}
|
|
754
937
|
}
|
|
755
938
|
break;
|
|
756
939
|
case 'HAS_MANY':
|
|
757
940
|
for await (const model of models) {
|
|
941
|
+
const index =
|
|
942
|
+
// explicit bi-directional @hasMany and @manyToMany
|
|
943
|
+
getIndex(
|
|
944
|
+
this.schema.namespaces[nameSpace].relationships[modelName]
|
|
945
|
+
.relationTypes,
|
|
946
|
+
srcModel
|
|
947
|
+
) ||
|
|
948
|
+
// uni and/or implicit @hasMany
|
|
949
|
+
getIndexFromAssociation(
|
|
950
|
+
this.schema.namespaces[nameSpace].relationships[modelName]
|
|
951
|
+
.indexes,
|
|
952
|
+
associatedWith
|
|
953
|
+
);
|
|
954
|
+
const keyValues = this.getIndexKeyValuesFromModel(model);
|
|
955
|
+
|
|
758
956
|
const childrenArray = await this.db
|
|
759
957
|
.transaction(storeName, 'readwrite')
|
|
760
958
|
.objectStore(storeName)
|
|
761
|
-
.index(index)
|
|
762
|
-
.getAll(
|
|
959
|
+
.index(index as string)
|
|
960
|
+
.getAll(this.canonicalKeyPath(keyValues));
|
|
763
961
|
|
|
764
962
|
await this.deleteTraverse(
|
|
765
963
|
this.schema.namespaces[nameSpace].relationships[modelName]
|
|
@@ -781,7 +979,7 @@ class IndexedDBAdapter implements Adapter {
|
|
|
781
979
|
}
|
|
782
980
|
|
|
783
981
|
deleteQueue.push({
|
|
784
|
-
storeName:
|
|
982
|
+
storeName: getStorename(nameSpace, srcModel),
|
|
785
983
|
items: models.map(record =>
|
|
786
984
|
this.modelInstanceCreator(
|
|
787
985
|
this.getModelConstructorByModelName(nameSpace, srcModel),
|
|
@@ -820,22 +1018,30 @@ class IndexedDBAdapter implements Adapter {
|
|
|
820
1018
|
const store = txn.store;
|
|
821
1019
|
|
|
822
1020
|
for (const item of items) {
|
|
1021
|
+
const namespaceName = this.namespaceResolver(modelConstructor);
|
|
1022
|
+
const modelName = modelConstructor.name;
|
|
1023
|
+
const model = this.modelInstanceCreator(modelConstructor, item);
|
|
1024
|
+
|
|
823
1025
|
const connectedModels = traverseModel(
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
this.schema.namespaces[
|
|
1026
|
+
modelName,
|
|
1027
|
+
model,
|
|
1028
|
+
this.schema.namespaces[namespaceName],
|
|
827
1029
|
this.modelInstanceCreator,
|
|
828
1030
|
this.getModelConstructorByModelName
|
|
829
1031
|
);
|
|
830
1032
|
|
|
831
|
-
const
|
|
832
|
-
const
|
|
833
|
-
|
|
1033
|
+
const keyValues = this.getIndexKeyValuesFromModel(model);
|
|
1034
|
+
const { _deleted } = item;
|
|
1035
|
+
|
|
1036
|
+
const index = store.index('byPk');
|
|
1037
|
+
|
|
1038
|
+
const key = await index.getKey(this.canonicalKeyPath(keyValues));
|
|
834
1039
|
|
|
835
1040
|
if (!_deleted) {
|
|
836
|
-
const { instance } = connectedModels.find(
|
|
837
|
-
|
|
838
|
-
|
|
1041
|
+
const { instance } = connectedModels.find(({ instance }) => {
|
|
1042
|
+
const instanceKeyValues = this.getIndexKeyValuesFromModel(instance);
|
|
1043
|
+
return keysEqual(instanceKeyValues, keyValues);
|
|
1044
|
+
});
|
|
839
1045
|
|
|
840
1046
|
result.push([
|
|
841
1047
|
<T>(<unknown>instance),
|
|
@@ -856,7 +1062,7 @@ class IndexedDBAdapter implements Adapter {
|
|
|
856
1062
|
return result;
|
|
857
1063
|
}
|
|
858
1064
|
|
|
859
|
-
private
|
|
1065
|
+
private createObjectStoreForModel(
|
|
860
1066
|
db: idb.IDBPDatabase,
|
|
861
1067
|
namespaceName: string,
|
|
862
1068
|
storeName: string,
|
|
@@ -866,12 +1072,30 @@ class IndexedDBAdapter implements Adapter {
|
|
|
866
1072
|
autoIncrement: true,
|
|
867
1073
|
});
|
|
868
1074
|
|
|
869
|
-
const indexes =
|
|
870
|
-
this.schema.namespaces[namespaceName].relationships[modelName]
|
|
871
|
-
indexes.forEach(index => store.createIndex(index, index));
|
|
1075
|
+
const { indexes } =
|
|
1076
|
+
this.schema.namespaces[namespaceName].relationships[modelName];
|
|
872
1077
|
|
|
873
|
-
|
|
1078
|
+
indexes.forEach(([idxName, keyPath, options]) => {
|
|
1079
|
+
store.createIndex(idxName, keyPath, options);
|
|
1080
|
+
});
|
|
1081
|
+
|
|
1082
|
+
return store;
|
|
874
1083
|
}
|
|
1084
|
+
|
|
1085
|
+
/**
|
|
1086
|
+
* Checks the given path against the browser's IndexedDB implementation for
|
|
1087
|
+
* necessary compatibility transformations, applying those transforms if needed.
|
|
1088
|
+
*
|
|
1089
|
+
* @param `keyArr` strings to compatibilize for browser-indexeddb index operations
|
|
1090
|
+
* @returns An array or string, depending on and given key,
|
|
1091
|
+
* that is ensured to be compatible with the IndexedDB implementation's nuances.
|
|
1092
|
+
*/
|
|
1093
|
+
private canonicalKeyPath = (keyArr: string[]) => {
|
|
1094
|
+
if (this.safariCompatabilityMode) {
|
|
1095
|
+
return keyArr.length > 1 ? keyArr : keyArr[0];
|
|
1096
|
+
}
|
|
1097
|
+
return keyArr;
|
|
1098
|
+
};
|
|
875
1099
|
}
|
|
876
1100
|
|
|
877
1101
|
export default new IndexedDBAdapter();
|