@instantdb/core 0.22.97 → 0.22.98

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 (126) hide show
  1. package/dist/commonjs/Connection.js +50 -51
  2. package/dist/commonjs/Connection.js.map +1 -1
  3. package/dist/commonjs/InMemoryStorage.js +13 -32
  4. package/dist/commonjs/InMemoryStorage.js.map +1 -1
  5. package/dist/commonjs/IndexedDBStorage.js +193 -217
  6. package/dist/commonjs/IndexedDBStorage.js.map +1 -1
  7. package/dist/commonjs/InstantError.js +1 -0
  8. package/dist/commonjs/InstantError.js.map +1 -1
  9. package/dist/commonjs/Reactor.js +566 -610
  10. package/dist/commonjs/Reactor.js.map +1 -1
  11. package/dist/commonjs/StorageAPI.js +51 -70
  12. package/dist/commonjs/StorageAPI.js.map +1 -1
  13. package/dist/commonjs/SyncTable.js +68 -81
  14. package/dist/commonjs/SyncTable.js.map +1 -1
  15. package/dist/commonjs/WindowNetworkListener.js +2 -13
  16. package/dist/commonjs/WindowNetworkListener.js.map +1 -1
  17. package/dist/commonjs/__types__/fieldsTypeTest.js +8 -16
  18. package/dist/commonjs/__types__/fieldsTypeTest.js.map +1 -1
  19. package/dist/commonjs/__types__/useDatesTypeTest.js +3 -6
  20. package/dist/commonjs/__types__/useDatesTypeTest.js.map +1 -1
  21. package/dist/commonjs/authAPI.js +62 -79
  22. package/dist/commonjs/authAPI.js.map +1 -1
  23. package/dist/commonjs/createRouteHandler.js +5 -15
  24. package/dist/commonjs/createRouteHandler.js.map +1 -1
  25. package/dist/commonjs/datalog.js +1 -1
  26. package/dist/commonjs/datalog.js.map +1 -1
  27. package/dist/commonjs/devtool.js +26 -8
  28. package/dist/commonjs/devtool.js.map +1 -1
  29. package/dist/commonjs/framework.d.ts.map +1 -1
  30. package/dist/commonjs/framework.js +142 -152
  31. package/dist/commonjs/framework.js.map +1 -1
  32. package/dist/commonjs/index.js +204 -190
  33. package/dist/commonjs/index.js.map +1 -1
  34. package/dist/commonjs/instaml.js +44 -30
  35. package/dist/commonjs/instaml.js.map +1 -1
  36. package/dist/commonjs/instaql.js +25 -33
  37. package/dist/commonjs/instaql.js.map +1 -1
  38. package/dist/commonjs/parseSchemaFromJSON.js +6 -7
  39. package/dist/commonjs/parseSchemaFromJSON.js.map +1 -1
  40. package/dist/commonjs/presence.js +7 -8
  41. package/dist/commonjs/presence.js.map +1 -1
  42. package/dist/commonjs/queryValidation.js +1 -2
  43. package/dist/commonjs/queryValidation.js.map +1 -1
  44. package/dist/commonjs/schema.js +8 -6
  45. package/dist/commonjs/schema.js.map +1 -1
  46. package/dist/commonjs/schemaTypes.js +22 -3
  47. package/dist/commonjs/schemaTypes.js.map +1 -1
  48. package/dist/commonjs/store.js +29 -38
  49. package/dist/commonjs/store.js.map +1 -1
  50. package/dist/commonjs/transactionValidation.js +1 -2
  51. package/dist/commonjs/transactionValidation.js.map +1 -1
  52. package/dist/commonjs/utils/Deferred.js +3 -0
  53. package/dist/commonjs/utils/Deferred.js.map +1 -1
  54. package/dist/commonjs/utils/PersistedObject.js +216 -233
  55. package/dist/commonjs/utils/PersistedObject.js.map +1 -1
  56. package/dist/commonjs/utils/fetch.js +9 -19
  57. package/dist/commonjs/utils/fetch.js.map +1 -1
  58. package/dist/commonjs/utils/linkIndex.js +2 -4
  59. package/dist/commonjs/utils/linkIndex.js.map +1 -1
  60. package/dist/commonjs/utils/object.js +1 -1
  61. package/dist/commonjs/utils/object.js.map +1 -1
  62. package/dist/esm/Connection.js +50 -51
  63. package/dist/esm/Connection.js.map +1 -1
  64. package/dist/esm/InMemoryStorage.js +13 -32
  65. package/dist/esm/InMemoryStorage.js.map +1 -1
  66. package/dist/esm/IndexedDBStorage.js +193 -217
  67. package/dist/esm/IndexedDBStorage.js.map +1 -1
  68. package/dist/esm/InstantError.js +1 -0
  69. package/dist/esm/InstantError.js.map +1 -1
  70. package/dist/esm/Reactor.js +566 -610
  71. package/dist/esm/Reactor.js.map +1 -1
  72. package/dist/esm/StorageAPI.js +51 -70
  73. package/dist/esm/StorageAPI.js.map +1 -1
  74. package/dist/esm/SyncTable.js +68 -81
  75. package/dist/esm/SyncTable.js.map +1 -1
  76. package/dist/esm/WindowNetworkListener.js +2 -13
  77. package/dist/esm/WindowNetworkListener.js.map +1 -1
  78. package/dist/esm/__types__/fieldsTypeTest.js +8 -16
  79. package/dist/esm/__types__/fieldsTypeTest.js.map +1 -1
  80. package/dist/esm/__types__/useDatesTypeTest.js +3 -6
  81. package/dist/esm/__types__/useDatesTypeTest.js.map +1 -1
  82. package/dist/esm/authAPI.js +62 -79
  83. package/dist/esm/authAPI.js.map +1 -1
  84. package/dist/esm/createRouteHandler.js +5 -15
  85. package/dist/esm/createRouteHandler.js.map +1 -1
  86. package/dist/esm/datalog.js +1 -1
  87. package/dist/esm/datalog.js.map +1 -1
  88. package/dist/esm/devtool.js +26 -8
  89. package/dist/esm/devtool.js.map +1 -1
  90. package/dist/esm/framework.d.ts.map +1 -1
  91. package/dist/esm/framework.js +142 -152
  92. package/dist/esm/framework.js.map +1 -1
  93. package/dist/esm/index.js +204 -190
  94. package/dist/esm/index.js.map +1 -1
  95. package/dist/esm/instaml.js +44 -30
  96. package/dist/esm/instaml.js.map +1 -1
  97. package/dist/esm/instaql.js +25 -33
  98. package/dist/esm/instaql.js.map +1 -1
  99. package/dist/esm/parseSchemaFromJSON.js +6 -7
  100. package/dist/esm/parseSchemaFromJSON.js.map +1 -1
  101. package/dist/esm/presence.js +7 -8
  102. package/dist/esm/presence.js.map +1 -1
  103. package/dist/esm/queryValidation.js +1 -2
  104. package/dist/esm/queryValidation.js.map +1 -1
  105. package/dist/esm/schema.js +8 -6
  106. package/dist/esm/schema.js.map +1 -1
  107. package/dist/esm/schemaTypes.js +22 -3
  108. package/dist/esm/schemaTypes.js.map +1 -1
  109. package/dist/esm/store.js +29 -38
  110. package/dist/esm/store.js.map +1 -1
  111. package/dist/esm/transactionValidation.js +1 -2
  112. package/dist/esm/transactionValidation.js.map +1 -1
  113. package/dist/esm/utils/Deferred.js +3 -0
  114. package/dist/esm/utils/Deferred.js.map +1 -1
  115. package/dist/esm/utils/PersistedObject.js +216 -233
  116. package/dist/esm/utils/PersistedObject.js.map +1 -1
  117. package/dist/esm/utils/fetch.js +9 -19
  118. package/dist/esm/utils/fetch.js.map +1 -1
  119. package/dist/esm/utils/linkIndex.js +2 -4
  120. package/dist/esm/utils/linkIndex.js.map +1 -1
  121. package/dist/esm/utils/object.js +1 -1
  122. package/dist/esm/utils/object.js.map +1 -1
  123. package/dist/standalone/index.js +2610 -2367
  124. package/dist/standalone/index.umd.cjs +3 -3
  125. package/package.json +2 -2
  126. package/src/framework.ts +0 -1
