remult 3.3.19-next.1 → 3.3.19-next.2

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.
@@ -9,12 +9,27 @@ export class IndexedDbIndexBuilder {
9
9
  return this;
10
10
  }
11
11
  }
12
+ const IDB_KEYS_STORE = '__remult_keys';
13
+ const IDB_DEK_ID = 'dek';
14
+ const IDB_ENC = '_enc';
15
+ const IDB_IV = '_iv';
12
16
  export class IndexedDbDataProvider {
13
17
  dbName;
14
18
  //@internal
15
19
  declaredIndexes = [];
20
+ //@internal
21
+ encrypt;
22
+ //@internal
23
+ customGetKey;
24
+ //@internal
25
+ dek;
16
26
  constructor(dbName = 'remult', options) {
17
27
  this.dbName = dbName;
28
+ this.encrypt = options?.encrypt === true;
29
+ this.customGetKey = options?.getEncryptionKey;
30
+ if (this.encrypt && !(typeof crypto !== 'undefined' && crypto.subtle)) {
31
+ throw new Error('Web Crypto API is required when IndexedDB encryption is enabled');
32
+ }
18
33
  if (options?.indexes) {
19
34
  const builder = new IndexedDbIndexBuilder();
20
35
  options.indexes(builder);
@@ -66,10 +81,14 @@ export class IndexedDbDataProvider {
66
81
  const storeName = storeNameOf(entity);
67
82
  const prefetch = await idbPrefetchFromFilter(entity, where, this.storeIndexes(storeName));
68
83
  this.lastFetch = prefetch;
69
- if (prefetch.type === 'all')
70
- return this.getAll(storeName);
71
- const ops = prefetch.type === 'or' ? prefetch.parts : [prefetch];
72
- return this.fetchOps(storeName, entity, ops);
84
+ const rows = prefetch.type === 'all'
85
+ ? await this.getAll(storeName)
86
+ : await this.fetchOps(storeName, entity, prefetch.type === 'or' ? prefetch.parts : [prefetch]);
87
+ if (!this.encrypt)
88
+ return rows;
89
+ const dek = await this.loadDek();
90
+ const names = await dbNamesOf(entity, (x) => x);
91
+ return Promise.all(rows.map((row) => decryptJson(storeName, row, entity, names, dek)));
73
92
  }
74
93
  //@internal
75
94
  storeIndexes(storeName) {
@@ -106,18 +125,24 @@ export class IndexedDbDataProvider {
106
125
  };
107
126
  }));
108
127
  const db = await this.open();
109
- const needsUpgrade = wanted.some((w) => {
110
- if (!db.objectStoreNames.contains(w.name))
111
- return true;
112
- const existing = this.storeIndexes(w.name).map((i) => i.name);
113
- return w.indexes.some((idx) => !existing.includes(idx.name));
114
- });
128
+ const needsKeyStore = this.usesKeyStore() && !db.objectStoreNames.contains(IDB_KEYS_STORE);
129
+ const needsUpgrade = needsKeyStore ||
130
+ wanted.some((w) => {
131
+ if (!db.objectStoreNames.contains(w.name))
132
+ return true;
133
+ const existing = this.storeIndexes(w.name).map((i) => i.name);
134
+ return w.indexes.some((idx) => !existing.includes(idx.name));
135
+ });
115
136
  if (!needsUpgrade)
116
137
  return;
117
138
  const nextVersion = db.version + 1;
118
139
  this.forget(db);
119
140
  db.close();
120
141
  await this.open(nextVersion, (upgradeDb, tx) => {
142
+ if (this.usesKeyStore() &&
143
+ !upgradeDb.objectStoreNames.contains(IDB_KEYS_STORE)) {
144
+ upgradeDb.createObjectStore(IDB_KEYS_STORE, { keyPath: 'id' });
145
+ }
121
146
  for (const { name, keyPath, autoIncrement, indexes } of wanted) {
122
147
  const store = upgradeDb.objectStoreNames.contains(name)
123
148
  ? tx.objectStore(name)
@@ -131,6 +156,63 @@ export class IndexedDbDataProvider {
131
156
  });
132
157
  }
133
158
  //@internal
159
+ usesKeyStore() {
160
+ return this.encrypt && !this.customGetKey;
161
+ }
162
+ //@internal
163
+ async loadDek() {
164
+ if (this.dek)
165
+ return this.dek;
166
+ if (this.customGetKey) {
167
+ const key = await this.customGetKey();
168
+ this.dek = key;
169
+ return key;
170
+ }
171
+ const existing = await this.withStore(IDB_KEYS_STORE, 'readonly', (s) => idbReq(s.get(IDB_DEK_ID)));
172
+ if (existing?.key) {
173
+ this.dek = existing.key;
174
+ return existing.key;
175
+ }
176
+ const generated = await crypto.subtle.generateKey({ name: 'AES-GCM', length: 256 }, false, ['encrypt', 'decrypt']);
177
+ const key = await this.withStore(IDB_KEYS_STORE, 'readwrite', async (s) => {
178
+ const rec = await idbReq(s.get(IDB_DEK_ID));
179
+ if (rec?.key)
180
+ return rec.key;
181
+ try {
182
+ await idbReq(s.add({ id: IDB_DEK_ID, key: generated }));
183
+ return generated;
184
+ }
185
+ catch (e) {
186
+ if (e?.name === 'ConstraintError') {
187
+ const again = await idbReq(s.get(IDB_DEK_ID));
188
+ if (!again?.key)
189
+ throw new Error('Failed to load IndexedDB encryption key');
190
+ return again.key;
191
+ }
192
+ throw e;
193
+ }
194
+ });
195
+ this.dek = key;
196
+ return key;
197
+ }
198
+ //@internal
199
+ async plaintextDbNames(entity) {
200
+ const names = await dbNamesOf(entity, (x) => x);
201
+ const set = new Set();
202
+ for (const f of entity.idMetadata.fields) {
203
+ set.add(names.$dbNameOf(f));
204
+ }
205
+ for (const def of this.indexesFor(entity)) {
206
+ const keys = Array.isArray(def) ? [...def] : [def];
207
+ for (const key of keys) {
208
+ const field = [...entity.fields].find((f) => f.key === key);
209
+ if (field)
210
+ set.add(names.$dbNameOf(field));
211
+ }
212
+ }
213
+ return set;
214
+ }
215
+ //@internal
134
216
  forget(db) {
135
217
  if (this.db === db)
136
218
  this.db = undefined;
@@ -246,23 +328,48 @@ class IndexedDbEntityDataProvider {
246
328
  delete json[idName];
247
329
  return json;
248
330
  });
249
- return this.provider.withStore(storeNameOf(this.entity), 'readwrite', async (store) => {
250
- const result = [];
251
- for (const json of rows) {
331
+ const storeName = storeNameOf(this.entity);
332
+ const dek = this.provider.encrypt
333
+ ? await this.provider.loadDek()
334
+ : undefined;
335
+ const keep = dek
336
+ ? await this.provider.plaintextDbNames(this.entity)
337
+ : undefined;
338
+ if (dek && auto) {
339
+ const keys = await this.provider.withStore(storeName, 'readwrite', async (store) => {
340
+ const allocated = [];
341
+ for (let i = 0; i < rows.length; i++) {
342
+ allocated.push(await idbReq(store.add({})));
343
+ }
344
+ return allocated;
345
+ });
346
+ for (let i = 0; i < rows.length; i++) {
347
+ rows[i][idName] = keys[i];
348
+ }
349
+ }
350
+ const toWrite = dek
351
+ ? await Promise.all(rows.map((json) => encryptJson(storeName, json, keep, keyFromRow(this.entity, names, json), dek)))
352
+ : rows;
353
+ await this.provider.withStore(storeName, 'readwrite', async (store) => {
354
+ for (let i = 0; i < toWrite.length; i++) {
252
355
  try {
253
- const key = await idbReq(store.add(json));
254
- if (auto)
255
- json[idName] = json[idName] ?? key;
356
+ if (dek && auto) {
357
+ await idbReq(store.put(toWrite[i]));
358
+ }
359
+ else {
360
+ const key = await idbReq(store.add(toWrite[i]));
361
+ if (auto)
362
+ rows[i][idName] = rows[i][idName] ?? key;
363
+ }
256
364
  }
257
365
  catch (e) {
258
366
  if (e?.name === 'ConstraintError')
259
367
  throw Error('id already exists');
260
368
  throw e;
261
369
  }
262
- result.push(helper.translateFromJson(json, names));
263
370
  }
264
- return result;
265
371
  });
372
+ return rows.map((json) => helper.translateFromJson(json, names));
266
373
  });
267
374
  }
268
375
  update(id, data) {
@@ -271,28 +378,38 @@ class IndexedDbEntityDataProvider {
271
378
  const helper = new ArrayEntityDataProvider(this.entity, () => []);
272
379
  const names = await helper.init();
273
380
  const key = toIdbKey(this.entity, id);
274
- return this.provider.withStore(storeNameOf(this.entity), 'readwrite', async (store) => {
275
- const existing = await idbReq(store.get(key));
276
- if (existing == null)
277
- throw new Error(`Couldn't find row with id "${id}" in entity "${this.entity.key}" to update`);
278
- const json = { ...existing };
279
- const keys = Object.keys(data);
280
- for (const f of this.entity.fields) {
281
- if (!isDbReadonly(f, names) && keys.includes(f.key)) {
282
- json[names.$dbNameOf(f)] = f.valueConverter.toJson(data[f.key]);
283
- }
381
+ const storeName = storeNameOf(this.entity);
382
+ const existingRaw = await this.provider.withStore(storeName, 'readonly', (store) => idbReq(store.get(key)));
383
+ if (existingRaw == null)
384
+ throw new Error(`Couldn't find row with id "${id}" in entity "${this.entity.key}" to update`);
385
+ const dek = this.provider.encrypt
386
+ ? await this.provider.loadDek()
387
+ : undefined;
388
+ const existing = dek
389
+ ? await decryptJson(storeName, existingRaw, this.entity, names, dek)
390
+ : existingRaw;
391
+ const json = { ...existing };
392
+ const keys = Object.keys(data);
393
+ for (const f of this.entity.fields) {
394
+ if (!isDbReadonly(f, names) && keys.includes(f.key)) {
395
+ json[names.$dbNameOf(f)] = f.valueConverter.toJson(data[f.key]);
284
396
  }
285
- helper.verifyThatRowHasAllNotNullColumns(json, names);
286
- const newKey = keyFromRow(this.entity, names, json);
397
+ }
398
+ helper.verifyThatRowHasAllNotNullColumns(json, names);
399
+ const newKey = keyFromRow(this.entity, names, json);
400
+ const toWrite = dek
401
+ ? await encryptJson(storeName, json, await this.provider.plaintextDbNames(this.entity), newKey, dek)
402
+ : json;
403
+ await this.provider.withStore(storeName, 'readwrite', async (store) => {
287
404
  if (!idbKeysEqual(key, newKey)) {
288
405
  const conflict = await idbReq(store.get(newKey));
289
406
  if (conflict != null)
290
407
  throw Error('id already exists');
291
408
  await idbReq(store.delete(key));
292
409
  }
293
- await idbReq(store.put(json));
294
- return helper.translateFromJson(json, names);
410
+ await idbReq(store.put(toWrite));
295
411
  });
412
+ return helper.translateFromJson(json, names);
296
413
  });
297
414
  }
298
415
  delete(ids) {
@@ -642,3 +759,53 @@ function idbReq(request) {
642
759
  request.onerror = () => reject(request.error);
643
760
  });
644
761
  }
762
+ function encAad(storeName, id) {
763
+ return new TextEncoder().encode(storeName + '\0' + JSON.stringify(id));
764
+ }
765
+ function asBufferSource(value, label) {
766
+ if (value instanceof Uint8Array || value instanceof ArrayBuffer)
767
+ return value;
768
+ throw new Error(`Failed to decrypt IndexedDB row: missing ${label}`);
769
+ }
770
+ async function encryptJson(storeName, json, plaintext, idbKey, dek) {
771
+ const stored = {};
772
+ const payload = {};
773
+ for (const k of Object.keys(json)) {
774
+ if (k === IDB_ENC || k === IDB_IV)
775
+ continue;
776
+ if (plaintext.has(k))
777
+ stored[k] = json[k];
778
+ else
779
+ payload[k] = json[k];
780
+ }
781
+ const iv = crypto.getRandomValues(new Uint8Array(12));
782
+ stored[IDB_ENC] = await crypto.subtle.encrypt({
783
+ name: 'AES-GCM',
784
+ iv,
785
+ additionalData: encAad(storeName, idbKey),
786
+ }, dek, new TextEncoder().encode(JSON.stringify(payload)));
787
+ stored[IDB_IV] = iv;
788
+ return stored;
789
+ }
790
+ async function decryptJson(storeName, row, entity, names, dek) {
791
+ if (row == null || row[IDB_ENC] == null)
792
+ return row;
793
+ const idbKey = keyFromRow(entity, names, row);
794
+ try {
795
+ const plain = await crypto.subtle.decrypt({
796
+ name: 'AES-GCM',
797
+ iv: asBufferSource(row[IDB_IV], IDB_IV),
798
+ additionalData: encAad(storeName, idbKey),
799
+ }, dek, asBufferSource(row[IDB_ENC], IDB_ENC));
800
+ const payload = JSON.parse(new TextDecoder().decode(plain));
801
+ const result = { ...row, ...payload };
802
+ delete result[IDB_ENC];
803
+ delete result[IDB_IV];
804
+ return result;
805
+ }
806
+ catch (e) {
807
+ if (e?.message?.startsWith('Failed to decrypt IndexedDB row'))
808
+ throw e;
809
+ throw new Error(`Failed to decrypt IndexedDB row in "${storeName}"`);
810
+ }
811
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remult",
3
- "version": "3.3.19-next.1",
3
+ "version": "3.3.19-next.2",
4
4
  "description": "A CRUD framework for full-stack TypeScript",
5
5
  "homepage": "https://remult.dev",
6
6
  "repository": {
@@ -7,6 +7,8 @@ export declare class IndexedDbIndexBuilder {
7
7
  }
8
8
  export type IndexedDbDataProviderOptions = {
9
9
  indexes?: (x: IndexedDbIndexBuilder) => void;
10
+ encrypt?: boolean;
11
+ getEncryptionKey?: () => CryptoKey | Promise<CryptoKey>;
10
12
  };
11
13
  export declare class IndexedDbDataProvider implements DataProvider {
12
14
  private dbName;
@@ -18,6 +18,10 @@ var IndexedDbIndexBuilder = /** @class */ (function () {
18
18
  return IndexedDbIndexBuilder;
19
19
  }());
20
20
  exports.IndexedDbIndexBuilder = IndexedDbIndexBuilder;
21
+ var IDB_KEYS_STORE = '__remult_keys';
22
+ var IDB_DEK_ID = 'dek';
23
+ var IDB_ENC = '_enc';
24
+ var IDB_IV = '_iv';
21
25
  var IndexedDbDataProvider = /** @class */ (function () {
22
26
  function IndexedDbDataProvider(dbName, options) {
23
27
  if (dbName === void 0) { dbName = 'remult'; }
@@ -26,6 +30,11 @@ var IndexedDbDataProvider = /** @class */ (function () {
26
30
  this.declaredIndexes = [];
27
31
  //@internal
28
32
  this.tail = Promise.resolve();
33
+ this.encrypt = (options === null || options === void 0 ? void 0 : options.encrypt) === true;
34
+ this.customGetKey = options === null || options === void 0 ? void 0 : options.getEncryptionKey;
35
+ if (this.encrypt && !(typeof crypto !== 'undefined' && crypto.subtle)) {
36
+ throw new Error('Web Crypto API is required when IndexedDB encryption is enabled');
37
+ }
29
38
  if (options === null || options === void 0 ? void 0 : options.indexes) {
30
39
  var builder = new IndexedDbIndexBuilder();
31
40
  options.indexes(builder);
@@ -114,19 +123,35 @@ var IndexedDbDataProvider = /** @class */ (function () {
114
123
  //@internal
115
124
  IndexedDbDataProvider.prototype.loadRows = function (entity, where) {
116
125
  return tslib_1.__awaiter(this, void 0, void 0, function () {
117
- var storeName, prefetch, ops;
118
- return tslib_1.__generator(this, function (_a) {
119
- switch (_a.label) {
126
+ var storeName, prefetch, rows, _a, dek, names;
127
+ return tslib_1.__generator(this, function (_b) {
128
+ switch (_b.label) {
120
129
  case 0:
121
130
  storeName = storeNameOf(entity);
122
131
  return [4 /*yield*/, idbPrefetchFromFilter(entity, where, this.storeIndexes(storeName))];
123
132
  case 1:
124
- prefetch = _a.sent();
133
+ prefetch = _b.sent();
125
134
  this.lastFetch = prefetch;
126
- if (prefetch.type === 'all')
127
- return [2 /*return*/, this.getAll(storeName)];
128
- ops = prefetch.type === 'or' ? prefetch.parts : [prefetch];
129
- return [2 /*return*/, this.fetchOps(storeName, entity, ops)];
135
+ if (!(prefetch.type === 'all')) return [3 /*break*/, 3];
136
+ return [4 /*yield*/, this.getAll(storeName)];
137
+ case 2:
138
+ _a = _b.sent();
139
+ return [3 /*break*/, 5];
140
+ case 3: return [4 /*yield*/, this.fetchOps(storeName, entity, prefetch.type === 'or' ? prefetch.parts : [prefetch])];
141
+ case 4:
142
+ _a = _b.sent();
143
+ _b.label = 5;
144
+ case 5:
145
+ rows = _a;
146
+ if (!this.encrypt)
147
+ return [2 /*return*/, rows];
148
+ return [4 /*yield*/, this.loadDek()];
149
+ case 6:
150
+ dek = _b.sent();
151
+ return [4 /*yield*/, (0, filter_consumer_bridge_to_sql_request_js_1.dbNamesOf)(entity, function (x) { return x; })];
152
+ case 7:
153
+ names = _b.sent();
154
+ return [2 /*return*/, Promise.all(rows.map(function (row) { return decryptJson(storeName, row, entity, names, dek); }))];
130
155
  }
131
156
  });
132
157
  });
@@ -157,7 +182,7 @@ var IndexedDbDataProvider = /** @class */ (function () {
157
182
  //@internal
158
183
  IndexedDbDataProvider.prototype.ensureStores = function (entities) {
159
184
  return tslib_1.__awaiter(this, void 0, void 0, function () {
160
- var wanted, db, needsUpgrade, nextVersion;
185
+ var wanted, db, needsKeyStore, needsUpgrade, nextVersion;
161
186
  var _this = this;
162
187
  return tslib_1.__generator(this, function (_a) {
163
188
  switch (_a.label) {
@@ -185,12 +210,14 @@ var IndexedDbDataProvider = /** @class */ (function () {
185
210
  return [4 /*yield*/, this.open()];
186
211
  case 2:
187
212
  db = _a.sent();
188
- needsUpgrade = wanted.some(function (w) {
189
- if (!db.objectStoreNames.contains(w.name))
190
- return true;
191
- var existing = _this.storeIndexes(w.name).map(function (i) { return i.name; });
192
- return w.indexes.some(function (idx) { return !existing.includes(idx.name); });
193
- });
213
+ needsKeyStore = this.usesKeyStore() && !db.objectStoreNames.contains(IDB_KEYS_STORE);
214
+ needsUpgrade = needsKeyStore ||
215
+ wanted.some(function (w) {
216
+ if (!db.objectStoreNames.contains(w.name))
217
+ return true;
218
+ var existing = _this.storeIndexes(w.name).map(function (i) { return i.name; });
219
+ return w.indexes.some(function (idx) { return !existing.includes(idx.name); });
220
+ });
194
221
  if (!needsUpgrade)
195
222
  return [2 /*return*/];
196
223
  nextVersion = db.version + 1;
@@ -198,6 +225,10 @@ var IndexedDbDataProvider = /** @class */ (function () {
198
225
  db.close();
199
226
  return [4 /*yield*/, this.open(nextVersion, function (upgradeDb, tx) {
200
227
  var e_1, _a, e_2, _b;
228
+ if (_this.usesKeyStore() &&
229
+ !upgradeDb.objectStoreNames.contains(IDB_KEYS_STORE)) {
230
+ upgradeDb.createObjectStore(IDB_KEYS_STORE, { keyPath: 'id' });
231
+ }
201
232
  try {
202
233
  for (var wanted_1 = tslib_1.__values(wanted), wanted_1_1 = wanted_1.next(); !wanted_1_1.done; wanted_1_1 = wanted_1.next()) {
203
234
  var _c = wanted_1_1.value, name_2 = _c.name, keyPath = _c.keyPath, autoIncrement = _c.autoIncrement, indexes = _c.indexes;
@@ -237,6 +268,136 @@ var IndexedDbDataProvider = /** @class */ (function () {
237
268
  });
238
269
  };
239
270
  //@internal
271
+ IndexedDbDataProvider.prototype.usesKeyStore = function () {
272
+ return this.encrypt && !this.customGetKey;
273
+ };
274
+ //@internal
275
+ IndexedDbDataProvider.prototype.loadDek = function () {
276
+ return tslib_1.__awaiter(this, void 0, void 0, function () {
277
+ var key_1, existing, generated, key;
278
+ var _this = this;
279
+ return tslib_1.__generator(this, function (_a) {
280
+ switch (_a.label) {
281
+ case 0:
282
+ if (this.dek)
283
+ return [2 /*return*/, this.dek];
284
+ if (!this.customGetKey) return [3 /*break*/, 2];
285
+ return [4 /*yield*/, this.customGetKey()];
286
+ case 1:
287
+ key_1 = _a.sent();
288
+ this.dek = key_1;
289
+ return [2 /*return*/, key_1];
290
+ case 2: return [4 /*yield*/, this.withStore(IDB_KEYS_STORE, 'readonly', function (s) {
291
+ return idbReq(s.get(IDB_DEK_ID));
292
+ })];
293
+ case 3:
294
+ existing = _a.sent();
295
+ if (existing === null || existing === void 0 ? void 0 : existing.key) {
296
+ this.dek = existing.key;
297
+ return [2 /*return*/, existing.key];
298
+ }
299
+ return [4 /*yield*/, crypto.subtle.generateKey({ name: 'AES-GCM', length: 256 }, false, ['encrypt', 'decrypt'])];
300
+ case 4:
301
+ generated = _a.sent();
302
+ return [4 /*yield*/, this.withStore(IDB_KEYS_STORE, 'readwrite', function (s) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
303
+ var rec, e_3, again;
304
+ return tslib_1.__generator(this, function (_a) {
305
+ switch (_a.label) {
306
+ case 0: return [4 /*yield*/, idbReq(s.get(IDB_DEK_ID))];
307
+ case 1:
308
+ rec = _a.sent();
309
+ if (rec === null || rec === void 0 ? void 0 : rec.key)
310
+ return [2 /*return*/, rec.key];
311
+ _a.label = 2;
312
+ case 2:
313
+ _a.trys.push([2, 4, , 7]);
314
+ return [4 /*yield*/, idbReq(s.add({ id: IDB_DEK_ID, key: generated }))];
315
+ case 3:
316
+ _a.sent();
317
+ return [2 /*return*/, generated];
318
+ case 4:
319
+ e_3 = _a.sent();
320
+ if (!((e_3 === null || e_3 === void 0 ? void 0 : e_3.name) === 'ConstraintError')) return [3 /*break*/, 6];
321
+ return [4 /*yield*/, idbReq(s.get(IDB_DEK_ID))];
322
+ case 5:
323
+ again = _a.sent();
324
+ if (!(again === null || again === void 0 ? void 0 : again.key))
325
+ throw new Error('Failed to load IndexedDB encryption key');
326
+ return [2 /*return*/, again.key];
327
+ case 6: throw e_3;
328
+ case 7: return [2 /*return*/];
329
+ }
330
+ });
331
+ }); })];
332
+ case 5:
333
+ key = _a.sent();
334
+ this.dek = key;
335
+ return [2 /*return*/, key];
336
+ }
337
+ });
338
+ });
339
+ };
340
+ //@internal
341
+ IndexedDbDataProvider.prototype.plaintextDbNames = function (entity) {
342
+ return tslib_1.__awaiter(this, void 0, void 0, function () {
343
+ var names, set, _a, _b, f, _c, _d, def, keys, _loop_1, keys_1, keys_1_1, key;
344
+ var e_4, _e, e_5, _f, e_6, _g;
345
+ return tslib_1.__generator(this, function (_h) {
346
+ switch (_h.label) {
347
+ case 0: return [4 /*yield*/, (0, filter_consumer_bridge_to_sql_request_js_1.dbNamesOf)(entity, function (x) { return x; })];
348
+ case 1:
349
+ names = _h.sent();
350
+ set = new Set();
351
+ try {
352
+ for (_a = tslib_1.__values(entity.idMetadata.fields), _b = _a.next(); !_b.done; _b = _a.next()) {
353
+ f = _b.value;
354
+ set.add(names.$dbNameOf(f));
355
+ }
356
+ }
357
+ catch (e_4_1) { e_4 = { error: e_4_1 }; }
358
+ finally {
359
+ try {
360
+ if (_b && !_b.done && (_e = _a.return)) _e.call(_a);
361
+ }
362
+ finally { if (e_4) throw e_4.error; }
363
+ }
364
+ try {
365
+ for (_c = tslib_1.__values(this.indexesFor(entity)), _d = _c.next(); !_d.done; _d = _c.next()) {
366
+ def = _d.value;
367
+ keys = Array.isArray(def) ? tslib_1.__spreadArray([], tslib_1.__read(def), false) : [def];
368
+ _loop_1 = function (key) {
369
+ var field = tslib_1.__spreadArray([], tslib_1.__read(entity.fields), false).find(function (f) { return f.key === key; });
370
+ if (field)
371
+ set.add(names.$dbNameOf(field));
372
+ };
373
+ try {
374
+ for (keys_1 = (e_6 = void 0, tslib_1.__values(keys)), keys_1_1 = keys_1.next(); !keys_1_1.done; keys_1_1 = keys_1.next()) {
375
+ key = keys_1_1.value;
376
+ _loop_1(key);
377
+ }
378
+ }
379
+ catch (e_6_1) { e_6 = { error: e_6_1 }; }
380
+ finally {
381
+ try {
382
+ if (keys_1_1 && !keys_1_1.done && (_g = keys_1.return)) _g.call(keys_1);
383
+ }
384
+ finally { if (e_6) throw e_6.error; }
385
+ }
386
+ }
387
+ }
388
+ catch (e_5_1) { e_5 = { error: e_5_1 }; }
389
+ finally {
390
+ try {
391
+ if (_d && !_d.done && (_f = _c.return)) _f.call(_c);
392
+ }
393
+ finally { if (e_5) throw e_5.error; }
394
+ }
395
+ return [2 /*return*/, set];
396
+ }
397
+ });
398
+ });
399
+ };
400
+ //@internal
240
401
  IndexedDbDataProvider.prototype.forget = function (db) {
241
402
  if (this.db === db)
242
403
  this.db = undefined;
@@ -288,17 +449,17 @@ var IndexedDbDataProvider = /** @class */ (function () {
288
449
  IndexedDbDataProvider.prototype.fetchOps = function (storeName, entity, ops) {
289
450
  var _this = this;
290
451
  return this.withStore(storeName, 'readonly', function (store) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
291
- var idKey, seen, rows, _loop_1, ops_1, ops_1_1, op, e_3_1;
292
- var e_3, _a;
452
+ var idKey, seen, rows, _loop_2, ops_1, ops_1_1, op, e_7_1;
453
+ var e_7, _a;
293
454
  return tslib_1.__generator(this, function (_b) {
294
455
  switch (_b.label) {
295
456
  case 0:
296
457
  idKey = entity.idMetadata.fields[0].key;
297
458
  seen = new Set();
298
459
  rows = [];
299
- _loop_1 = function (op) {
460
+ _loop_2 = function (op) {
300
461
  var source, chunk, _c, _d, chunk_1, chunk_1_1, row, k;
301
- var e_4, _e;
462
+ var e_8, _e;
302
463
  return tslib_1.__generator(this, function (_f) {
303
464
  switch (_f.label) {
304
465
  case 0:
@@ -323,7 +484,7 @@ var IndexedDbDataProvider = /** @class */ (function () {
323
484
  case 7:
324
485
  chunk = _c;
325
486
  try {
326
- for (chunk_1 = (e_4 = void 0, tslib_1.__values(chunk)), chunk_1_1 = chunk_1.next(); !chunk_1_1.done; chunk_1_1 = chunk_1.next()) {
487
+ for (chunk_1 = (e_8 = void 0, tslib_1.__values(chunk)), chunk_1_1 = chunk_1.next(); !chunk_1_1.done; chunk_1_1 = chunk_1.next()) {
327
488
  row = chunk_1_1.value;
328
489
  if (row == null)
329
490
  continue;
@@ -334,12 +495,12 @@ var IndexedDbDataProvider = /** @class */ (function () {
334
495
  rows.push(row);
335
496
  }
336
497
  }
337
- catch (e_4_1) { e_4 = { error: e_4_1 }; }
498
+ catch (e_8_1) { e_8 = { error: e_8_1 }; }
338
499
  finally {
339
500
  try {
340
501
  if (chunk_1_1 && !chunk_1_1.done && (_e = chunk_1.return)) _e.call(chunk_1);
341
502
  }
342
- finally { if (e_4) throw e_4.error; }
503
+ finally { if (e_8) throw e_8.error; }
343
504
  }
344
505
  return [2 /*return*/];
345
506
  }
@@ -353,7 +514,7 @@ var IndexedDbDataProvider = /** @class */ (function () {
353
514
  case 2:
354
515
  if (!!ops_1_1.done) return [3 /*break*/, 5];
355
516
  op = ops_1_1.value;
356
- return [5 /*yield**/, _loop_1(op)];
517
+ return [5 /*yield**/, _loop_2(op)];
357
518
  case 3:
358
519
  _b.sent();
359
520
  _b.label = 4;
@@ -362,14 +523,14 @@ var IndexedDbDataProvider = /** @class */ (function () {
362
523
  return [3 /*break*/, 2];
363
524
  case 5: return [3 /*break*/, 8];
364
525
  case 6:
365
- e_3_1 = _b.sent();
366
- e_3 = { error: e_3_1 };
526
+ e_7_1 = _b.sent();
527
+ e_7 = { error: e_7_1 };
367
528
  return [3 /*break*/, 8];
368
529
  case 7:
369
530
  try {
370
531
  if (ops_1_1 && !ops_1_1.done && (_a = ops_1.return)) _a.call(ops_1);
371
532
  }
372
- finally { if (e_3) throw e_3.error; }
533
+ finally { if (e_7) throw e_7.error; }
373
534
  return [7 /*endfinally*/];
374
535
  case 8: return [2 /*return*/, rows];
375
536
  }
@@ -428,17 +589,17 @@ var IndexedDbEntityDataProvider = /** @class */ (function () {
428
589
  IndexedDbEntityDataProvider.prototype.insert = function (data) {
429
590
  var _this = this;
430
591
  return this.provider.enqueue(function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
431
- var helper, names, auto, idName, rows;
592
+ var helper, names, auto, idName, rows, storeName, dek, _a, keep, _b, keys, i, toWrite, _c;
432
593
  var _this = this;
433
- return tslib_1.__generator(this, function (_a) {
434
- switch (_a.label) {
594
+ return tslib_1.__generator(this, function (_d) {
595
+ switch (_d.label) {
435
596
  case 0: return [4 /*yield*/, this.provider.ensureStores([this.entity])];
436
597
  case 1:
437
- _a.sent();
598
+ _d.sent();
438
599
  helper = new array_entity_data_provider_js_1.ArrayEntityDataProvider(this.entity, function () { return []; });
439
600
  return [4 /*yield*/, helper.init()];
440
601
  case 2:
441
- names = _a.sent();
602
+ names = _d.sent();
442
603
  auto = (0, RepositoryImplementation_js_1.isAutoIncrement)(this.entity.idMetadata.field);
443
604
  idName = names.$dbNameOf(this.entity.idMetadata.field);
444
605
  rows = data.map(function (row) {
@@ -448,57 +609,109 @@ var IndexedDbEntityDataProvider = /** @class */ (function () {
448
609
  delete json[idName];
449
610
  return json;
450
611
  });
451
- return [2 /*return*/, this.provider.withStore(storeNameOf(this.entity), 'readwrite', function (store) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
452
- var result, rows_1, rows_1_1, json, key, e_5, e_6_1;
453
- var e_6, _a;
454
- var _b;
612
+ storeName = storeNameOf(this.entity);
613
+ if (!this.provider.encrypt) return [3 /*break*/, 4];
614
+ return [4 /*yield*/, this.provider.loadDek()];
615
+ case 3:
616
+ _a = _d.sent();
617
+ return [3 /*break*/, 5];
618
+ case 4:
619
+ _a = undefined;
620
+ _d.label = 5;
621
+ case 5:
622
+ dek = _a;
623
+ if (!dek) return [3 /*break*/, 7];
624
+ return [4 /*yield*/, this.provider.plaintextDbNames(this.entity)];
625
+ case 6:
626
+ _b = _d.sent();
627
+ return [3 /*break*/, 8];
628
+ case 7:
629
+ _b = undefined;
630
+ _d.label = 8;
631
+ case 8:
632
+ keep = _b;
633
+ if (!(dek && auto)) return [3 /*break*/, 10];
634
+ return [4 /*yield*/, this.provider.withStore(storeName, 'readwrite', function (store) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
635
+ var allocated, i, _a, _b;
455
636
  return tslib_1.__generator(this, function (_c) {
456
637
  switch (_c.label) {
457
638
  case 0:
458
- result = [];
639
+ allocated = [];
640
+ i = 0;
459
641
  _c.label = 1;
460
642
  case 1:
461
- _c.trys.push([1, 9, 10, 11]);
462
- rows_1 = tslib_1.__values(rows), rows_1_1 = rows_1.next();
463
- _c.label = 2;
643
+ if (!(i < rows.length)) return [3 /*break*/, 4];
644
+ _b = (_a = allocated).push;
645
+ return [4 /*yield*/, idbReq(store.add({}))];
464
646
  case 2:
465
- if (!!rows_1_1.done) return [3 /*break*/, 8];
466
- json = rows_1_1.value;
647
+ _b.apply(_a, [_c.sent()]);
467
648
  _c.label = 3;
468
649
  case 3:
469
- _c.trys.push([3, 5, , 6]);
470
- return [4 /*yield*/, idbReq(store.add(json))];
471
- case 4:
472
- key = _c.sent();
473
- if (auto)
474
- json[idName] = (_b = json[idName]) !== null && _b !== void 0 ? _b : key;
650
+ i++;
651
+ return [3 /*break*/, 1];
652
+ case 4: return [2 /*return*/, allocated];
653
+ }
654
+ });
655
+ }); })];
656
+ case 9:
657
+ keys = _d.sent();
658
+ for (i = 0; i < rows.length; i++) {
659
+ rows[i][idName] = keys[i];
660
+ }
661
+ _d.label = 10;
662
+ case 10:
663
+ if (!dek) return [3 /*break*/, 12];
664
+ return [4 /*yield*/, Promise.all(rows.map(function (json) {
665
+ return encryptJson(storeName, json, keep, keyFromRow(_this.entity, names, json), dek);
666
+ }))];
667
+ case 11:
668
+ _c = _d.sent();
669
+ return [3 /*break*/, 13];
670
+ case 12:
671
+ _c = rows;
672
+ _d.label = 13;
673
+ case 13:
674
+ toWrite = _c;
675
+ return [4 /*yield*/, this.provider.withStore(storeName, 'readwrite', function (store) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
676
+ var i, key, e_9;
677
+ var _a;
678
+ return tslib_1.__generator(this, function (_b) {
679
+ switch (_b.label) {
680
+ case 0:
681
+ i = 0;
682
+ _b.label = 1;
683
+ case 1:
684
+ if (!(i < toWrite.length)) return [3 /*break*/, 9];
685
+ _b.label = 2;
686
+ case 2:
687
+ _b.trys.push([2, 7, , 8]);
688
+ if (!(dek && auto)) return [3 /*break*/, 4];
689
+ return [4 /*yield*/, idbReq(store.put(toWrite[i]))];
690
+ case 3:
691
+ _b.sent();
475
692
  return [3 /*break*/, 6];
693
+ case 4: return [4 /*yield*/, idbReq(store.add(toWrite[i]))];
476
694
  case 5:
477
- e_5 = _c.sent();
478
- if ((e_5 === null || e_5 === void 0 ? void 0 : e_5.name) === 'ConstraintError')
479
- throw Error('id already exists');
480
- throw e_5;
481
- case 6:
482
- result.push(helper.translateFromJson(json, names));
483
- _c.label = 7;
695
+ key = _b.sent();
696
+ if (auto)
697
+ rows[i][idName] = (_a = rows[i][idName]) !== null && _a !== void 0 ? _a : key;
698
+ _b.label = 6;
699
+ case 6: return [3 /*break*/, 8];
484
700
  case 7:
485
- rows_1_1 = rows_1.next();
486
- return [3 /*break*/, 2];
487
- case 8: return [3 /*break*/, 11];
488
- case 9:
489
- e_6_1 = _c.sent();
490
- e_6 = { error: e_6_1 };
491
- return [3 /*break*/, 11];
492
- case 10:
493
- try {
494
- if (rows_1_1 && !rows_1_1.done && (_a = rows_1.return)) _a.call(rows_1);
495
- }
496
- finally { if (e_6) throw e_6.error; }
497
- return [7 /*endfinally*/];
498
- case 11: return [2 /*return*/, result];
701
+ e_9 = _b.sent();
702
+ if ((e_9 === null || e_9 === void 0 ? void 0 : e_9.name) === 'ConstraintError')
703
+ throw Error('id already exists');
704
+ throw e_9;
705
+ case 8:
706
+ i++;
707
+ return [3 /*break*/, 1];
708
+ case 9: return [2 /*return*/];
499
709
  }
500
710
  });
501
711
  }); })];
712
+ case 14:
713
+ _d.sent();
714
+ return [2 /*return*/, rows.map(function (json) { return helper.translateFromJson(json, names); })];
502
715
  }
503
716
  });
504
717
  }); });
@@ -506,64 +719,104 @@ var IndexedDbEntityDataProvider = /** @class */ (function () {
506
719
  IndexedDbEntityDataProvider.prototype.update = function (id, data) {
507
720
  var _this = this;
508
721
  return this.provider.enqueue(function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
509
- var helper, names, key;
722
+ var helper, names, key, storeName, existingRaw, dek, _a, existing, _b, json, keys, _c, _d, f, newKey, toWrite, _e, _f, _g;
723
+ var e_10, _h;
510
724
  var _this = this;
511
- return tslib_1.__generator(this, function (_a) {
512
- switch (_a.label) {
725
+ return tslib_1.__generator(this, function (_j) {
726
+ switch (_j.label) {
513
727
  case 0: return [4 /*yield*/, this.provider.ensureStores([this.entity])];
514
728
  case 1:
515
- _a.sent();
729
+ _j.sent();
516
730
  helper = new array_entity_data_provider_js_1.ArrayEntityDataProvider(this.entity, function () { return []; });
517
731
  return [4 /*yield*/, helper.init()];
518
732
  case 2:
519
- names = _a.sent();
733
+ names = _j.sent();
520
734
  key = toIdbKey(this.entity, id);
521
- return [2 /*return*/, this.provider.withStore(storeNameOf(this.entity), 'readwrite', function (store) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
522
- var existing, json, keys, _a, _b, f, newKey, conflict;
523
- var e_7, _c;
524
- return tslib_1.__generator(this, function (_d) {
525
- switch (_d.label) {
526
- case 0: return [4 /*yield*/, idbReq(store.get(key))];
527
- case 1:
528
- existing = _d.sent();
529
- if (existing == null)
530
- throw new Error("Couldn't find row with id \"".concat(id, "\" in entity \"").concat(this.entity.key, "\" to update"));
531
- json = tslib_1.__assign({}, existing);
532
- keys = Object.keys(data);
533
- try {
534
- for (_a = tslib_1.__values(this.entity.fields), _b = _a.next(); !_b.done; _b = _a.next()) {
535
- f = _b.value;
536
- if (!(0, filter_consumer_bridge_to_sql_request_js_1.isDbReadonly)(f, names) && keys.includes(f.key)) {
537
- json[names.$dbNameOf(f)] = f.valueConverter.toJson(data[f.key]);
538
- }
539
- }
540
- }
541
- catch (e_7_1) { e_7 = { error: e_7_1 }; }
542
- finally {
543
- try {
544
- if (_b && !_b.done && (_c = _a.return)) _c.call(_a);
545
- }
546
- finally { if (e_7) throw e_7.error; }
547
- }
548
- helper.verifyThatRowHasAllNotNullColumns(json, names);
549
- newKey = keyFromRow(this.entity, names, json);
550
- if (!!idbKeysEqual(key, newKey)) return [3 /*break*/, 4];
735
+ storeName = storeNameOf(this.entity);
736
+ return [4 /*yield*/, this.provider.withStore(storeName, 'readonly', function (store) { return idbReq(store.get(key)); })];
737
+ case 3:
738
+ existingRaw = _j.sent();
739
+ if (existingRaw == null)
740
+ throw new Error("Couldn't find row with id \"".concat(id, "\" in entity \"").concat(this.entity.key, "\" to update"));
741
+ if (!this.provider.encrypt) return [3 /*break*/, 5];
742
+ return [4 /*yield*/, this.provider.loadDek()];
743
+ case 4:
744
+ _a = _j.sent();
745
+ return [3 /*break*/, 6];
746
+ case 5:
747
+ _a = undefined;
748
+ _j.label = 6;
749
+ case 6:
750
+ dek = _a;
751
+ if (!dek) return [3 /*break*/, 8];
752
+ return [4 /*yield*/, decryptJson(storeName, existingRaw, this.entity, names, dek)];
753
+ case 7:
754
+ _b = _j.sent();
755
+ return [3 /*break*/, 9];
756
+ case 8:
757
+ _b = existingRaw;
758
+ _j.label = 9;
759
+ case 9:
760
+ existing = _b;
761
+ json = tslib_1.__assign({}, existing);
762
+ keys = Object.keys(data);
763
+ try {
764
+ for (_c = tslib_1.__values(this.entity.fields), _d = _c.next(); !_d.done; _d = _c.next()) {
765
+ f = _d.value;
766
+ if (!(0, filter_consumer_bridge_to_sql_request_js_1.isDbReadonly)(f, names) && keys.includes(f.key)) {
767
+ json[names.$dbNameOf(f)] = f.valueConverter.toJson(data[f.key]);
768
+ }
769
+ }
770
+ }
771
+ catch (e_10_1) { e_10 = { error: e_10_1 }; }
772
+ finally {
773
+ try {
774
+ if (_d && !_d.done && (_h = _c.return)) _h.call(_c);
775
+ }
776
+ finally { if (e_10) throw e_10.error; }
777
+ }
778
+ helper.verifyThatRowHasAllNotNullColumns(json, names);
779
+ newKey = keyFromRow(this.entity, names, json);
780
+ if (!dek) return [3 /*break*/, 12];
781
+ _f = encryptJson;
782
+ _g = [storeName,
783
+ json];
784
+ return [4 /*yield*/, this.provider.plaintextDbNames(this.entity)];
785
+ case 10: return [4 /*yield*/, _f.apply(void 0, _g.concat([_j.sent(), newKey,
786
+ dek]))];
787
+ case 11:
788
+ _e = _j.sent();
789
+ return [3 /*break*/, 13];
790
+ case 12:
791
+ _e = json;
792
+ _j.label = 13;
793
+ case 13:
794
+ toWrite = _e;
795
+ return [4 /*yield*/, this.provider.withStore(storeName, 'readwrite', function (store) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
796
+ var conflict;
797
+ return tslib_1.__generator(this, function (_a) {
798
+ switch (_a.label) {
799
+ case 0:
800
+ if (!!idbKeysEqual(key, newKey)) return [3 /*break*/, 3];
551
801
  return [4 /*yield*/, idbReq(store.get(newKey))];
552
- case 2:
553
- conflict = _d.sent();
802
+ case 1:
803
+ conflict = _a.sent();
554
804
  if (conflict != null)
555
805
  throw Error('id already exists');
556
806
  return [4 /*yield*/, idbReq(store.delete(key))];
557
- case 3:
558
- _d.sent();
559
- _d.label = 4;
560
- case 4: return [4 /*yield*/, idbReq(store.put(json))];
561
- case 5:
562
- _d.sent();
563
- return [2 /*return*/, helper.translateFromJson(json, names)];
807
+ case 2:
808
+ _a.sent();
809
+ _a.label = 3;
810
+ case 3: return [4 /*yield*/, idbReq(store.put(toWrite))];
811
+ case 4:
812
+ _a.sent();
813
+ return [2 /*return*/];
564
814
  }
565
815
  });
566
816
  }); })];
817
+ case 14:
818
+ _j.sent();
819
+ return [2 /*return*/, helper.translateFromJson(json, names)];
567
820
  }
568
821
  });
569
822
  }); });
@@ -580,8 +833,8 @@ var IndexedDbEntityDataProvider = /** @class */ (function () {
580
833
  case 1:
581
834
  _a.sent();
582
835
  return [2 /*return*/, this.provider.withStore(storeNameOf(this.entity), 'readwrite', function (store) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
583
- var ids_1, ids_1_1, id, key, existing, e_8_1;
584
- var e_8, _a;
836
+ var ids_1, ids_1_1, id, key, existing, e_11_1;
837
+ var e_11, _a;
585
838
  return tslib_1.__generator(this, function (_b) {
586
839
  switch (_b.label) {
587
840
  case 0:
@@ -606,14 +859,14 @@ var IndexedDbEntityDataProvider = /** @class */ (function () {
606
859
  return [3 /*break*/, 1];
607
860
  case 5: return [3 /*break*/, 8];
608
861
  case 6:
609
- e_8_1 = _b.sent();
610
- e_8 = { error: e_8_1 };
862
+ e_11_1 = _b.sent();
863
+ e_11 = { error: e_11_1 };
611
864
  return [3 /*break*/, 8];
612
865
  case 7:
613
866
  try {
614
867
  if (ids_1_1 && !ids_1_1.done && (_a = ids_1.return)) _a.call(ids_1);
615
868
  }
616
- finally { if (e_8) throw e_8.error; }
869
+ finally { if (e_11) throw e_11.error; }
617
870
  return [7 /*endfinally*/];
618
871
  case 8: return [2 /*return*/];
619
872
  }
@@ -632,7 +885,7 @@ function storeNameOf(entity) {
632
885
  function idbPrefetchFromFilter(entity_1, where_1) {
633
886
  return tslib_1.__awaiter(this, arguments, void 0, function (entity, where, indexes) {
634
887
  var names, c, r, ordered, ordered_1, ordered_1_1, idx, r;
635
- var e_9, _a;
888
+ var e_12, _a;
636
889
  if (indexes === void 0) { indexes = []; }
637
890
  return tslib_1.__generator(this, function (_b) {
638
891
  switch (_b.label) {
@@ -658,12 +911,12 @@ function idbPrefetchFromFilter(entity_1, where_1) {
658
911
  return [2 /*return*/, withIndex(r, idx.name)];
659
912
  }
660
913
  }
661
- catch (e_9_1) { e_9 = { error: e_9_1 }; }
914
+ catch (e_12_1) { e_12 = { error: e_12_1 }; }
662
915
  finally {
663
916
  try {
664
917
  if (ordered_1_1 && !ordered_1_1.done && (_a = ordered_1.return)) _a.call(ordered_1);
665
918
  }
666
- finally { if (e_9) throw e_9.error; }
919
+ finally { if (e_12) throw e_12.error; }
667
920
  }
668
921
  return [2 /*return*/, { type: 'all' }];
669
922
  }
@@ -734,7 +987,7 @@ function pathLen(keyPath) {
734
987
  return Array.isArray(keyPath) ? keyPath.length : 1;
735
988
  }
736
989
  function fieldByDbName(entity, names, dbName) {
737
- var e_10, _a;
990
+ var e_13, _a;
738
991
  try {
739
992
  for (var _b = tslib_1.__values(entity.fields), _c = _b.next(); !_c.done; _c = _b.next()) {
740
993
  var f = _c.value;
@@ -742,32 +995,32 @@ function fieldByDbName(entity, names, dbName) {
742
995
  return f;
743
996
  }
744
997
  }
745
- catch (e_10_1) { e_10 = { error: e_10_1 }; }
998
+ catch (e_13_1) { e_13 = { error: e_13_1 }; }
746
999
  finally {
747
1000
  try {
748
1001
  if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
749
1002
  }
750
- finally { if (e_10) throw e_10.error; }
1003
+ finally { if (e_13) throw e_13.error; }
751
1004
  }
752
1005
  }
753
1006
  function cartesian(lists) {
754
- var e_11, _a;
1007
+ var e_14, _a;
755
1008
  var acc = [[]];
756
- var _loop_2 = function (list) {
1009
+ var _loop_3 = function (list) {
757
1010
  acc = acc.flatMap(function (prefix) { return list.map(function (v) { return tslib_1.__spreadArray(tslib_1.__spreadArray([], tslib_1.__read(prefix), false), [v], false); }); });
758
1011
  };
759
1012
  try {
760
1013
  for (var lists_1 = tslib_1.__values(lists), lists_1_1 = lists_1.next(); !lists_1_1.done; lists_1_1 = lists_1.next()) {
761
1014
  var list = lists_1_1.value;
762
- _loop_2(list);
1015
+ _loop_3(list);
763
1016
  }
764
1017
  }
765
- catch (e_11_1) { e_11 = { error: e_11_1 }; }
1018
+ catch (e_14_1) { e_14 = { error: e_14_1 }; }
766
1019
  finally {
767
1020
  try {
768
1021
  if (lists_1_1 && !lists_1_1.done && (_a = lists_1.return)) _a.call(lists_1);
769
1022
  }
770
- finally { if (e_11) throw e_11.error; }
1023
+ finally { if (e_14) throw e_14.error; }
771
1024
  }
772
1025
  return acc;
773
1026
  }
@@ -924,7 +1177,7 @@ var IdbPrefetchCollector = /** @class */ (function () {
924
1177
  return IdbPrefetchCollector;
925
1178
  }());
926
1179
  function mergeOps(ops) {
927
- var e_12, _a;
1180
+ var e_15, _a;
928
1181
  var _b, _c;
929
1182
  var keyGroups = new Map();
930
1183
  var ranges = [];
@@ -939,12 +1192,12 @@ function mergeOps(ops) {
939
1192
  ranges.push(op);
940
1193
  }
941
1194
  }
942
- catch (e_12_1) { e_12 = { error: e_12_1 }; }
1195
+ catch (e_15_1) { e_15 = { error: e_15_1 }; }
943
1196
  finally {
944
1197
  try {
945
1198
  if (ops_2_1 && !ops_2_1.done && (_a = ops_2.return)) _a.call(ops_2);
946
1199
  }
947
- finally { if (e_12) throw e_12.error; }
1200
+ finally { if (e_15) throw e_15.error; }
948
1201
  }
949
1202
  var parts = tslib_1.__spreadArray(tslib_1.__spreadArray([], tslib_1.__read(tslib_1.__spreadArray([], tslib_1.__read(keyGroups.entries()), false).map(function (_a) {
950
1203
  var _b = tslib_1.__read(_a, 2), index = _b[0], keys = _b[1];
@@ -957,7 +1210,7 @@ function mergeOps(ops) {
957
1210
  function resolveIndexDefs(entity, defs, storeKeyPath) {
958
1211
  return tslib_1.__awaiter(this, void 0, void 0, function () {
959
1212
  var names, result, seen, pk, defs_1, defs_1_1, def, keys, keyPath, path, name_3;
960
- var e_13, _a;
1213
+ var e_16, _a;
961
1214
  return tslib_1.__generator(this, function (_b) {
962
1215
  switch (_b.label) {
963
1216
  case 0:
@@ -989,12 +1242,12 @@ function resolveIndexDefs(entity, defs, storeKeyPath) {
989
1242
  result.push({ name: name_3, keyPath: path });
990
1243
  }
991
1244
  }
992
- catch (e_13_1) { e_13 = { error: e_13_1 }; }
1245
+ catch (e_16_1) { e_16 = { error: e_16_1 }; }
993
1246
  finally {
994
1247
  try {
995
1248
  if (defs_1_1 && !defs_1_1.done && (_a = defs_1.return)) _a.call(defs_1);
996
1249
  }
997
- finally { if (e_13) throw e_13.error; }
1250
+ finally { if (e_16) throw e_16.error; }
998
1251
  }
999
1252
  return [2 /*return*/, result];
1000
1253
  }
@@ -1046,3 +1299,89 @@ function idbReq(request) {
1046
1299
  request.onerror = function () { return reject(request.error); };
1047
1300
  });
1048
1301
  }
1302
+ function encAad(storeName, id) {
1303
+ return new TextEncoder().encode(storeName + '\0' + JSON.stringify(id));
1304
+ }
1305
+ function asBufferSource(value, label) {
1306
+ if (value instanceof Uint8Array || value instanceof ArrayBuffer)
1307
+ return value;
1308
+ throw new Error("Failed to decrypt IndexedDB row: missing ".concat(label));
1309
+ }
1310
+ function encryptJson(storeName, json, plaintext, idbKey, dek) {
1311
+ return tslib_1.__awaiter(this, void 0, void 0, function () {
1312
+ var stored, payload, _a, _b, k, iv, _c, _d;
1313
+ var e_17, _e;
1314
+ return tslib_1.__generator(this, function (_f) {
1315
+ switch (_f.label) {
1316
+ case 0:
1317
+ stored = {};
1318
+ payload = {};
1319
+ try {
1320
+ for (_a = tslib_1.__values(Object.keys(json)), _b = _a.next(); !_b.done; _b = _a.next()) {
1321
+ k = _b.value;
1322
+ if (k === IDB_ENC || k === IDB_IV)
1323
+ continue;
1324
+ if (plaintext.has(k))
1325
+ stored[k] = json[k];
1326
+ else
1327
+ payload[k] = json[k];
1328
+ }
1329
+ }
1330
+ catch (e_17_1) { e_17 = { error: e_17_1 }; }
1331
+ finally {
1332
+ try {
1333
+ if (_b && !_b.done && (_e = _a.return)) _e.call(_a);
1334
+ }
1335
+ finally { if (e_17) throw e_17.error; }
1336
+ }
1337
+ iv = crypto.getRandomValues(new Uint8Array(12));
1338
+ _c = stored;
1339
+ _d = IDB_ENC;
1340
+ return [4 /*yield*/, crypto.subtle.encrypt({
1341
+ name: 'AES-GCM',
1342
+ iv: iv,
1343
+ additionalData: encAad(storeName, idbKey),
1344
+ }, dek, new TextEncoder().encode(JSON.stringify(payload)))];
1345
+ case 1:
1346
+ _c[_d] = _f.sent();
1347
+ stored[IDB_IV] = iv;
1348
+ return [2 /*return*/, stored];
1349
+ }
1350
+ });
1351
+ });
1352
+ }
1353
+ function decryptJson(storeName, row, entity, names, dek) {
1354
+ return tslib_1.__awaiter(this, void 0, void 0, function () {
1355
+ var idbKey, plain, payload, result, e_18;
1356
+ var _a;
1357
+ return tslib_1.__generator(this, function (_b) {
1358
+ switch (_b.label) {
1359
+ case 0:
1360
+ if (row == null || row[IDB_ENC] == null)
1361
+ return [2 /*return*/, row];
1362
+ idbKey = keyFromRow(entity, names, row);
1363
+ _b.label = 1;
1364
+ case 1:
1365
+ _b.trys.push([1, 3, , 4]);
1366
+ return [4 /*yield*/, crypto.subtle.decrypt({
1367
+ name: 'AES-GCM',
1368
+ iv: asBufferSource(row[IDB_IV], IDB_IV),
1369
+ additionalData: encAad(storeName, idbKey),
1370
+ }, dek, asBufferSource(row[IDB_ENC], IDB_ENC))];
1371
+ case 2:
1372
+ plain = _b.sent();
1373
+ payload = JSON.parse(new TextDecoder().decode(plain));
1374
+ result = tslib_1.__assign(tslib_1.__assign({}, row), payload);
1375
+ delete result[IDB_ENC];
1376
+ delete result[IDB_IV];
1377
+ return [2 /*return*/, result];
1378
+ case 3:
1379
+ e_18 = _b.sent();
1380
+ if ((_a = e_18 === null || e_18 === void 0 ? void 0 : e_18.message) === null || _a === void 0 ? void 0 : _a.startsWith('Failed to decrypt IndexedDB row'))
1381
+ throw e_18;
1382
+ throw new Error("Failed to decrypt IndexedDB row in \"".concat(storeName, "\""));
1383
+ case 4: return [2 /*return*/];
1384
+ }
1385
+ });
1386
+ });
1387
+ }