@luvio/environments 0.138.0 → 0.138.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.
Files changed (43) hide show
  1. package/dist/es/es2018/environments.js +858 -852
  2. package/dist/es/es2018/{DurableStore.d.ts → types/DurableStore.d.ts} +134 -134
  3. package/dist/{umd/es5 → es/es2018/types}/DurableTTLStore.d.ts +25 -25
  4. package/dist/{umd/es5 → es/es2018/types}/events.d.ts +18 -18
  5. package/dist/{umd/es5 → es/es2018/types}/main.d.ts +5 -5
  6. package/dist/es/es2018/{makeDurable → types/makeDurable}/error.d.ts +11 -11
  7. package/dist/es/es2018/{makeDurable → types/makeDurable}/flush.d.ts +4 -4
  8. package/dist/{umd/es5 → es/es2018/types}/makeDurable/revive.d.ts +38 -38
  9. package/dist/es/es2018/{makeDurable → types/makeDurable}/stagingStore.d.ts +6 -6
  10. package/dist/es/es2018/{makeDurable → types/makeDurable}/ttl.d.ts +3 -3
  11. package/dist/es/es2018/{makeDurable → types/makeDurable}/utils.d.ts +2 -2
  12. package/dist/es/es2018/{makeDurable.d.ts → types/makeDurable.d.ts} +44 -44
  13. package/dist/es/es2018/{utils → types/utils}/deep-freeze.d.ts +1 -1
  14. package/dist/es/es2018/{utils → types/utils}/language.d.ts +19 -19
  15. package/dist/umd/es2018/environments.js +858 -852
  16. package/dist/umd/es2018/{DurableStore.d.ts → types/DurableStore.d.ts} +134 -134
  17. package/dist/{es/es2018 → umd/es2018/types}/DurableTTLStore.d.ts +25 -25
  18. package/dist/{es/es2018 → umd/es2018/types}/events.d.ts +18 -18
  19. package/dist/umd/es2018/{main.d.ts → types/main.d.ts} +5 -5
  20. package/dist/umd/{es5 → es2018/types}/makeDurable/error.d.ts +11 -11
  21. package/dist/umd/es2018/{makeDurable → types/makeDurable}/flush.d.ts +4 -4
  22. package/dist/{es/es2018 → umd/es2018/types}/makeDurable/revive.d.ts +38 -38
  23. package/dist/umd/{es5 → es2018/types}/makeDurable/stagingStore.d.ts +6 -6
  24. package/dist/umd/es2018/{makeDurable → types/makeDurable}/ttl.d.ts +3 -3
  25. package/dist/umd/es2018/{makeDurable → types/makeDurable}/utils.d.ts +2 -2
  26. package/dist/umd/{es5 → es2018/types}/makeDurable.d.ts +44 -44
  27. package/dist/umd/es2018/{utils → types/utils}/deep-freeze.d.ts +1 -1
  28. package/dist/umd/es2018/{utils → types/utils}/language.d.ts +19 -19
  29. package/dist/umd/es5/environments.js +942 -936
  30. package/dist/umd/es5/{DurableStore.d.ts → types/DurableStore.d.ts} +134 -134
  31. package/dist/umd/{es2018 → es5/types}/DurableTTLStore.d.ts +25 -25
  32. package/dist/umd/{es2018 → es5/types}/events.d.ts +18 -18
  33. package/dist/{es/es2018 → umd/es5/types}/main.d.ts +5 -5
  34. package/dist/umd/{es2018 → es5/types}/makeDurable/error.d.ts +11 -11
  35. package/dist/umd/es5/{makeDurable → types/makeDurable}/flush.d.ts +4 -4
  36. package/dist/umd/{es2018 → es5/types}/makeDurable/revive.d.ts +38 -38
  37. package/dist/umd/{es2018 → es5/types}/makeDurable/stagingStore.d.ts +6 -6
  38. package/dist/umd/es5/{makeDurable → types/makeDurable}/ttl.d.ts +3 -3
  39. package/dist/umd/es5/{makeDurable → types/makeDurable}/utils.d.ts +2 -2
  40. package/dist/umd/{es2018 → es5/types}/makeDurable.d.ts +44 -44
  41. package/dist/umd/es5/{utils → types/utils}/deep-freeze.d.ts +1 -1
  42. package/dist/umd/es5/{utils → types/utils}/language.d.ts +19 -19
  43. package/package.json +4 -4
