@conduit-client/command-cache-control 2.0.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.
package/LICENSE.txt ADDED
@@ -0,0 +1,27 @@
1
+ *Attorney/Client Privileged + Confidential*
2
+
3
+ Terms of Use for Public Code (Non-OSS)
4
+
5
+ *NOTE:* Before publishing code under this license/these Terms of Use, please review https://salesforce.quip.com/WFfvAMKB18AL and confirm that you’ve completed all prerequisites described therein. *These Terms of Use may not be used or modified without input from IP and Product Legal.*
6
+
7
+ *Terms of Use*
8
+
9
+ Copyright 2022 Salesforce, Inc. All rights reserved.
10
+
11
+ These Terms of Use govern the download, installation, and/or use of this software provided by Salesforce, Inc. (“Salesforce”) (the “Software”), were last updated on April 15, 2022, ** and constitute a legally binding agreement between you and Salesforce. If you do not agree to these Terms of Use, do not install or use the Software.
12
+
13
+ Salesforce grants you a worldwide, non-exclusive, no-charge, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute the Software and derivative works subject to these Terms. These Terms shall be included in all copies or substantial portions of the Software.
14
+
15
+ Subject to the limited rights expressly granted hereunder, Salesforce reserves all rights, title, and interest in and to all intellectual property subsisting in the Software. No rights are granted to you hereunder other than as expressly set forth herein. Users residing in countries on the United States Office of Foreign Assets Control sanction list, or which are otherwise subject to a US export embargo, may not use the Software.
16
+
17
+ Implementation of the Software may require development work, for which you are responsible. The Software may contain bugs, errors and incompatibilities and is made available on an AS IS basis without support, updates, or service level commitments.
18
+
19
+ Salesforce reserves the right at any time to modify, suspend, or discontinue, the Software (or any part thereof) with or without notice. You agree that Salesforce shall not be liable to you or to any third party for any modification, suspension, or discontinuance.
20
+
21
+ You agree to defend Salesforce against any claim, demand, suit or proceeding made or brought against Salesforce by a third party arising out of or accruing from (a) your use of the Software, and (b) any application you develop with the Software that infringes any copyright, trademark, trade secret, trade dress, patent, or other intellectual property right of any person or defames any person or violates their rights of publicity or privacy (each a “Claim Against Salesforce”), and will indemnify Salesforce from any damages, attorney fees, and costs finally awarded against Salesforce as a result of, or for any amounts paid by Salesforce under a settlement approved by you in writing of, a Claim Against Salesforce, provided Salesforce (x) promptly gives you written notice of the Claim Against Salesforce, (y) gives you sole control of the defense and settlement of the Claim Against Salesforce (except that you may not settle any Claim Against Salesforce unless it unconditionally releases Salesforce of all liability), and (z) gives you all reasonable assistance, at your expense.
22
+
23
+ WITHOUT LIMITING THE GENERALITY OF THE FOREGOING, THE SOFTWARE IS NOT SUPPORTED AND IS PROVIDED "AS IS," WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. IN NO EVENT SHALL SALESFORCE HAVE ANY LIABILITY FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, INDIRECT, SPECIAL, INCIDENTAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES, OR DAMAGES BASED ON LOST PROFITS, DATA, OR USE, IN CONNECTION WITH THE SOFTWARE, HOWEVER CAUSED AND WHETHER IN CONTRACT, TORT, OR UNDER ANY OTHER THEORY OF LIABILITY, WHETHER OR NOT YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
24
+
25
+ These Terms of Use shall be governed exclusively by the internal laws of the State of California, without regard to its conflicts of laws rules. Each party hereby consents to the exclusive jurisdiction of the state and federal courts located in San Francisco County, California to adjudicate any dispute arising out of or relating to these Terms of Use and the download, installation, and/or use of the Software. Except as expressly stated herein, these Terms of Use constitute the entire agreement between the parties, and supersede all prior and contemporaneous agreements, proposals, or representations, written or oral, concerning their subject matter. No modification, amendment, or waiver of any provision of these Terms of Use shall be effective unless it is by an update to these Terms of Use that Salesforce makes available, or is in writing and signed by the party against whom the modification, amendment, or waiver is to be asserted.
26
+
27
+ _*Data Privacy*_: Salesforce may collect, process, and store device, system, and other information related to your use of the Software. This information includes, but is not limited to, IP address, user metrics, and other data (“Usage Data”). Salesforce may use Usage Data for analytics, product development, and marketing purposes. You acknowledge that files generated in conjunction with the Software may contain sensitive or confidential data, and you are solely responsible for anonymizing and protecting such data.
package/README.md ADDED
@@ -0,0 +1 @@
1
+ This software is provided as-is with no support provided.
@@ -0,0 +1,6 @@
1
+ /*!
2
+ * Copyright (c) 2022, Salesforce, Inc.,
3
+ * All rights reserved.
4
+ * For full license text, see the LICENSE.txt file
5
+ */
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;"}
File without changes
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,12 @@
1
+ import { DurableCacheInclusionPolicy } from '@conduit-client/service-cache-inclusion-policy/v1';
2
+ import { Cache } from '@conduit-client/service-cache/v1';
3
+ import { Key } from '@conduit-client/service-cache/v1';
4
+ import { SyncOrAsync } from '@conduit-client/utils';
5
+ export declare class TestDurableCacheInclusionPolicy extends DurableCacheInclusionPolicy {
6
+ l2: Cache;
7
+ constructor();
8
+ revive(keys: Set<Key>, l1: Cache): SyncOrAsync<Set<Key>>;
9
+ syncToL2Cache(updatedKeys: Set<Key>, l1: Cache): SyncOrAsync<void>;
10
+ find: import("vitest").Mock<(...args: any[]) => any>;
11
+ findAndModify: import("vitest").Mock<(...args: any[]) => any>;
12
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,297 @@
1
+ import { BaseCommand } from '@conduit-client/command-base/v1';
2
+ import { type NamedPubSubService } from '@conduit-client/service-pubsub/v1';
3
+ import type { Callback, Result, Unsubscribe, SubscribableResult, SyncOrAsync, RefreshResult } from '@conduit-client/utils';
4
+ import type { Cache, ReadonlyCache } from '@conduit-client/service-cache/v1';
5
+ import type { CacheControlStrategyConfig, NamedCacheControllerService } from '@conduit-client/service-cache-control/v1';
6
+ import { InstrumentationAttributes } from '@conduit-client/service-instrumentation/v1';
7
+ import { CacheControlRequestRunner } from './cache-control-request-runner';
8
+ type ExecuteOverrides = {
9
+ now?: number;
10
+ cacheControlConfig?: Omit<CacheControlStrategyConfig, 'now'>;
11
+ };
12
+ /**
13
+ * An implementation of BaseCommand that allows for extending abstract cache methods
14
+ *
15
+ * @typeParam Data cache result for read operations
16
+ * @typeParam NetworkResult cache result including network metadata
17
+ * @typeParam ExtraServices additional named services needed by a subclass
18
+ */
19
+ export declare abstract class CacheControlCommand<ReturnData, NetworkData, ExtraServices extends object = object> extends BaseCommand<SyncOrAsync<SubscribableResult<ReturnData, Error>>> {
20
+ protected services: NamedCacheControllerService & Partial<NamedPubSubService> & ExtraServices;
21
+ instantiationTime: number;
22
+ instrumentationAttributes?: InstrumentationAttributes;
23
+ private keysUsed;
24
+ private keysUpdated;
25
+ private _isInternalExecution;
26
+ get isInternalExecution(): boolean;
27
+ /**
28
+ * Creates a new CacheControlCommand instance
29
+ *
30
+ * @param services - Required services including cache controller and optional pub/sub service
31
+ * @param services.cacheController - The cache controller service for managing cache operations
32
+ * @param services.pubSub - Optional pub/sub service for cache invalidation notifications
33
+ * @param services - Additional services specific to the implementation
34
+ */
35
+ constructor(services: NamedCacheControllerService & Partial<NamedPubSubService> & ExtraServices);
36
+ /**
37
+ * Executes the cache control command with optional overrides
38
+ *
39
+ * This method orchestrates the cache control flow by:
40
+ * 1. Clearing any existing subscriptions
41
+ * 2. Merging configuration overrides with the base strategy config
42
+ * 3. Building a request runner for cache operations
43
+ * 4. Executing the cache controller with the request runner
44
+ * 5. Handling the result and setting up subscriptions if needed
45
+ *
46
+ * @param overrides - Optional execution overrides including timestamp and cache control config
47
+ * @param overrides.now - Override the current timestamp for cache control calculations
48
+ * @param overrides.cacheControlConfig - Override cache control strategy configuration
49
+ * @returns A subscribable result containing either the cached/network data or an error
50
+ */
51
+ execute(overrides?: ExecuteOverrides): SyncOrAsync<SubscribableResult<ReturnData, Error>>;
52
+ /**
53
+ * Handles the result from the cache controller and builds the appropriate subscribable result
54
+ *
55
+ * This method processes the cache controller execution result and determines the appropriate
56
+ * response based on network errors, cache errors, and available data. It handles graceful
57
+ * degradation scenarios where network data is available even when cache operations fail.
58
+ *
59
+ * @param result - The result from the cache controller execution
60
+ * @param requestRunner - The request runner containing network data and errors
61
+ * @returns A subscribable result with the appropriate data or error
62
+ */
63
+ handleCacheControllerResult(result: Result<void, Error>, requestRunner: CacheControlRequestRunner<ReturnData, NetworkData>): SyncOrAsync<SubscribableResult<ReturnData, Error>>;
64
+ /**
65
+ * Builds a request runner that orchestrates cache read, network request, and cache write operations
66
+ *
67
+ * The request runner encapsulates the three main operations:
68
+ * 1. Reading from cache with subscription setup
69
+ * 2. Requesting data from the network
70
+ * 3. Writing network results to cache and recording keys
71
+ *
72
+ * @returns A configured request runner for the cache controller
73
+ */
74
+ protected buildRequestRunner(): CacheControlRequestRunner<ReturnData, NetworkData>;
75
+ /**
76
+ * Publishes cache update events for keys that were modified during the operation
77
+ *
78
+ * This method notifies other parts of the system about cache changes by publishing
79
+ * a 'cacheUpdate' event with the set of keys that were updated. This enables
80
+ * cache invalidation and reactive updates across the application.
81
+ *
82
+ * @returns A promise that resolves when the update event is published (or immediately if no pub/sub service)
83
+ */
84
+ protected publishUpdatedKeys(): SyncOrAsync<void>;
85
+ protected get operationType(): 'query' | 'mutation';
86
+ protected lastResult: {
87
+ type: 'data';
88
+ data: ReturnData;
89
+ } | {
90
+ type: 'error';
91
+ error: Error;
92
+ } | undefined;
93
+ /**
94
+ * Subscribes to cache update and invalidation events for reactive updates
95
+ *
96
+ * This method sets up subscriptions to listen for changes that affect the data returned
97
+ * by this Command.
98
+ *
99
+ * By default, it subscribes to two types of events on the PubSub service:
100
+ * - 'cacheUpdate': Triggers a rebuild with the original instantiation time
101
+ * - 'cacheInvalidation': Triggers a full refresh without time constraints
102
+ *
103
+ * This method can be extended by subclasses to add additional subscriptions.
104
+ *
105
+ * Note: ALL subscriptions should push an unsubscribe function to the unsubscribers array,
106
+ * for the lifecycle to work correctly and avoid memory leaks.
107
+ */
108
+ protected subscribe(): void;
109
+ /**
110
+ * Unsubscribes from all stored subscriptions
111
+ *
112
+ * This method calls all stored unsubscribe functions to clean up event listeners
113
+ * and prevent memory leaks. It should be called when the command is no longer
114
+ * needed and is also called before setting up new subscriptions.
115
+ */
116
+ private unsubscribe;
117
+ protected unsubscribers: Unsubscribe[];
118
+ protected subscriptions: Callback<Result<ReturnData, Error>>[];
119
+ protected abstract readonly cacheControlStrategyConfig: CacheControlStrategyConfig;
120
+ /**
121
+ * Compares two result values for equality to determine if a cache update should trigger a rerun
122
+ *
123
+ * This method is used to prevent unnecessary reruns when the cached data hasn't actually changed.
124
+ * The default implementation uses deep equality comparison, but subclasses can override this
125
+ * to provide more efficient or domain-specific comparison logic.
126
+ *
127
+ * @param result1 - The first result to compare
128
+ * @param result2 - The second result to compare
129
+ * @returns True if the results are equal, false otherwise
130
+ *
131
+ * @todo This should likely be abstract in v2. For v1, provide default comparison logic.
132
+ */
133
+ protected equals(result1: ReturnData | undefined, result2: ReturnData | undefined): boolean;
134
+ /**
135
+ * Reads from the cache and returns the cache result or error
136
+ *
137
+ * In case of a missing or partial result, this should return either a DataNotFoundError or
138
+ * DataIncompleteError, respectively, in the Error response
139
+ *
140
+ * Note that any subclass should JUST try to read the data from the cache here; it should
141
+ * NOT try to take metadata (eg cache control semantics) into account while reading the
142
+ * data. The CacheController is responsible for enforcing those semantics.
143
+ *
144
+ * @param cache source of cached data
145
+ * @returns result or error from the cache
146
+ */
147
+ abstract readFromCache(cache: ReadonlyCache): SyncOrAsync<Result<ReturnData, Error>>;
148
+ /**
149
+ * Requests the resource from network, returning the result along with network metadata
150
+ *
151
+ * @returns network result including metadata
152
+ */
153
+ abstract requestFromNetwork(): SyncOrAsync<Result<NetworkData, Error>>;
154
+ /**
155
+ * Writes the given result to the cache
156
+ *
157
+ * @param cache source of cached data
158
+ * @param networkResult network result with metadata to write to the cache. This param will
159
+ * be the result of a previous requestFromNetwork call, if the CacheController determines
160
+ * that the data should be cached
161
+ */
162
+ abstract writeToCache(cache: Cache, networkResult: Result<NetworkData, Error>): SyncOrAsync<void>;
163
+ /**
164
+ * Hook method called after a network request completes
165
+ *
166
+ * This method provides a point for subclasses to perform post-request operations
167
+ * such as logging, metrics collection, or cleanup. The default implementation
168
+ * is empty and can be overridden by subclasses as needed.
169
+ *
170
+ * @param _options - Request completion options
171
+ * @param _options.statusCode - HTTP status code from the network response
172
+ */
173
+ afterRequestHooks(_options: {
174
+ statusCode: number;
175
+ }): Promise<void>;
176
+ /**
177
+ * Forces a refresh of the cached data by bypassing cache and fetching from network
178
+ *
179
+ * This method executes the command with a "no-cache" configuration, ensuring that
180
+ * fresh data is fetched from the network regardless of cache state. It's useful
181
+ * for scenarios where you need to ensure the most up-to-date data.
182
+ *
183
+ * @returns A refresh result indicating success or failure of the refresh operation
184
+ */
185
+ refresh(): SyncOrAsync<RefreshResult<Error>>;
186
+ /**
187
+ * Writes network result to cache and records the keys that were updated
188
+ *
189
+ * This method wraps the cache write operation with key tracking functionality.
190
+ * It uses a recordable cache wrapper to capture which keys are modified during
191
+ * the write operation, then updates the internal tracking of used and updated keys.
192
+ *
193
+ * @param cache - The cache instance to write to
194
+ * @param networkResult - The network result containing data to write to cache
195
+ * @returns A result indicating success or failure of the write operation
196
+ */
197
+ writeToCacheAndRecordKeys(cache: Cache, networkResult: Result<NetworkData, Error>): SyncOrAsync<Result<void, Error>>;
198
+ /**
199
+ * Builds a subscribable result by reading from cache and setting up subscriptions
200
+ *
201
+ * This method reads data from the cache and wraps the result in a subscribable
202
+ * structure that allows consumers to subscribe to updates. It also tracks which
203
+ * cache keys were read for future invalidation purposes.
204
+ *
205
+ * @param cache - The readonly cache to read from
206
+ * @returns A subscribable result containing the cached data or error
207
+ */
208
+ buildResultWithSubscribe(cache: ReadonlyCache): PromiseLike<import("@conduit-client/utils").Ok<{
209
+ subscribe(callback: Callback<Result<any, any>>): Unsubscribe;
210
+ refresh(): SyncOrAsync<RefreshResult<any>>;
211
+ } & {
212
+ data: any;
213
+ }, {
214
+ subscribe(callback: Callback<Result<any, any>>): Unsubscribe;
215
+ refresh(): SyncOrAsync<RefreshResult<any>>;
216
+ } & {
217
+ failure: any;
218
+ }> | import("@conduit-client/utils").Err<{
219
+ subscribe(callback: Callback<Result<any, any>>): Unsubscribe;
220
+ refresh(): SyncOrAsync<RefreshResult<any>>;
221
+ } & {
222
+ data: any;
223
+ }, {
224
+ subscribe(callback: Callback<Result<any, any>>): Unsubscribe;
225
+ refresh(): SyncOrAsync<RefreshResult<any>>;
226
+ } & {
227
+ failure: any;
228
+ }>>;
229
+ /**
230
+ * Builds a function that subscribes to cache changes via the pubsub service. Whenever
231
+ * relevant cache updates occur, it re-reads the data and compares it against
232
+ * the last known value. If a change is detected, the provided
233
+ * callback is invoked.
234
+ *
235
+ * @returns an unsubscribe function to stop watching for updates
236
+ */
237
+ protected buildSubscribe(): (consumerCallback: Callback<Result<ReturnData, Error>>) => Unsubscribe;
238
+ /**
239
+ * Re-runs the command execution with optional overrides and notifies subscribers of changes
240
+ *
241
+ * This method is called internally when cache updates occur that affect the command's data.
242
+ * It executes the command with the provided overrides and compares the result with the
243
+ * last known result. If the data has changed, it notifies all subscribers with the new data.
244
+ *
245
+ * The method handles deduplication to prevent unnecessary notifications when the data
246
+ * hasn't actually changed, and properly manages the internal execution state.
247
+ *
248
+ * @param overrides - Optional execution overrides for the rerun
249
+ * @returns A promise that resolves to the execution result
250
+ */
251
+ protected rerun(overrides?: ExecuteOverrides): PromiseLike<import("@conduit-client/utils").Ok<{
252
+ subscribe(callback: Callback<Result<ReturnData, Error>>): Unsubscribe;
253
+ refresh(): SyncOrAsync<RefreshResult<Error>>;
254
+ } & {
255
+ data: ReturnData;
256
+ }, {
257
+ subscribe(callback: Callback<Result<ReturnData, Error>>): Unsubscribe;
258
+ refresh(): SyncOrAsync<RefreshResult<Error>>;
259
+ } & {
260
+ failure: Error;
261
+ }> | import("@conduit-client/utils").Err<{
262
+ subscribe(callback: Callback<Result<ReturnData, Error>>): Unsubscribe;
263
+ refresh(): SyncOrAsync<RefreshResult<Error>>;
264
+ } & {
265
+ data: ReturnData;
266
+ }, {
267
+ subscribe(callback: Callback<Result<ReturnData, Error>>): Unsubscribe;
268
+ refresh(): SyncOrAsync<RefreshResult<Error>>;
269
+ } & {
270
+ failure: Error;
271
+ }>>;
272
+ /**
273
+ * Invokes all registered consumer callbacks with the provided data
274
+ *
275
+ * This private method safely calls all registered subscriber callbacks with the
276
+ * provided result data. It includes error handling to prevent callback failures
277
+ * from affecting other callbacks or the overall system.
278
+ *
279
+ * @param data - The result data to send to all subscribers
280
+ */
281
+ private invokeConsumerCallbacks;
282
+ }
283
+ /**
284
+ * Merges a base cache control strategy configuration with execution overrides
285
+ *
286
+ * This utility function combines a base cache control configuration with optional
287
+ * execution overrides, handling the merging of nested configuration properties
288
+ * and ensuring that override values take precedence over base values.
289
+ *
290
+ * @param baseConfig - The base cache control strategy configuration
291
+ * @param overrides - Optional execution overrides to merge with the base config
292
+ * @param overrides.now - Override timestamp for cache control calculations
293
+ * @param overrides.cacheControlConfig - Override cache control strategy configuration
294
+ * @returns A merged cache control strategy configuration
295
+ */
296
+ export declare function mergeCacheControlConfigs(baseConfig: CacheControlStrategyConfig, overrides?: ExecuteOverrides): CacheControlStrategyConfig;
297
+ export {};
@@ -0,0 +1,14 @@
1
+ import { Cache, ReadonlyCache } from '@conduit-client/service-cache/v1';
2
+ import { Err, Ok, Result, SubscribableResult, SyncOrAsync } from '@conduit-client/utils';
3
+ export declare class CacheControlRequestRunner<ReturnData, NetworkData> {
4
+ returnData: SubscribableResult<ReturnData, Error> | undefined;
5
+ networkData: Ok<NetworkData, Error> | undefined;
6
+ networkError: Err<never, Error> | undefined;
7
+ readFromCacheInternal: (cache: ReadonlyCache) => SyncOrAsync<SubscribableResult<ReturnData, Error>>;
8
+ requestFromNetworkInternal: () => SyncOrAsync<Result<NetworkData, Error>>;
9
+ writeToCacheInternal: (cache: Cache, networkResult: Result<NetworkData, Error>) => SyncOrAsync<Result<void, Error>>;
10
+ constructor(readFromCache: (cache: ReadonlyCache) => SyncOrAsync<SubscribableResult<ReturnData, Error>>, requestFromNetwork: () => SyncOrAsync<Result<NetworkData, Error>>, writeToCache: (cache: Cache, networkResult: Result<NetworkData, Error>) => SyncOrAsync<Result<void, Error>>);
11
+ readFromCache(cache: ReadonlyCache): PromiseLike<Err<never, Error> | Ok<undefined, never>>;
12
+ requestFromNetwork(): AsyncGenerator<Result<NetworkData, Error>, void, unknown>;
13
+ writeToCache(cache: Cache, networkResult: Result<NetworkData, Error>): SyncOrAsync<Result<void, Error>>;
14
+ }
@@ -0,0 +1,6 @@
1
+ import { type ServiceDescriptor } from '@conduit-client/utils';
2
+ import { CacheControlCommand } from './cache-control-command';
3
+ export { CacheControlCommand } from './cache-control-command';
4
+ export { CacheControlRequestRunner } from './cache-control-request-runner';
5
+ export type CacheControlCommandServiceDescriptor = ServiceDescriptor<typeof CacheControlCommand, 'cacheControlCommandBaseClass', '1.0'>;
6
+ export declare function buildServiceDescriptor(): CacheControlCommandServiceDescriptor;
@@ -0,0 +1,2 @@
1
+ import type { Key } from '@conduit-client/service-cache/v1';
2
+ export declare function setOverlaps(setA: Set<Key>, setB: Set<Key>): boolean;
@@ -0,0 +1,558 @@
1
+ /*!
2
+ * Copyright (c) 2022, Salesforce, Inc.,
3
+ * All rights reserved.
4
+ * For full license text, see the LICENSE.txt file
5
+ */
6
+ import { BaseCommand } from "@conduit-client/command-base/v1";
7
+ /*!
8
+ * Copyright (c) 2022, Salesforce, Inc.,
9
+ * All rights reserved.
10
+ * For full license text, see the LICENSE.txt file
11
+ */
12
+ const { isArray } = Array;
13
+ class Ok {
14
+ constructor(value) {
15
+ this.value = value;
16
+ }
17
+ isOk() {
18
+ return true;
19
+ }
20
+ isErr() {
21
+ return !this.isOk();
22
+ }
23
+ }
24
+ class Err {
25
+ constructor(error) {
26
+ this.error = error;
27
+ }
28
+ isOk() {
29
+ return false;
30
+ }
31
+ isErr() {
32
+ return !this.isOk();
33
+ }
34
+ }
35
+ const ok = (value) => new Ok(value);
36
+ const err = (err2) => new Err(err2);
37
+ function buildSubscribableResult(result, subscribe, refresh) {
38
+ if (result.isOk()) {
39
+ return ok({ data: result.value, subscribe, refresh });
40
+ } else {
41
+ return err({ failure: result.error, subscribe, refresh });
42
+ }
43
+ }
44
+ function resolvedPromiseLike(result) {
45
+ if (isPromiseLike(result)) {
46
+ return result.then((nextResult) => nextResult);
47
+ }
48
+ return {
49
+ then: (onFulfilled, _onRejected) => {
50
+ try {
51
+ return resolvedPromiseLike(onFulfilled(result));
52
+ } catch (e) {
53
+ if (onFulfilled === void 0) {
54
+ return resolvedPromiseLike(result);
55
+ }
56
+ return rejectedPromiseLike(e);
57
+ }
58
+ }
59
+ };
60
+ }
61
+ function rejectedPromiseLike(reason) {
62
+ if (isPromiseLike(reason)) {
63
+ return reason.then((nextResult) => nextResult);
64
+ }
65
+ return {
66
+ then: (_onFulfilled, onRejected) => {
67
+ if (typeof onRejected === "function") {
68
+ try {
69
+ return resolvedPromiseLike(onRejected(reason));
70
+ } catch (e) {
71
+ return rejectedPromiseLike(e);
72
+ }
73
+ }
74
+ return rejectedPromiseLike(reason);
75
+ }
76
+ };
77
+ }
78
+ function isPromiseLike(x) {
79
+ return typeof (x == null ? void 0 : x.then) === "function";
80
+ }
81
+ function deepEquals(x, y) {
82
+ if (x === void 0) {
83
+ return y === void 0;
84
+ } else if (x === null) {
85
+ return y === null;
86
+ } else if (y === null) {
87
+ return x === null;
88
+ } else if (isArray(x)) {
89
+ if (!isArray(y) || x.length !== y.length) {
90
+ return false;
91
+ }
92
+ for (let i = 0; i < x.length; ++i) {
93
+ if (!deepEquals(x[i], y[i])) {
94
+ return false;
95
+ }
96
+ }
97
+ return true;
98
+ } else if (typeof x === "object") {
99
+ if (typeof y !== "object") {
100
+ return false;
101
+ }
102
+ const xkeys = Object.keys(x);
103
+ const ykeys = Object.keys(y);
104
+ if (xkeys.length !== ykeys.length) {
105
+ return false;
106
+ }
107
+ for (let i = 0; i < xkeys.length; ++i) {
108
+ const key = xkeys[i];
109
+ if (!deepEquals(x[key], y[key])) {
110
+ return false;
111
+ }
112
+ }
113
+ return true;
114
+ }
115
+ return x === y;
116
+ }
117
+ function setOverlaps(setA, setB) {
118
+ if (setA.size > setB.size) {
119
+ for (const key of setB.keys()) {
120
+ if (setA.has(key)) {
121
+ return true;
122
+ }
123
+ }
124
+ } else {
125
+ for (const key of setA) {
126
+ if (setB.has(key)) {
127
+ return true;
128
+ }
129
+ }
130
+ }
131
+ return false;
132
+ }
133
+ class CacheControlRequestRunner {
134
+ constructor(readFromCache, requestFromNetwork, writeToCache) {
135
+ this.readFromCacheInternal = readFromCache;
136
+ this.requestFromNetworkInternal = requestFromNetwork;
137
+ this.writeToCacheInternal = writeToCache;
138
+ }
139
+ readFromCache(cache) {
140
+ const resultPromise = this.readFromCacheInternal(cache);
141
+ return resultPromise.then((result) => {
142
+ if (result.isErr()) {
143
+ return err(result.error.failure);
144
+ }
145
+ this.returnData = result;
146
+ return ok(void 0);
147
+ });
148
+ }
149
+ requestFromNetwork() {
150
+ const that = this;
151
+ return (async function* () {
152
+ const result = await that.requestFromNetworkInternal();
153
+ if (result.isErr()) {
154
+ that.networkError = result;
155
+ } else {
156
+ that.networkData = result;
157
+ }
158
+ yield result;
159
+ })();
160
+ }
161
+ writeToCache(cache, networkResult) {
162
+ return this.writeToCacheInternal(cache, networkResult);
163
+ }
164
+ }
165
+ class CacheControlCommand extends BaseCommand {
166
+ /**
167
+ * Creates a new CacheControlCommand instance
168
+ *
169
+ * @param services - Required services including cache controller and optional pub/sub service
170
+ * @param services.cacheController - The cache controller service for managing cache operations
171
+ * @param services.pubSub - Optional pub/sub service for cache invalidation notifications
172
+ * @param services - Additional services specific to the implementation
173
+ */
174
+ constructor(services) {
175
+ super();
176
+ this.services = services;
177
+ this.keysUsed = /* @__PURE__ */ new Set();
178
+ this.keysUpdated = void 0;
179
+ this._isInternalExecution = false;
180
+ this.lastResult = void 0;
181
+ this.unsubscribers = [];
182
+ this.subscriptions = [];
183
+ this.instantiationTime = Date.now() / 1e3;
184
+ }
185
+ get isInternalExecution() {
186
+ return this._isInternalExecution;
187
+ }
188
+ /**
189
+ * Executes the cache control command with optional overrides
190
+ *
191
+ * This method orchestrates the cache control flow by:
192
+ * 1. Clearing any existing subscriptions
193
+ * 2. Merging configuration overrides with the base strategy config
194
+ * 3. Building a request runner for cache operations
195
+ * 4. Executing the cache controller with the request runner
196
+ * 5. Handling the result and setting up subscriptions if needed
197
+ *
198
+ * @param overrides - Optional execution overrides including timestamp and cache control config
199
+ * @param overrides.now - Override the current timestamp for cache control calculations
200
+ * @param overrides.cacheControlConfig - Override cache control strategy configuration
201
+ * @returns A subscribable result containing either the cached/network data or an error
202
+ */
203
+ execute(overrides) {
204
+ this.keysUpdated = void 0;
205
+ this.unsubscribe();
206
+ const mergedCacheControlConfig = mergeCacheControlConfigs(
207
+ this.cacheControlStrategyConfig,
208
+ overrides
209
+ );
210
+ const requestRunner = this.buildRequestRunner();
211
+ const resultPromise = this.services.cacheController.execute(mergedCacheControlConfig, requestRunner, {
212
+ instrumentationAttributes: this.instrumentationAttributes
213
+ });
214
+ return resultPromise.then((result) => {
215
+ return this.handleCacheControllerResult(result, requestRunner).then((result2) => {
216
+ if (this.lastResult === void 0) {
217
+ if (result2.isErr()) {
218
+ this.lastResult = { type: "error", error: result2.error.failure };
219
+ } else {
220
+ this.lastResult = { type: "data", data: result2.value.data };
221
+ }
222
+ }
223
+ return result2;
224
+ });
225
+ });
226
+ }
227
+ /**
228
+ * Handles the result from the cache controller and builds the appropriate subscribable result
229
+ *
230
+ * This method processes the cache controller execution result and determines the appropriate
231
+ * response based on network errors, cache errors, and available data. It handles graceful
232
+ * degradation scenarios where network data is available even when cache operations fail.
233
+ *
234
+ * @param result - The result from the cache controller execution
235
+ * @param requestRunner - The request runner containing network data and errors
236
+ * @returns A subscribable result with the appropriate data or error
237
+ */
238
+ handleCacheControllerResult(result, requestRunner) {
239
+ const { networkError, networkData, returnData } = requestRunner;
240
+ return this.publishUpdatedKeys().then(() => {
241
+ if (networkError) {
242
+ return buildSubscribableResult(
243
+ networkError,
244
+ this.buildSubscribe(),
245
+ () => this.refresh()
246
+ );
247
+ }
248
+ if (result.isErr()) {
249
+ if (networkData) {
250
+ return buildSubscribableResult(
251
+ networkData,
252
+ this.buildSubscribe(),
253
+ () => this.refresh()
254
+ );
255
+ }
256
+ return buildSubscribableResult(result, this.buildSubscribe(), () => this.refresh());
257
+ }
258
+ if (returnData === void 0) {
259
+ if (networkData) {
260
+ return buildSubscribableResult(
261
+ networkData,
262
+ this.buildSubscribe(),
263
+ () => this.refresh()
264
+ );
265
+ }
266
+ return buildSubscribableResult(
267
+ err(new Error("Cache miss after fetching from network")),
268
+ this.buildSubscribe(),
269
+ () => this.refresh()
270
+ );
271
+ }
272
+ if (this.subscriptions.length > 0) {
273
+ this.subscribe();
274
+ }
275
+ return returnData;
276
+ });
277
+ }
278
+ /**
279
+ * Builds a request runner that orchestrates cache read, network request, and cache write operations
280
+ *
281
+ * The request runner encapsulates the three main operations:
282
+ * 1. Reading from cache with subscription setup
283
+ * 2. Requesting data from the network
284
+ * 3. Writing network results to cache and recording keys
285
+ *
286
+ * @returns A configured request runner for the cache controller
287
+ */
288
+ buildRequestRunner() {
289
+ return new CacheControlRequestRunner(
290
+ (cache) => this.buildResultWithSubscribe(cache),
291
+ () => this.requestFromNetwork(),
292
+ (cache, networkResult) => this.writeToCacheAndRecordKeys(cache, networkResult)
293
+ );
294
+ }
295
+ /**
296
+ * Publishes cache update events for keys that were modified during the operation
297
+ *
298
+ * This method notifies other parts of the system about cache changes by publishing
299
+ * a 'cacheUpdate' event with the set of keys that were updated. This enables
300
+ * cache invalidation and reactive updates across the application.
301
+ *
302
+ * @returns A promise that resolves when the update event is published (or immediately if no pub/sub service)
303
+ */
304
+ publishUpdatedKeys() {
305
+ if (this.services.pubSub) {
306
+ if (this.keysUpdated !== void 0 && this.keysUpdated.size > 0) {
307
+ return this.services.pubSub.publish({
308
+ type: "cacheUpdate",
309
+ data: this.keysUpdated
310
+ });
311
+ }
312
+ }
313
+ return resolvedPromiseLike(void 0);
314
+ }
315
+ get operationType() {
316
+ return "query";
317
+ }
318
+ /**
319
+ * Subscribes to cache update and invalidation events for reactive updates
320
+ *
321
+ * This method sets up subscriptions to listen for changes that affect the data returned
322
+ * by this Command.
323
+ *
324
+ * By default, it subscribes to two types of events on the PubSub service:
325
+ * - 'cacheUpdate': Triggers a rebuild with the original instantiation time
326
+ * - 'cacheInvalidation': Triggers a full refresh without time constraints
327
+ *
328
+ * This method can be extended by subclasses to add additional subscriptions.
329
+ *
330
+ * Note: ALL subscriptions should push an unsubscribe function to the unsubscribers array,
331
+ * for the lifecycle to work correctly and avoid memory leaks.
332
+ */
333
+ subscribe() {
334
+ this.unsubscribe();
335
+ const { pubSub } = this.services;
336
+ if (!pubSub) {
337
+ return;
338
+ }
339
+ const rebuildUnsubscribe = pubSub.subscribe({
340
+ type: "cacheUpdate",
341
+ predicate: (event) => setOverlaps(event.data, this.keysUsed),
342
+ callback: () => this.rerun({ now: this.instantiationTime }).then(() => void 0),
343
+ keys: this.keysUsed
344
+ });
345
+ const refreshUnsubscribe = pubSub.subscribe({
346
+ type: "cacheInvalidation",
347
+ predicate: (event) => setOverlaps(event.data, this.keysUsed),
348
+ callback: () => this.rerun().then(() => void 0),
349
+ keys: this.keysUsed
350
+ });
351
+ this.unsubscribers.push(rebuildUnsubscribe, refreshUnsubscribe);
352
+ }
353
+ /**
354
+ * Unsubscribes from all stored subscriptions
355
+ *
356
+ * This method calls all stored unsubscribe functions to clean up event listeners
357
+ * and prevent memory leaks. It should be called when the command is no longer
358
+ * needed and is also called before setting up new subscriptions.
359
+ */
360
+ unsubscribe() {
361
+ while (this.unsubscribers.length > 0) {
362
+ const unsubscriber = this.unsubscribers.pop();
363
+ unsubscriber == null ? void 0 : unsubscriber();
364
+ }
365
+ }
366
+ /**
367
+ * Compares two result values for equality to determine if a cache update should trigger a rerun
368
+ *
369
+ * This method is used to prevent unnecessary reruns when the cached data hasn't actually changed.
370
+ * The default implementation uses deep equality comparison, but subclasses can override this
371
+ * to provide more efficient or domain-specific comparison logic.
372
+ *
373
+ * @param result1 - The first result to compare
374
+ * @param result2 - The second result to compare
375
+ * @returns True if the results are equal, false otherwise
376
+ *
377
+ * @todo This should likely be abstract in v2. For v1, provide default comparison logic.
378
+ */
379
+ equals(result1, result2) {
380
+ return deepEquals(result1, result2);
381
+ }
382
+ /**
383
+ * Hook method called after a network request completes
384
+ *
385
+ * This method provides a point for subclasses to perform post-request operations
386
+ * such as logging, metrics collection, or cleanup. The default implementation
387
+ * is empty and can be overridden by subclasses as needed.
388
+ *
389
+ * @param _options - Request completion options
390
+ * @param _options.statusCode - HTTP status code from the network response
391
+ */
392
+ async afterRequestHooks(_options) {
393
+ }
394
+ /**
395
+ * Forces a refresh of the cached data by bypassing cache and fetching from network
396
+ *
397
+ * This method executes the command with a "no-cache" configuration, ensuring that
398
+ * fresh data is fetched from the network regardless of cache state. It's useful
399
+ * for scenarios where you need to ensure the most up-to-date data.
400
+ *
401
+ * @returns A refresh result indicating success or failure of the refresh operation
402
+ */
403
+ refresh() {
404
+ return this.rerun({ cacheControlConfig: { type: "no-cache" } }).then((result) => {
405
+ if (result.isErr()) {
406
+ return err(result.error.failure);
407
+ }
408
+ return ok(void 0);
409
+ });
410
+ }
411
+ /**
412
+ * Writes network result to cache and records the keys that were updated
413
+ *
414
+ * This method wraps the cache write operation with key tracking functionality.
415
+ * It uses a recordable cache wrapper to capture which keys are modified during
416
+ * the write operation, then updates the internal tracking of used and updated keys.
417
+ *
418
+ * @param cache - The cache instance to write to
419
+ * @param networkResult - The network result containing data to write to cache
420
+ * @returns A result indicating success or failure of the write operation
421
+ */
422
+ writeToCacheAndRecordKeys(cache, networkResult) {
423
+ const recordableCache = cache.record();
424
+ return this.writeToCache(recordableCache, networkResult).then((result) => {
425
+ this.instantiationTime = Date.now() / 1e3;
426
+ this.keysUpdated = recordableCache.keysUpdated;
427
+ return ok(result);
428
+ });
429
+ }
430
+ /**
431
+ * Builds a subscribable result by reading from cache and setting up subscriptions
432
+ *
433
+ * This method reads data from the cache and wraps the result in a subscribable
434
+ * structure that allows consumers to subscribe to updates. It also tracks which
435
+ * cache keys were read for future invalidation purposes.
436
+ *
437
+ * @param cache - The readonly cache to read from
438
+ * @returns A subscribable result containing the cached data or error
439
+ */
440
+ buildResultWithSubscribe(cache) {
441
+ const recordableCache = cache.record();
442
+ const result = this.readFromCache(recordableCache);
443
+ return result.then((readResult) => {
444
+ if (readResult.isErr()) {
445
+ return buildSubscribableResult(
446
+ readResult,
447
+ this.buildSubscribe(),
448
+ () => this.refresh()
449
+ );
450
+ } else {
451
+ const data = readResult.value;
452
+ this.keysUsed = recordableCache.keysRead;
453
+ return buildSubscribableResult(
454
+ ok(data),
455
+ this.buildSubscribe(),
456
+ () => this.refresh()
457
+ );
458
+ }
459
+ });
460
+ }
461
+ /**
462
+ * Builds a function that subscribes to cache changes via the pubsub service. Whenever
463
+ * relevant cache updates occur, it re-reads the data and compares it against
464
+ * the last known value. If a change is detected, the provided
465
+ * callback is invoked.
466
+ *
467
+ * @returns an unsubscribe function to stop watching for updates
468
+ */
469
+ buildSubscribe() {
470
+ return (consumerCallback) => {
471
+ if (this.subscriptions.length === 0 && this.operationType === "query") {
472
+ this.subscribe();
473
+ }
474
+ this.subscriptions.push(consumerCallback);
475
+ return () => {
476
+ this.subscriptions = this.subscriptions.filter((cb) => cb !== consumerCallback);
477
+ if (this.subscriptions.length === 0) {
478
+ this.unsubscribe();
479
+ }
480
+ };
481
+ };
482
+ }
483
+ /**
484
+ * Re-runs the command execution with optional overrides and notifies subscribers of changes
485
+ *
486
+ * This method is called internally when cache updates occur that affect the command's data.
487
+ * It executes the command with the provided overrides and compares the result with the
488
+ * last known result. If the data has changed, it notifies all subscribers with the new data.
489
+ *
490
+ * The method handles deduplication to prevent unnecessary notifications when the data
491
+ * hasn't actually changed, and properly manages the internal execution state.
492
+ *
493
+ * @param overrides - Optional execution overrides for the rerun
494
+ * @returns A promise that resolves to the execution result
495
+ */
496
+ rerun(overrides) {
497
+ this._isInternalExecution = true;
498
+ return this.execute(overrides).then((result) => {
499
+ this._isInternalExecution = false;
500
+ if (result.isErr()) {
501
+ this.lastResult = { type: "error", error: result.error.failure };
502
+ this.invokeConsumerCallbacks(err(result.error.failure));
503
+ return result;
504
+ }
505
+ if (this.lastResult === void 0 || this.lastResult.type === "error" || !this.equals(this.lastResult.data, result.value.data)) {
506
+ this.lastResult = { type: "data", data: result.value.data };
507
+ this.invokeConsumerCallbacks(ok(result.value.data));
508
+ }
509
+ return result;
510
+ });
511
+ }
512
+ /**
513
+ * Invokes all registered consumer callbacks with the provided data
514
+ *
515
+ * This private method safely calls all registered subscriber callbacks with the
516
+ * provided result data. It includes error handling to prevent callback failures
517
+ * from affecting other callbacks or the overall system.
518
+ *
519
+ * @param data - The result data to send to all subscribers
520
+ */
521
+ invokeConsumerCallbacks(data) {
522
+ this.subscriptions.forEach((cb) => {
523
+ try {
524
+ cb(data);
525
+ } catch (error) {
526
+ }
527
+ });
528
+ }
529
+ }
530
+ function mergeCacheControlConfigs(baseConfig, overrides) {
531
+ if (!overrides) {
532
+ return baseConfig;
533
+ }
534
+ const now = overrides.now ?? baseConfig.now;
535
+ if (!overrides.cacheControlConfig) {
536
+ return {
537
+ ...baseConfig,
538
+ now
539
+ };
540
+ }
541
+ return {
542
+ ...overrides.cacheControlConfig,
543
+ now
544
+ };
545
+ }
546
+ function buildServiceDescriptor() {
547
+ return {
548
+ type: "cacheControlCommandBaseClass",
549
+ version: "1.0",
550
+ service: CacheControlCommand
551
+ };
552
+ }
553
+ export {
554
+ CacheControlCommand,
555
+ CacheControlRequestRunner,
556
+ buildServiceDescriptor
557
+ };
558
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../../../../utils/dist/index.js","../../src/v1/utils.ts","../../src/v1/cache-control-request-runner.ts","../../src/v1/cache-control-command.ts","../../src/v1/index.ts"],"sourcesContent":["/*!\n * Copyright (c) 2022, Salesforce, Inc.,\n * All rights reserved.\n * For full license text, see the LICENSE.txt file\n */\nfunction bfs(start, predicate, getChildren) {\n const queue = [...start];\n const visited = /* @__PURE__ */ new Set([...start]);\n const matches2 = /* @__PURE__ */ new Set();\n while (queue.length) {\n const curr = queue.shift();\n if (predicate(curr)) {\n matches2.add(curr);\n }\n const children = getChildren(curr);\n for (const child of children) {\n if (!visited.has(child)) {\n visited.add(child);\n queue.push(child);\n }\n }\n }\n return matches2;\n}\nfunction lineFormatter(position, message, filePath) {\n return `${message} (${filePath}:${position.line}:${position.column})`;\n}\nclass DefaultFileParserLogger {\n constructor(services, filePath) {\n this.services = services;\n this.filePath = filePath;\n }\n trace(position, message) {\n this.services.logger.trace(this.format(position, message));\n }\n debug(position, message) {\n this.services.logger.debug(this.format(position, message));\n }\n info(position, message) {\n this.services.logger.info(this.format(position, message));\n }\n warn(position, message) {\n this.services.logger.warn(this.format(position, message));\n }\n error(position, message) {\n this.services.logger.error(this.format(position, message));\n }\n format(position, message) {\n return lineFormatter(position, message, this.filePath);\n }\n}\nfunction matches(test, s) {\n if (test === void 0) {\n return false;\n } else if (typeof test === \"string\") {\n return s === test;\n } else if (test instanceof RegExp) {\n return test.test(s);\n } else if (typeof test === \"function\") {\n return test(s);\n }\n return test.some((m) => matches(m, s));\n}\nfunction includes(incexc, s) {\n if (matches(incexc.exclude, s)) {\n return false;\n }\n if (matches(incexc.include, s)) {\n return true;\n }\n if (incexc.include) {\n return false;\n }\n return true;\n}\nconst { create, freeze, keys, entries } = Object;\nconst { hasOwnProperty } = Object.prototype;\nconst { isArray } = Array;\nconst { push, indexOf, slice } = Array.prototype;\nconst { stringify, parse } = JSON;\nconst WeakSetConstructor = WeakSet;\nconst LogLevelMap = {\n TRACE: 4,\n DEBUG: 3,\n INFO: 2,\n WARN: 1,\n ERROR: 0\n};\nclass ConsoleLogger {\n constructor(level = \"WARN\", printer = console.log, formatter = (level2, message) => `${level2}: ${message}`) {\n this.level = level;\n this.printer = printer;\n this.formatter = formatter;\n this.messages = [];\n }\n trace(message) {\n this.log(\"TRACE\", message);\n }\n debug(message) {\n this.log(\"DEBUG\", message);\n }\n info(message) {\n this.log(\"INFO\", message);\n }\n warn(message) {\n this.log(\"WARN\", message);\n }\n error(message) {\n this.log(\"ERROR\", message);\n }\n log(level, message) {\n if (LogLevelMap[level] > LogLevelMap[this.level]) {\n return;\n }\n this.printer(this.formatter(level, message));\n }\n}\nfunction loggerService(level, printer, formatter) {\n return new ConsoleLogger(level, printer, formatter);\n}\nclass Ok {\n constructor(value) {\n this.value = value;\n }\n isOk() {\n return true;\n }\n isErr() {\n return !this.isOk();\n }\n}\nclass Err {\n constructor(error) {\n this.error = error;\n }\n isOk() {\n return false;\n }\n isErr() {\n return !this.isOk();\n }\n}\nconst ok = (value) => new Ok(value);\nconst err = (err2) => new Err(err2);\nclass DataNotFoundError extends Error {\n constructor(message) {\n super(message);\n this.name = \"DataNotFoundError\";\n }\n}\nclass DataIncompleteError extends Error {\n constructor(message, partialData) {\n super(message);\n this.partialData = partialData;\n this.name = \"DataIncompleteError\";\n }\n}\nfunction isDataNotFoundError(error) {\n return error instanceof DataNotFoundError || error.name === \"DataNotFoundError\";\n}\nfunction isDataIncompleteError(error) {\n return error instanceof DataIncompleteError || error.name === \"DataIncompleteError\";\n}\nfunction isCacheHitOrError(value) {\n if (value.isErr() && (isDataIncompleteError(value.error) || isDataNotFoundError(value.error))) {\n return false;\n }\n return true;\n}\nfunction isCacheMiss(value) {\n return !isCacheHitOrError(value);\n}\nfunction isResult(value) {\n return value != null && typeof value === \"object\" && \"isOk\" in value && \"isErr\" in value && typeof value.isOk === \"function\" && typeof value.isErr === \"function\" && (value.isOk() === true && value.isErr() === false && \"value\" in value || value.isOk() === false && value.isErr() === true && \"error\" in value);\n}\nfunction setOverlaps(setA, setB) {\n for (const element of setA) {\n if (setB.has(element)) {\n return true;\n }\n }\n return false;\n}\nfunction setDifference(setA, setB) {\n const differenceSet = /* @__PURE__ */ new Set();\n for (const element of setA) {\n if (!setB.has(element)) {\n differenceSet.add(element);\n }\n }\n return differenceSet;\n}\nfunction addAllToSet(targetSet, sourceSet) {\n for (const element of sourceSet) {\n targetSet.add(element);\n }\n}\nconst toTypeScriptSafeIdentifier = (s) => s.length >= 1 ? s[0].replace(/[^$_\\p{ID_Start}]/u, \"_\") + s.slice(1).replace(/[^$\\u200c\\u200d\\p{ID_Continue}]/gu, \"_\") : \"\";\nfunction isSubscribable(obj) {\n return typeof obj === \"object\" && obj !== null && \"subscribe\" in obj && typeof obj.subscribe === \"function\" && \"refresh\" in obj && typeof obj.refresh === \"function\";\n}\nfunction isSubscribableResult(x) {\n if (!isResult(x)) {\n return false;\n }\n return isSubscribable(x.isOk() ? x.value : x.error);\n}\nfunction buildSubscribableResult(result, subscribe, refresh) {\n if (result.isOk()) {\n return ok({ data: result.value, subscribe, refresh });\n } else {\n return err({ failure: result.error, subscribe, refresh });\n }\n}\nfunction resolvedPromiseLike(result) {\n if (isPromiseLike(result)) {\n return result.then((nextResult) => nextResult);\n }\n return {\n then: (onFulfilled, _onRejected) => {\n try {\n return resolvedPromiseLike(onFulfilled(result));\n } catch (e) {\n if (onFulfilled === void 0) {\n return resolvedPromiseLike(result);\n }\n return rejectedPromiseLike(e);\n }\n }\n };\n}\nfunction rejectedPromiseLike(reason) {\n if (isPromiseLike(reason)) {\n return reason.then((nextResult) => nextResult);\n }\n return {\n then: (_onFulfilled, onRejected) => {\n if (typeof onRejected === \"function\") {\n try {\n return resolvedPromiseLike(onRejected(reason));\n } catch (e) {\n return rejectedPromiseLike(e);\n }\n }\n return rejectedPromiseLike(reason);\n }\n };\n}\nfunction isPromiseLike(x) {\n return typeof (x == null ? void 0 : x.then) === \"function\";\n}\nfunction racesync(values) {\n for (const value of values) {\n let settled = void 0;\n if (isPromiseLike(value)) {\n value.then(\n (_) => {\n settled = value;\n },\n (_) => {\n settled = value;\n }\n );\n } else {\n settled = resolvedPromiseLike(value);\n }\n if (settled !== void 0) {\n return settled;\n }\n }\n return Promise.race(values);\n}\nfunction withResolvers() {\n let resolve, reject;\n const promise = new Promise((res, rej) => {\n resolve = res;\n reject = rej;\n });\n return { promise, resolve, reject };\n}\nfunction deepEquals(x, y) {\n if (x === void 0) {\n return y === void 0;\n } else if (x === null) {\n return y === null;\n } else if (y === null) {\n return x === null;\n } else if (isArray(x)) {\n if (!isArray(y) || x.length !== y.length) {\n return false;\n }\n for (let i = 0; i < x.length; ++i) {\n if (!deepEquals(x[i], y[i])) {\n return false;\n }\n }\n return true;\n } else if (typeof x === \"object\") {\n if (typeof y !== \"object\") {\n return false;\n }\n const xkeys = Object.keys(x);\n const ykeys = Object.keys(y);\n if (xkeys.length !== ykeys.length) {\n return false;\n }\n for (let i = 0; i < xkeys.length; ++i) {\n const key = xkeys[i];\n if (!deepEquals(x[key], y[key])) {\n return false;\n }\n }\n return true;\n }\n return x === y;\n}\nfunction stableJSONStringify(node) {\n if (node && node.toJSON && typeof node.toJSON === \"function\") {\n node = node.toJSON();\n }\n if (node === void 0) {\n return;\n }\n if (typeof node === \"number\") {\n return isFinite(node) ? \"\" + node : \"null\";\n }\n if (typeof node !== \"object\") {\n return stringify(node);\n }\n let i;\n let out;\n if (isArray(node)) {\n out = \"[\";\n for (i = 0; i < node.length; i++) {\n if (i) {\n out += \",\";\n }\n out += stableJSONStringify(node[i]) || \"null\";\n }\n return out + \"]\";\n }\n if (node === null) {\n return \"null\";\n }\n const objKeys = keys(node).sort();\n out = \"\";\n for (i = 0; i < objKeys.length; i++) {\n const key = objKeys[i];\n const value = stableJSONStringify(node[key]);\n if (!value) {\n continue;\n }\n if (out) {\n out += \",\";\n }\n out += stringify(key) + \":\" + value;\n }\n return \"{\" + out + \"}\";\n}\nfunction toError(x) {\n if (x instanceof Error) {\n return x;\n }\n return new Error(typeof x === \"string\" ? x : JSON.stringify(x));\n}\nfunction deepCopy(x) {\n const stringified = stringify(x);\n return stringified ? parse(stringified) : void 0;\n}\nfunction readableStreamToAsyncIterable(stream) {\n if (stream.locked) {\n return err(new Error(\"ReadableStream is already locked\"));\n }\n if (Symbol.asyncIterator in stream) {\n return ok(stream);\n }\n const reader = stream.getReader();\n return ok({\n [Symbol.asyncIterator]: () => ({\n next: async () => {\n try {\n const result = await reader.read();\n if (result.done) {\n try {\n reader.releaseLock();\n } catch {\n }\n return { done: true, value: void 0 };\n }\n return {\n done: false,\n value: result.value\n };\n } catch (e) {\n try {\n reader.releaseLock();\n } catch {\n }\n throw e;\n }\n },\n return: async (value) => {\n try {\n await reader.cancel();\n } catch {\n }\n try {\n reader.releaseLock();\n } catch {\n }\n return { done: true, value };\n },\n throw: async (exception) => {\n try {\n await reader.cancel();\n } catch {\n }\n try {\n reader.releaseLock();\n } catch {\n }\n throw exception;\n }\n })\n });\n}\nfunction satisfies(provided, requested) {\n const providedN = provided.split(\".\").map((s) => parseInt(s));\n const requestedN = requested.split(\".\").map((s) => parseInt(s));\n return providedN[0] === requestedN[0] && providedN[1] >= requestedN[1];\n}\nfunction stringIsVersion(s) {\n const versionParts = s.split(\".\");\n return (versionParts.length === 2 || versionParts.length === 3) && versionParts.every((part) => part.match(/^\\d+$/));\n}\nvar HttpStatusCode = /* @__PURE__ */ ((HttpStatusCode2) => {\n HttpStatusCode2[HttpStatusCode2[\"Ok\"] = 200] = \"Ok\";\n HttpStatusCode2[HttpStatusCode2[\"Created\"] = 201] = \"Created\";\n HttpStatusCode2[HttpStatusCode2[\"NoContent\"] = 204] = \"NoContent\";\n HttpStatusCode2[HttpStatusCode2[\"NotModified\"] = 304] = \"NotModified\";\n HttpStatusCode2[HttpStatusCode2[\"BadRequest\"] = 400] = \"BadRequest\";\n HttpStatusCode2[HttpStatusCode2[\"Unauthorized\"] = 401] = \"Unauthorized\";\n HttpStatusCode2[HttpStatusCode2[\"Forbidden\"] = 403] = \"Forbidden\";\n HttpStatusCode2[HttpStatusCode2[\"NotFound\"] = 404] = \"NotFound\";\n HttpStatusCode2[HttpStatusCode2[\"ServerError\"] = 500] = \"ServerError\";\n HttpStatusCode2[HttpStatusCode2[\"GatewayTimeout\"] = 504] = \"GatewayTimeout\";\n return HttpStatusCode2;\n})(HttpStatusCode || {});\nfunction getFetchResponseFromAuraError(err2) {\n if (err2.data !== void 0 && err2.data.statusCode !== void 0) {\n let data = {};\n data = err2.data;\n if (err2.id !== void 0) {\n data.id = err2.id;\n }\n return new FetchResponse(data.statusCode, data);\n }\n return new FetchResponse(500, {\n error: err2.message\n });\n}\nasync function coerceResponseToFetchResponse(response) {\n const { status } = response;\n const responseHeaders = {};\n response.headers.forEach((value, key) => {\n responseHeaders[key] = value;\n });\n let responseBody = null;\n if (status !== 204) {\n const contentType = responseHeaders[\"content-type\"];\n responseBody = contentType && contentType.startsWith(\"application/json\") ? await response.json() : await response.text();\n }\n return new FetchResponse(status, responseBody, responseHeaders);\n}\nfunction getStatusText(status) {\n switch (status) {\n case 200:\n return \"OK\";\n case 201:\n return \"Created\";\n case 304:\n return \"Not Modified\";\n case 400:\n return \"Bad Request\";\n case 404:\n return \"Not Found\";\n case 500:\n return \"Server Error\";\n default:\n return `Unexpected HTTP Status Code: ${status}`;\n }\n}\nclass FetchResponse extends Error {\n constructor(status, body, headers) {\n super();\n this.status = status;\n this.body = body;\n this.headers = headers || {};\n this.ok = status >= 200 && this.status <= 299;\n this.statusText = getStatusText(status);\n }\n}\nconst deeplyFrozen = new WeakSetConstructor();\nfunction deepFreeze(value) {\n if (typeof value !== \"object\" || value === null || deeplyFrozen.has(value)) {\n return;\n }\n deeplyFrozen.add(value);\n if (isArray(value)) {\n for (let i = 0, len = value.length; i < len; i += 1) {\n deepFreeze(value[i]);\n }\n } else {\n const keys$1 = keys(value);\n for (let i = 0, len = keys$1.length; i < len; i += 1) {\n deepFreeze(value[keys$1[i]]);\n }\n }\n freeze(value);\n}\nfunction isScalar(value) {\n return typeof value === \"string\" || typeof value === \"number\" || typeof value === \"boolean\" || value === null || value === void 0;\n}\nfunction isScalarObject(value) {\n return Object.values(value).every((value2) => isScalar(value2));\n}\nfunction isScalarArray(value) {\n return value.every((item) => isScalar(item));\n}\nfunction encodeQueryParam(paramName, value, explode) {\n switch (typeof value) {\n case \"string\":\n return [`${paramName}=${encodeURIComponent(value)}`];\n case \"number\":\n case \"boolean\":\n return [`${paramName}=${value}`];\n case \"object\":\n if (value === null) {\n return [];\n }\n if (isArray(value)) {\n if (!isScalarArray(value)) {\n throw new Error(`Unsupported non-scalar array type for ${paramName}`);\n }\n if (explode) {\n return value.map(\n (item) => `${paramName}=${item ? encodeURIComponent(item) : item}`\n );\n }\n return [\n `${paramName}=${value.map((item) => item ? encodeURIComponent(item) : item).join(\",\")}`\n ];\n }\n if (!isScalarObject(value)) {\n throw new Error(`Unsupported non-scalar object type for ${paramName}`);\n }\n if (explode) {\n return entries(value).map(\n ([key, value2]) => `${key}=${value2 ? encodeURIComponent(value2) : value2}`\n );\n }\n return [\n `${paramName}=${entries(value).flat().map((item) => item ? encodeURIComponent(item) : item).join(\",\")}`\n ];\n default:\n return [];\n }\n}\nclass InternalError extends Error {\n constructor(data) {\n super();\n this.data = data;\n this.type = \"internal\";\n }\n}\nclass UserVisibleError extends Error {\n constructor(data) {\n super();\n this.data = data;\n this.type = \"user-visible\";\n }\n}\nfunction applyDecorators(baseCommand, decorators, options) {\n if (!decorators || decorators.length === 0) {\n return baseCommand;\n }\n return decorators.reduce((command, decorator) => decorator(command, options), baseCommand);\n}\nexport {\n isArray as ArrayIsArray,\n indexOf as ArrayPrototypeIndexOf,\n push as ArrayPrototypePush,\n slice as ArrayPrototypeSlice,\n ConsoleLogger,\n DataIncompleteError,\n DataNotFoundError,\n DefaultFileParserLogger,\n Err,\n FetchResponse,\n HttpStatusCode,\n InternalError,\n parse as JSONParse,\n stringify as JSONStringify,\n LogLevelMap,\n create as ObjectCreate,\n entries as ObjectEntries,\n freeze as ObjectFreeze,\n keys as ObjectKeys,\n hasOwnProperty as ObjectPrototypeHasOwnProperty,\n Ok,\n UserVisibleError,\n WeakSetConstructor,\n addAllToSet,\n applyDecorators,\n bfs,\n buildSubscribableResult,\n coerceResponseToFetchResponse,\n deepCopy,\n deepEquals,\n deepFreeze,\n encodeQueryParam,\n err,\n getFetchResponseFromAuraError,\n includes,\n isCacheHitOrError,\n isCacheMiss,\n isDataIncompleteError,\n isDataNotFoundError,\n isPromiseLike,\n isResult,\n isSubscribable,\n isSubscribableResult,\n lineFormatter,\n loggerService,\n ok,\n racesync,\n readableStreamToAsyncIterable,\n rejectedPromiseLike,\n resolvedPromiseLike,\n satisfies,\n setDifference,\n setOverlaps,\n stableJSONStringify,\n stringIsVersion,\n toError,\n toTypeScriptSafeIdentifier,\n withResolvers\n};\n//# sourceMappingURL=index.js.map\n","import type { Key } from '@conduit-client/service-cache/v1';\n\n// Needs TS 2023+ (esnext) AND node 20+ to remove\nexport function setOverlaps(setA: Set<Key>, setB: Set<Key>): boolean {\n if (setA.size > setB.size) {\n for (const key of setB.keys()) {\n if (setA.has(key)) {\n return true;\n }\n }\n } else {\n for (const key of setA) {\n if (setB.has(key)) {\n return true;\n }\n }\n }\n return false;\n}\n","import { Cache, ReadonlyCache } from '@conduit-client/service-cache/v1';\nimport { Err, err, Ok, ok, Result, SubscribableResult, SyncOrAsync } from '@conduit-client/utils';\n\nexport class CacheControlRequestRunner<ReturnData, NetworkData> {\n returnData: SubscribableResult<ReturnData, Error> | undefined;\n networkData: Ok<NetworkData, Error> | undefined;\n networkError: Err<never, Error> | undefined;\n readFromCacheInternal;\n requestFromNetworkInternal;\n writeToCacheInternal;\n\n constructor(\n readFromCache: (cache: ReadonlyCache) => SyncOrAsync<SubscribableResult<ReturnData, Error>>,\n requestFromNetwork: () => SyncOrAsync<Result<NetworkData, Error>>,\n writeToCache: (\n cache: Cache,\n networkResult: Result<NetworkData, Error>\n ) => SyncOrAsync<Result<void, Error>>\n ) {\n this.readFromCacheInternal = readFromCache;\n this.requestFromNetworkInternal = requestFromNetwork;\n this.writeToCacheInternal = writeToCache;\n }\n\n readFromCache(cache: ReadonlyCache) {\n const resultPromise = this.readFromCacheInternal(cache);\n\n return resultPromise.then((result) => {\n if (result.isErr()) {\n return err(result.error.failure);\n }\n\n this.returnData = result;\n return ok(undefined);\n });\n }\n\n requestFromNetwork() {\n const that = this;\n return (async function* () {\n const result = await that.requestFromNetworkInternal();\n if (result.isErr()) {\n that.networkError = result;\n } else {\n that.networkData = result;\n }\n yield result;\n })();\n }\n\n writeToCache(cache: Cache, networkResult: Result<NetworkData, Error>) {\n return this.writeToCacheInternal(cache, networkResult);\n }\n}\n","import { BaseCommand } from '@conduit-client/command-base/v1';\nimport { type Event, type NamedPubSubService } from '@conduit-client/service-pubsub/v1';\nimport {\n ok,\n err,\n deepEquals,\n resolvedPromiseLike,\n buildSubscribableResult,\n} from '@conduit-client/utils';\nimport type {\n Callback,\n Result,\n Unsubscribe,\n SubscribableResult,\n SyncOrAsync,\n RefreshResult,\n} from '@conduit-client/utils';\nimport type { Cache, Key, ReadonlyCache } from '@conduit-client/service-cache/v1';\nimport type {\n CacheControlStrategyConfig,\n NamedCacheControllerService,\n} from '@conduit-client/service-cache-control/v1';\nimport { InstrumentationAttributes } from '@conduit-client/service-instrumentation/v1';\nimport { setOverlaps } from './utils';\nimport { CacheControlRequestRunner } from './cache-control-request-runner';\n\ntype ExecuteOverrides = {\n now?: number;\n cacheControlConfig?: Omit<CacheControlStrategyConfig, 'now'>;\n};\n\n/**\n * An implementation of BaseCommand that allows for extending abstract cache methods\n *\n * @typeParam Data cache result for read operations\n * @typeParam NetworkResult cache result including network metadata\n * @typeParam ExtraServices additional named services needed by a subclass\n */\nexport abstract class CacheControlCommand<\n ReturnData,\n NetworkData,\n ExtraServices extends object = object,\n> extends BaseCommand<SyncOrAsync<SubscribableResult<ReturnData, Error>>> {\n instantiationTime: number;\n instrumentationAttributes?: InstrumentationAttributes;\n private keysUsed = new Set<Key>();\n private keysUpdated: Set<Key> | undefined = undefined;\n private _isInternalExecution = false;\n\n get isInternalExecution(): boolean {\n return this._isInternalExecution;\n }\n\n /**\n * Creates a new CacheControlCommand instance\n *\n * @param services - Required services including cache controller and optional pub/sub service\n * @param services.cacheController - The cache controller service for managing cache operations\n * @param services.pubSub - Optional pub/sub service for cache invalidation notifications\n * @param services - Additional services specific to the implementation\n */\n constructor(\n protected services: NamedCacheControllerService &\n Partial<NamedPubSubService> &\n ExtraServices\n ) {\n super();\n this.instantiationTime = Date.now() / 1000; // in seconds\n }\n\n /**\n * Executes the cache control command with optional overrides\n *\n * This method orchestrates the cache control flow by:\n * 1. Clearing any existing subscriptions\n * 2. Merging configuration overrides with the base strategy config\n * 3. Building a request runner for cache operations\n * 4. Executing the cache controller with the request runner\n * 5. Handling the result and setting up subscriptions if needed\n *\n * @param overrides - Optional execution overrides including timestamp and cache control config\n * @param overrides.now - Override the current timestamp for cache control calculations\n * @param overrides.cacheControlConfig - Override cache control strategy configuration\n * @returns A subscribable result containing either the cached/network data or an error\n */\n execute(overrides?: ExecuteOverrides): SyncOrAsync<SubscribableResult<ReturnData, Error>> {\n this.keysUpdated = undefined;\n this.unsubscribe();\n\n const mergedCacheControlConfig = mergeCacheControlConfigs(\n this.cacheControlStrategyConfig,\n overrides\n );\n\n const requestRunner = this.buildRequestRunner();\n\n const resultPromise = this.services.cacheController.execute<\n Result<NetworkData, Error>,\n Error\n >(mergedCacheControlConfig, requestRunner, {\n instrumentationAttributes: this.instrumentationAttributes,\n });\n\n return resultPromise.then((result) => {\n return this.handleCacheControllerResult(result, requestRunner).then((result) => {\n // This is our first pass through the Command, so we need to set the lastResult.\n // Subsequent results are kept up to date in rerun()\n if (this.lastResult === undefined) {\n if (result.isErr()) {\n this.lastResult = { type: 'error', error: result.error.failure };\n } else {\n this.lastResult = { type: 'data', data: result.value.data };\n }\n }\n return result;\n });\n });\n }\n\n /**\n * Handles the result from the cache controller and builds the appropriate subscribable result\n *\n * This method processes the cache controller execution result and determines the appropriate\n * response based on network errors, cache errors, and available data. It handles graceful\n * degradation scenarios where network data is available even when cache operations fail.\n *\n * @param result - The result from the cache controller execution\n * @param requestRunner - The request runner containing network data and errors\n * @returns A subscribable result with the appropriate data or error\n */\n handleCacheControllerResult(\n result: Result<void, Error>,\n requestRunner: CacheControlRequestRunner<ReturnData, NetworkData>\n ): SyncOrAsync<SubscribableResult<ReturnData, Error>> {\n const { networkError, networkData, returnData } = requestRunner;\n return this.publishUpdatedKeys().then(() => {\n // First check if there was an error from the network\n if (networkError) {\n return buildSubscribableResult(networkError, this.buildSubscribe(), () =>\n this.refresh()\n );\n }\n\n // Then return other errors from read/write\n if (result.isErr()) {\n // If the result was an error but we got network data, gracefully degrade to returning the\n // network data without caching/subscriptions\n if (networkData) {\n return buildSubscribableResult(networkData, this.buildSubscribe(), () =>\n this.refresh()\n );\n }\n // otherwise return the error\n return buildSubscribableResult(result, this.buildSubscribe(), () => this.refresh());\n }\n\n if (returnData === undefined) {\n if (networkData) {\n return buildSubscribableResult(networkData, this.buildSubscribe(), () =>\n this.refresh()\n );\n }\n return buildSubscribableResult(\n err(new Error('Cache miss after fetching from network')),\n this.buildSubscribe(),\n () => this.refresh()\n );\n }\n\n if (this.subscriptions.length > 0) {\n this.subscribe();\n }\n return returnData;\n });\n }\n\n /**\n * Builds a request runner that orchestrates cache read, network request, and cache write operations\n *\n * The request runner encapsulates the three main operations:\n * 1. Reading from cache with subscription setup\n * 2. Requesting data from the network\n * 3. Writing network results to cache and recording keys\n *\n * @returns A configured request runner for the cache controller\n */\n protected buildRequestRunner(): CacheControlRequestRunner<ReturnData, NetworkData> {\n return new CacheControlRequestRunner<ReturnData, NetworkData>(\n (cache: ReadonlyCache) => this.buildResultWithSubscribe(cache),\n () => this.requestFromNetwork(),\n (cache: Cache, networkResult: Result<NetworkData, Error>) =>\n this.writeToCacheAndRecordKeys(cache, networkResult)\n );\n }\n\n /**\n * Publishes cache update events for keys that were modified during the operation\n *\n * This method notifies other parts of the system about cache changes by publishing\n * a 'cacheUpdate' event with the set of keys that were updated. This enables\n * cache invalidation and reactive updates across the application.\n *\n * @returns A promise that resolves when the update event is published (or immediately if no pub/sub service)\n */\n protected publishUpdatedKeys(): SyncOrAsync<void> {\n if (this.services.pubSub) {\n if (this.keysUpdated !== undefined && this.keysUpdated.size > 0) {\n return this.services.pubSub.publish({\n type: 'cacheUpdate',\n data: this.keysUpdated,\n });\n }\n }\n return resolvedPromiseLike(undefined);\n }\n\n protected get operationType(): 'query' | 'mutation' {\n return 'query';\n }\n protected lastResult:\n | { type: 'data'; data: ReturnData }\n | { type: 'error'; error: Error }\n | undefined = undefined;\n\n /**\n * Subscribes to cache update and invalidation events for reactive updates\n *\n * This method sets up subscriptions to listen for changes that affect the data returned\n * by this Command.\n *\n * By default, it subscribes to two types of events on the PubSub service:\n * - 'cacheUpdate': Triggers a rebuild with the original instantiation time\n * - 'cacheInvalidation': Triggers a full refresh without time constraints\n *\n * This method can be extended by subclasses to add additional subscriptions.\n *\n * Note: ALL subscriptions should push an unsubscribe function to the unsubscribers array,\n * for the lifecycle to work correctly and avoid memory leaks.\n */\n protected subscribe(): void {\n this.unsubscribe();\n const { pubSub } = this.services;\n if (!pubSub) {\n return;\n }\n\n const rebuildUnsubscribe = pubSub.subscribe({\n type: 'cacheUpdate',\n predicate: (event: Event<Set<Key>>) => setOverlaps(event.data, this.keysUsed),\n callback: () => this.rerun({ now: this.instantiationTime }).then(() => undefined),\n keys: this.keysUsed,\n } as any);\n\n const refreshUnsubscribe = pubSub.subscribe({\n type: 'cacheInvalidation',\n predicate: (event: Event<Set<Key>>) => setOverlaps(event.data, this.keysUsed),\n callback: () => this.rerun().then(() => undefined),\n keys: this.keysUsed,\n } as any);\n\n this.unsubscribers.push(rebuildUnsubscribe, refreshUnsubscribe);\n }\n\n /**\n * Unsubscribes from all stored subscriptions\n *\n * This method calls all stored unsubscribe functions to clean up event listeners\n * and prevent memory leaks. It should be called when the command is no longer\n * needed and is also called before setting up new subscriptions.\n */\n private unsubscribe() {\n while (this.unsubscribers.length > 0) {\n const unsubscriber = this.unsubscribers.pop();\n unsubscriber?.();\n }\n }\n\n protected unsubscribers: Unsubscribe[] = [];\n\n protected subscriptions: Callback<Result<ReturnData, Error>>[] = [];\n\n protected abstract readonly cacheControlStrategyConfig: CacheControlStrategyConfig;\n\n /**\n * Compares two result values for equality to determine if a cache update should trigger a rerun\n *\n * This method is used to prevent unnecessary reruns when the cached data hasn't actually changed.\n * The default implementation uses deep equality comparison, but subclasses can override this\n * to provide more efficient or domain-specific comparison logic.\n *\n * @param result1 - The first result to compare\n * @param result2 - The second result to compare\n * @returns True if the results are equal, false otherwise\n *\n * @todo This should likely be abstract in v2. For v1, provide default comparison logic.\n */\n protected equals(result1: ReturnData | undefined, result2: ReturnData | undefined): boolean {\n return deepEquals(result1, result2);\n }\n\n /**\n * Reads from the cache and returns the cache result or error\n *\n * In case of a missing or partial result, this should return either a DataNotFoundError or\n * DataIncompleteError, respectively, in the Error response\n *\n * Note that any subclass should JUST try to read the data from the cache here; it should\n * NOT try to take metadata (eg cache control semantics) into account while reading the\n * data. The CacheController is responsible for enforcing those semantics.\n *\n * @param cache source of cached data\n * @returns result or error from the cache\n */\n abstract readFromCache(cache: ReadonlyCache): SyncOrAsync<Result<ReturnData, Error>>;\n\n /**\n * Requests the resource from network, returning the result along with network metadata\n *\n * @returns network result including metadata\n */\n abstract requestFromNetwork(): SyncOrAsync<Result<NetworkData, Error>>;\n\n /**\n * Writes the given result to the cache\n *\n * @param cache source of cached data\n * @param networkResult network result with metadata to write to the cache. This param will\n * be the result of a previous requestFromNetwork call, if the CacheController determines\n * that the data should be cached\n */\n abstract writeToCache(\n cache: Cache,\n networkResult: Result<NetworkData, Error>\n ): SyncOrAsync<void>;\n\n /**\n * Hook method called after a network request completes\n *\n * This method provides a point for subclasses to perform post-request operations\n * such as logging, metrics collection, or cleanup. The default implementation\n * is empty and can be overridden by subclasses as needed.\n *\n * @param _options - Request completion options\n * @param _options.statusCode - HTTP status code from the network response\n */\n async afterRequestHooks(_options: { statusCode: number }) {}\n\n /**\n * Forces a refresh of the cached data by bypassing cache and fetching from network\n *\n * This method executes the command with a \"no-cache\" configuration, ensuring that\n * fresh data is fetched from the network regardless of cache state. It's useful\n * for scenarios where you need to ensure the most up-to-date data.\n *\n * @returns A refresh result indicating success or failure of the refresh operation\n */\n refresh(): SyncOrAsync<RefreshResult<Error>> {\n return this.rerun({ cacheControlConfig: { type: 'no-cache' } }).then((result) => {\n if (result.isErr()) {\n return err(result.error.failure);\n }\n\n return ok(undefined);\n });\n }\n\n /**\n * Writes network result to cache and records the keys that were updated\n *\n * This method wraps the cache write operation with key tracking functionality.\n * It uses a recordable cache wrapper to capture which keys are modified during\n * the write operation, then updates the internal tracking of used and updated keys.\n *\n * @param cache - The cache instance to write to\n * @param networkResult - The network result containing data to write to cache\n * @returns A result indicating success or failure of the write operation\n */\n writeToCacheAndRecordKeys(\n cache: Cache,\n networkResult: Result<NetworkData, Error>\n ): SyncOrAsync<Result<void, Error>> {\n const recordableCache = cache.record();\n return this.writeToCache(recordableCache, networkResult).then((result) => {\n this.instantiationTime = Date.now() / 1000;\n this.keysUpdated = recordableCache.keysUpdated;\n return ok(result);\n });\n }\n\n /**\n * Builds a subscribable result by reading from cache and setting up subscriptions\n *\n * This method reads data from the cache and wraps the result in a subscribable\n * structure that allows consumers to subscribe to updates. It also tracks which\n * cache keys were read for future invalidation purposes.\n *\n * @param cache - The readonly cache to read from\n * @returns A subscribable result containing the cached data or error\n */\n buildResultWithSubscribe(cache: ReadonlyCache) {\n const recordableCache = cache.record();\n const result = this.readFromCache(recordableCache);\n return result.then((readResult) => {\n if (readResult.isErr()) {\n return buildSubscribableResult(readResult, this.buildSubscribe(), () =>\n this.refresh()\n );\n } else {\n const data = readResult.value;\n this.keysUsed = recordableCache.keysRead;\n return buildSubscribableResult(ok(data), this.buildSubscribe(), () =>\n this.refresh()\n );\n }\n });\n }\n\n /**\n * Builds a function that subscribes to cache changes via the pubsub service. Whenever\n * relevant cache updates occur, it re-reads the data and compares it against\n * the last known value. If a change is detected, the provided\n * callback is invoked.\n *\n * @returns an unsubscribe function to stop watching for updates\n */\n protected buildSubscribe() {\n return (consumerCallback: Callback<Result<ReturnData, Error>>): Unsubscribe => {\n if (this.subscriptions.length === 0 && this.operationType === 'query') {\n this.subscribe();\n }\n this.subscriptions.push(consumerCallback);\n return () => {\n this.subscriptions = this.subscriptions.filter((cb) => cb !== consumerCallback);\n if (this.subscriptions.length === 0) {\n this.unsubscribe();\n }\n };\n };\n }\n\n /**\n * Re-runs the command execution with optional overrides and notifies subscribers of changes\n *\n * This method is called internally when cache updates occur that affect the command's data.\n * It executes the command with the provided overrides and compares the result with the\n * last known result. If the data has changed, it notifies all subscribers with the new data.\n *\n * The method handles deduplication to prevent unnecessary notifications when the data\n * hasn't actually changed, and properly manages the internal execution state.\n *\n * @param overrides - Optional execution overrides for the rerun\n * @returns A promise that resolves to the execution result\n */\n protected rerun(overrides?: ExecuteOverrides) {\n this._isInternalExecution = true;\n\n return this.execute(overrides).then((result) => {\n this._isInternalExecution = false;\n\n // if we're emitting an error Result, we do not dedupe the emits against previous emits\n if (result.isErr()) {\n this.lastResult = { type: 'error', error: result.error.failure };\n this.invokeConsumerCallbacks(err(result.error.failure));\n return result;\n }\n if (\n this.lastResult === undefined ||\n this.lastResult.type === 'error' ||\n !this.equals(this.lastResult.data, result.value.data)\n ) {\n this.lastResult = { type: 'data', data: result.value.data };\n this.invokeConsumerCallbacks(ok(result.value.data));\n }\n\n return result;\n });\n }\n\n /**\n * Invokes all registered consumer callbacks with the provided data\n *\n * This private method safely calls all registered subscriber callbacks with the\n * provided result data. It includes error handling to prevent callback failures\n * from affecting other callbacks or the overall system.\n *\n * @param data - The result data to send to all subscribers\n */\n private invokeConsumerCallbacks(data: Result<ReturnData, Error>) {\n this.subscriptions.forEach((cb) => {\n try {\n cb(data);\n } catch (error) {\n // TODO: Add logging here\n }\n });\n }\n}\n/**\n * Merges a base cache control strategy configuration with execution overrides\n *\n * This utility function combines a base cache control configuration with optional\n * execution overrides, handling the merging of nested configuration properties\n * and ensuring that override values take precedence over base values.\n *\n * @param baseConfig - The base cache control strategy configuration\n * @param overrides - Optional execution overrides to merge with the base config\n * @param overrides.now - Override timestamp for cache control calculations\n * @param overrides.cacheControlConfig - Override cache control strategy configuration\n * @returns A merged cache control strategy configuration\n */\nexport function mergeCacheControlConfigs(\n baseConfig: CacheControlStrategyConfig,\n overrides?: ExecuteOverrides\n): CacheControlStrategyConfig {\n if (!overrides) {\n return baseConfig;\n }\n const now = overrides.now ?? baseConfig.now;\n\n if (!overrides.cacheControlConfig) {\n return {\n ...baseConfig,\n now,\n };\n }\n\n return {\n ...overrides.cacheControlConfig,\n now,\n } as CacheControlStrategyConfig;\n}\n","import { type ServiceDescriptor } from '@conduit-client/utils';\nimport { CacheControlCommand } from './cache-control-command';\n\nexport { CacheControlCommand } from './cache-control-command';\nexport { CacheControlRequestRunner } from './cache-control-request-runner';\n\nexport type CacheControlCommandServiceDescriptor = ServiceDescriptor<\n typeof CacheControlCommand,\n 'cacheControlCommandBaseClass',\n '1.0'\n>;\n\nexport function buildServiceDescriptor(): CacheControlCommandServiceDescriptor {\n return {\n type: 'cacheControlCommandBaseClass',\n version: '1.0',\n service: CacheControlCommand,\n };\n}\n"],"names":["result"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@conduit-client/command-cache-control",
3
+ "version": "2.0.0",
4
+ "private": false,
5
+ "description": "Luvio Cache Control Command",
6
+ "type": "module",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/salesforce-experience-platform-emu/luvio-next.git",
10
+ "directory": "packages/@conduit-client/commands/cache-control"
11
+ },
12
+ "license": "SEE LICENSE IN LICENSE.txt",
13
+ "exports": {
14
+ "./v1": {
15
+ "import": "./dist/v1/index.js",
16
+ "types": "./dist/types/v1/index.d.ts",
17
+ "require": "./dist/v1/index.js"
18
+ }
19
+ },
20
+ "main": "./dist/main/index.js",
21
+ "module": "./dist/main/index.js",
22
+ "types": "./dist/main/index.d.ts",
23
+ "files": [
24
+ "dist/"
25
+ ],
26
+ "scripts": {
27
+ "build": "vite build && tsc --build --emitDeclarationOnly",
28
+ "clean": "rm -rf dist",
29
+ "test": "vitest run",
30
+ "test:size": "size-limit",
31
+ "watch": "npm run build --watch"
32
+ },
33
+ "dependencies": {
34
+ "@conduit-client/command-base": "2.0.0",
35
+ "@conduit-client/service-cache-control": "2.0.0",
36
+ "@conduit-client/service-instrumentation": "2.0.0",
37
+ "@conduit-client/utils": "2.0.0"
38
+ },
39
+ "volta": {
40
+ "extends": "../../../../package.json"
41
+ },
42
+ "size-limit": [
43
+ {
44
+ "path": "./dist/v1/index.js",
45
+ "limit": "4.3 kB"
46
+ }
47
+ ]
48
+ }