@luvio/environments 0.60.0 → 0.62.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. package/dist/es/es2018/environments.js +189 -34
  2. package/dist/es/es2018/makeDurable/cachepolicies/cache-and-network.d.ts +3 -0
  3. package/dist/es/es2018/makeDurable/cachepolicies/index.d.ts +4 -0
  4. package/dist/es/es2018/makeDurable/cachepolicies/no-cache.d.ts +3 -0
  5. package/dist/es/es2018/makeDurable/cachepolicies/only-if-cached.d.ts +3 -0
  6. package/dist/es/es2018/makeDurable/cachepolicies/stale-while-revalidate.d.ts +1 -1
  7. package/dist/es/es2018/makeDurable/cachepolicies/utils.d.ts +1 -1
  8. package/dist/es/es2018/makeDurable/cachepolicies/valid-at.d.ts +3 -0
  9. package/dist/umd/es2018/environments.js +188 -33
  10. package/dist/umd/es2018/makeDurable/cachepolicies/cache-and-network.d.ts +3 -0
  11. package/dist/umd/es2018/makeDurable/cachepolicies/index.d.ts +4 -0
  12. package/dist/umd/es2018/makeDurable/cachepolicies/no-cache.d.ts +3 -0
  13. package/dist/umd/es2018/makeDurable/cachepolicies/only-if-cached.d.ts +3 -0
  14. package/dist/umd/es2018/makeDurable/cachepolicies/stale-while-revalidate.d.ts +1 -1
  15. package/dist/umd/es2018/makeDurable/cachepolicies/utils.d.ts +1 -1
  16. package/dist/umd/es2018/makeDurable/cachepolicies/valid-at.d.ts +3 -0
  17. package/dist/umd/es5/environments.js +188 -34
  18. package/dist/umd/es5/makeDurable/cachepolicies/cache-and-network.d.ts +3 -0
  19. package/dist/umd/es5/makeDurable/cachepolicies/index.d.ts +4 -0
  20. package/dist/umd/es5/makeDurable/cachepolicies/no-cache.d.ts +3 -0
  21. package/dist/umd/es5/makeDurable/cachepolicies/only-if-cached.d.ts +3 -0
  22. package/dist/umd/es5/makeDurable/cachepolicies/stale-while-revalidate.d.ts +1 -1
  23. package/dist/umd/es5/makeDurable/cachepolicies/utils.d.ts +1 -1
  24. package/dist/umd/es5/makeDurable/cachepolicies/valid-at.d.ts +3 -0
  25. package/package.json +4 -3
@@ -1,4 +1,4 @@
1
- import { StoreResolveResultState, Store, buildStaleWhileRevalidateImplementation as buildStaleWhileRevalidateImplementation$1 } from '@luvio/engine';
1
+ import { StoreResolveResultState, HttpStatusCode, Store, buildStaleWhileRevalidateImplementation as buildStaleWhileRevalidateImplementation$1 } from '@luvio/engine';
2
2
 