@@ -4,23 +4,23 @@
4
4
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.luvioEnvironments = {}, global.luvioEngine));
5
5
  })(this, (function (exports, engine) { 'use strict';
6
6
 
7
- // the last version the metadata shape was altered
8
- var DURABLE_METADATA_VERSION = '0.111.0';
9
- function isDeprecatedDurableStoreEntry(durableRecord) {
10
- if (durableRecord.expiration !== undefined) {
11
- return true;
12
- }
13
- var metadata = durableRecord.metadata;
14
- if (metadata !== undefined) {
15
- var metadataVersion = metadata.metadataVersion;
16
- // eventually we will want to assert that metadataVersion is defined
17
- if (metadataVersion !== undefined && metadataVersion !== DURABLE_METADATA_VERSION) {
18
- return true;
19
- }
20
- }
21
- // Add more deprecated shape checks here
22
- return false;
23
- }
7
+ // the last version the metadata shape was altered
8
+ var DURABLE_METADATA_VERSION = '0.111.0';
9
+ function isDeprecatedDurableStoreEntry(durableRecord) {
10
+ if (durableRecord.expiration !== undefined) {
11
+ return true;
12
+ }
13
+ var metadata = durableRecord.metadata;
14
+ if (metadata !== undefined) {
15
+ var metadataVersion = metadata.metadataVersion;
16
+ // eventually we will want to assert that metadataVersion is defined
17
+ if (metadataVersion !== undefined && metadataVersion !== DURABLE_METADATA_VERSION) {
18
+ return true;
19
+ }
20
+ }
21
+ // Add more deprecated shape checks here
22
+ return false;
23
+ }
24
24
  var DefaultDurableSegment = 'DEFAULT';
25
25
 
26
26
  /******************************************************************************
@@ -97,945 +97,951 @@
97
97
  return to.concat(ar || Array.prototype.slice.call(from));
98
98
  }
99
99
 
100
- var keys = Object.keys, create = Object.create, freeze = Object.freeze;
100
+ var keys = Object.keys, create = Object.create, freeze = Object.freeze;
101
101
  var isArray = Array.isArray;
102
102
 
103
- //Durable store error instrumentation key
104
- var DURABLE_STORE_ERROR = 'durable-store-error';
105
- /**
106
- * Returns a function that processes errors from durable store promise rejections.
107
- * If running in a non-production environment, the error is rethrown.
108
- * When running in production the error is sent to instrumentation.
109
- * @param instrument Instrumentation function implementation
110
- */
111
- function handleDurableStoreRejection(instrument) {
112
- return function (error) {
113
- if (process.env.NODE_ENV !== 'production') {
114
- throw error;
115
- }
116
- if (instrument !== undefined) {
117
- instrument(function () {
118
- var _a;
119
- return _a = {},
120
- _a[DURABLE_STORE_ERROR] = true,
121
- _a.error = error,
122
- _a;
123
- });
124
- }
125
- };
103
+ //Durable store error instrumentation key
104
+ var DURABLE_STORE_ERROR = 'durable-store-error';
105
+ /**
106
+ * Returns a function that processes errors from durable store promise rejections.
107
+ * If running in a non-production environment, the error is rethrown.
108
+ * When running in production the error is sent to instrumentation.
109
+ * @param instrument Instrumentation function implementation
110
+ */
111
+ function handleDurableStoreRejection(instrument) {
112
+ return function (error) {
113
+ if (process.env.NODE_ENV !== 'production') {
114
+ throw error;
115
+ }
116
+ if (instrument !== undefined) {
117
+ instrument(function () {
118
+ var _a;
119
+ return _a = {},
120
+ _a[DURABLE_STORE_ERROR] = true,
121
+ _a.error = error,
122
+ _a;
123
+ });
124
+ }
125
+ };
126
126
  }
127
127
 
128
- function deepFreeze(value) {
129
- // No need to freeze primitives
130
- if (typeof value !== 'object' || value === null) {
131
- return;
132
- }
133
- if (isArray(value)) {
134
- for (var i = 0, len = value.length; i < len; i += 1) {
135
- deepFreeze(value[i]);
136
- }
137
- }
138
- else {
139
- var keys$1 = keys(value);
140
- for (var i = 0, len = keys$1.length; i < len; i += 1) {
141
- deepFreeze(value[keys$1[i]]);
142
- }
143
- }
144
- freeze(value);
128
+ function deepFreeze(value) {
129
+ // No need to freeze primitives
130
+ if (typeof value !== 'object' || value === null) {
131
+ return;
132
+ }
133
+ if (isArray(value)) {
134
+ for (var i = 0, len = value.length; i < len; i += 1) {
135
+ deepFreeze(value[i]);
136
+ }
137
+ }
138
+ else {
139
+ var keys$1 = keys(value);
140
+ for (var i = 0, len = keys$1.length; i < len; i += 1) {
141
+ deepFreeze(value[keys$1[i]]);
142
+ }
143
+ }
144
+ freeze(value);
145
145
  }
146
146
 
147
- function isStoreEntryError(storeRecord) {
148
- return storeRecord.__type === 'error';
147
+ function isStoreEntryError(storeRecord) {
148
+ return storeRecord.__type === 'error';
149
149
  }
150
150
 
151
- /**
152
- * Takes a set of entries from DurableStore and publishes them via the passed in funcs.
153
- * This respects expiration and checks for valid DurableStore data shapes. This should
154
- * be used over manually parsing DurableStoreEntries
155
- *
156
- * @param durableRecords The DurableStoreEntries to parse
157
- * @param publish A function to call with the data of each DurableStoreEntry
158
- * @param publishMetadata A function to call with the metadata of each DurableStoreEntry
159
- * @param pendingWriter the PendingWriter (this is going away soon)
160
- * @returns
161
- */
162
- function publishDurableStoreEntries(durableRecords, publish, publishMetadata) {
163
- var revivedKeys = new engine.StoreKeySet();
164
- var hadUnexpectedShape = false;
165
- if (durableRecords === undefined) {
166
- return { revivedKeys: revivedKeys, hadUnexpectedShape: hadUnexpectedShape };
167
- }
168
- var durableKeys = keys(durableRecords);
169
- if (durableKeys.length === 0) {
170
- // no records to revive
171
- return { revivedKeys: revivedKeys, hadUnexpectedShape: hadUnexpectedShape };
172
- }
173
- for (var i = 0, len = durableKeys.length; i < len; i += 1) {
174
- var key = durableKeys[i];
175
- var durableRecord = durableRecords[key];
176
- if (isDeprecatedDurableStoreEntry(durableRecord)) {
177
- // had the old shape, skip reviving this entry.
178
- hadUnexpectedShape = true;
179
- continue;
180
- }
181
- var metadata = durableRecord.metadata, data = durableRecord.data;
182
- if (data === undefined) {
183
- // if unexpected data skip reviving
184
- hadUnexpectedShape = true;
185
- continue;
186
- }
187
- if (metadata !== undefined) {
188
- var expirationTimestamp = metadata.expirationTimestamp;
189
- if (expirationTimestamp === undefined) {
190
- // if unexpected expiration data skip reviving
191
- hadUnexpectedShape = true;
192
- continue;
193
- }
194
- publishMetadata(key, metadata);
195
- }
196
- if (isStoreEntryError(data)) {
197
- // freeze errors on way into L1
198
- deepFreeze(data.error);
199
- }
200
- publish(key, data);
201
- revivedKeys.add(key);
202
- }
203
- return { revivedKeys: revivedKeys, hadUnexpectedShape: hadUnexpectedShape };
204
- }
205
- /**
206
- * This method returns a Promise to a snapshot that is revived from L2 cache. If
207
- * L2 does not have the entries necessary to fulfill the snapshot then this method
208
- * will refresh the snapshot from network, and then run the results from network
209
- * through L2 ingestion, returning the subsequent revived snapshot.
210
- */
211
- function reviveSnapshot(baseEnvironment, durableStore,
212
- // TODO [W-10165787]: We should only allow Unfulfilled snapshot be passed in
213
- unavailableSnapshot, durableStoreErrorHandler, buildL1Snapshot, reviveMetrics) {
214
- if (reviveMetrics === void 0) { reviveMetrics = { l2Trips: [] }; }
215
- var recordId = unavailableSnapshot.recordId, select = unavailableSnapshot.select, seenRecords = unavailableSnapshot.seenRecords, state = unavailableSnapshot.state;
216
- // L2 can only revive Unfulfilled snapshots that have a selector since they have the
217
- // info needed to revive (like missingLinks) and rebuild. Otherwise return L1 snapshot.
218
- if (state !== 'Unfulfilled' || select === undefined) {
219
- return Promise.resolve({
220
- snapshot: unavailableSnapshot,
221
- metrics: reviveMetrics,
222
- });
223
- }
224
- // in case L1 store changes/deallocs a record while we are doing the async read
225
- // we attempt to read all keys from L2 - so combine recordId with any seenRecords
226
- var keysToReviveSet = new engine.StoreKeySet().add(recordId);
227
- keysToReviveSet.merge(seenRecords);
228
- var keysToRevive = keysToReviveSet.keysAsArray();
229
- var canonicalKeys = keysToRevive.map(function (x) {
230
- return engine.serializeStructuredKey(baseEnvironment.storeGetCanonicalKey(x));
231
- });
232
- var start = Date.now();
233
- var l2Trips = reviveMetrics.l2Trips;
234
- return durableStore.getEntries(canonicalKeys, DefaultDurableSegment).then(function (durableRecords) {
235
- l2Trips.push({
236
- duration: Date.now() - start,
237
- keysRequestedCount: canonicalKeys.length,
238
- });
239
- var _a = publishDurableStoreEntries(durableRecords,
240
- // TODO [W-10072584]: instead of implicitly using L1 we should take in
241
- // publish and publishMetadata funcs, so callers can decide where to
242
- // revive to (like they pass in how to do the buildL1Snapshot)
243
- baseEnvironment.storePublish.bind(baseEnvironment), baseEnvironment.publishStoreMetadata.bind(baseEnvironment)), revivedKeys = _a.revivedKeys, hadUnexpectedShape = _a.hadUnexpectedShape;
244
- // if the data coming back from DS had an unexpected shape then just
245
- // return the L1 snapshot
246
- if (hadUnexpectedShape === true) {
247
- return { snapshot: unavailableSnapshot, metrics: reviveMetrics };
248
- }
249
- if (revivedKeys.size() === 0) {
250
- // durable store doesn't have what we asked for so return L1 snapshot
251
- return { snapshot: unavailableSnapshot, metrics: reviveMetrics };
252
- }
253
- // try building the snapshot from L1 now that we have revived the missingLinks
254
- var snapshot = buildL1Snapshot();
255
- // if snapshot is pending then some other in-flight refresh will broadcast
256
- // later
257
- if (snapshot.state === 'Pending') {
258
- return { snapshot: snapshot, metrics: reviveMetrics };
259
- }
260
- if (snapshot.state === 'Unfulfilled') {
261
- // have to check if the new snapshot has any additional seenRecords
262
- // and revive again if so
263
- var newSnapshotSeenRecords = snapshot.seenRecords, newSnapshotRecordId = snapshot.recordId;
264
- var newKeysToReviveSet = new engine.StoreKeySet();
265
- newKeysToReviveSet.add(newSnapshotRecordId);
266
- newKeysToReviveSet.merge(newSnapshotSeenRecords);
267
- var newKeys = newKeysToReviveSet.keysAsArray();
268
- // in case DS returned additional entries we combine the requested
269
- // and returned keys
270
- var alreadyRequestedOrRevivedSet = keysToReviveSet;
271
- alreadyRequestedOrRevivedSet.merge(revivedKeys);
272
- // if there's any seen keys in the newly rebuilt snapshot that
273
- // haven't already been requested or returned then revive again
274
- for (var i = 0, len = newKeys.length; i < len; i++) {
275
- var newSnapshotSeenKey = newKeys[i];
276
- if (!alreadyRequestedOrRevivedSet.has(newSnapshotSeenKey)) {
277
- return reviveSnapshot(baseEnvironment, durableStore, snapshot, durableStoreErrorHandler, buildL1Snapshot, reviveMetrics);
278
- }
279
- }
280
- }
281
- return { snapshot: snapshot, metrics: reviveMetrics };
282
- }, function (error) {
283
- durableStoreErrorHandler(error);
284
- // getEntries failed, return the L1 snapshot
285
- return { snapshot: unavailableSnapshot, metrics: reviveMetrics };
286
- });
151
+ /**
152
+ * Takes a set of entries from DurableStore and publishes them via the passed in funcs.
153
+ * This respects expiration and checks for valid DurableStore data shapes. This should
154
+ * be used over manually parsing DurableStoreEntries
155
+ *
156
+ * @param durableRecords The DurableStoreEntries to parse
157
+ * @param publish A function to call with the data of each DurableStoreEntry
158
+ * @param publishMetadata A function to call with the metadata of each DurableStoreEntry
159
+ * @param pendingWriter the PendingWriter (this is going away soon)
160
+ * @returns
161
+ */
162
+ function publishDurableStoreEntries(durableRecords, publish, publishMetadata) {
163
+ var revivedKeys = new engine.StoreKeySet();
164
+ var hadUnexpectedShape = false;
165
+ if (durableRecords === undefined) {
166
+ return { revivedKeys: revivedKeys, hadUnexpectedShape: hadUnexpectedShape };
167
+ }
168
+ var durableKeys = keys(durableRecords);
169
+ if (durableKeys.length === 0) {
170
+ // no records to revive
171
+ return { revivedKeys: revivedKeys, hadUnexpectedShape: hadUnexpectedShape };
172
+ }
173
+ for (var i = 0, len = durableKeys.length; i < len; i += 1) {
174
+ var key = durableKeys[i];
175
+ var durableRecord = durableRecords[key];
176
+ if (isDeprecatedDurableStoreEntry(durableRecord)) {
177
+ // had the old shape, skip reviving this entry.
178
+ hadUnexpectedShape = true;
179
+ continue;
180
+ }
181
+ var metadata = durableRecord.metadata, data = durableRecord.data;
182
+ if (data === undefined) {
183
+ // if unexpected data skip reviving
184
+ hadUnexpectedShape = true;
185
+ continue;
186
+ }
187
+ if (metadata !== undefined) {
188
+ var expirationTimestamp = metadata.expirationTimestamp;
189
+ if (expirationTimestamp === undefined) {
190
+ // if unexpected expiration data skip reviving
191
+ hadUnexpectedShape = true;
192
+ continue;
193
+ }
194
+ publishMetadata(key, metadata);
195
+ }
196
+ if (isStoreEntryError(data)) {
197
+ // freeze errors on way into L1
198
+ deepFreeze(data.error);
199
+ }
200
+ publish(key, data);
201
+ revivedKeys.add(key);
202
+ }
203
+ return { revivedKeys: revivedKeys, hadUnexpectedShape: hadUnexpectedShape };
204
+ }
205
+ /**
206
+ * This method returns a Promise to a snapshot that is revived from L2 cache. If
207
+ * L2 does not have the entries necessary to fulfill the snapshot then this method
208
+ * will refresh the snapshot from network, and then run the results from network
209
+ * through L2 ingestion, returning the subsequent revived snapshot.
210
+ */
211
+ function reviveSnapshot(baseEnvironment, durableStore,
212
+ // TODO [W-10165787]: We should only allow Unfulfilled snapshot be passed in
213
+ unavailableSnapshot, durableStoreErrorHandler, buildL1Snapshot, reviveMetrics) {
214
+ if (reviveMetrics === void 0) { reviveMetrics = { l2Trips: [] }; }
215
+ var recordId = unavailableSnapshot.recordId, select = unavailableSnapshot.select, seenRecords = unavailableSnapshot.seenRecords, state = unavailableSnapshot.state;
216
+ // L2 can only revive Unfulfilled snapshots that have a selector since they have the
217
+ // info needed to revive (like missingLinks) and rebuild. Otherwise return L1 snapshot.
218
+ if (state !== 'Unfulfilled' || select === undefined) {
219
+ return Promise.resolve({
220
+ snapshot: unavailableSnapshot,
221
+ metrics: reviveMetrics,
222
+ });
223
+ }
224
+ // in case L1 store changes/deallocs a record while we are doing the async read
225
+ // we attempt to read all keys from L2 - so combine recordId with any seenRecords
226
+ var keysToReviveSet = new engine.StoreKeySet().add(recordId);
227
+ keysToReviveSet.merge(seenRecords);
228
+ var keysToRevive = keysToReviveSet.keysAsArray();
229
+ var canonicalKeys = keysToRevive.map(function (x) {
230
+ return engine.serializeStructuredKey(baseEnvironment.storeGetCanonicalKey(x));
231
+ });
232
+ var start = Date.now();
233
+ var l2Trips = reviveMetrics.l2Trips;
234
+ return durableStore.getEntries(canonicalKeys, DefaultDurableSegment).then(function (durableRecords) {
235
+ l2Trips.push({
236
+ duration: Date.now() - start,
237
+ keysRequestedCount: canonicalKeys.length,
238
+ });
239
+ var _a = publishDurableStoreEntries(durableRecords,
240
+ // TODO [W-10072584]: instead of implicitly using L1 we should take in
241
+ // publish and publishMetadata funcs, so callers can decide where to
242
+ // revive to (like they pass in how to do the buildL1Snapshot)
243
+ baseEnvironment.storePublish.bind(baseEnvironment), baseEnvironment.publishStoreMetadata.bind(baseEnvironment)), revivedKeys = _a.revivedKeys, hadUnexpectedShape = _a.hadUnexpectedShape;
244
+ // if the data coming back from DS had an unexpected shape then just
245
+ // return the L1 snapshot
246
+ if (hadUnexpectedShape === true) {
247
+ return { snapshot: unavailableSnapshot, metrics: reviveMetrics };
248
+ }
249
+ if (revivedKeys.size() === 0) {
250
+ // durable store doesn't have what we asked for so return L1 snapshot
251
+ return { snapshot: unavailableSnapshot, metrics: reviveMetrics };
252
+ }
253
+ // try building the snapshot from L1 now that we have revived the missingLinks
254
+ var snapshot = buildL1Snapshot();
255
+ // if snapshot is pending then some other in-flight refresh will broadcast
256
+ // later
257
+ if (snapshot.state === 'Pending') {
258
+ return { snapshot: snapshot, metrics: reviveMetrics };
259
+ }
260
+ if (snapshot.state === 'Unfulfilled') {
261
+ // have to check if the new snapshot has any additional seenRecords
262
+ // and revive again if so
263
+ var newSnapshotSeenRecords = snapshot.seenRecords, newSnapshotRecordId = snapshot.recordId;
264
+ var newKeysToReviveSet = new engine.StoreKeySet();
265
+ newKeysToReviveSet.add(newSnapshotRecordId);
266
+ newKeysToReviveSet.merge(newSnapshotSeenRecords);
267
+ var newKeys = newKeysToReviveSet.keysAsArray();
268
+ // in case DS returned additional entries we combine the requested
269
+ // and returned keys
270
+ var alreadyRequestedOrRevivedSet = keysToReviveSet;
271
+ alreadyRequestedOrRevivedSet.merge(revivedKeys);
272
+ // if there's any seen keys in the newly rebuilt snapshot that
273
+ // haven't already been requested or returned then revive again
274
+ for (var i = 0, len = newKeys.length; i < len; i++) {
275
+ var newSnapshotSeenKey = newKeys[i];
276
+ if (!alreadyRequestedOrRevivedSet.has(newSnapshotSeenKey)) {
277
+ return reviveSnapshot(baseEnvironment, durableStore, snapshot, durableStoreErrorHandler, buildL1Snapshot, reviveMetrics);
278
+ }
279
+ }
280
+ }
281
+ return { snapshot: snapshot, metrics: reviveMetrics };
282
+ }, function (error) {
283
+ durableStoreErrorHandler(error);
284
+ // getEntries failed, return the L1 snapshot
285
+ return { snapshot: unavailableSnapshot, metrics: reviveMetrics };
286
+ });
287
287
  }
288
288
 
289
- var TTL_DURABLE_SEGMENT = 'TTL_DURABLE_SEGMENT';
290
- var TTL_DEFAULT_KEY = 'TTL_DEFAULT_KEY';
291
- function buildDurableTTLOverrideStoreKey(namespace, representationName) {
292
- return "".concat(namespace, "::").concat(representationName);
293
- }
294
- function isEntryDurableTTLOverride(entry) {
295
- if (typeof entry === 'object' && entry !== undefined && entry !== null) {
296
- var data = entry.data;
297
- if (data !== undefined) {
298
- return (data.namespace !== undefined &&
299
- data.representationName !== undefined &&
300
- data.ttl !== undefined);
301
- }
302
- }
303
- return false;
304
- }
305
- function isDefaultDurableTTLOverride(override) {
306
- return (override.namespace === TTL_DEFAULT_KEY && override.representationName === TTL_DEFAULT_KEY);
307
- }
308
- /**
309
- * Class to set and get the TTL override values in the Durable Store
310
- */
311
- var DurableTTLStore = /** @class */ (function () {
312
- function DurableTTLStore(durableStore) {
313
- this.durableStore = durableStore;
314
- }
315
- DurableTTLStore.prototype.setDefaultDurableTTLOverrides = function (ttl) {
316
- var _a;
317
- return this.durableStore.setEntries((_a = {},
318
- _a[buildDurableTTLOverrideStoreKey(TTL_DEFAULT_KEY, TTL_DEFAULT_KEY)] = {
319
- data: {
320
- namespace: TTL_DEFAULT_KEY,
321
- representationName: TTL_DEFAULT_KEY,
322
- ttl: ttl,
323
- },
324
- },
325
- _a), TTL_DURABLE_SEGMENT);
326
- };
327
- DurableTTLStore.prototype.setDurableTTLOverride = function (namespace, representationName, ttl) {
328
- var _a;
329
- return this.durableStore.setEntries((_a = {},
330
- _a[buildDurableTTLOverrideStoreKey(namespace, representationName)] = {
331
- data: { namespace: namespace, representationName: representationName, ttl: ttl },
332
- },
333
- _a), TTL_DURABLE_SEGMENT);
334
- };
335
- DurableTTLStore.prototype.getDurableTTLOverrides = function () {
336
- return this.durableStore
337
- .getAllEntries(TTL_DURABLE_SEGMENT)
338
- .then(function (entries) {
339
- var overrides = [];
340
- var defaultTTL = undefined;
341
- if (entries === undefined) {
342
- return {
343
- defaultTTL: defaultTTL,
344
- overrides: overrides,
345
- };
346
- }
347
- var keys$1 = keys(entries);
348
- for (var i = 0, len = keys$1.length; i < len; i++) {
349
- var key = keys$1[i];
350
- var entry = entries[key];
351
- if (entry !== undefined && isEntryDurableTTLOverride(entry)) {
352
- if (isDefaultDurableTTLOverride(entry.data)) {
353
- defaultTTL = entry.data;
354
- }
355
- else {
356
- overrides.push(entry.data);
357
- }
358
- }
359
- }
360
- return {
361
- defaultTTL: defaultTTL,
362
- overrides: overrides,
363
- };
364
- });
365
- };
366
- return DurableTTLStore;
289
+ var TTL_DURABLE_SEGMENT = 'TTL_DURABLE_SEGMENT';
290
+ var TTL_DEFAULT_KEY = 'TTL_DEFAULT_KEY';
291
+ function buildDurableTTLOverrideStoreKey(namespace, representationName) {
292
+ return "".concat(namespace, "::").concat(representationName);
293
+ }
294
+ function isEntryDurableTTLOverride(entry) {
295
+ if (typeof entry === 'object' && entry !== undefined && entry !== null) {
296
+ var data = entry.data;
297
+ if (data !== undefined) {
298
+ return (data.namespace !== undefined &&
299
+ data.representationName !== undefined &&
300
+ data.ttl !== undefined);
301
+ }
302
+ }
303
+ return false;
304
+ }
305
+ function isDefaultDurableTTLOverride(override) {
306
+ return (override.namespace === TTL_DEFAULT_KEY && override.representationName === TTL_DEFAULT_KEY);
307
+ }
308
+ /**
309
+ * Class to set and get the TTL override values in the Durable Store
310
+ */
311
+ var DurableTTLStore = /** @class */ (function () {
312
+ function DurableTTLStore(durableStore) {
313
+ this.durableStore = durableStore;
314
+ }
315
+ DurableTTLStore.prototype.setDefaultDurableTTLOverrides = function (ttl) {
316
+ var _a;
317
+ return this.durableStore.setEntries((_a = {},
318
+ _a[buildDurableTTLOverrideStoreKey(TTL_DEFAULT_KEY, TTL_DEFAULT_KEY)] = {
319
+ data: {
320
+ namespace: TTL_DEFAULT_KEY,
321
+ representationName: TTL_DEFAULT_KEY,
322
+ ttl: ttl,
323
+ },
324
+ },
325
+ _a), TTL_DURABLE_SEGMENT);
326
+ };
327
+ DurableTTLStore.prototype.setDurableTTLOverride = function (namespace, representationName, ttl) {
328
+ var _a;
329
+ return this.durableStore.setEntries((_a = {},
330
+ _a[buildDurableTTLOverrideStoreKey(namespace, representationName)] = {
331
+ data: { namespace: namespace, representationName: representationName, ttl: ttl },
332
+ },
333
+ _a), TTL_DURABLE_SEGMENT);
334
+ };
335
+ DurableTTLStore.prototype.getDurableTTLOverrides = function () {
336
+ return this.durableStore
337
+ .getAllEntries(TTL_DURABLE_SEGMENT)
338
+ .then(function (entries) {
339
+ var overrides = [];
340
+ var defaultTTL = undefined;
341
+ if (entries === undefined) {
342
+ return {
343
+ defaultTTL: defaultTTL,
344
+ overrides: overrides,
345
+ };
346
+ }
347
+ var keys$1 = keys(entries);
348
+ for (var i = 0, len = keys$1.length; i < len; i++) {
349
+ var key = keys$1[i];
350
+ var entry = entries[key];
351
+ if (entry !== undefined && isEntryDurableTTLOverride(entry)) {
352
+ if (isDefaultDurableTTLOverride(entry.data)) {
353
+ defaultTTL = entry.data;
354
+ }
355
+ else {
356
+ overrides.push(entry.data);
357
+ }
358
+ }
359
+ }
360
+ return {
361
+ defaultTTL: defaultTTL,
362
+ overrides: overrides,
363
+ };
364
+ });
365
+ };
366
+ return DurableTTLStore;
367
367
  }());
368
368
 
369
- function copy(source) {
370
- if (typeof source !== 'object' || source === null) {
371
- return source;
372
- }
373
- if (isArray(source)) {
374
- // TS doesn't trust that this new array is an array unless we cast it
375
- return __spreadArray([], source, true);
376
- }
377
- return __assign({}, source);
378
- }
379
- function flushInMemoryStoreValuesToDurableStore(store, durableStore, durableStoreErrorHandler) {
380
- var durableRecords = create(null);
381
- var evictedRecords = create(null);
382
- var _a = store.fallbackStringKeyInMemoryStore, records = _a.records, storeMetadata = _a.metadata, visitedIds = _a.visitedIds, refreshedIds = _a.refreshedIds;
383
- // TODO: W-8909393 Once metadata is stored in its own segment we need to
384
- // call setEntries for the visitedIds on default segment and call setEntries
385
- // on the metadata segment for the refreshedIds
386
- var keys$1 = keys(__assign(__assign({}, visitedIds), refreshedIds));
387
- for (var i = 0, len = keys$1.length; i < len; i += 1) {
388
- var key = keys$1[i];
389
- var record = records[key];
390
- // this record has been evicted, evict from DS
391
- if (record === undefined) {
392
- evictedRecords[key] = true;
393
- continue;
394
- }
395
- var metadata = storeMetadata[key];
396
- durableRecords[key] = {
397
- // copy the data in case the store is mutated during the
398
- // async setEntries call
399
- data: copy(record),
400
- };
401
- if (metadata !== undefined) {
402
- durableRecords[key].metadata = __assign(__assign({}, metadata), { metadataVersion: DURABLE_METADATA_VERSION });
403
- }
404
- }
405
- var durableStoreOperations = [];
406
- // publishes
407
- var recordKeys = keys(durableRecords);
408
- if (recordKeys.length > 0) {
409
- durableStoreOperations.push({
410
- type: 'setEntries',
411
- entries: durableRecords,
412
- segment: DefaultDurableSegment,
413
- });
414
- }
415
- // evicts
416
- var evictedKeys = keys(evictedRecords);
417
- if (evictedKeys.length > 0) {
418
- durableStoreOperations.push({
419
- type: 'evictEntries',
420
- ids: evictedKeys,
421
- segment: DefaultDurableSegment,
422
- });
423
- }
424
- if (durableStoreOperations.length > 0) {
425
- return durableStore.batchOperations(durableStoreOperations).catch(durableStoreErrorHandler);
426
- }
427
- return Promise.resolve();
369
+ function copy(source) {
370
+ if (typeof source !== 'object' || source === null) {
371
+ return source;
372
+ }
373
+ if (isArray(source)) {
374
+ // TS doesn't trust that this new array is an array unless we cast it
375
+ return __spreadArray([], source, true);
376
+ }
377
+ return __assign({}, source);
378
+ }
379
+ function flushInMemoryStoreValuesToDurableStore(store, durableStore, durableStoreErrorHandler) {
380
+ var durableRecords = create(null);
381
+ var evictedRecords = create(null);
382
+ var _a = store.fallbackStringKeyInMemoryStore, records = _a.records, storeMetadata = _a.metadata, visitedIds = _a.visitedIds, refreshedIds = _a.refreshedIds;
383
+ // TODO: W-8909393 Once metadata is stored in its own segment we need to
384
+ // call setEntries for the visitedIds on default segment and call setEntries
385
+ // on the metadata segment for the refreshedIds
386
+ var keys$1 = keys(__assign(__assign({}, visitedIds), refreshedIds));
387
+ for (var i = 0, len = keys$1.length; i < len; i += 1) {
388
+ var key = keys$1[i];
389
+ var record = records[key];
390
+ // this record has been evicted, evict from DS
391
+ if (record === undefined) {
392
+ evictedRecords[key] = true;
393
+ continue;
394
+ }
395
+ var metadata = storeMetadata[key];
396
+ durableRecords[key] = {
397
+ // copy the data in case the store is mutated during the
398
+ // async setEntries call
399
+ data: copy(record),
400
+ };
401
+ if (metadata !== undefined) {
402
+ durableRecords[key].metadata = __assign(__assign({}, metadata), { metadataVersion: DURABLE_METADATA_VERSION });
403
+ }
404
+ }
405
+ var durableStoreOperations = [];
406
+ // publishes
407
+ var recordKeys = keys(durableRecords);
408
+ if (recordKeys.length > 0) {
409
+ durableStoreOperations.push({
410
+ type: 'setEntries',
411
+ entries: durableRecords,
412
+ segment: DefaultDurableSegment,
413
+ });
414
+ }
415
+ // evicts
416
+ var evictedKeys = keys(evictedRecords);
417
+ if (evictedKeys.length > 0) {
418
+ durableStoreOperations.push({
419
+ type: 'evictEntries',
420
+ ids: evictedKeys,
421
+ segment: DefaultDurableSegment,
422
+ });
423
+ }
424
+ if (durableStoreOperations.length > 0) {
425
+ return durableStore.batchOperations(durableStoreOperations).catch(durableStoreErrorHandler);
426
+ }
427
+ return Promise.resolve();
428
428
  }
429
429
 
430
- var DurableEnvironmentEventDiscriminator = 'durable';
431
- function isDurableEnvironmentEvent(event) {
432
- return (event.type === 'environment' && event.environment === DurableEnvironmentEventDiscriminator);
433
- }
434
- function emitDurableEnvironmentAdapterEvent(eventData, observers) {
435
- engine.emitAdapterEvent({
436
- type: 'environment',
437
- timestamp: Date.now(),
438
- environment: DurableEnvironmentEventDiscriminator,
439
- data: eventData,
440
- }, observers);
430
+ var DurableEnvironmentEventDiscriminator = 'durable';
431
+ function isDurableEnvironmentEvent(event) {
432
+ return (event.type === 'environment' && event.environment === DurableEnvironmentEventDiscriminator);
433
+ }
434
+ function emitDurableEnvironmentAdapterEvent(eventData, observers) {
435
+ engine.emitAdapterEvent({
436
+ type: 'environment',
437
+ timestamp: Date.now(),
438
+ environment: DurableEnvironmentEventDiscriminator,
439
+ data: eventData,
440
+ }, observers);
441
441
  }
442
442
 
443
- function reviveTTLOverrides(ttlStore, environment) {
444
- return __awaiter(this, void 0, void 0, function () {
445
- var map, defaultTTL, overrides, i, len, _a, namespace, representationName, ttl;
446
- return __generator(this, function (_b) {
447
- switch (_b.label) {
448
- case 0: return [4 /*yield*/, ttlStore.getDurableTTLOverrides()];
449
- case 1:
450
- map = _b.sent();
451
- defaultTTL = map.defaultTTL, overrides = map.overrides;
452
- if (defaultTTL !== undefined) {
453
- environment.storeSetDefaultTTLOverride(defaultTTL.ttl);
454
- }
455
- for (i = 0, len = overrides.length; i < len; i++) {
456
- _a = overrides[i], namespace = _a.namespace, representationName = _a.representationName, ttl = _a.ttl;
457
- environment.storeSetTTLOverride(namespace, representationName, ttl);
458
- }
459
- return [2 /*return*/];
460
- }
461
- });
462
- });
443
+ function reviveTTLOverrides(ttlStore, environment) {
444
+ return __awaiter(this, void 0, void 0, function () {
445
+ var map, defaultTTL, overrides, i, len, _a, namespace, representationName, ttl;
446
+ return __generator(this, function (_b) {
447
+ switch (_b.label) {
448
+ case 0: return [4 /*yield*/, ttlStore.getDurableTTLOverrides()];
449
+ case 1:
450
+ map = _b.sent();
451
+ defaultTTL = map.defaultTTL, overrides = map.overrides;
452
+ if (defaultTTL !== undefined) {
453
+ environment.storeSetDefaultTTLOverride(defaultTTL.ttl);
454
+ }
455
+ for (i = 0, len = overrides.length; i < len; i++) {
456
+ _a = overrides[i], namespace = _a.namespace, representationName = _a.representationName, ttl = _a.ttl;
457
+ environment.storeSetTTLOverride(namespace, representationName, ttl);
458
+ }
459
+ return [2 /*return*/];
460
+ }
461
+ });
462
+ });
463
463
  }
464
464
 
465
- /**
466
- * Returns an empty InMemoryStore that can be used for ingestion. Copies over
467
- * the TTLOverrides from the given Environment's Store.
468
- */
469
- function buildIngestStagingStore(environment) {
470
- return environment.storeBuildIngestionStagingStore();
465
+ /**
466
+ * Returns an empty InMemoryStore that can be used for ingestion. Copies over
467
+ * the TTLOverrides from the given Environment's Store.
468
+ */
469
+ function buildIngestStagingStore(environment) {
470
+ return environment.storeBuildIngestionStagingStore();
471
471
  }
472
472
 
473
- var AdapterContextSegment = 'ADAPTER-CONTEXT';
474
- var ADAPTER_CONTEXT_ID_SUFFIX = '__NAMED_CONTEXT';
475
- function reviveOrCreateContext(adapterId, durableStore, durableStoreErrorHandler, contextStores, pendingContextStoreKeys, onContextLoaded) {
476
- return __awaiter(this, void 0, void 0, function () {
477
- var context, contextReturn, entries, error_1;
478
- return __generator(this, function (_a) {
479
- switch (_a.label) {
480
- case 0:
481
- // initialize empty context store
482
- contextStores[adapterId] = create(null);
483
- context = {
484
- set: function (key, value) {
485
- var _a;
486
- contextStores[adapterId][key] = value;
487
- durableStore.setEntries((_a = {},
488
- _a[adapterId] = { data: contextStores[adapterId] },
489
- _a), AdapterContextSegment);
490
- pendingContextStoreKeys.add(adapterId);
491
- },
492
- get: function (key) {
493
- return contextStores[adapterId][key];
494
- },
495
- };
496
- contextReturn = function () {
497
- if (onContextLoaded !== undefined) {
498
- return onContextLoaded(context).then(function () {
499
- return context;
500
- });
501
- }
502
- return context;
503
- };
504
- _a.label = 1;
505
- case 1:
506
- _a.trys.push([1, 3, , 4]);
507
- return [4 /*yield*/, durableStore.getEntries([adapterId], AdapterContextSegment)];
508
- case 2:
509
- entries = _a.sent();
510
- if (entries !== undefined && entries[adapterId] !== undefined) {
511
- // if durable store has a saved context then load it in the store
512
- contextStores[adapterId] = entries[adapterId].data;
513
- }
514
- return [3 /*break*/, 4];
515
- case 3:
516
- error_1 = _a.sent();
517
- durableStoreErrorHandler(error_1);
518
- return [3 /*break*/, 4];
519
- case 4: return [2 /*return*/, contextReturn()];
520
- }
521
- });
522
- });
523
- }
524
- function isUnfulfilledSnapshot(cachedSnapshotResult) {
525
- if (cachedSnapshotResult === undefined) {
526
- return false;
527
- }
528
- if ('then' in cachedSnapshotResult) {
529
- return false;
530
- }
531
- return cachedSnapshotResult.state === 'Unfulfilled';
532
- }
533
- /**
534
- * Configures the environment to persist data into a durable store and attempt to resolve
535
- * data from the persistent store before hitting the network.
536
- *
537
- * @param environment The base environment
538
- * @param durableStore A DurableStore implementation
539
- * @param instrumentation An instrumentation function implementation
540
- */
541
- function makeDurable(environment, _a) {
542
- var _this = this;
543
- var durableStore = _a.durableStore, instrumentation = _a.instrumentation;
544
- var ingestStagingStore = null;
545
- var durableTTLStore = new DurableTTLStore(durableStore);
546
- var mergeKeysPromiseMap = new engine.StoreKeyMap();
547
- // When a context store is mutated we write it to L2, which causes DS on change
548
- // event. If this instance of makeDurable caused that L2 write we can ignore that
549
- // on change event. This Set helps us do that.
550
- var pendingContextStoreKeys = new Set();
551
- var contextStores = create(null);
552
- var initializationPromise = new Promise(function (resolve) {
553
- var finish = function () {
554
- resolve();
555
- initializationPromise = undefined;
556
- };
557
- reviveTTLOverrides(durableTTLStore, environment).then(finish);
558
- });
559
- //instrumentation for durable store errors
560
- var durableStoreErrorHandler = handleDurableStoreRejection(instrumentation);
561
- var disposed = false;
562
- var validateNotDisposed = function () {
563
- if (disposed === true) {
564
- throw new Error('This makeDurable instance has been disposed');
565
- }
566
- };
567
- var unsubscribe = durableStore.registerOnChangedListener(function (changes) { return __awaiter(_this, void 0, void 0, function () {
568
- var defaultSegmentKeys, adapterContextSegmentKeys, i, len, change, adapterContextKeysFromDifferentInstance, _i, adapterContextSegmentKeys_1, key, entries, entryKeys, i, len, entryKey, entry, error_2, defaultSegmentKeysLength, i, key, canonical;
569
- return __generator(this, function (_a) {
570
- switch (_a.label) {
571
- case 0:
572
- defaultSegmentKeys = [];
573
- adapterContextSegmentKeys = [];
574
- for (i = 0, len = changes.length; i < len; i++) {
575
- change = changes[i];
576
- // we only care about changes to the data which is stored in the default
577
- // segment or the adapter context
578
- if (change.segment === DefaultDurableSegment) {
579
- defaultSegmentKeys.push.apply(defaultSegmentKeys, change.ids);
580
- }
581
- else if (change.segment === AdapterContextSegment) {
582
- adapterContextSegmentKeys.push.apply(adapterContextSegmentKeys, change.ids);
583
- }
584
- }
585
- adapterContextKeysFromDifferentInstance = [];
586
- for (_i = 0, adapterContextSegmentKeys_1 = adapterContextSegmentKeys; _i < adapterContextSegmentKeys_1.length; _i++) {
587
- key = adapterContextSegmentKeys_1[_i];
588
- if (pendingContextStoreKeys.has(key)) {
589
- // if this instance caused the L2 write then remove from the
590
- // "pending" Set and move on
591
- pendingContextStoreKeys.delete(key);
592
- }
593
- else {
594
- // else it came from another luvio instance and we need to
595
- // read from L2
596
- adapterContextKeysFromDifferentInstance.push(key);
597
- }
598
- }
599
- if (!(adapterContextKeysFromDifferentInstance.length > 0)) return [3 /*break*/, 4];
600
- _a.label = 1;
601
- case 1:
602
- _a.trys.push([1, 3, , 4]);
603
- return [4 /*yield*/, durableStore.getEntries(adapterContextKeysFromDifferentInstance, AdapterContextSegment)];
604
- case 2:
605
- entries = _a.sent();
606
- if (entries !== undefined) {
607
- entryKeys = keys(entries);
608
- for (i = 0, len = entryKeys.length; i < len; i++) {
609
- entryKey = entryKeys[i];
610
- entry = entries[entryKey];
611
- contextStores[entryKey] = entry.data;
612
- }
613
- }
614
- return [3 /*break*/, 4];
615
- case 3:
616
- error_2 = _a.sent();
617
- durableStoreErrorHandler(error_2);
618
- return [3 /*break*/, 4];
619
- case 4:
620
- defaultSegmentKeysLength = defaultSegmentKeys.length;
621
- if (!(defaultSegmentKeysLength > 0)) return [3 /*break*/, 6];
622
- for (i = 0; i < defaultSegmentKeysLength; i++) {
623
- key = defaultSegmentKeys[i];
624
- canonical = environment.storeGetCanonicalKey(key);
625
- if (canonical !== key) {
626
- continue;
627
- }
628
- // TODO: W-8909393 If expiration is the only thing that changed we should not evict the data... so
629
- // if we stored expiration and data at different keys (or same keys in different segments)
630
- // then we could know if only the expiration has changed and we wouldn't need to evict
631
- // and go through an entire broadcast/revive cycle for unchanged data
632
- // call base environment storeEvict so this evict is not tracked for durable deletion
633
- environment.storeEvict(key);
634
- }
635
- return [4 /*yield*/, environment.storeBroadcast(rebuildSnapshot, environment.snapshotAvailable)];
636
- case 5:
637
- _a.sent();
638
- _a.label = 6;
639
- case 6: return [2 /*return*/];
640
- }
641
- });
642
- }); });
643
- var dispose = function () {
644
- validateNotDisposed();
645
- disposed = true;
646
- return unsubscribe();
647
- };
648
- var storePublish = function (key, data) {
649
- validateNotDisposed();
650
- if (ingestStagingStore === null) {
651
- ingestStagingStore = buildIngestStagingStore(environment);
652
- }
653
- ingestStagingStore.publish(key, data);
654
- // remove record from main luvio L1 cache while we are on the synchronous path
655
- // because we do not want some other code attempting to use the
656
- // in-memory values before the durable store onChanged handler
657
- // calls back and revives the values to in-memory
658
- environment.storeEvict(key);
659
- };
660
- var publishStoreMetadata = function (recordId, storeMetadata) {
661
- validateNotDisposed();
662
- if (ingestStagingStore === null) {
663
- ingestStagingStore = buildIngestStagingStore(environment);
664
- }
665
- ingestStagingStore.publishMetadata(recordId, storeMetadata);
666
- };
667
- var storeIngest = function (key, ingest, response, luvio) {
668
- validateNotDisposed();
669
- // we don't ingest to the luvio L1 store from network directly, we ingest to
670
- // L2 and let DurableStore on change event revive keys into luvio L1 store
671
- if (ingestStagingStore === null) {
672
- ingestStagingStore = buildIngestStagingStore(environment);
673
- }
674
- environment.storeIngest(key, ingest, response, luvio, ingestStagingStore);
675
- };
676
- var storeIngestError = function (key, errorSnapshot, storeMetadataParams, _storeOverride) {
677
- validateNotDisposed();
678
- if (ingestStagingStore === null) {
679
- ingestStagingStore = buildIngestStagingStore(environment);
680
- }
681
- environment.storeIngestError(key, errorSnapshot, storeMetadataParams, ingestStagingStore);
682
- };
683
- var storeBroadcast = function (_rebuildSnapshot, _snapshotDataAvailable) {
684
- validateNotDisposed();
685
- // publishing to L2 is essentially "broadcasting" because the onChanged
686
- // handler will fire which will revive records to the main L1 store and
687
- // call the base storeBroadcast
688
- return publishChangesToDurableStore();
689
- };
690
- var publishChangesToDurableStore = function () {
691
- validateNotDisposed();
692
- if (ingestStagingStore === null) {
693
- return Promise.resolve();
694
- }
695
- var promise = flushInMemoryStoreValuesToDurableStore(ingestStagingStore, durableStore, durableStoreErrorHandler);
696
- ingestStagingStore = null;
697
- return promise;
698
- };
699
- var storeLookup = function (sel, createSnapshot, refresh, ttlStrategy) {
700
- validateNotDisposed();
701
- // if this lookup is right after an ingest there will be a staging store
702
- if (ingestStagingStore !== null) {
703
- var reader = new engine.Reader(ingestStagingStore, sel.variables, refresh, undefined, ttlStrategy);
704
- return reader.read(sel);
705
- }
706
- // otherwise this is from buildCachedSnapshot and we should use the luvio
707
- // L1 store
708
- return environment.storeLookup(sel, createSnapshot, refresh, ttlStrategy);
709
- };
710
- var storeEvict = function (key) {
711
- validateNotDisposed();
712
- if (ingestStagingStore === null) {
713
- ingestStagingStore = buildIngestStagingStore(environment);
714
- }
715
- ingestStagingStore.evict(key);
716
- };
717
- var getNode = function (key) {
718
- validateNotDisposed();
719
- if (ingestStagingStore === null) {
720
- ingestStagingStore = buildIngestStagingStore(environment);
721
- }
722
- return environment.getNode(key, ingestStagingStore);
723
- };
724
- var wrapNormalizedGraphNode = function (normalized) {
725
- validateNotDisposed();
726
- if (ingestStagingStore === null) {
727
- ingestStagingStore = buildIngestStagingStore(environment);
728
- }
729
- return environment.wrapNormalizedGraphNode(normalized, ingestStagingStore);
730
- };
731
- var rebuildSnapshot = function (snapshot, onRebuild) {
732
- validateNotDisposed();
733
- // try rebuilding from memory
734
- environment.rebuildSnapshot(snapshot, function (rebuilt) {
735
- // only try reviving from durable store if snapshot is unfulfilled
736
- if (rebuilt.state !== 'Unfulfilled') {
737
- onRebuild(rebuilt);
738
- return;
739
- }
740
- // Do an L2 revive and emit to subscriber using the callback.
741
- reviveSnapshot(environment, durableStore, rebuilt, durableStoreErrorHandler, function () {
742
- // reviveSnapshot will revive into L1, and since "records" is a reference
743
- // (and not a copy) to the L1 records we can use it for rebuild
744
- var rebuiltSnap;
745
- environment.rebuildSnapshot(snapshot, function (rebuilt) {
746
- rebuiltSnap = rebuilt;
747
- });
748
- return rebuiltSnap;
749
- }).then(function (result) {
750
- onRebuild(result.snapshot);
751
- });
752
- });
753
- };
754
- var withContext = function (adapter, options) {
755
- validateNotDisposed();
756
- var contextId = options.contextId, contextVersion = options.contextVersion, onContextLoaded = options.onContextLoaded;
757
- var context = undefined;
758
- var contextKey = "".concat(contextId);
759
- // if a context version is supplied, key with the version encoded
760
- if (contextVersion !== undefined) {
761
- contextKey += "::".concat(contextVersion);
762
- }
763
- contextKey += ADAPTER_CONTEXT_ID_SUFFIX;
764
- var contextAsPromise = reviveOrCreateContext(contextKey, durableStore, durableStoreErrorHandler, contextStores, pendingContextStoreKeys, onContextLoaded);
765
- return function (config, requestContext) {
766
- if (context === undefined) {
767
- return contextAsPromise.then(function (revivedContext) {
768
- context = revivedContext;
769
- return adapter(config, context, requestContext); // TODO - remove as any cast after https://github.com/salesforce-experience-platform-emu/luvio/pull/230
770
- });
771
- }
772
- return adapter(config, context, requestContext);
773
- };
774
- };
775
- var storeRedirect = function (existingKey, canonicalKey) {
776
- validateNotDisposed();
777
- // call redirect on staging store so "old" keys are removed from L2 on
778
- // the next publishChangesToDurableStore. NOTE: we don't need to call
779
- // redirect on the base environment store because staging store and base
780
- // L1 store share the same redirect and reverseRedirectKeys
781
- if (ingestStagingStore === null) {
782
- ingestStagingStore = buildIngestStagingStore(environment);
783
- }
784
- ingestStagingStore.redirect(existingKey, canonicalKey);
785
- };
786
- var storeSetTTLOverride = function (namespace, representationName, ttl) {
787
- validateNotDisposed();
788
- return Promise.all([
789
- environment.storeSetTTLOverride(namespace, representationName, ttl),
790
- durableTTLStore.setDurableTTLOverride(namespace, representationName, ttl),
791
- ]).then();
792
- };
793
- var storeSetDefaultTTLOverride = function (ttl) {
794
- validateNotDisposed();
795
- return Promise.all([
796
- environment.storeSetDefaultTTLOverride(ttl),
797
- durableTTLStore.setDefaultDurableTTLOverrides(ttl),
798
- ]).then();
799
- };
800
- var getDurableTTLOverrides = function () {
801
- validateNotDisposed();
802
- return durableTTLStore.getDurableTTLOverrides();
803
- };
804
- var dispatchResourceRequest = function (request, context, eventObservers) {
805
- return __awaiter(this, void 0, void 0, function () {
806
- return __generator(this, function (_a) {
807
- switch (_a.label) {
808
- case 0:
809
- validateNotDisposed();
810
- if (!(initializationPromise !== undefined)) return [3 /*break*/, 2];
811
- return [4 /*yield*/, initializationPromise];
812
- case 1:
813
- _a.sent();
814
- _a.label = 2;
815
- case 2: return [2 /*return*/, environment.dispatchResourceRequest(request, context, eventObservers)];
816
- }
817
- });
818
- });
819
- };
820
- // NOTE: we can't use "async" keyword on this function because that would
821
- // force it to always be an async response. The signature is a union
822
- // of sync/async so no "awaiting" in this function, just promise-chaining
823
- var applyCachePolicy = function (luvio, adapterRequestContext, buildSnapshotContext, buildCachedSnapshot, buildNetworkSnapshot) {
824
- validateNotDisposed();
825
- var wrappedCacheLookup = function (injectedBuildSnapshotContext, injectedStoreLookup) {
826
- var snapshot = buildCachedSnapshot(injectedBuildSnapshotContext, injectedStoreLookup, luvio);
827
- // if the adapter attempted to do an L1 lookup and it was unfulfilled
828
- // then we can attempt an L2 lookup
829
- if (isUnfulfilledSnapshot(snapshot)) {
830
- var start_1 = Date.now();
831
- emitDurableEnvironmentAdapterEvent({ type: 'l2-revive-start' }, adapterRequestContext.eventObservers);
832
- var revivedSnapshot = reviveSnapshot(environment, durableStore, snapshot, durableStoreErrorHandler, function () { return injectedStoreLookup(snapshot.select, snapshot.refresh); }).then(function (result) {
833
- emitDurableEnvironmentAdapterEvent({
834
- type: 'l2-revive-end',
835
- snapshot: result.snapshot,
836
- duration: Date.now() - start_1,
837
- l2Trips: result.metrics.l2Trips,
838
- }, adapterRequestContext.eventObservers);
839
- return result.snapshot;
840
- });
841
- return revivedSnapshot;
842
- }
843
- // otherwise just return what buildCachedSnapshot gave us
844
- return snapshot;
845
- };
846
- var wrappedApplyCachePolicy = function () {
847
- return environment.applyCachePolicy(luvio, adapterRequestContext, buildSnapshotContext, wrappedCacheLookup, buildNetworkSnapshot);
848
- };
849
- // GET adapters call applyCachePolicy before any other luvio
850
- // function so this is our chance to ensure we're initialized
851
- return initializationPromise !== undefined
852
- ? initializationPromise.then(wrappedApplyCachePolicy)
853
- : wrappedApplyCachePolicy();
854
- };
855
- var getIngestStagingStoreRecords = function () {
856
- validateNotDisposed();
857
- if (ingestStagingStore !== null) {
858
- return ingestStagingStore.fallbackStringKeyInMemoryStore.records;
859
- }
860
- return {};
861
- };
862
- var getIngestStagingStoreMetadata = function () {
863
- validateNotDisposed();
864
- if (ingestStagingStore !== null) {
865
- return ingestStagingStore.fallbackStringKeyInMemoryStore.metadata;
866
- }
867
- return {};
868
- };
869
- var handleSuccessResponse = function (ingestAndBroadcastFunc, getResponseCacheKeysFunc) {
870
- return __awaiter(this, void 0, void 0, function () {
871
- var cacheKeyMap, cacheKeyMapKeys, keysToRevive, _i, cacheKeyMapKeys_1, cacheKeyMapKey, cacheKey, snapshotFromMemoryIngest, keysToReviveAsArray, readWritePromise, _a, keysToReviveAsArray_1, key, _b, keysToReviveAsArray_2, key, pendingPromise, _c, select, refresh, result;
872
- var _this = this;
873
- return __generator(this, function (_d) {
874
- switch (_d.label) {
875
- case 0:
876
- validateNotDisposed();
877
- cacheKeyMap = getResponseCacheKeysFunc();
878
- cacheKeyMapKeys = cacheKeyMap.keysAsArray();
879
- keysToRevive = new engine.StoreKeySet();
880
- for (_i = 0, cacheKeyMapKeys_1 = cacheKeyMapKeys; _i < cacheKeyMapKeys_1.length; _i++) {
881
- cacheKeyMapKey = cacheKeyMapKeys_1[_i];
882
- cacheKey = cacheKeyMap.get(cacheKeyMapKey);
883
- if (cacheKey.mergeable === true) {
884
- keysToRevive.add(cacheKeyMapKey);
885
- }
886
- }
887
- snapshotFromMemoryIngest = undefined;
888
- keysToReviveAsArray = Array.from(keysToRevive.keysAsStrings());
889
- if (!(keysToReviveAsArray.length > 0)) return [3 /*break*/, 5];
890
- readWritePromise = (function () { return __awaiter(_this, void 0, void 0, function () {
891
- var pendingPromises, _i, keysToReviveAsArray_3, key, pendingPromise, entries;
892
- return __generator(this, function (_a) {
893
- switch (_a.label) {
894
- case 0:
895
- pendingPromises = [];
896
- for (_i = 0, keysToReviveAsArray_3 = keysToReviveAsArray; _i < keysToReviveAsArray_3.length; _i++) {
897
- key = keysToReviveAsArray_3[_i];
898
- pendingPromise = mergeKeysPromiseMap.get(key);
899
- if (pendingPromise !== undefined) {
900
- // IMPORTANT: while on the synchronous code path we get a
901
- // handle to pendingPromise and push it onto the array.
902
- // This is important because later in this synchronous code
903
- // path we will upsert readWritePromise into the
904
- // mergeKeysPromiseMap (essentially overwriting pendingPromise
905
- // in the map).
906
- pendingPromises.push(pendingPromise);
907
- }
908
- }
909
- return [4 /*yield*/, Promise.all(pendingPromises)];
910
- case 1:
911
- _a.sent();
912
- return [4 /*yield*/, durableStore.getEntries(keysToReviveAsArray, DefaultDurableSegment)];
913
- case 2:
914
- entries = _a.sent();
915
- ingestStagingStore = buildIngestStagingStore(environment);
916
- publishDurableStoreEntries(entries, function (key, record) {
917
- if (typeof key === 'string') {
918
- ingestStagingStore.fallbackStringKeyInMemoryStore.records[key] =
919
- record;
920
- }
921
- else {
922
- ingestStagingStore.recordsMap.set(key, record);
923
- }
924
- },
925
- // we don't need to prime metadata
926
- function () { });
927
- return [4 /*yield*/, ingestAndBroadcastFunc()];
928
- case 3:
929
- snapshotFromMemoryIngest = _a.sent();
930
- return [2 /*return*/];
931
- }
932
- });
933
- }); })();
934
- for (_a = 0, keysToReviveAsArray_1 = keysToReviveAsArray; _a < keysToReviveAsArray_1.length; _a++) {
935
- key = keysToReviveAsArray_1[_a];
936
- // we are overwriting the previous promise at this key, but that
937
- // is ok because we got a handle to it earlier (see the IMPORTANT
938
- // comment about 35 lines up)
939
- mergeKeysPromiseMap.set(key, readWritePromise);
940
- }
941
- _d.label = 1;
942
- case 1:
943
- _d.trys.push([1, , 3, 4]);
944
- return [4 /*yield*/, readWritePromise];
945
- case 2:
946
- _d.sent();
947
- return [3 /*break*/, 4];
948
- case 3:
949
- for (_b = 0, keysToReviveAsArray_2 = keysToReviveAsArray; _b < keysToReviveAsArray_2.length; _b++) {
950
- key = keysToReviveAsArray_2[_b];
951
- pendingPromise = mergeKeysPromiseMap.get(key);
952
- // cleanup the entry from the map if this is the last promise
953
- // for that key
954
- if (pendingPromise === readWritePromise) {
955
- mergeKeysPromiseMap.delete(key);
956
- }
957
- }
958
- return [7 /*endfinally*/];
959
- case 4: return [3 /*break*/, 7];
960
- case 5:
961
- // we aren't doing any merging so we don't have to synchronize, the
962
- // underlying DurableStore implementation takes care of R/W sync
963
- // so all we have to do is ingest then write to L2
964
- ingestStagingStore = buildIngestStagingStore(environment);
965
- return [4 /*yield*/, ingestAndBroadcastFunc()];
966
- case 6:
967
- snapshotFromMemoryIngest = _d.sent();
968
- _d.label = 7;
969
- case 7:
970
- if (snapshotFromMemoryIngest === undefined) {
971
- return [2 /*return*/, undefined];
972
- }
973
- if (snapshotFromMemoryIngest.state !== 'Unfulfilled') {
974
- return [2 /*return*/, snapshotFromMemoryIngest];
975
- }
976
- _c = snapshotFromMemoryIngest, select = _c.select, refresh = _c.refresh;
977
- return [4 /*yield*/, reviveSnapshot(environment, durableStore, snapshotFromMemoryIngest, durableStoreErrorHandler, function () { return environment.storeLookup(select, environment.createSnapshot, refresh); })];
978
- case 8:
979
- result = _d.sent();
980
- return [2 /*return*/, result.snapshot];
981
- }
982
- });
983
- });
984
- };
985
- var handleErrorResponse = function (ingestAndBroadcastFunc) {
986
- return __awaiter(this, void 0, void 0, function () {
987
- return __generator(this, function (_a) {
988
- validateNotDisposed();
989
- ingestStagingStore = buildIngestStagingStore(environment);
990
- return [2 /*return*/, ingestAndBroadcastFunc()];
991
- });
992
- });
993
- };
994
- var getNotifyChangeStoreEntries = function (keys) {
995
- validateNotDisposed();
996
- return durableStore
997
- .getEntries(keys.map(engine.serializeStructuredKey), DefaultDurableSegment)
998
- .then(function (durableRecords) {
999
- var entries = [];
1000
- publishDurableStoreEntries(durableRecords, function (key, record) {
1001
- entries.push({
1002
- key: key,
1003
- record: record,
1004
- });
1005
- }, function () { });
1006
- return entries;
1007
- });
1008
- };
1009
- environment.defaultCachePolicy = {
1010
- type: 'stale-while-revalidate',
1011
- implementation: engine.buildStaleWhileRevalidateImplementation(Number.MAX_SAFE_INTEGER),
1012
- };
1013
- return create(environment, {
1014
- publishStoreMetadata: { value: publishStoreMetadata },
1015
- storeIngest: { value: storeIngest },
1016
- storeIngestError: { value: storeIngestError },
1017
- storeBroadcast: { value: storeBroadcast },
1018
- storeLookup: { value: storeLookup },
1019
- storeEvict: { value: storeEvict },
1020
- wrapNormalizedGraphNode: { value: wrapNormalizedGraphNode },
1021
- getNode: { value: getNode },
1022
- rebuildSnapshot: { value: rebuildSnapshot },
1023
- withContext: { value: withContext },
1024
- storeSetTTLOverride: { value: storeSetTTLOverride },
1025
- storeSetDefaultTTLOverride: { value: storeSetDefaultTTLOverride },
1026
- storePublish: { value: storePublish },
1027
- storeRedirect: { value: storeRedirect },
1028
- dispose: { value: dispose },
1029
- publishChangesToDurableStore: { value: publishChangesToDurableStore },
1030
- getDurableTTLOverrides: { value: getDurableTTLOverrides },
1031
- dispatchResourceRequest: { value: dispatchResourceRequest },
1032
- applyCachePolicy: { value: applyCachePolicy },
1033
- getIngestStagingStoreRecords: { value: getIngestStagingStoreRecords },
1034
- getIngestStagingStoreMetadata: { value: getIngestStagingStoreMetadata },
1035
- handleSuccessResponse: { value: handleSuccessResponse },
1036
- handleErrorResponse: { value: handleErrorResponse },
1037
- getNotifyChangeStoreEntries: { value: getNotifyChangeStoreEntries },
1038
- });
473
+ var AdapterContextSegment = 'ADAPTER-CONTEXT';
474
+ var ADAPTER_CONTEXT_ID_SUFFIX = '__NAMED_CONTEXT';
475
+ function reviveOrCreateContext(adapterId, durableStore, durableStoreErrorHandler, contextStores, pendingContextStoreKeys, onContextLoaded) {
476
+ return __awaiter(this, void 0, void 0, function () {
477
+ var context, contextReturn, entries, error_1;
478
+ return __generator(this, function (_a) {
479
+ switch (_a.label) {
480
+ case 0:
481
+ // initialize empty context store
482
+ contextStores[adapterId] = create(null);
483
+ context = {
484
+ set: function (key, value) {
485
+ var _a;
486
+ contextStores[adapterId][key] = value;
487
+ durableStore.setEntries((_a = {},
488
+ _a[adapterId] = { data: contextStores[adapterId] },
489
+ _a), AdapterContextSegment);
490
+ pendingContextStoreKeys.add(adapterId);
491
+ },
492
+ get: function (key) {
493
+ return contextStores[adapterId][key];
494
+ },
495
+ };
496
+ contextReturn = function () {
497
+ if (onContextLoaded !== undefined) {
498
+ return onContextLoaded(context).then(function () {
499
+ return context;
500
+ });
501
+ }
502
+ return context;
503
+ };
504
+ _a.label = 1;
505
+ case 1:
506
+ _a.trys.push([1, 3, , 4]);
507
+ return [4 /*yield*/, durableStore.getEntries([adapterId], AdapterContextSegment)];
508
+ case 2:
509
+ entries = _a.sent();
510
+ if (entries !== undefined && entries[adapterId] !== undefined) {
511
+ // if durable store has a saved context then load it in the store
512
+ contextStores[adapterId] = entries[adapterId].data;
513
+ }
514
+ return [3 /*break*/, 4];
515
+ case 3:
516
+ error_1 = _a.sent();
517
+ durableStoreErrorHandler(error_1);
518
+ return [3 /*break*/, 4];
519
+ case 4: return [2 /*return*/, contextReturn()];
520
+ }
521
+ });
522
+ });
523
+ }
524
+ function isUnfulfilledSnapshot(cachedSnapshotResult) {
525
+ if (cachedSnapshotResult === undefined) {
526
+ return false;
527
+ }
528
+ if ('then' in cachedSnapshotResult) {
529
+ return false;
530
+ }
531
+ return cachedSnapshotResult.state === 'Unfulfilled';
532
+ }
533
+ /**
534
+ * Configures the environment to persist data into a durable store and attempt to resolve
535
+ * data from the persistent store before hitting the network.
536
+ *
537
+ * @param environment The base environment
538
+ * @param durableStore A DurableStore implementation
539
+ * @param instrumentation An instrumentation function implementation
540
+ */
541
+ function makeDurable(environment, _a) {
542
+ var _this = this;
543
+ var durableStore = _a.durableStore, instrumentation = _a.instrumentation;
544
+ var ingestStagingStore = null;
545
+ var durableTTLStore = new DurableTTLStore(durableStore);
546
+ var mergeKeysPromiseMap = new engine.StoreKeyMap();
547
+ // When a context store is mutated we write it to L2, which causes DS on change
548
+ // event. If this instance of makeDurable caused that L2 write we can ignore that
549
+ // on change event. This Set helps us do that.
550
+ var pendingContextStoreKeys = new Set();
551
+ var contextStores = create(null);
552
+ var initializationPromise = new Promise(function (resolve) {
553
+ var finish = function () {
554
+ resolve();
555
+ initializationPromise = undefined;
556
+ };
557
+ reviveTTLOverrides(durableTTLStore, environment).then(finish);
558
+ });
559
+ //instrumentation for durable store errors
560
+ var durableStoreErrorHandler = handleDurableStoreRejection(instrumentation);
561
+ var disposed = false;
562
+ var validateNotDisposed = function () {
563
+ if (disposed === true) {
564
+ throw new Error('This makeDurable instance has been disposed');
565
+ }
566
+ };
567
+ var unsubscribe = durableStore.registerOnChangedListener(function (changes) { return __awaiter(_this, void 0, void 0, function () {
568
+ var defaultSegmentKeys, adapterContextSegmentKeys, i, len, change, adapterContextKeysFromDifferentInstance, _i, adapterContextSegmentKeys_1, key, entries, entryKeys, i, len, entryKey, entry, error_2, defaultSegmentKeysLength, i, key, canonical;
569
+ return __generator(this, function (_a) {
570
+ switch (_a.label) {
571
+ case 0:
572
+ defaultSegmentKeys = [];
573
+ adapterContextSegmentKeys = [];
574
+ for (i = 0, len = changes.length; i < len; i++) {
575
+ change = changes[i];
576
+ // we only care about changes to the data which is stored in the default
577
+ // segment or the adapter context
578
+ if (change.segment === DefaultDurableSegment) {
579
+ defaultSegmentKeys.push.apply(defaultSegmentKeys, change.ids);
580
+ }
581
+ else if (change.segment === AdapterContextSegment) {
582
+ adapterContextSegmentKeys.push.apply(adapterContextSegmentKeys, change.ids);
583
+ }
584
+ }
585
+ adapterContextKeysFromDifferentInstance = [];
586
+ for (_i = 0, adapterContextSegmentKeys_1 = adapterContextSegmentKeys; _i < adapterContextSegmentKeys_1.length; _i++) {
587
+ key = adapterContextSegmentKeys_1[_i];
588
+ if (pendingContextStoreKeys.has(key)) {
589
+ // if this instance caused the L2 write then remove from the
590
+ // "pending" Set and move on
591
+ pendingContextStoreKeys.delete(key);
592
+ }
593
+ else {
594
+ // else it came from another luvio instance and we need to
595
+ // read from L2
596
+ adapterContextKeysFromDifferentInstance.push(key);
597
+ }
598
+ }
599
+ if (!(adapterContextKeysFromDifferentInstance.length > 0)) return [3 /*break*/, 4];
600
+ _a.label = 1;
601
+ case 1:
602
+ _a.trys.push([1, 3, , 4]);
603
+ return [4 /*yield*/, durableStore.getEntries(adapterContextKeysFromDifferentInstance, AdapterContextSegment)];
604
+ case 2:
605
+ entries = _a.sent();
606
+ if (entries !== undefined) {
607
+ entryKeys = keys(entries);
608
+ for (i = 0, len = entryKeys.length; i < len; i++) {
609
+ entryKey = entryKeys[i];
610
+ entry = entries[entryKey];
611
+ contextStores[entryKey] = entry.data;
612
+ }
613
+ }
614
+ return [3 /*break*/, 4];
615
+ case 3:
616
+ error_2 = _a.sent();
617
+ durableStoreErrorHandler(error_2);
618
+ return [3 /*break*/, 4];
619
+ case 4:
620
+ defaultSegmentKeysLength = defaultSegmentKeys.length;
621
+ if (!(defaultSegmentKeysLength > 0)) return [3 /*break*/, 6];
622
+ for (i = 0; i < defaultSegmentKeysLength; i++) {
623
+ key = defaultSegmentKeys[i];
624
+ canonical = environment.storeGetCanonicalKey(key);
625
+ if (canonical !== key) {
626
+ continue;
627
+ }
628
+ // TODO: W-8909393 If expiration is the only thing that changed we should not evict the data... so
629
+ // if we stored expiration and data at different keys (or same keys in different segments)
630
+ // then we could know if only the expiration has changed and we wouldn't need to evict
631
+ // and go through an entire broadcast/revive cycle for unchanged data
632
+ // call base environment storeEvict so this evict is not tracked for durable deletion
633
+ environment.storeEvict(key);
634
+ }
635
+ return [4 /*yield*/, environment.storeBroadcast(rebuildSnapshot, environment.snapshotAvailable)];
636
+ case 5:
637
+ _a.sent();
638
+ _a.label = 6;
639
+ case 6: return [2 /*return*/];
640
+ }
641
+ });
642
+ }); });
643
+ var dispose = function () {
644
+ validateNotDisposed();
645
+ disposed = true;
646
+ return unsubscribe();
647
+ };
648
+ var storePublish = function (key, data) {
649
+ validateNotDisposed();
650
+ if (ingestStagingStore === null) {
651
+ ingestStagingStore = buildIngestStagingStore(environment);
652
+ }
653
+ ingestStagingStore.publish(key, data);
654
+ // remove record from main luvio L1 cache while we are on the synchronous path
655
+ // because we do not want some other code attempting to use the
656
+ // in-memory values before the durable store onChanged handler
657
+ // calls back and revives the values to in-memory
658
+ environment.storeEvict(key);
659
+ };
660
+ var publishStoreMetadata = function (recordId, storeMetadata) {
661
+ validateNotDisposed();
662
+ if (ingestStagingStore === null) {
663
+ ingestStagingStore = buildIngestStagingStore(environment);
664
+ }
665
+ ingestStagingStore.publishMetadata(recordId, storeMetadata);
666
+ };
667
+ var storeIngest = function (key, ingest, response, luvio) {
668
+ validateNotDisposed();
669
+ // we don't ingest to the luvio L1 store from network directly, we ingest to
670
+ // L2 and let DurableStore on change event revive keys into luvio L1 store
671
+ if (ingestStagingStore === null) {
672
+ ingestStagingStore = buildIngestStagingStore(environment);
673
+ }
674
+ environment.storeIngest(key, ingest, response, luvio, ingestStagingStore);
675
+ };
676
+ var storeIngestError = function (key, errorSnapshot, storeMetadataParams, _storeOverride) {
677
+ validateNotDisposed();
678
+ if (ingestStagingStore === null) {
679
+ ingestStagingStore = buildIngestStagingStore(environment);
680
+ }
681
+ environment.storeIngestError(key, errorSnapshot, storeMetadataParams, ingestStagingStore);
682
+ };
683
+ var storeBroadcast = function (_rebuildSnapshot, _snapshotDataAvailable) {
684
+ validateNotDisposed();
685
+ // publishing to L2 is essentially "broadcasting" because the onChanged
686
+ // handler will fire which will revive records to the main L1 store and
687
+ // call the base storeBroadcast
688
+ return publishChangesToDurableStore();
689
+ };
690
+ var publishChangesToDurableStore = function () {
691
+ validateNotDisposed();
692
+ if (ingestStagingStore === null) {
693
+ return Promise.resolve();
694
+ }
695
+ var promise = flushInMemoryStoreValuesToDurableStore(ingestStagingStore, durableStore, durableStoreErrorHandler);
696
+ ingestStagingStore = null;
697
+ return promise;
698
+ };
699
+ var storeLookup = function (sel, createSnapshot, refresh, ttlStrategy) {
700
+ validateNotDisposed();
701
+ // if this lookup is right after an ingest there will be a staging store
702
+ if (ingestStagingStore !== null) {
703
+ var reader = new engine.Reader(ingestStagingStore, sel.variables, refresh, undefined, ttlStrategy);
704
+ return reader.read(sel);
705
+ }
706
+ // otherwise this is from buildCachedSnapshot and we should use the luvio
707
+ // L1 store
708
+ return environment.storeLookup(sel, createSnapshot, refresh, ttlStrategy);
709
+ };
710
+ var storeEvict = function (key) {
711
+ validateNotDisposed();
712
+ if (ingestStagingStore === null) {
713
+ ingestStagingStore = buildIngestStagingStore(environment);
714
+ }
715
+ ingestStagingStore.evict(key);
716
+ };
717
+ var getNode = function (key) {
718
+ validateNotDisposed();
719
+ if (ingestStagingStore === null) {
720
+ ingestStagingStore = buildIngestStagingStore(environment);
721
+ }
722
+ return environment.getNode(key, ingestStagingStore);
723
+ };
724
+ var wrapNormalizedGraphNode = function (normalized) {
725
+ validateNotDisposed();
726
+ if (ingestStagingStore === null) {
727
+ ingestStagingStore = buildIngestStagingStore(environment);
728
+ }
729
+ return environment.wrapNormalizedGraphNode(normalized, ingestStagingStore);
730
+ };
731
+ var rebuildSnapshot = function (snapshot, onRebuild) {
732
+ validateNotDisposed();
733
+ // try rebuilding from memory
734
+ environment.rebuildSnapshot(snapshot, function (rebuilt) {
735
+ // only try reviving from durable store if snapshot is unfulfilled
736
+ if (rebuilt.state !== 'Unfulfilled') {
737
+ onRebuild(rebuilt);
738
+ return;
739
+ }
740
+ // Do an L2 revive and emit to subscriber using the callback.
741
+ reviveSnapshot(environment, durableStore, rebuilt, durableStoreErrorHandler, function () {
742
+ // reviveSnapshot will revive into L1, and since "records" is a reference
743
+ // (and not a copy) to the L1 records we can use it for rebuild
744
+ var rebuiltSnap;
745
+ environment.rebuildSnapshot(snapshot, function (rebuilt) {
746
+ rebuiltSnap = rebuilt;
747
+ });
748
+ return rebuiltSnap;
749
+ }).then(function (result) {
750
+ onRebuild(result.snapshot);
751
+ });
752
+ });
753
+ };
754
+ var withContext = function (adapter, options) {
755
+ validateNotDisposed();
756
+ var contextId = options.contextId, contextVersion = options.contextVersion, onContextLoaded = options.onContextLoaded;
757
+ var context = undefined;
758
+ var contextKey = "".concat(contextId);
759
+ // if a context version is supplied, key with the version encoded
760
+ if (contextVersion !== undefined) {
761
+ contextKey += "::".concat(contextVersion);
762
+ }
763
+ contextKey += ADAPTER_CONTEXT_ID_SUFFIX;
764
+ var contextAsPromise = reviveOrCreateContext(contextKey, durableStore, durableStoreErrorHandler, contextStores, pendingContextStoreKeys, onContextLoaded);
765
+ return function (config, requestContext) {
766
+ if (context === undefined) {
767
+ return contextAsPromise.then(function (revivedContext) {
768
+ context = revivedContext;
769
+ return adapter(config, context, requestContext); // TODO - remove as any cast after https://github.com/salesforce-experience-platform-emu/luvio/pull/230
770
+ });
771
+ }
772
+ return adapter(config, context, requestContext);
773
+ };
774
+ };
775
+ var storeRedirect = function (existingKey, canonicalKey) {
776
+ validateNotDisposed();
777
+ // call redirect on staging store so "old" keys are removed from L2 on
778
+ // the next publishChangesToDurableStore. NOTE: we don't need to call
779
+ // redirect on the base environment store because staging store and base
780
+ // L1 store share the same redirect and reverseRedirectKeys
781
+ if (ingestStagingStore === null) {
782
+ ingestStagingStore = buildIngestStagingStore(environment);
783
+ }
784
+ ingestStagingStore.redirect(existingKey, canonicalKey);
785
+ };
786
+ var storeSetTTLOverride = function (namespace, representationName, ttl) {
787
+ validateNotDisposed();
788
+ return Promise.all([
789
+ environment.storeSetTTLOverride(namespace, representationName, ttl),
790
+ durableTTLStore.setDurableTTLOverride(namespace, representationName, ttl),
791
+ ]).then();
792
+ };
793
+ var storeSetDefaultTTLOverride = function (ttl) {
794
+ validateNotDisposed();
795
+ return Promise.all([
796
+ environment.storeSetDefaultTTLOverride(ttl),
797
+ durableTTLStore.setDefaultDurableTTLOverrides(ttl),
798
+ ]).then();
799
+ };
800
+ var getDurableTTLOverrides = function () {
801
+ validateNotDisposed();
802
+ return durableTTLStore.getDurableTTLOverrides();
803
+ };
804
+ var dispatchResourceRequest = function (request, context, eventObservers) {
805
+ return __awaiter(this, void 0, void 0, function () {
806
+ return __generator(this, function (_a) {
807
+ switch (_a.label) {
808
+ case 0:
809
+ validateNotDisposed();
810
+ if (!(initializationPromise !== undefined)) return [3 /*break*/, 2];
811
+ return [4 /*yield*/, initializationPromise];
812
+ case 1:
813
+ _a.sent();
814
+ _a.label = 2;
815
+ case 2: return [2 /*return*/, environment.dispatchResourceRequest(request, context, eventObservers)];
816
+ }
817
+ });
818
+ });
819
+ };
820
+ // NOTE: we can't use "async" keyword on this function because that would
821
+ // force it to always be an async response. The signature is a union
822
+ // of sync/async so no "awaiting" in this function, just promise-chaining
823
+ var applyCachePolicy = function (luvio, adapterRequestContext, buildSnapshotContext, buildCachedSnapshot, buildNetworkSnapshot) {
824
+ validateNotDisposed();
825
+ var wrappedCacheLookup = function (injectedBuildSnapshotContext, injectedStoreLookup) {
826
+ var snapshot = buildCachedSnapshot(injectedBuildSnapshotContext, injectedStoreLookup, luvio);
827
+ // if the adapter attempted to do an L1 lookup and it was unfulfilled
828
+ // then we can attempt an L2 lookup
829
+ if (isUnfulfilledSnapshot(snapshot)) {
830
+ var start_1 = Date.now();
831
+ emitDurableEnvironmentAdapterEvent({ type: 'l2-revive-start' }, adapterRequestContext.eventObservers);
832
+ var revivedSnapshot = reviveSnapshot(environment, durableStore, snapshot, durableStoreErrorHandler, function () { return injectedStoreLookup(snapshot.select, snapshot.refresh); }).then(function (result) {
833
+ emitDurableEnvironmentAdapterEvent({
834
+ type: 'l2-revive-end',
835
+ snapshot: result.snapshot,
836
+ duration: Date.now() - start_1,
837
+ l2Trips: result.metrics.l2Trips,
838
+ }, adapterRequestContext.eventObservers);
839
+ return result.snapshot;
840
+ });
841
+ return revivedSnapshot;
842
+ }
843
+ // otherwise just return what buildCachedSnapshot gave us
844
+ return snapshot;
845
+ };
846
+ var wrappedApplyCachePolicy = function () {
847
+ return environment.applyCachePolicy(luvio, adapterRequestContext, buildSnapshotContext, wrappedCacheLookup, buildNetworkSnapshot);
848
+ };
849
+ // GET adapters call applyCachePolicy before any other luvio
850
+ // function so this is our chance to ensure we're initialized
851
+ return initializationPromise !== undefined
852
+ ? initializationPromise.then(wrappedApplyCachePolicy)
853
+ : wrappedApplyCachePolicy();
854
+ };
855
+ var getIngestStagingStoreRecords = function () {
856
+ validateNotDisposed();
857
+ if (ingestStagingStore !== null) {
858
+ return ingestStagingStore.fallbackStringKeyInMemoryStore.records;
859
+ }
860
+ return {};
861
+ };
862
+ var getIngestStagingStoreMetadata = function () {
863
+ validateNotDisposed();
864
+ if (ingestStagingStore !== null) {
865
+ return ingestStagingStore.fallbackStringKeyInMemoryStore.metadata;
866
+ }
867
+ return {};
868
+ };
869
+ var handleSuccessResponse = function (ingestAndBroadcastFunc, getResponseCacheKeysFunc) {
870
+ return __awaiter(this, void 0, void 0, function () {
871
+ var cacheKeyMap, cacheKeyMapKeys, keysToRevive, _i, cacheKeyMapKeys_1, cacheKeyMapKey, cacheKey, snapshotFromMemoryIngest, keysToReviveAsArray, readWritePromise, _a, keysToReviveAsArray_1, key, _b, keysToReviveAsArray_2, key, pendingPromise, _c, select, refresh, result;
872
+ var _this = this;
873
+ return __generator(this, function (_d) {
874
+ switch (_d.label) {
875
+ case 0:
876
+ validateNotDisposed();
877
+ cacheKeyMap = getResponseCacheKeysFunc();
878
+ cacheKeyMapKeys = cacheKeyMap.keysAsArray();
879
+ keysToRevive = new engine.StoreKeySet();
880
+ for (_i = 0, cacheKeyMapKeys_1 = cacheKeyMapKeys; _i < cacheKeyMapKeys_1.length; _i++) {
881
+ cacheKeyMapKey = cacheKeyMapKeys_1[_i];
882
+ cacheKey = cacheKeyMap.get(cacheKeyMapKey);
883
+ if (cacheKey.mergeable === true) {
884
+ keysToRevive.add(cacheKeyMapKey);
885
+ }
886
+ }
887
+ snapshotFromMemoryIngest = undefined;
888
+ keysToReviveAsArray = Array.from(keysToRevive.keysAsStrings());
889
+ if (!(keysToReviveAsArray.length > 0)) return [3 /*break*/, 5];
890
+ readWritePromise = (function () { return __awaiter(_this, void 0, void 0, function () {
891
+ var pendingPromises, _i, keysToReviveAsArray_3, key, pendingPromise, entries;
892
+ return __generator(this, function (_a) {
893
+ switch (_a.label) {
894
+ case 0:
895
+ pendingPromises = [];
896
+ for (_i = 0, keysToReviveAsArray_3 = keysToReviveAsArray; _i < keysToReviveAsArray_3.length; _i++) {
897
+ key = keysToReviveAsArray_3[_i];
898
+ pendingPromise = mergeKeysPromiseMap.get(key);
899
+ if (pendingPromise !== undefined) {
900
+ // IMPORTANT: while on the synchronous code path we get a
901
+ // handle to pendingPromise and push it onto the array.
902
+ // This is important because later in this synchronous code
903
+ // path we will upsert readWritePromise into the
904
+ // mergeKeysPromiseMap (essentially overwriting pendingPromise
905
+ // in the map).
906
+ pendingPromises.push(pendingPromise);
907
+ }
908
+ }
909
+ return [4 /*yield*/, Promise.all(pendingPromises)];
910
+ case 1:
911
+ _a.sent();
912
+ return [4 /*yield*/, durableStore.getEntries(keysToReviveAsArray, DefaultDurableSegment)];
913
+ case 2:
914
+ entries = _a.sent();
915
+ ingestStagingStore = buildIngestStagingStore(environment);
916
+ publishDurableStoreEntries(entries, function (key, record) {
917
+ if (typeof key === 'string') {
918
+ ingestStagingStore.fallbackStringKeyInMemoryStore.records[key] =
919
+ record;
920
+ }
921
+ else {
922
+ ingestStagingStore.recordsMap.set(key, record);
923
+ }
924
+ }, function (key, metadata) {
925
+ if (typeof key === 'string') {
926
+ ingestStagingStore.fallbackStringKeyInMemoryStore.metadata[key] =
927
+ metadata;
928
+ }
929
+ else {
930
+ ingestStagingStore.metadataMap.set(key, metadata);
931
+ }
932
+ });
933
+ return [4 /*yield*/, ingestAndBroadcastFunc()];
934
+ case 3:
935
+ snapshotFromMemoryIngest = _a.sent();
936
+ return [2 /*return*/];
937
+ }
938
+ });
939
+ }); })();
940
+ for (_a = 0, keysToReviveAsArray_1 = keysToReviveAsArray; _a < keysToReviveAsArray_1.length; _a++) {
941
+ key = keysToReviveAsArray_1[_a];
942
+ // we are overwriting the previous promise at this key, but that
943
+ // is ok because we got a handle to it earlier (see the IMPORTANT
944
+ // comment about 35 lines up)
945
+ mergeKeysPromiseMap.set(key, readWritePromise);
946
+ }
947
+ _d.label = 1;
948
+ case 1:
949
+ _d.trys.push([1, , 3, 4]);
950
+ return [4 /*yield*/, readWritePromise];
951
+ case 2:
952
+ _d.sent();
953
+ return [3 /*break*/, 4];
954
+ case 3:
955
+ for (_b = 0, keysToReviveAsArray_2 = keysToReviveAsArray; _b < keysToReviveAsArray_2.length; _b++) {
956
+ key = keysToReviveAsArray_2[_b];
957
+ pendingPromise = mergeKeysPromiseMap.get(key);
958
+ // cleanup the entry from the map if this is the last promise
959
+ // for that key
960
+ if (pendingPromise === readWritePromise) {
961
+ mergeKeysPromiseMap.delete(key);
962
+ }
963
+ }
964
+ return [7 /*endfinally*/];
965
+ case 4: return [3 /*break*/, 7];
966
+ case 5:
967
+ // we aren't doing any merging so we don't have to synchronize, the
968
+ // underlying DurableStore implementation takes care of R/W sync
969
+ // so all we have to do is ingest then write to L2
970
+ ingestStagingStore = buildIngestStagingStore(environment);
971
+ return [4 /*yield*/, ingestAndBroadcastFunc()];
972
+ case 6:
973
+ snapshotFromMemoryIngest = _d.sent();
974
+ _d.label = 7;
975
+ case 7:
976
+ if (snapshotFromMemoryIngest === undefined) {
977
+ return [2 /*return*/, undefined];
978
+ }
979
+ if (snapshotFromMemoryIngest.state !== 'Unfulfilled') {
980
+ return [2 /*return*/, snapshotFromMemoryIngest];
981
+ }
982
+ _c = snapshotFromMemoryIngest, select = _c.select, refresh = _c.refresh;
983
+ return [4 /*yield*/, reviveSnapshot(environment, durableStore, snapshotFromMemoryIngest, durableStoreErrorHandler, function () { return environment.storeLookup(select, environment.createSnapshot, refresh); })];
984
+ case 8:
985
+ result = _d.sent();
986
+ return [2 /*return*/, result.snapshot];
987
+ }
988
+ });
989
+ });
990
+ };
991
+ var handleErrorResponse = function (ingestAndBroadcastFunc) {
992
+ return __awaiter(this, void 0, void 0, function () {
993
+ return __generator(this, function (_a) {
994
+ validateNotDisposed();
995
+ ingestStagingStore = buildIngestStagingStore(environment);
996
+ return [2 /*return*/, ingestAndBroadcastFunc()];
997
+ });
998
+ });
999
+ };
1000
+ var getNotifyChangeStoreEntries = function (keys) {
1001
+ validateNotDisposed();
1002
+ return durableStore
1003
+ .getEntries(keys.map(engine.serializeStructuredKey), DefaultDurableSegment)
1004
+ .then(function (durableRecords) {
1005
+ var entries = [];
1006
+ publishDurableStoreEntries(durableRecords, function (key, record) {
1007
+ entries.push({
1008
+ key: key,
1009
+ record: record,
1010
+ });
1011
+ }, function () { });
1012
+ return entries;
1013
+ });
1014
+ };
1015
+ environment.defaultCachePolicy = {
1016
+ type: 'stale-while-revalidate',
1017
+ implementation: engine.buildStaleWhileRevalidateImplementation(Number.MAX_SAFE_INTEGER),
1018
+ };
1019
+ return create(environment, {
1020
+ publishStoreMetadata: { value: publishStoreMetadata },
1021
+ storeIngest: { value: storeIngest },
1022
+ storeIngestError: { value: storeIngestError },
1023
+ storeBroadcast: { value: storeBroadcast },
1024
+ storeLookup: { value: storeLookup },
1025
+ storeEvict: { value: storeEvict },
1026
+ wrapNormalizedGraphNode: { value: wrapNormalizedGraphNode },
1027
+ getNode: { value: getNode },
1028
+ rebuildSnapshot: { value: rebuildSnapshot },
1029
+ withContext: { value: withContext },
1030
+ storeSetTTLOverride: { value: storeSetTTLOverride },
1031
+ storeSetDefaultTTLOverride: { value: storeSetDefaultTTLOverride },
1032
+ storePublish: { value: storePublish },
1033
+ storeRedirect: { value: storeRedirect },
1034
+ dispose: { value: dispose },
1035
+ publishChangesToDurableStore: { value: publishChangesToDurableStore },
1036
+ getDurableTTLOverrides: { value: getDurableTTLOverrides },
1037
+ dispatchResourceRequest: { value: dispatchResourceRequest },
1038
+ applyCachePolicy: { value: applyCachePolicy },
1039
+ getIngestStagingStoreRecords: { value: getIngestStagingStoreRecords },
1040
+ getIngestStagingStoreMetadata: { value: getIngestStagingStoreMetadata },
1041
+ handleSuccessResponse: { value: handleSuccessResponse },
1042
+ handleErrorResponse: { value: handleErrorResponse },
1043
+ getNotifyChangeStoreEntries: { value: getNotifyChangeStoreEntries },
1044
+ });
1039
1045
  }
1040
1046
 
1041
1047
  exports.DURABLE_METADATA_VERSION = DURABLE_METADATA_VERSION;