@@ -12,15 +12,6 @@
12
12
  // Each PersistedObject provides it's own `onMerge`
13
13
  // function to handle the merge of data from storage and memory
14
14
  // on load
15
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
16
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
17
- return new (P || (P = Promise))(function (resolve, reject) {
18
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
19
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
20
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
21
- step((generator = generator.apply(thisArg, _arguments || [])).next());
22
- });
23
- };
24
15
  // Uses `requestIdleCallback` if available, otherwise calls the
25
16
  // callback immediately
26
17
  import { create } from 'mutative';
@@ -37,29 +28,40 @@ export class StorageInterface {
37
28
  constructor(appId, storeName) { }
38
29
  }
39
30
  export class PersistedObject {
31
+ currentValue;
32
+ _subs = [];
33
+ _persister;
34
+ _merge;
35
+ serialize;
36
+ parse;
37
+ _saveThrottleMs;
38
+ _idleCallbackMaxWaitMs;
39
+ _nextSave = null;
40
+ _nextGc = null;
41
+ _pendingSaveKeys = new Set();
42
+ _loadedKeys = new Set();
43
+ _loadingKeys;
44
+ _objectSize;
45
+ _log;
46
+ onKeyLoaded;
47
+ _version = 0;
48
+ _meta = {
49
+ isLoading: true,
50
+ onLoadCbs: [],
51
+ value: null,
52
+ error: null,
53
+ attempts: 0,
54
+ };
55
+ _gcOpts;
40
56
  constructor(opts) {
41
- var _a, _b;
42
- this._subs = [];
43
- this._nextSave = null;
44
- this._nextGc = null;
45
- this._pendingSaveKeys = new Set();
46
- this._loadedKeys = new Set();
47
- this._version = 0;
48
- this._meta = {
49
- isLoading: true,
50
- onLoadCbs: [],
51
- value: null,
52
- error: null,
53
- attempts: 0,
54
- };
55
57
  this._persister = opts.persister;
56
58
  this._merge = opts.merge;
57
59
  this.serialize = opts.serialize;
58
60
  this.parse = opts.parse;
59
61
  this._objectSize = opts.objectSize;
60
62
  this._log = opts.logger;
61
- this._saveThrottleMs = (_a = opts.saveThrottleMs) !== null && _a !== void 0 ? _a : 100;
62
- this._idleCallbackMaxWaitMs = (_b = opts.idleCallbackMaxWaitMs) !== null && _b !== void 0 ? _b : 1000;
63
+ this._saveThrottleMs = opts.saveThrottleMs ?? 100;
64
+ this._idleCallbackMaxWaitMs = opts.idleCallbackMaxWaitMs ?? 1000;
63
65
  this._gcOpts = opts.gc;
64
66
  this.currentValue = {};
65
67
  this._loadedKeys = new Set();
@@ -69,97 +71,85 @@ export class PersistedObject {
69
71
  this._preloadEntries(opts.preloadEntryCount);
70
72
  }
71
73
  }
72
- _initMeta() {
73
- return __awaiter(this, void 0, void 0, function* () {
74
- var _a, _b, _c;
75
- if (this._meta.loadingPromise) {
76
- yield this._meta.loadingPromise;
77
- }
78
- try {
79
- const p = this._persister.getItem(META_KEY);
80
- this._meta.loadingPromise = p;
81
- const v = yield p;
82
- this._meta.isLoading = false;
83
- this._meta.error = null;
84
- this._meta.loadingPromise = null;
85
- this._meta.attempts = 0;
86
- const existingObjects = (_b = (_a = this._meta.value) === null || _a === void 0 ? void 0 : _a.objects) !== null && _b !== void 0 ? _b : {};
87
- const value = v !== null && v !== void 0 ? v : {};
88
- const objects = (_c = value.objects) !== null && _c !== void 0 ? _c : {};
89
- // Merge the values from storage with in-memory values
90
- this._meta.value = Object.assign(Object.assign({}, value), { objects: Object.assign(Object.assign({}, existingObjects), objects) });
91
- }
92
- catch (e) {
93
- this._meta.error = e;
94
- this._meta.attempts++;
95
- this._meta.loadingPromise = null;
96
- }
97
- });
74
+ async _initMeta() {
75
+ if (this._meta.loadingPromise) {
76
+ await this._meta.loadingPromise;
77
+ }
78
+ try {
79
+ const p = this._persister.getItem(META_KEY);
80
+ this._meta.loadingPromise = p;
81
+ const v = await p;
82
+ this._meta.isLoading = false;
83
+ this._meta.error = null;
84
+ this._meta.loadingPromise = null;
85
+ this._meta.attempts = 0;
86
+ const existingObjects = this._meta.value?.objects ?? {};
87
+ const value = v ?? {};
88
+ const objects = value.objects ?? {};
89
+ // Merge the values from storage with in-memory values
90
+ this._meta.value = {
91
+ ...value,
92
+ objects: { ...existingObjects, ...objects },
93
+ };
94
+ }
95
+ catch (e) {
96
+ this._meta.error = e;
97
+ this._meta.attempts++;
98
+ this._meta.loadingPromise = null;
99
+ }
98
100
  }
99
- _getMeta() {
100
- return __awaiter(this, void 0, void 0, function* () {
101
- if (this._meta.value) {
102
- return this._meta.value;
103
- }
104
- if (this._meta.loadingPromise) {
105
- yield this._meta.loadingPromise;
106
- return this._meta.value;
107
- }
108
- this._initMeta();
109
- yield this._meta.loadingPromise;
101
+ async _getMeta() {
102
+ if (this._meta.value) {
110
103
  return this._meta.value;
111
- });
112
- }
113
- _refreshMeta() {
114
- return __awaiter(this, void 0, void 0, function* () {
115
- yield this._initMeta();
104
+ }
105
+ if (this._meta.loadingPromise) {
106
+ await this._meta.loadingPromise;
116
107
  return this._meta.value;
117
- });
108
+ }
109
+ this._initMeta();
110
+ await this._meta.loadingPromise;
111
+ return this._meta.value;
118
112
  }
119
- _preloadEntries(n) {
120
- return __awaiter(this, void 0, void 0, function* () {
121
- const meta = yield this.waitForMetaToLoad();
122
- if (!meta)
123
- return;
124
- const entries = Object.entries(meta.objects);
125
- entries.sort(([_k_a, a_meta], [_k_b, b_meta]) => {
126
- return b_meta.updatedAt - a_meta.updatedAt;
127
- });
128
- for (const [k] of entries.slice(0, n)) {
129
- this._loadKey(k);
130
- }
131
- });
113
+ async _refreshMeta() {
114
+ await this._initMeta();
115
+ return this._meta.value;
132
116
  }
133
- _getFromStorage(key) {
134
- return __awaiter(this, void 0, void 0, function* () {
135
- try {
136
- const data = yield this._persister.getItem(key);
137
- if (!data) {
138
- return data;
139
- }
140
- const parsed = this.parse(key, data);
141
- return parsed;
142
- }
143
- catch (e) {
144
- console.error(`Unable to read from storage for key=${key}`, e);
145
- return null;
146
- }
117
+ async _preloadEntries(n) {
118
+ const meta = await this.waitForMetaToLoad();
119
+ if (!meta)
120
+ return;
121
+ const entries = Object.entries(meta.objects);
122
+ entries.sort(([_k_a, a_meta], [_k_b, b_meta]) => {
123
+ return b_meta.updatedAt - a_meta.updatedAt;
147
124
  });
125
+ for (const [k] of entries.slice(0, n)) {
126
+ this._loadKey(k);
127
+ }
148
128
  }
149
- waitForKeyToLoad(k) {
150
- return __awaiter(this, void 0, void 0, function* () {
151
- if (this._loadedKeys.has(k)) {
152
- return this.currentValue[k];
129
+ async _getFromStorage(key) {
130
+ try {
131
+ const data = await this._persister.getItem(key);
132
+ if (!data) {
133
+ return data;
153
134
  }
154
- yield (this._loadingKeys[k] || this._loadKey(k));
135
+ const parsed = this.parse(key, data);
136
+ return parsed;
137
+ }
138
+ catch (e) {
139
+ console.error(`Unable to read from storage for key=${key}`, e);
140
+ return null;
141
+ }
142
+ }
143
+ async waitForKeyToLoad(k) {
144
+ if (this._loadedKeys.has(k)) {
155
145
  return this.currentValue[k];
156
- });
146
+ }
147
+ await (this._loadingKeys[k] || this._loadKey(k));
148
+ return this.currentValue[k];
157
149
  }
158
150
  // Used for tests
159
- waitForMetaToLoad() {
160
- return __awaiter(this, void 0, void 0, function* () {
161
- return this._getMeta();
162
- });
151
+ async waitForMetaToLoad() {
152
+ return this._getMeta();
163
153
  }
164
154
  // Unloads the key so that it can be garbage collected, but does not
165
155
  // delete it. Removes the key from currentValue.
@@ -168,41 +158,38 @@ export class PersistedObject {
168
158
  delete this._loadingKeys[k];
169
159
  delete this.currentValue[k];
170
160
  }
171
- _loadKey(k) {
172
- return __awaiter(this, void 0, void 0, function* () {
173
- if (this._loadedKeys.has(k) || k in this._loadingKeys)
174
- return;
175
- const p = this._getFromStorage(k);
176
- this._loadingKeys[k] = p;
177
- const value = yield p;
178
- delete this._loadingKeys[k];
179
- this._loadedKeys.add(k);
180
- if (value) {
181
- const merged = this._merge(k, value, this.currentValue[k]);
182
- if (merged) {
183
- this.currentValue[k] = merged;
184
- }
161
+ async _loadKey(k) {
162
+ if (this._loadedKeys.has(k) || k in this._loadingKeys)
163
+ return;
164
+ const p = this._getFromStorage(k);
165
+ this._loadingKeys[k] = p;
166
+ const value = await p;
167
+ delete this._loadingKeys[k];
168
+ this._loadedKeys.add(k);
169
+ if (value) {
170
+ const merged = this._merge(k, value, this.currentValue[k]);
171
+ if (merged) {
172
+ this.currentValue[k] = merged;
185
173
  }
186
- this.onKeyLoaded && this.onKeyLoaded(k);
187
- });
174
+ }
175
+ this.onKeyLoaded && this.onKeyLoaded(k);
188
176
  }
189
177
  // Returns a promise with a number so that we can wait for flush
190
178
  // to finish in the tests. The number is the number of operations
191
179
  // it performed, but it's mostly there so that typescript will warn
192
180
  // us if we forget to retun the promise from the function.
193
181
  _writeToStorage(opts) {
194
- var _a, _b;
195
182
  const promises = [];
196
- const skipGc = opts === null || opts === void 0 ? void 0 : opts.skipGc;
183
+ const skipGc = opts?.skipGc;
197
184
  if (this._meta.isLoading) {
198
185
  // Wait for meta to load and try again, give it a delay so that
199
186
  // we don't spend too much time retrying
200
187
  const p = new Promise((resolve, reject) => {
201
- var _a;
202
188
  setTimeout(() => this._enqueuePersist(opts
203
- ? Object.assign(Object.assign({}, opts), { attempts: (opts.attempts || 0) + 1 }) : { attempts: 1 })
189
+ ? { ...opts, attempts: (opts.attempts || 0) + 1 }
190
+ : { attempts: 1 })
204
191
  .then(resolve)
205
- .catch(reject), 10 + ((_a = opts === null || opts === void 0 ? void 0 : opts.attempts) !== null && _a !== void 0 ? _a : 0) * 1000);
192
+ .catch(reject), 10 + (opts?.attempts ?? 0) * 1000);
206
193
  });
207
194
  promises.push(p);
208
195
  return Promise.all(promises).then((vs) => vs.reduce((acc, x) => acc + x, 0));
@@ -233,14 +220,14 @@ export class PersistedObject {
233
220
  }
234
221
  const keysToLoad = [];
235
222
  const kvPairs = [[META_KEY, metaValue]];
236
- const metaObjects = (_a = metaValue.objects) !== null && _a !== void 0 ? _a : {};
223
+ const metaObjects = metaValue.objects ?? {};
237
224
  metaValue.objects = metaObjects;
238
225
  for (const k of keysToUpdate) {
239
226
  if (this._loadedKeys.has(k)) {
240
227
  const serializedV = this.serialize(k, this.currentValue[k]);
241
228
  kvPairs.push([k, serializedV]);
242
229
  const size = this._objectSize(serializedV);
243
- const m = (_b = metaObjects[k]) !== null && _b !== void 0 ? _b : {
230
+ const m = metaObjects[k] ?? {
244
231
  createdAt: Date.now(),
245
232
  updatedAt: Date.now(),
246
233
  size,
@@ -271,119 +258,115 @@ export class PersistedObject {
271
258
  return vs.reduce((acc, x) => acc + x, 0);
272
259
  });
273
260
  }
274
- flush() {
275
- return __awaiter(this, void 0, void 0, function* () {
276
- if (!this._nextSave) {
277
- return;
278
- }
279
- clearTimeout(this._nextSave);
280
- this._nextSave = null;
281
- const p = this._writeToStorage();
282
- return p;
283
- });
261
+ async flush() {
262
+ if (!this._nextSave) {
263
+ return;
264
+ }
265
+ clearTimeout(this._nextSave);
266
+ this._nextSave = null;
267
+ const p = this._writeToStorage();
268
+ return p;
284
269
  }
285
- _gc() {
286
- return __awaiter(this, void 0, void 0, function* () {
287
- if (!this._gcOpts) {
288
- return;
289
- }
290
- const keys = new Set(yield this._persister.getAllKeys());
291
- keys.delete(META_KEY);
292
- // Keys we can't delete
293
- const sacredKeys = new Set(Object.keys(this.currentValue));
294
- for (const k of Object.keys(this._loadingKeys)) {
295
- sacredKeys.add(k);
296
- }
297
- for (const k of this._loadedKeys) {
298
- sacredKeys.add(k);
299
- }
300
- // Refresh meta from the store so that we're less likely to
301
- // clobber data from other tabs
302
- const meta = yield this._refreshMeta();
303
- if (!meta) {
304
- this._log.info('Could not gc because we were not able to load meta');
305
- return;
306
- }
307
- const promises = [];
308
- const deets = {
309
- gcOpts: this._gcOpts,
310
- keys,
311
- sacredKeys,
312
- removed: [],
313
- metaRemoved: [],
314
- removedMissingCount: 0,
315
- removedOldCount: 0,
316
- removedThresholdCount: 0,
317
- removedSizeCount: 0,
318
- };
319
- // First, remove all keys we don't know about
320
- for (const key of keys) {
321
- if (sacredKeys.has(key) || key in meta.objects) {
322
- continue;
323
- }
324
- this._log.info('Lost track of key in meta', key);
325
- promises.push(this._persister.removeItem(key));
326
- deets.removed.push(key);
327
- deets.removedMissingCount++;
328
- }
329
- // Remove anything over the max age
330
- const now = Date.now();
331
- for (const [k, m] of Object.entries(meta.objects)) {
332
- if (!sacredKeys.has(k) &&
333
- m.updatedAt < now - this._gcOpts.maxAgeMs) {
334
- promises.push(this._persister.removeItem(k));
335
- delete meta.objects[k];
336
- deets.removed.push(k);
337
- deets.removedOldCount++;
338
- }
339
- }
340
- // Keep queries under max queries
341
- const maxEntries = Object.entries(meta.objects);
342
- maxEntries.sort(([_k_a, a_meta], [_k_b, b_meta]) => {
343
- return a_meta.updatedAt - b_meta.updatedAt;
344
- });
345
- const deletableMaxEntries = maxEntries.filter(([x]) => !sacredKeys.has(x));
346
- if (maxEntries.length > this._gcOpts.maxEntries) {
347
- for (const [k] of deletableMaxEntries.slice(0, maxEntries.length - this._gcOpts.maxEntries)) {
348
- promises.push(this._persister.removeItem(k));
349
- delete meta.objects[k];
350
- deets.removed.push(k);
351
- deets.removedThresholdCount++;
352
- }
270
+ async _gc() {
271
+ if (!this._gcOpts) {
272
+ return;
273
+ }
274
+ const keys = new Set(await this._persister.getAllKeys());
275
+ keys.delete(META_KEY);
276
+ // Keys we can't delete
277
+ const sacredKeys = new Set(Object.keys(this.currentValue));
278
+ for (const k of Object.keys(this._loadingKeys)) {
279
+ sacredKeys.add(k);
280
+ }
281
+ for (const k of this._loadedKeys) {
282
+ sacredKeys.add(k);
283
+ }
284
+ // Refresh meta from the store so that we're less likely to
285
+ // clobber data from other tabs
286
+ const meta = await this._refreshMeta();
287
+ if (!meta) {
288
+ this._log.info('Could not gc because we were not able to load meta');
289
+ return;
290
+ }
291
+ const promises = [];
292
+ const deets = {
293
+ gcOpts: this._gcOpts,
294
+ keys,
295
+ sacredKeys,
296
+ removed: [],
297
+ metaRemoved: [],
298
+ removedMissingCount: 0,
299
+ removedOldCount: 0,
300
+ removedThresholdCount: 0,
301
+ removedSizeCount: 0,
302
+ };
303
+ // First, remove all keys we don't know about
304
+ for (const key of keys) {
305
+ if (sacredKeys.has(key) || key in meta.objects) {
306
+ continue;
353
307
  }
354
- // Remove oldest entries until we are under max size
355
- const delEntries = Object.entries(meta.objects);
356
- delEntries.sort(([_k_a, a_meta], [_k_b, b_meta]) => {
357
- return a_meta.updatedAt - b_meta.updatedAt;
358
- });
359
- const deletableDelEntries = delEntries.filter(([x]) => !sacredKeys.has(x));
360
- let currentSize = delEntries.reduce((acc, [_k, m]) => {
361
- return acc + m.size;
362
- }, 0);
363
- while (currentSize > 0 &&
364
- currentSize > this._gcOpts.maxSize &&
365
- deletableDelEntries.length) {
366
- const [[k, m]] = deletableDelEntries.splice(0, 1);
367
- currentSize -= m.size;
308
+ this._log.info('Lost track of key in meta', key);
309
+ promises.push(this._persister.removeItem(key));
310
+ deets.removed.push(key);
311
+ deets.removedMissingCount++;
312
+ }
313
+ // Remove anything over the max age
314
+ const now = Date.now();
315
+ for (const [k, m] of Object.entries(meta.objects)) {
316
+ if (!sacredKeys.has(k) &&
317
+ m.updatedAt < now - this._gcOpts.maxAgeMs) {
368
318
  promises.push(this._persister.removeItem(k));
369
319
  delete meta.objects[k];
370
320
  deets.removed.push(k);
371
- deets.removedSizeCount++;
321
+ deets.removedOldCount++;
372
322
  }
373
- // Update meta to remove keys that are no longer in the store
374
- for (const k of Object.keys(meta.objects)) {
375
- if (!keys.has(k) && !sacredKeys.has(k)) {
376
- delete meta.objects[k];
377
- }
378
- }
379
- if (deets.removed.length || deets.metaRemoved.length) {
380
- // Trigger a flush of the meta
381
- promises.push(this._enqueuePersist({ skipGc: true }));
323
+ }
324
+ // Keep queries under max queries
325
+ const maxEntries = Object.entries(meta.objects);
326
+ maxEntries.sort(([_k_a, a_meta], [_k_b, b_meta]) => {
327
+ return a_meta.updatedAt - b_meta.updatedAt;
328
+ });
329
+ const deletableMaxEntries = maxEntries.filter(([x]) => !sacredKeys.has(x));
330
+ if (maxEntries.length > this._gcOpts.maxEntries) {
331
+ for (const [k] of deletableMaxEntries.slice(0, maxEntries.length - this._gcOpts.maxEntries)) {
332
+ promises.push(this._persister.removeItem(k));
333
+ delete meta.objects[k];
334
+ deets.removed.push(k);
335
+ deets.removedThresholdCount++;
382
336
  }
383
- this._log.info('Completed GC', deets);
384
- yield Promise.all(promises);
385
- return deets;
337
+ }
338
+ // Remove oldest entries until we are under max size
339
+ const delEntries = Object.entries(meta.objects);
340
+ delEntries.sort(([_k_a, a_meta], [_k_b, b_meta]) => {
341
+ return a_meta.updatedAt - b_meta.updatedAt;
386
342
  });
343
+ const deletableDelEntries = delEntries.filter(([x]) => !sacredKeys.has(x));
344
+ let currentSize = delEntries.reduce((acc, [_k, m]) => {
345
+ return acc + m.size;
346
+ }, 0);
347
+ while (currentSize > 0 &&
348
+ currentSize > this._gcOpts.maxSize &&
349
+ deletableDelEntries.length) {
350
+ const [[k, m]] = deletableDelEntries.splice(0, 1);
351
+ currentSize -= m.size;
352
+ promises.push(this._persister.removeItem(k));
353
+ delete meta.objects[k];
354
+ deets.removed.push(k);
355
+ deets.removedSizeCount++;
356
+ }
357
+ // Update meta to remove keys that are no longer in the store
358
+ for (const k of Object.keys(meta.objects)) {
359
+ if (!keys.has(k) && !sacredKeys.has(k)) {
360
+ delete meta.objects[k];
361
+ }
362
+ }
363
+ if (deets.removed.length || deets.metaRemoved.length) {
364
+ // Trigger a flush of the meta
365
+ promises.push(this._enqueuePersist({ skipGc: true }));
366
+ }
367
+ this._log.info('Completed GC', deets);
368
+ await Promise.all(promises);
369
+ return deets;
387
370
  }
388
371
  // Schedules a GC to run in one minute (unless it is already scheduled)
389
372
  gc() {