3
3
  function isDeprecatedDurableStoreEntry(durableRecord) {
4
4
  if (durableRecord.expiration !== undefined) {
@@ -17,12 +17,13 @@ const DefaultDurableSegment = 'DEFAULT';
17
17
  const { keys, create, assign, freeze } = Object;
18
18
  const { isArray } = Array;
19
19
 
20
- function buildTTLStrategy(staleDuration = 0) {
20
+ function buildTTLStrategy(staleDurationMilliseconds = 0) {
21
21
  return (timestamp, metadata, valueIsError) => {
22
22
  if (metadata !== undefined) {
23
23
  const { expirationTimestamp } = metadata;
24
24
  if (timestamp > expirationTimestamp) {
25
- if (timestamp <= expirationTimestamp + staleDuration && valueIsError !== true) {
25
+ if (timestamp <= expirationTimestamp + staleDurationMilliseconds &&
26
+ valueIsError !== true) {
26
27
  return StoreResolveResultState.Stale;
27
28
  }
28
29
  return StoreResolveResultState.NotPresent;
@@ -38,6 +39,61 @@ function appendTTLStrategy(storeLookup, ttlStrategy) {
38
39
  return (sel, refresh) => storeLookup(sel, refresh, ttlStrategy);
39
40
  }
40
41
 
42
+ function buildCacheAndNetworkImplementation(funcs, staleDurationSeconds) {
43
+ return function (args) {
44
+ funcs.validateNotDisposed();
45
+ const { buildInMemorySnapshot, buildNetworkSnapshot, buildSnapshotContext, dispatchResourceRequest, storeLookup, } = args;
46
+ const staleDurationMilliseconds = staleDurationSeconds === undefined ? undefined : staleDurationSeconds * 1000;
47
+ const cachePolicyStoreLookup = appendTTLStrategy(storeLookup, buildTTLStrategy(staleDurationMilliseconds));
48
+ const snapshot = buildInMemorySnapshot(buildSnapshotContext, cachePolicyStoreLookup);
49
+ if (snapshot !== undefined) {
50
+ // data found in L1 cache
51
+ if (snapshot.state === 'Fulfilled' || snapshot.state === 'Error') {
52
+ // kick off network request, do not await it
53
+ buildNetworkSnapshot(buildSnapshotContext, dispatchResourceRequest);
54
+ // return the cached snapshot to caller
55
+ return snapshot;
56
+ }
57
+ // stale data found in L1 cache
58
+ if (snapshot.state === 'Stale') {
59
+ // kick off network request, do not await it
60
+ // offline environment is already doing this; uncomment once we get rid of offline environment
61
+ // buildNetworkSnapshot(buildSnapshotContext, dispatchResourceRequest);
62
+ // return the cached snapshot to caller
63
+ return snapshot;
64
+ }
65
+ // if unfulfilled we have enough info to do an L2 lookup
66
+ if (snapshot.state === 'Unfulfilled') {
67
+ return funcs
68
+ .reviveSnapshotWithCachePolicy(snapshot, cachePolicyStoreLookup)
69
+ .then((revivedSnapshot) => {
70
+ // data found in L2 cache
71
+ if (revivedSnapshot.state === 'Fulfilled' ||
72
+ revivedSnapshot.state === 'Error') {
73
+ // kick off network request, do not await it
74
+ buildNetworkSnapshot(buildSnapshotContext, dispatchResourceRequest);
75
+ // return the L2 cached snapshot to caller
76
+ return revivedSnapshot;
77
+ }
78
+ if (revivedSnapshot.state === 'Pending') ;
79
+ // stale data found in L2 cache
80
+ if (revivedSnapshot.state === 'Stale') {
81
+ // kick off network request, do not await it
82
+ // offline environment is already doing this; uncomment once we get rid of offline environment
83
+ // buildNetworkSnapshot(buildSnapshotContext, dispatchResourceRequest);
84
+ // return the L2 cached snapshot to caller
85
+ return revivedSnapshot;
86
+ }
87
+ // data not found in L2 cache, go to the network
88
+ return buildNetworkSnapshot(buildSnapshotContext, dispatchResourceRequest);
89
+ });
90
+ }
91
+ if (snapshot.state === 'Pending') ;
92
+ }
93
+ return buildNetworkSnapshot(buildSnapshotContext, dispatchResourceRequest);
94
+ };
95
+ }
96
+
41
97
  function buildCacheThenNetworkImplementation(funcs) {
42
98
  return function (args) {
43
99
  funcs.validateNotDisposed();
@@ -69,11 +125,89 @@ function buildCacheThenNetworkImplementation(funcs) {
69
125
  };
70
126
  }
71
127
 
72
- function buildStaleWhileRevalidateImplementation(funcs, staleDuration) {
128
+ function buildNoCacheImplementation(funcs) {
129
+ return function (args) {
130
+ funcs.validateNotDisposed();
131
+ const { buildNetworkSnapshot, buildSnapshotContext, dispatchResourceRequest } = args;
132
+ return buildNetworkSnapshot(buildSnapshotContext, dispatchResourceRequest).then((snapshot) => {
133
+ if (snapshot.state === 'Pending') ;
134
+ return snapshot;
135
+ });
136
+ };
137
+ }
138
+
139
+ function deepFreeze(value) {
140
+ // No need to freeze primitives
141
+ if (typeof value !== 'object' || value === null) {
142
+ return;
143
+ }
144
+ if (isArray(value)) {
145
+ for (let i = 0, len = value.length; i < len; i += 1) {
146
+ deepFreeze(value[i]);
147
+ }
148
+ }
149
+ else {
150
+ const keys$1 = keys(value);
151
+ for (let i = 0, len = keys$1.length; i < len; i += 1) {
152
+ deepFreeze(value[keys$1[i]]);
153
+ }
154
+ }
155
+ freeze(value);
156
+ }
157
+
158
+ // TODO[@W-10165595]: consolidate this code with the corresponding logic in the default environment's only-if-cached.ts
159
+ function buildNotCachedErrorSnapshot() {
160
+ const error = {
161
+ body: undefined,
162
+ headers: {},
163
+ ok: false,
164
+ status: HttpStatusCode.GatewayTimeout,
165
+ statusText: 'Gateway Timeout',
166
+ };
167
+ deepFreeze(error);
168
+ return {
169
+ error,
170
+ state: 'Error',
171
+ data: undefined,
172
+ };
173
+ }
174
+ function buildOnlyIfCachedImplementation(funcs) {
175
+ return function (args) {
176
+ funcs.validateNotDisposed();
177
+ const { buildInMemorySnapshot, buildSnapshotContext, storeLookup } = args;
178
+ const cachePolicyStoreLookup = appendTTLStrategy(storeLookup, buildTTLStrategy());
179
+ const snapshot = buildInMemorySnapshot(buildSnapshotContext, cachePolicyStoreLookup);
180
+ if (snapshot !== undefined) {
181
+ // data found in L1 cache
182
+ if (snapshot.state === 'Fulfilled' || snapshot.state === 'Error') {
183
+ return snapshot;
184
+ }
185
+ // network request outstanding, data is not cached
186
+ if (snapshot.state === 'Pending') {
187
+ return buildNotCachedErrorSnapshot();
188
+ }
189
+ // data not found in L1 cache, try L2 cache
190
+ return funcs
191
+ .reviveSnapshotWithCachePolicy(snapshot, cachePolicyStoreLookup)
192
+ .then((revivedSnapshot) => {
193
+ // data found in L2 cache
194
+ if (revivedSnapshot.state === 'Fulfilled' ||
195
+ revivedSnapshot.state === 'Error') {
196
+ return revivedSnapshot;
197
+ }
198
+ // data is not cached
199
+ return buildNotCachedErrorSnapshot();
200
+ });
201
+ }
202
+ return buildNotCachedErrorSnapshot();
203
+ };
204
+ }
205
+
206
+ function buildStaleWhileRevalidateImplementation(funcs, staleDurationSeconds) {
73
207
  return function (args) {
74
208
  funcs.validateNotDisposed();
75
209
  const { buildInMemorySnapshot, buildNetworkSnapshot, buildSnapshotContext, dispatchResourceRequest, storeLookup, } = args;
76
- const cachePolicyStoreLookup = appendTTLStrategy(storeLookup, buildTTLStrategy(staleDuration));
210
+ const cachePolicyStoreLookup = appendTTLStrategy(storeLookup, buildTTLStrategy(staleDurationSeconds * 1000));
77
211
  const snapshot = buildInMemorySnapshot(buildSnapshotContext, cachePolicyStoreLookup);
78
212
  if (snapshot !== undefined) {
79
213
  // data found in L1 cache
@@ -112,6 +246,28 @@ function buildStaleWhileRevalidateImplementation(funcs, staleDuration) {
112
246
  };
113
247
  }
114
248
 
249
+ function buildValidAtImplementation(funcs, basePolicyImplementation, timestamp) {
250
+ return function validAtImplementation(args) {
251
+ funcs.validateNotDisposed();
252
+ // This somewhat convoluted code is used to force the basePolicyImplementation's
253
+ // TTLStrategy to use the the valid-at cache policy's timestamp. The flow goes:
254
+ //
255
+ // Environment.applyCachePolicy => validAtImplementation (this function) =>
256
+ // basePolicyImplementation => adapter's buildInMemorySnapshot =>
257
+ // basePolicyImplementation's storeLookup => validAtStoreLookup (below) =>
258
+ // Environment.applyCachePolicy's storeLookup => Store/Reader code =>
259
+ // valid-at TTLStrategy (below) =>
260
+ // basePolicyImplementation's TTLStrategy (with valid-at timestamp)
261
+ const validAtStoreLookup = (sel, refresh, ttlStrategy) => args.storeLookup(sel, refresh, (_readerTimestamp, metadata, valueIsError) => ttlStrategy(timestamp, metadata, valueIsError));
262
+ // let basePolicy make all the decisions, but have it use our storeLookup
263
+ // so we can override the timestamp passed to the basePolicy's TTLStrategy
264
+ return basePolicyImplementation({
265
+ ...args,
266
+ storeLookup: validAtStoreLookup,
267
+ });
268
+ };
269
+ }
270
+
115
271
  //Durable store error instrumentation key
116
272
  const DURABLE_STORE_ERROR = 'durable-store-error';
117
273
  /**
@@ -136,25 +292,6 @@ function handleDurableStoreRejection(instrument) {
136
292
  };
137
293
  }
138
294
 
139
- function deepFreeze(value) {
140
- // No need to freeze primitives
141
- if (typeof value !== 'object' || value === null) {
142
- return;
143
- }
144
- if (isArray(value)) {
145
- for (let i = 0, len = value.length; i < len; i += 1) {
146
- deepFreeze(value[i]);
147
- }
148
- }
149
- else {
150
- const keys$1 = keys(value);
151
- for (let i = 0, len = keys$1.length; i < len; i += 1) {
152
- deepFreeze(value[keys$1[i]]);
153
- }
154
- }
155
- freeze(value);
156
- }
157
-
158
295
  const SELECTOR_PAGINATION_TOKEN = 'tokenDataKey';
159
296
  function isFragmentUnionSelection(sel) {
160
297
  return sel.union === true;
@@ -303,7 +440,9 @@ function publishDurableStoreEntries(durableRecords, publish, publishMetadata) {
303
440
  * will refresh the snapshot from network, and then run the results from network
304
441
  * through L2 ingestion, returning the subsequent revived snapshot.
305
442
  */
306
- function reviveSnapshot(baseEnvironment, durableStore, unavailableSnapshot, durableStoreErrorHandler, buildL1Snapshot) {
443
+ function reviveSnapshot(baseEnvironment, durableStore,
444
+ // TODO [W-10165787]: We should only allow Unfulfilled snapshot be passed in
445
+ unavailableSnapshot, durableStoreErrorHandler, buildL1Snapshot) {
307
446
  const { recordId, select, seenRecords, state } = unavailableSnapshot;
308
447
  // L2 can only revive Unfulfilled snapshots that have a selector since they have the
309
448
  // info needed to revive (like missingLinks) and rebuild. Otherwise return L1 snapshot.
@@ -795,18 +934,34 @@ function makeDurable(environment, { durableStore, instrumentation }) {
795
934
  return buildStaleWhileRevalidateImplementation({
796
935
  validateNotDisposed,
797
936
  reviveSnapshotWithCachePolicy,
798
- },
799
- // TODO[@W-10077764]: remove staleDuration once Komaci switches to cache-then-network
800
- 'staleDurationSeconds' in cachePolicy
801
- ? cachePolicy.staleDurationSeconds
802
- : cachePolicy.staleDuration
803
- //cachePolicy.staleDurationSeconds
804
- );
937
+ }, cachePolicy.staleDurationSeconds);
938
+ case 'cache-and-network':
939
+ return buildCacheAndNetworkImplementation({
940
+ validateNotDisposed,
941
+ reviveSnapshotWithCachePolicy,
942
+ }, cachePolicy.staleDurationSeconds);
805
943
  case 'cache-then-network':
806
944
  return buildCacheThenNetworkImplementation({
807
945
  validateNotDisposed,
808
946
  reviveSnapshotWithCachePolicy,
809
947
  });
948
+ case 'no-cache':
949
+ return buildNoCacheImplementation({
950
+ validateNotDisposed,
951
+ reviveSnapshotWithCachePolicy,
952
+ });
953
+ case 'only-if-cached':
954
+ return buildOnlyIfCachedImplementation({
955
+ validateNotDisposed,
956
+ reviveSnapshotWithCachePolicy,
957
+ });
958
+ case 'valid-at': {
959
+ const basePolicy = resolveCachePolicy(cachePolicy.basePolicy);
960
+ return buildValidAtImplementation({
961
+ validateNotDisposed,
962
+ reviveSnapshotWithCachePolicy,
963
+ }, basePolicy, cachePolicy.timestamp);
964
+ }
810
965
  default: {
811
966
  if (process.env.NODE_ENV !== 'production') {
812
967
  throw new Error(`unrecognized cache policy: ${JSON.stringify(cachePolicy)}`);
@@ -815,9 +970,9 @@ function makeDurable(environment, { durableStore, instrumentation }) {
815
970
  }
816
971
  }
817
972
  }
818
- const applyCachePolicy = function (cachePolicy, buildSnapshotContext, buildInMemorySnapshot, buildNetworkSnapshot) {
973
+ const applyCachePolicy = function (adapterRequestContext, buildSnapshotContext, buildInMemorySnapshot, buildNetworkSnapshot) {
819
974
  validateNotDisposed();
820
- const cachePolicyImpl = resolveCachePolicy(cachePolicy);
975
+ const cachePolicyImpl = resolveCachePolicy(adapterRequestContext.cachePolicy);
821
976
  const storeLookup = (sel, refresh, ttlStrategy) => environment.storeLookup(sel, environment.createSnapshot, refresh, ttlStrategy);
822
977
  const applyCachePolicy = () => {
823
978
  return cachePolicyImpl({
@@ -0,0 +1,3 @@
1
+ import { CachePolicyImplementationArgs, Snapshot } from '@luvio/engine';
2
+ import { DurableCachePolicyFunctions } from './utils';
3
+ export declare function buildCacheAndNetworkImplementation(funcs: DurableCachePolicyFunctions, staleDurationSeconds?: number): <C, D>(args: CachePolicyImplementationArgs<C, D>) => import("@luvio/engine").ErrorSnapshot | import("@luvio/engine").FulfilledSnapshot<D, unknown> | import("@luvio/engine").UnfulfilledSnapshot<D, unknown> | import("@luvio/engine").StaleSnapshot<D, unknown> | import("@luvio/engine").PendingSnapshot<D, unknown> | Promise<Snapshot<D, unknown>>;
@@ -1,2 +1,6 @@
1
+ export { buildCacheAndNetworkImplementation } from './cache-and-network';
1
2
  export { buildCacheThenNetworkImplementation } from './cache-then-network';
3
+ export { buildNoCacheImplementation } from './no-cache';
4
+ export { buildOnlyIfCachedImplementation } from './only-if-cached';
2
5
  export { buildStaleWhileRevalidateImplementation } from './stale-while-revalidate';
6
+ export { buildValidAtImplementation } from './valid-at';
@@ -0,0 +1,3 @@
1
+ import { CachePolicyImplementationArgs, Snapshot } from '@luvio/engine';
2
+ import { DurableCachePolicyFunctions } from './utils';
3
+ export declare function buildNoCacheImplementation(funcs: DurableCachePolicyFunctions): <C, D>(args: CachePolicyImplementationArgs<C, D>) => import("@luvio/engine").ErrorSnapshot | import("@luvio/engine").FulfilledSnapshot<D, unknown> | import("@luvio/engine").UnfulfilledSnapshot<D, unknown> | import("@luvio/engine").StaleSnapshot<D, unknown> | import("@luvio/engine").PendingSnapshot<D, unknown> | Promise<Snapshot<D, unknown>>;
@@ -0,0 +1,3 @@
1
+ import { CachePolicyImplementationArgs, ErrorSnapshot, Snapshot } from '@luvio/engine';
2
+ import { DurableCachePolicyFunctions } from './utils';
3
+ export declare function buildOnlyIfCachedImplementation(funcs: DurableCachePolicyFunctions): <C, D>(args: CachePolicyImplementationArgs<C, D>) => ErrorSnapshot | import("@luvio/engine").FulfilledSnapshot<D, unknown> | import("@luvio/engine").UnfulfilledSnapshot<D, unknown> | import("@luvio/engine").StaleSnapshot<D, unknown> | import("@luvio/engine").PendingSnapshot<D, unknown> | Promise<Snapshot<D, unknown>>;
@@ -1,3 +1,3 @@
1
1
  import { CachePolicyImplementationArgs, Snapshot } from '@luvio/engine';
2
2
  import { DurableCachePolicyFunctions } from './utils';
3
- export declare function buildStaleWhileRevalidateImplementation(funcs: DurableCachePolicyFunctions, staleDuration: number): <C, D>(args: CachePolicyImplementationArgs<C, D>) => import("@luvio/engine").ErrorSnapshot | import("@luvio/engine").FulfilledSnapshot<D, unknown> | import("@luvio/engine").UnfulfilledSnapshot<D, unknown> | import("@luvio/engine").StaleSnapshot<D, unknown> | import("@luvio/engine").PendingSnapshot<D, unknown> | Promise<Snapshot<D, unknown>>;
3
+ export declare function buildStaleWhileRevalidateImplementation(funcs: DurableCachePolicyFunctions, staleDurationSeconds: number): <C, D>(args: CachePolicyImplementationArgs<C, D>) => import("@luvio/engine").ErrorSnapshot | import("@luvio/engine").FulfilledSnapshot<D, unknown> | import("@luvio/engine").UnfulfilledSnapshot<D, unknown> | import("@luvio/engine").StaleSnapshot<D, unknown> | import("@luvio/engine").PendingSnapshot<D, unknown> | Promise<Snapshot<D, unknown>>;
@@ -3,5 +3,5 @@ export declare type DurableCachePolicyFunctions = {
3
3
  validateNotDisposed: () => void;
4
4
  reviveSnapshotWithCachePolicy: <D, V>(unavailableSnapshot: UnAvailableSnapshot<D, V>, storeLookup: StoreLookup<D, V>) => Promise<Snapshot<D, V>>;
5
5
  };
6
- export declare function buildTTLStrategy(staleDuration?: number): TTLStrategy;
6
+ export declare function buildTTLStrategy(staleDurationMilliseconds?: number): TTLStrategy;
7
7
  export declare function appendTTLStrategy<C, D>(storeLookup: CachePolicyImplementationArgs<C, D>['storeLookup'], ttlStrategy: TTLStrategy): Parameters<BuildInMemorySnapshot<C, D>>[1];
@@ -0,0 +1,3 @@
1
+ import { CachePolicyImplementation, CachePolicyImplementationArgs, Snapshot } from '@luvio/engine';
2
+ import { DurableCachePolicyFunctions } from './utils';
3
+ export declare function buildValidAtImplementation<C, D>(funcs: DurableCachePolicyFunctions, basePolicyImplementation: CachePolicyImplementation<C, D>, timestamp: number): (args: CachePolicyImplementationArgs<C, D>) => Snapshot<D> | Promise<Snapshot<D>>;
@@ -20,12 +20,13 @@
20
20
  const { keys, create, assign, freeze } = Object;
21
21
  const { isArray } = Array;
22
22
 
23
- function buildTTLStrategy(staleDuration = 0) {
23
+ function buildTTLStrategy(staleDurationMilliseconds = 0) {
24
24
  return (timestamp, metadata, valueIsError) => {
25
25
  if (metadata !== undefined) {
26
26
  const { expirationTimestamp } = metadata;
27
27
  if (timestamp > expirationTimestamp) {
28
- if (timestamp <= expirationTimestamp + staleDuration && valueIsError !== true) {
28
+ if (timestamp <= expirationTimestamp + staleDurationMilliseconds &&
29
+ valueIsError !== true) {
29
30
  return engine.StoreResolveResultState.Stale;
30
31
  }
31
32
  return engine.StoreResolveResultState.NotPresent;
@@ -41,6 +42,61 @@
41
42
  return (sel, refresh) => storeLookup(sel, refresh, ttlStrategy);
42
43
  }
43
44
 
45
+ function buildCacheAndNetworkImplementation(funcs, staleDurationSeconds) {
46
+ return function (args) {
47
+ funcs.validateNotDisposed();
48
+ const { buildInMemorySnapshot, buildNetworkSnapshot, buildSnapshotContext, dispatchResourceRequest, storeLookup, } = args;
49
+ const staleDurationMilliseconds = staleDurationSeconds === undefined ? undefined : staleDurationSeconds * 1000;
50
+ const cachePolicyStoreLookup = appendTTLStrategy(storeLookup, buildTTLStrategy(staleDurationMilliseconds));
51
+ const snapshot = buildInMemorySnapshot(buildSnapshotContext, cachePolicyStoreLookup);
52
+ if (snapshot !== undefined) {
53
+ // data found in L1 cache
54
+ if (snapshot.state === 'Fulfilled' || snapshot.state === 'Error') {
55
+ // kick off network request, do not await it
56
+ buildNetworkSnapshot(buildSnapshotContext, dispatchResourceRequest);
57
+ // return the cached snapshot to caller
58
+ return snapshot;
59
+ }
60
+ // stale data found in L1 cache
61
+ if (snapshot.state === 'Stale') {
62
+ // kick off network request, do not await it
63
+ // offline environment is already doing this; uncomment once we get rid of offline environment
64
+ // buildNetworkSnapshot(buildSnapshotContext, dispatchResourceRequest);
65
+ // return the cached snapshot to caller
66
+ return snapshot;
67
+ }
68
+ // if unfulfilled we have enough info to do an L2 lookup
69
+ if (snapshot.state === 'Unfulfilled') {
70
+ return funcs
71
+ .reviveSnapshotWithCachePolicy(snapshot, cachePolicyStoreLookup)
72
+ .then((revivedSnapshot) => {
73
+ // data found in L2 cache
74
+ if (revivedSnapshot.state === 'Fulfilled' ||
75
+ revivedSnapshot.state === 'Error') {
76
+ // kick off network request, do not await it
77
+ buildNetworkSnapshot(buildSnapshotContext, dispatchResourceRequest);
78
+ // return the L2 cached snapshot to caller
79
+ return revivedSnapshot;
80
+ }
81
+ if (revivedSnapshot.state === 'Pending') ;
82
+ // stale data found in L2 cache
83
+ if (revivedSnapshot.state === 'Stale') {
84
+ // kick off network request, do not await it
85
+ // offline environment is already doing this; uncomment once we get rid of offline environment
86
+ // buildNetworkSnapshot(buildSnapshotContext, dispatchResourceRequest);
87
+ // return the L2 cached snapshot to caller
88
+ return revivedSnapshot;
89
+ }
90
+ // data not found in L2 cache, go to the network
91
+ return buildNetworkSnapshot(buildSnapshotContext, dispatchResourceRequest);
92
+ });
93
+ }
94
+ if (snapshot.state === 'Pending') ;
95
+ }
96
+ return buildNetworkSnapshot(buildSnapshotContext, dispatchResourceRequest);
97
+ };
98
+ }
99
+
44
100
  function buildCacheThenNetworkImplementation(funcs) {
45
101
  return function (args) {
46
102
  funcs.validateNotDisposed();
@@ -72,11 +128,89 @@
72
128
  };
73
129
  }
74
130
 
75
- function buildStaleWhileRevalidateImplementation(funcs, staleDuration) {
131
+ function buildNoCacheImplementation(funcs) {
132
+ return function (args) {
133
+ funcs.validateNotDisposed();
134
+ const { buildNetworkSnapshot, buildSnapshotContext, dispatchResourceRequest } = args;
135
+ return buildNetworkSnapshot(buildSnapshotContext, dispatchResourceRequest).then((snapshot) => {
136
+ if (snapshot.state === 'Pending') ;
137
+ return snapshot;
138
+ });
139
+ };
140
+ }
141
+
142
+ function deepFreeze(value) {
143
+ // No need to freeze primitives
144
+ if (typeof value !== 'object' || value === null) {
145
+ return;
146
+ }
147
+ if (isArray(value)) {
148
+ for (let i = 0, len = value.length; i < len; i += 1) {
149
+ deepFreeze(value[i]);
150
+ }
151
+ }
152
+ else {
153
+ const keys$1 = keys(value);
154
+ for (let i = 0, len = keys$1.length; i < len; i += 1) {
155
+ deepFreeze(value[keys$1[i]]);
156
+ }
157
+ }
158
+ freeze(value);
159
+ }
160
+
161
+ // TODO[@W-10165595]: consolidate this code with the corresponding logic in the default environment's only-if-cached.ts
162
+ function buildNotCachedErrorSnapshot() {
163
+ const error = {
164
+ body: undefined,
165
+ headers: {},
166
+ ok: false,
167
+ status: engine.HttpStatusCode.GatewayTimeout,
168
+ statusText: 'Gateway Timeout',
169
+ };
170
+ deepFreeze(error);
171
+ return {
172
+ error,
173
+ state: 'Error',
174
+ data: undefined,
175
+ };
176
+ }
177
+ function buildOnlyIfCachedImplementation(funcs) {
178
+ return function (args) {
179
+ funcs.validateNotDisposed();
180
+ const { buildInMemorySnapshot, buildSnapshotContext, storeLookup } = args;
181
+ const cachePolicyStoreLookup = appendTTLStrategy(storeLookup, buildTTLStrategy());
182
+ const snapshot = buildInMemorySnapshot(buildSnapshotContext, cachePolicyStoreLookup);
183
+ if (snapshot !== undefined) {
184
+ // data found in L1 cache
185
+ if (snapshot.state === 'Fulfilled' || snapshot.state === 'Error') {
186
+ return snapshot;
187
+ }
188
+ // network request outstanding, data is not cached
189
+ if (snapshot.state === 'Pending') {
190
+ return buildNotCachedErrorSnapshot();
191
+ }
192
+ // data not found in L1 cache, try L2 cache
193
+ return funcs
194
+ .reviveSnapshotWithCachePolicy(snapshot, cachePolicyStoreLookup)
195
+ .then((revivedSnapshot) => {
196
+ // data found in L2 cache
197
+ if (revivedSnapshot.state === 'Fulfilled' ||
198
+ revivedSnapshot.state === 'Error') {
199
+ return revivedSnapshot;
200
+ }
201
+ // data is not cached
202
+ return buildNotCachedErrorSnapshot();
203
+ });
204
+ }
205
+ return buildNotCachedErrorSnapshot();
206
+ };
207
+ }
208
+
209
+ function buildStaleWhileRevalidateImplementation(funcs, staleDurationSeconds) {
76
210
  return function (args) {
77
211
  funcs.validateNotDisposed();
78
212
  const { buildInMemorySnapshot, buildNetworkSnapshot, buildSnapshotContext, dispatchResourceRequest, storeLookup, } = args;
79
- const cachePolicyStoreLookup = appendTTLStrategy(storeLookup, buildTTLStrategy(staleDuration));
213
+ const cachePolicyStoreLookup = appendTTLStrategy(storeLookup, buildTTLStrategy(staleDurationSeconds * 1000));
80
214
  const snapshot = buildInMemorySnapshot(buildSnapshotContext, cachePolicyStoreLookup);
81
215
  if (snapshot !== undefined) {
82
216
  // data found in L1 cache
@@ -115,6 +249,28 @@
115
249
  };
116
250
  }
117
251
 
252
+ function buildValidAtImplementation(funcs, basePolicyImplementation, timestamp) {
253
+ return function validAtImplementation(args) {
254
+ funcs.validateNotDisposed();
255
+ // This somewhat convoluted code is used to force the basePolicyImplementation's
256
+ // TTLStrategy to use the the valid-at cache policy's timestamp. The flow goes:
257
+ //
258
+ // Environment.applyCachePolicy => validAtImplementation (this function) =>
259
+ // basePolicyImplementation => adapter's buildInMemorySnapshot =>
260
+ // basePolicyImplementation's storeLookup => validAtStoreLookup (below) =>
261
+ // Environment.applyCachePolicy's storeLookup => Store/Reader code =>
262
+ // valid-at TTLStrategy (below) =>
263
+ // basePolicyImplementation's TTLStrategy (with valid-at timestamp)
264
+ const validAtStoreLookup = (sel, refresh, ttlStrategy) => args.storeLookup(sel, refresh, (_readerTimestamp, metadata, valueIsError) => ttlStrategy(timestamp, metadata, valueIsError));
265
+ // let basePolicy make all the decisions, but have it use our storeLookup
266
+ // so we can override the timestamp passed to the basePolicy's TTLStrategy
267
+ return basePolicyImplementation({
268
+ ...args,
269
+ storeLookup: validAtStoreLookup,
270
+ });
271
+ };
272
+ }
273
+
118
274
  //Durable store error instrumentation key
119
275
  const DURABLE_STORE_ERROR = 'durable-store-error';
120
276
  /**
@@ -139,25 +295,6 @@
139
295
  };
140
296
  }
141
297
 
142
- function deepFreeze(value) {
143
- // No need to freeze primitives
144
- if (typeof value !== 'object' || value === null) {
145
- return;
146
- }
147
- if (isArray(value)) {
148
- for (let i = 0, len = value.length; i < len; i += 1) {
149
- deepFreeze(value[i]);
150
- }
151
- }
152
- else {
153
- const keys$1 = keys(value);
154
- for (let i = 0, len = keys$1.length; i < len; i += 1) {
155
- deepFreeze(value[keys$1[i]]);
156
- }
157
- }
158
- freeze(value);
159
- }
160
-
161
298
  const SELECTOR_PAGINATION_TOKEN = 'tokenDataKey';
162
299
  function isFragmentUnionSelection(sel) {
163
300
  return sel.union === true;
@@ -306,7 +443,9 @@
306
443
  * will refresh the snapshot from network, and then run the results from network
307
444
  * through L2 ingestion, returning the subsequent revived snapshot.
308
445
  */
309
- function reviveSnapshot(baseEnvironment, durableStore, unavailableSnapshot, durableStoreErrorHandler, buildL1Snapshot) {
446
+ function reviveSnapshot(baseEnvironment, durableStore,
447
+ // TODO [W-10165787]: We should only allow Unfulfilled snapshot be passed in
448
+ unavailableSnapshot, durableStoreErrorHandler, buildL1Snapshot) {
310
449
  const { recordId, select, seenRecords, state } = unavailableSnapshot;
311
450
  // L2 can only revive Unfulfilled snapshots that have a selector since they have the
312
451
  // info needed to revive (like missingLinks) and rebuild. Otherwise return L1 snapshot.
@@ -798,18 +937,34 @@
798
937
  return buildStaleWhileRevalidateImplementation({
799
938
  validateNotDisposed,
800
939
  reviveSnapshotWithCachePolicy,
801
- },
802
- // TODO[@W-10077764]: remove staleDuration once Komaci switches to cache-then-network
803
- 'staleDurationSeconds' in cachePolicy
804
- ? cachePolicy.staleDurationSeconds
805
- : cachePolicy.staleDuration
806
- //cachePolicy.staleDurationSeconds
807
- );
940
+ }, cachePolicy.staleDurationSeconds);
941
+ case 'cache-and-network':
942
+ return buildCacheAndNetworkImplementation({
943
+ validateNotDisposed,
944
+ reviveSnapshotWithCachePolicy,
945
+ }, cachePolicy.staleDurationSeconds);
808
946
  case 'cache-then-network':
809
947
  return buildCacheThenNetworkImplementation({
810
948
  validateNotDisposed,
811
949
  reviveSnapshotWithCachePolicy,
812
950
  });
951
+ case 'no-cache':
952
+ return buildNoCacheImplementation({
953
+ validateNotDisposed,
954
+ reviveSnapshotWithCachePolicy,
955
+ });
956
+ case 'only-if-cached':
957
+ return buildOnlyIfCachedImplementation({
958
+ validateNotDisposed,
959
+ reviveSnapshotWithCachePolicy,
960
+ });
961
+ case 'valid-at': {
962
+ const basePolicy = resolveCachePolicy(cachePolicy.basePolicy);
963
+ return buildValidAtImplementation({
964
+ validateNotDisposed,
965
+ reviveSnapshotWithCachePolicy,
966
+ }, basePolicy, cachePolicy.timestamp);
967
+ }
813
968
  default: {
814
969
  if (process.env.NODE_ENV !== 'production') {
815
970
  throw new Error(`unrecognized cache policy: ${JSON.stringify(cachePolicy)}`);
@@ -818,9 +973,9 @@
818
973
  }
819
974
  }
820
975
  }
821
- const applyCachePolicy = function (cachePolicy, buildSnapshotContext, buildInMemorySnapshot, buildNetworkSnapshot) {
976
+ const applyCachePolicy = function (adapterRequestContext, buildSnapshotContext, buildInMemorySnapshot, buildNetworkSnapshot) {
822
977
  validateNotDisposed();
823
- const cachePolicyImpl = resolveCachePolicy(cachePolicy);
978
+ const cachePolicyImpl = resolveCachePolicy(adapterRequestContext.cachePolicy);
824
979
  const storeLookup = (sel, refresh, ttlStrategy) => environment.storeLookup(sel, environment.createSnapshot, refresh, ttlStrategy);
825
980
  const applyCachePolicy = () => {
826
981
  return cachePolicyImpl({
@@ -0,0 +1,3 @@
1
+ import { CachePolicyImplementationArgs, Snapshot } from '@luvio/engine';
2
+ import { DurableCachePolicyFunctions } from './utils';
3
+ export declare function buildCacheAndNetworkImplementation(funcs: DurableCachePolicyFunctions, staleDurationSeconds?: number): <C, D>(args: CachePolicyImplementationArgs<C, D>) => import("@luvio/engine").ErrorSnapshot | import("@luvio/engine").FulfilledSnapshot<D, unknown> | import("@luvio/engine").UnfulfilledSnapshot<D, unknown> | import("@luvio/engine").StaleSnapshot<D, unknown> | import("@luvio/engine").PendingSnapshot<D, unknown> | Promise<Snapshot<D, unknown>>;
@@ -1,2 +1,6 @@
1
+ export { buildCacheAndNetworkImplementation } from './cache-and-network';
1
2
  export { buildCacheThenNetworkImplementation } from './cache-then-network';
3
+ export { buildNoCacheImplementation } from './no-cache';
4
+ export { buildOnlyIfCachedImplementation } from './only-if-cached';
2
5
  export { buildStaleWhileRevalidateImplementation } from './stale-while-revalidate';
6
+ export { buildValidAtImplementation } from './valid-at';
@@ -0,0 +1,3 @@
1
+ import { CachePolicyImplementationArgs, Snapshot } from '@luvio/engine';
2
+ import { DurableCachePolicyFunctions } from './utils';
3
+ export declare function buildNoCacheImplementation(funcs: DurableCachePolicyFunctions): <C, D>(args: CachePolicyImplementationArgs<C, D>) => import("@luvio/engine").ErrorSnapshot | import("@luvio/engine").FulfilledSnapshot<D, unknown> | import("@luvio/engine").UnfulfilledSnapshot<D, unknown> | import("@luvio/engine").StaleSnapshot<D, unknown> | import("@luvio/engine").PendingSnapshot<D, unknown> | Promise<Snapshot<D, unknown>>;
@@ -0,0 +1,3 @@
1
+ import { CachePolicyImplementationArgs, ErrorSnapshot, Snapshot } from '@luvio/engine';
2
+ import { DurableCachePolicyFunctions } from './utils';
3
+ export declare function buildOnlyIfCachedImplementation(funcs: DurableCachePolicyFunctions): <C, D>(args: CachePolicyImplementationArgs<C, D>) => ErrorSnapshot | import("@luvio/engine").FulfilledSnapshot<D, unknown> | import("@luvio/engine").UnfulfilledSnapshot<D, unknown> | import("@luvio/engine").StaleSnapshot<D, unknown> | import("@luvio/engine").PendingSnapshot<D, unknown> | Promise<Snapshot<D, unknown>>;
@@ -1,3 +1,3 @@
1
1
  import { CachePolicyImplementationArgs, Snapshot } from '@luvio/engine';
2
2
  import { DurableCachePolicyFunctions } from './utils';
3
- export declare function buildStaleWhileRevalidateImplementation(funcs: DurableCachePolicyFunctions, staleDuration: number): <C, D>(args: CachePolicyImplementationArgs<C, D>) => import("@luvio/engine").ErrorSnapshot | import("@luvio/engine").FulfilledSnapshot<D, unknown> | import("@luvio/engine").UnfulfilledSnapshot<D, unknown> | import("@luvio/engine").StaleSnapshot<D, unknown> | import("@luvio/engine").PendingSnapshot<D, unknown> | Promise<Snapshot<D, unknown>>;
3
+ export declare function buildStaleWhileRevalidateImplementation(funcs: DurableCachePolicyFunctions, staleDurationSeconds: number): <C, D>(args: CachePolicyImplementationArgs<C, D>) => import("@luvio/engine").ErrorSnapshot | import("@luvio/engine").FulfilledSnapshot<D, unknown> | import("@luvio/engine").UnfulfilledSnapshot<D, unknown> | import("@luvio/engine").StaleSnapshot<D, unknown> | import("@luvio/engine").PendingSnapshot<D, unknown> | Promise<Snapshot<D, unknown>>;
@@ -3,5 +3,5 @@ export declare type DurableCachePolicyFunctions = {
3
3
  validateNotDisposed: () => void;
4
4
  reviveSnapshotWithCachePolicy: <D, V>(unavailableSnapshot: UnAvailableSnapshot<D, V>, storeLookup: StoreLookup<D, V>) => Promise<Snapshot<D, V>>;
5
5
  };
6
- export declare function buildTTLStrategy(staleDuration?: number): TTLStrategy;
6
+ export declare function buildTTLStrategy(staleDurationMilliseconds?: number): TTLStrategy;
7
7
  export declare function appendTTLStrategy<C, D>(storeLookup: CachePolicyImplementationArgs<C, D>['storeLookup'], ttlStrategy: TTLStrategy): Parameters<BuildInMemorySnapshot<C, D>>[1];
@@ -0,0 +1,3 @@
1
+ import { CachePolicyImplementation, CachePolicyImplementationArgs, Snapshot } from '@luvio/engine';
2
+ import { DurableCachePolicyFunctions } from './utils';
3
+ export declare function buildValidAtImplementation<C, D>(funcs: DurableCachePolicyFunctions, basePolicyImplementation: CachePolicyImplementation<C, D>, timestamp: number): (args: CachePolicyImplementationArgs<C, D>) => Snapshot<D> | Promise<Snapshot<D>>;
@@ -54,13 +54,14 @@
54
54
  var keys = Object.keys, create = Object.create, assign = Object.assign, freeze = Object.freeze;
55
55
  var isArray = Array.isArray;
56
56
 
57
- function buildTTLStrategy(staleDuration) {
58
- if (staleDuration === void 0) { staleDuration = 0; }
57
+ function buildTTLStrategy(staleDurationMilliseconds) {
58
+ if (staleDurationMilliseconds === void 0) { staleDurationMilliseconds = 0; }
59
59
  return function (timestamp, metadata, valueIsError) {
60
60
  if (metadata !== undefined) {
61
61
  var expirationTimestamp = metadata.expirationTimestamp;
62
62
  if (timestamp > expirationTimestamp) {
63
- if (timestamp <= expirationTimestamp + staleDuration && valueIsError !== true) {
63
+ if (timestamp <= expirationTimestamp + staleDurationMilliseconds &&
64
+ valueIsError !== true) {
64
65
  return engine.StoreResolveResultState.Stale;
65
66
  }
66
67
  return engine.StoreResolveResultState.NotPresent;
@@ -78,6 +79,61 @@
78
79
  };
79
80
  }
80
81
 
82
+ function buildCacheAndNetworkImplementation(funcs, staleDurationSeconds) {
83
+ return function (args) {
84
+ funcs.validateNotDisposed();
85
+ var buildInMemorySnapshot = args.buildInMemorySnapshot, buildNetworkSnapshot = args.buildNetworkSnapshot, buildSnapshotContext = args.buildSnapshotContext, dispatchResourceRequest = args.dispatchResourceRequest, storeLookup = args.storeLookup;
86
+ var staleDurationMilliseconds = staleDurationSeconds === undefined ? undefined : staleDurationSeconds * 1000;
87
+ var cachePolicyStoreLookup = appendTTLStrategy(storeLookup, buildTTLStrategy(staleDurationMilliseconds));
88
+ var snapshot = buildInMemorySnapshot(buildSnapshotContext, cachePolicyStoreLookup);
89
+ if (snapshot !== undefined) {
90
+ // data found in L1 cache
91
+ if (snapshot.state === 'Fulfilled' || snapshot.state === 'Error') {
92
+ // kick off network request, do not await it
93
+ buildNetworkSnapshot(buildSnapshotContext, dispatchResourceRequest);
94
+ // return the cached snapshot to caller
95
+ return snapshot;
96
+ }
97
+ // stale data found in L1 cache
98
+ if (snapshot.state === 'Stale') {
99
+ // kick off network request, do not await it
100
+ // offline environment is already doing this; uncomment once we get rid of offline environment
101
+ // buildNetworkSnapshot(buildSnapshotContext, dispatchResourceRequest);
102
+ // return the cached snapshot to caller
103
+ return snapshot;
104
+ }
105
+ // if unfulfilled we have enough info to do an L2 lookup
106
+ if (snapshot.state === 'Unfulfilled') {
107
+ return funcs
108
+ .reviveSnapshotWithCachePolicy(snapshot, cachePolicyStoreLookup)
109
+ .then(function (revivedSnapshot) {
110
+ // data found in L2 cache
111
+ if (revivedSnapshot.state === 'Fulfilled' ||
112
+ revivedSnapshot.state === 'Error') {
113
+ // kick off network request, do not await it
114
+ buildNetworkSnapshot(buildSnapshotContext, dispatchResourceRequest);
115
+ // return the L2 cached snapshot to caller
116
+ return revivedSnapshot;
117
+ }
118
+ if (revivedSnapshot.state === 'Pending') ;
119
+ // stale data found in L2 cache
120
+ if (revivedSnapshot.state === 'Stale') {
121
+ // kick off network request, do not await it
122
+ // offline environment is already doing this; uncomment once we get rid of offline environment
123
+ // buildNetworkSnapshot(buildSnapshotContext, dispatchResourceRequest);
124
+ // return the L2 cached snapshot to caller
125
+ return revivedSnapshot;
126
+ }
127
+ // data not found in L2 cache, go to the network
128
+ return buildNetworkSnapshot(buildSnapshotContext, dispatchResourceRequest);
129
+ });
130
+ }
131
+ if (snapshot.state === 'Pending') ;
132
+ }
133
+ return buildNetworkSnapshot(buildSnapshotContext, dispatchResourceRequest);
134
+ };
135
+ }
136
+
81
137
  function buildCacheThenNetworkImplementation(funcs) {
82
138
  return function (args) {
83
139
  funcs.validateNotDisposed();
@@ -109,11 +165,89 @@
109
165
  };
110
166
  }
111
167
 
112
- function buildStaleWhileRevalidateImplementation(funcs, staleDuration) {
168
+ function buildNoCacheImplementation(funcs) {
169
+ return function (args) {
170
+ funcs.validateNotDisposed();
171
+ var buildNetworkSnapshot = args.buildNetworkSnapshot, buildSnapshotContext = args.buildSnapshotContext, dispatchResourceRequest = args.dispatchResourceRequest;
172
+ return buildNetworkSnapshot(buildSnapshotContext, dispatchResourceRequest).then(function (snapshot) {
173
+ if (snapshot.state === 'Pending') ;
174
+ return snapshot;
175
+ });
176
+ };
177
+ }
178
+
179
+ function deepFreeze(value) {
180
+ // No need to freeze primitives
181
+ if (typeof value !== 'object' || value === null) {
182
+ return;
183
+ }
184
+ if (isArray(value)) {
185
+ for (var i = 0, len = value.length; i < len; i += 1) {
186
+ deepFreeze(value[i]);
187
+ }
188
+ }
189
+ else {
190
+ var keys$1 = keys(value);
191
+ for (var i = 0, len = keys$1.length; i < len; i += 1) {
192
+ deepFreeze(value[keys$1[i]]);
193
+ }
194
+ }
195
+ freeze(value);
196
+ }
197
+
198
+ // TODO[@W-10165595]: consolidate this code with the corresponding logic in the default environment's only-if-cached.ts
199
+ function buildNotCachedErrorSnapshot() {
200
+ var error = {
201
+ body: undefined,
202
+ headers: {},
203
+ ok: false,
204
+ status: engine.HttpStatusCode.GatewayTimeout,
205
+ statusText: 'Gateway Timeout',
206
+ };
207
+ deepFreeze(error);
208
+ return {
209
+ error: error,
210
+ state: 'Error',
211
+ data: undefined,
212
+ };
213
+ }
214
+ function buildOnlyIfCachedImplementation(funcs) {
215
+ return function (args) {
216
+ funcs.validateNotDisposed();
217
+ var buildInMemorySnapshot = args.buildInMemorySnapshot, buildSnapshotContext = args.buildSnapshotContext, storeLookup = args.storeLookup;
218
+ var cachePolicyStoreLookup = appendTTLStrategy(storeLookup, buildTTLStrategy());
219
+ var snapshot = buildInMemorySnapshot(buildSnapshotContext, cachePolicyStoreLookup);
220
+ if (snapshot !== undefined) {
221
+ // data found in L1 cache
222
+ if (snapshot.state === 'Fulfilled' || snapshot.state === 'Error') {
223
+ return snapshot;
224
+ }
225
+ // network request outstanding, data is not cached
226
+ if (snapshot.state === 'Pending') {
227
+ return buildNotCachedErrorSnapshot();
228
+ }
229
+ // data not found in L1 cache, try L2 cache
230
+ return funcs
231
+ .reviveSnapshotWithCachePolicy(snapshot, cachePolicyStoreLookup)
232
+ .then(function (revivedSnapshot) {
233
+ // data found in L2 cache
234
+ if (revivedSnapshot.state === 'Fulfilled' ||
235
+ revivedSnapshot.state === 'Error') {
236
+ return revivedSnapshot;
237
+ }
238
+ // data is not cached
239
+ return buildNotCachedErrorSnapshot();
240
+ });
241
+ }
242
+ return buildNotCachedErrorSnapshot();
243
+ };
244
+ }
245
+
246
+ function buildStaleWhileRevalidateImplementation(funcs, staleDurationSeconds) {
113
247
  return function (args) {
114
248
  funcs.validateNotDisposed();
115
249
  var buildInMemorySnapshot = args.buildInMemorySnapshot, buildNetworkSnapshot = args.buildNetworkSnapshot, buildSnapshotContext = args.buildSnapshotContext, dispatchResourceRequest = args.dispatchResourceRequest, storeLookup = args.storeLookup;
116
- var cachePolicyStoreLookup = appendTTLStrategy(storeLookup, buildTTLStrategy(staleDuration));
250
+ var cachePolicyStoreLookup = appendTTLStrategy(storeLookup, buildTTLStrategy(staleDurationSeconds * 1000));
117
251
  var snapshot = buildInMemorySnapshot(buildSnapshotContext, cachePolicyStoreLookup);
118
252
  if (snapshot !== undefined) {
119
253
  // data found in L1 cache
@@ -152,6 +286,27 @@
152
286
  };
153
287
  }
154
288
 
289
+ function buildValidAtImplementation(funcs, basePolicyImplementation, timestamp) {
290
+ return function validAtImplementation(args) {
291
+ funcs.validateNotDisposed();
292
+ // This somewhat convoluted code is used to force the basePolicyImplementation's
293
+ // TTLStrategy to use the the valid-at cache policy's timestamp. The flow goes:
294
+ //
295
+ // Environment.applyCachePolicy => validAtImplementation (this function) =>
296
+ // basePolicyImplementation => adapter's buildInMemorySnapshot =>
297
+ // basePolicyImplementation's storeLookup => validAtStoreLookup (below) =>
298
+ // Environment.applyCachePolicy's storeLookup => Store/Reader code =>
299
+ // valid-at TTLStrategy (below) =>
300
+ // basePolicyImplementation's TTLStrategy (with valid-at timestamp)
301
+ var validAtStoreLookup = function (sel, refresh, ttlStrategy) {
302
+ return args.storeLookup(sel, refresh, function (_readerTimestamp, metadata, valueIsError) { return ttlStrategy(timestamp, metadata, valueIsError); });
303
+ };
304
+ // let basePolicy make all the decisions, but have it use our storeLookup
305
+ // so we can override the timestamp passed to the basePolicy's TTLStrategy
306
+ return basePolicyImplementation(__assign(__assign({}, args), { storeLookup: validAtStoreLookup }));
307
+ };
308
+ }
309
+
155
310
  //Durable store error instrumentation key
156
311
  var DURABLE_STORE_ERROR = 'durable-store-error';
157
312
  /**
@@ -177,25 +332,6 @@
177
332
  };
178
333
  }
179
334
 
180
- function deepFreeze(value) {
181
- // No need to freeze primitives
182
- if (typeof value !== 'object' || value === null) {
183
- return;
184
- }
185
- if (isArray(value)) {
186
- for (var i = 0, len = value.length; i < len; i += 1) {
187
- deepFreeze(value[i]);
188
- }
189
- }
190
- else {
191
- var keys$1 = keys(value);
192
- for (var i = 0, len = keys$1.length; i < len; i += 1) {
193
- deepFreeze(value[keys$1[i]]);
194
- }
195
- }
196
- freeze(value);
197
- }
198
-
199
335
  var SELECTOR_PAGINATION_TOKEN = 'tokenDataKey';
200
336
  function isFragmentUnionSelection(sel) {
201
337
  return sel.union === true;
@@ -338,7 +474,9 @@
338
474
  * will refresh the snapshot from network, and then run the results from network
339
475
  * through L2 ingestion, returning the subsequent revived snapshot.
340
476
  */
341
- function reviveSnapshot(baseEnvironment, durableStore, unavailableSnapshot, durableStoreErrorHandler, buildL1Snapshot) {
477
+ function reviveSnapshot(baseEnvironment, durableStore,
478
+ // TODO [W-10165787]: We should only allow Unfulfilled snapshot be passed in
479
+ unavailableSnapshot, durableStoreErrorHandler, buildL1Snapshot) {
342
480
  var _a;
343
481
  var recordId = unavailableSnapshot.recordId, select = unavailableSnapshot.select, seenRecords = unavailableSnapshot.seenRecords, state = unavailableSnapshot.state;
344
482
  // L2 can only revive Unfulfilled snapshots that have a selector since they have the
@@ -840,18 +978,34 @@
840
978
  return buildStaleWhileRevalidateImplementation({
841
979
  validateNotDisposed: validateNotDisposed,
842
980
  reviveSnapshotWithCachePolicy: reviveSnapshotWithCachePolicy,
843
- },
844
- // TODO[@W-10077764]: remove staleDuration once Komaci switches to cache-then-network
845
- 'staleDurationSeconds' in cachePolicy
846
- ? cachePolicy.staleDurationSeconds
847
- : cachePolicy.staleDuration
848
- //cachePolicy.staleDurationSeconds
849
- );
981
+ }, cachePolicy.staleDurationSeconds);
982
+ case 'cache-and-network':
983
+ return buildCacheAndNetworkImplementation({
984
+ validateNotDisposed: validateNotDisposed,
985
+ reviveSnapshotWithCachePolicy: reviveSnapshotWithCachePolicy,
986
+ }, cachePolicy.staleDurationSeconds);
850
987
  case 'cache-then-network':
851
988
  return buildCacheThenNetworkImplementation({
852
989
  validateNotDisposed: validateNotDisposed,
853
990
  reviveSnapshotWithCachePolicy: reviveSnapshotWithCachePolicy,
854
991
  });
992
+ case 'no-cache':
993
+ return buildNoCacheImplementation({
994
+ validateNotDisposed: validateNotDisposed,
995
+ reviveSnapshotWithCachePolicy: reviveSnapshotWithCachePolicy,
996
+ });
997
+ case 'only-if-cached':
998
+ return buildOnlyIfCachedImplementation({
999
+ validateNotDisposed: validateNotDisposed,
1000
+ reviveSnapshotWithCachePolicy: reviveSnapshotWithCachePolicy,
1001
+ });
1002
+ case 'valid-at': {
1003
+ var basePolicy = resolveCachePolicy(cachePolicy.basePolicy);
1004
+ return buildValidAtImplementation({
1005
+ validateNotDisposed: validateNotDisposed,
1006
+ reviveSnapshotWithCachePolicy: reviveSnapshotWithCachePolicy,
1007
+ }, basePolicy, cachePolicy.timestamp);
1008
+ }
855
1009
  default: {
856
1010
  if (process.env.NODE_ENV !== 'production') {
857
1011
  throw new Error("unrecognized cache policy: " + JSON.stringify(cachePolicy));
@@ -860,9 +1014,9 @@
860
1014
  }
861
1015
  }
862
1016
  }
863
- var applyCachePolicy = function (cachePolicy, buildSnapshotContext, buildInMemorySnapshot, buildNetworkSnapshot) {
1017
+ var applyCachePolicy = function (adapterRequestContext, buildSnapshotContext, buildInMemorySnapshot, buildNetworkSnapshot) {
864
1018
  validateNotDisposed();
865
- var cachePolicyImpl = resolveCachePolicy(cachePolicy);
1019
+ var cachePolicyImpl = resolveCachePolicy(adapterRequestContext.cachePolicy);
866
1020
  var storeLookup = function (sel, refresh, ttlStrategy) { return environment.storeLookup(sel, environment.createSnapshot, refresh, ttlStrategy); };
867
1021
  var applyCachePolicy = function () {
868
1022
  return cachePolicyImpl({
@@ -0,0 +1,3 @@
1
+ import { CachePolicyImplementationArgs, Snapshot } from '@luvio/engine';
2
+ import { DurableCachePolicyFunctions } from './utils';
3
+ export declare function buildCacheAndNetworkImplementation(funcs: DurableCachePolicyFunctions, staleDurationSeconds?: number): <C, D>(args: CachePolicyImplementationArgs<C, D>) => import("@luvio/engine").ErrorSnapshot | import("@luvio/engine").FulfilledSnapshot<D, unknown> | import("@luvio/engine").UnfulfilledSnapshot<D, unknown> | import("@luvio/engine").StaleSnapshot<D, unknown> | import("@luvio/engine").PendingSnapshot<D, unknown> | Promise<Snapshot<D, unknown>>;
@@ -1,2 +1,6 @@
1
+ export { buildCacheAndNetworkImplementation } from './cache-and-network';
1
2
  export { buildCacheThenNetworkImplementation } from './cache-then-network';
3
+ export { buildNoCacheImplementation } from './no-cache';
4
+ export { buildOnlyIfCachedImplementation } from './only-if-cached';
2
5
  export { buildStaleWhileRevalidateImplementation } from './stale-while-revalidate';
6
+ export { buildValidAtImplementation } from './valid-at';
@@ -0,0 +1,3 @@
1
+ import { CachePolicyImplementationArgs, Snapshot } from '@luvio/engine';
2
+ import { DurableCachePolicyFunctions } from './utils';
3
+ export declare function buildNoCacheImplementation(funcs: DurableCachePolicyFunctions): <C, D>(args: CachePolicyImplementationArgs<C, D>) => import("@luvio/engine").ErrorSnapshot | import("@luvio/engine").FulfilledSnapshot<D, unknown> | import("@luvio/engine").UnfulfilledSnapshot<D, unknown> | import("@luvio/engine").StaleSnapshot<D, unknown> | import("@luvio/engine").PendingSnapshot<D, unknown> | Promise<Snapshot<D, unknown>>;
@@ -0,0 +1,3 @@
1
+ import { CachePolicyImplementationArgs, ErrorSnapshot, Snapshot } from '@luvio/engine';
2
+ import { DurableCachePolicyFunctions } from './utils';
3
+ export declare function buildOnlyIfCachedImplementation(funcs: DurableCachePolicyFunctions): <C, D>(args: CachePolicyImplementationArgs<C, D>) => ErrorSnapshot | import("@luvio/engine").FulfilledSnapshot<D, unknown> | import("@luvio/engine").UnfulfilledSnapshot<D, unknown> | import("@luvio/engine").StaleSnapshot<D, unknown> | import("@luvio/engine").PendingSnapshot<D, unknown> | Promise<Snapshot<D, unknown>>;
@@ -1,3 +1,3 @@
1
1
  import { CachePolicyImplementationArgs, Snapshot } from '@luvio/engine';
2
2
  import { DurableCachePolicyFunctions } from './utils';
3
- export declare function buildStaleWhileRevalidateImplementation(funcs: DurableCachePolicyFunctions, staleDuration: number): <C, D>(args: CachePolicyImplementationArgs<C, D>) => import("@luvio/engine").ErrorSnapshot | import("@luvio/engine").FulfilledSnapshot<D, unknown> | import("@luvio/engine").UnfulfilledSnapshot<D, unknown> | import("@luvio/engine").StaleSnapshot<D, unknown> | import("@luvio/engine").PendingSnapshot<D, unknown> | Promise<Snapshot<D, unknown>>;
3
+ export declare function buildStaleWhileRevalidateImplementation(funcs: DurableCachePolicyFunctions, staleDurationSeconds: number): <C, D>(args: CachePolicyImplementationArgs<C, D>) => import("@luvio/engine").ErrorSnapshot | import("@luvio/engine").FulfilledSnapshot<D, unknown> | import("@luvio/engine").UnfulfilledSnapshot<D, unknown> | import("@luvio/engine").StaleSnapshot<D, unknown> | import("@luvio/engine").PendingSnapshot<D, unknown> | Promise<Snapshot<D, unknown>>;
@@ -3,5 +3,5 @@ export declare type DurableCachePolicyFunctions = {
3
3
  validateNotDisposed: () => void;
4
4
  reviveSnapshotWithCachePolicy: <D, V>(unavailableSnapshot: UnAvailableSnapshot<D, V>, storeLookup: StoreLookup<D, V>) => Promise<Snapshot<D, V>>;
5
5
  };
6
- export declare function buildTTLStrategy(staleDuration?: number): TTLStrategy;
6
+ export declare function buildTTLStrategy(staleDurationMilliseconds?: number): TTLStrategy;
7
7
  export declare function appendTTLStrategy<C, D>(storeLookup: CachePolicyImplementationArgs<C, D>['storeLookup'], ttlStrategy: TTLStrategy): Parameters<BuildInMemorySnapshot<C, D>>[1];
@@ -0,0 +1,3 @@
1
+ import { CachePolicyImplementation, CachePolicyImplementationArgs, Snapshot } from '@luvio/engine';
2
+ import { DurableCachePolicyFunctions } from './utils';
3
+ export declare function buildValidAtImplementation<C, D>(funcs: DurableCachePolicyFunctions, basePolicyImplementation: CachePolicyImplementation<C, D>, timestamp: number): (args: CachePolicyImplementationArgs<C, D>) => Snapshot<D> | Promise<Snapshot<D>>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@luvio/environments",
3
- "version": "0.60.0",
3
+ "version": "0.62.0",
4
4
  "description": "Luvio Environments",
5
5
  "main": "dist/umd/es2018/environments.js",
6
6
  "module": "dist/es/es2018/environments.js",
@@ -10,12 +10,13 @@
10
10
  "clean": "rm -rf dist",
11
11
  "build": "rollup --config rollup.config.js",
12
12
  "watch": "yarn build --watch",
13
- "test": "jest"
13
+ "test": "jest",
14
+ "test:debug": "node --inspect-brk ../../../node_modules/jest/bin/jest.js --config ./jest.config.js --runInBand"
14
15
  },
15
16
  "files": [
16
17
  "dist/"
17
18
  ],
18
19
  "dependencies": {
19
- "@luvio/engine": "0.60.0"
20
+ "@luvio/engine": "0.62.0"
20
21
  }
21
22
  }