import { BaseCommand } from '@luvio/command-base/v1'; import { type NamedPubSubService } from '@luvio/service-pubsub/v1'; import type { Callback, Result, Unsubscribe, SubscribableResult, SyncOrAsync, RefreshResult } from '@luvio/utils'; import type { Cache, ReadonlyCache } from '@luvio/service-cache/v1'; import type { CacheControlStrategyConfig, NamedCacheControllerService } from '@luvio/service-cache-control/v1'; import { InstrumentationAttributes } from '@luvio/service-instrumentation/v1'; import { CacheControlRequestRunner } from './cache-control-request-runner'; type ExecuteOverrides = { now?: number; cacheControlConfig?: Omit; }; /** * An implementation of BaseCommand that allows for extending abstract cache methods * * @typeParam Data cache result for read operations * @typeParam NetworkResult cache result including network metadata * @typeParam ExtraServices additional named services needed by a subclass */ export declare abstract class CacheControlCommand extends BaseCommand>> { protected services: NamedCacheControllerService & Partial & ExtraServices; instantiationTime: number; instrumentationAttributes?: InstrumentationAttributes; private keysUsed; private keysUpdated; private _isInternalExecution; get isInternalExecution(): boolean; /** * Creates a new CacheControlCommand instance * * @param services - Required services including cache controller and optional pub/sub service * @param services.cacheController - The cache controller service for managing cache operations * @param services.pubSub - Optional pub/sub service for cache invalidation notifications * @param services - Additional services specific to the implementation */ constructor(services: NamedCacheControllerService & Partial & ExtraServices); /** * Executes the cache control command with optional overrides * * This method orchestrates the cache control flow by: * 1. Clearing any existing subscriptions * 2. Merging configuration overrides with the base strategy config * 3. Building a request runner for cache operations * 4. Executing the cache controller with the request runner * 5. Handling the result and setting up subscriptions if needed * * @param overrides - Optional execution overrides including timestamp and cache control config * @param overrides.now - Override the current timestamp for cache control calculations * @param overrides.cacheControlConfig - Override cache control strategy configuration * @returns A subscribable result containing either the cached/network data or an error */ execute(overrides?: ExecuteOverrides): SyncOrAsync>; /** * Handles the result from the cache controller and builds the appropriate subscribable result * * This method processes the cache controller execution result and determines the appropriate * response based on network errors, cache errors, and available data. It handles graceful * degradation scenarios where network data is available even when cache operations fail. * * @param result - The result from the cache controller execution * @param requestRunner - The request runner containing network data and errors * @returns A subscribable result with the appropriate data or error */ handleCacheControllerResult(result: Result, requestRunner: CacheControlRequestRunner): SyncOrAsync>; /** * Builds a request runner that orchestrates cache read, network request, and cache write operations * * The request runner encapsulates the three main operations: * 1. Reading from cache with subscription setup * 2. Requesting data from the network * 3. Writing network results to cache and recording keys * * @returns A configured request runner for the cache controller */ protected buildRequestRunner(): CacheControlRequestRunner; /** * Publishes cache update events for keys that were modified during the operation * * This method notifies other parts of the system about cache changes by publishing * a 'cacheUpdate' event with the set of keys that were updated. This enables * cache invalidation and reactive updates across the application. * * @returns A promise that resolves when the update event is published (or immediately if no pub/sub service) */ protected publishUpdatedKeys(): SyncOrAsync; protected get operationType(): 'query' | 'mutation'; protected lastResult: { type: 'data'; data: ReturnData; } | { type: 'error'; error: Error; } | undefined; /** * Subscribes to cache update and invalidation events for reactive updates * * This method sets up subscriptions to listen for changes that affect the data returned * by this Command. * * By default, it subscribes to two types of events on the PubSub service: * - 'cacheUpdate': Triggers a rebuild with the original instantiation time * - 'cacheInvalidation': Triggers a full refresh without time constraints * * This method can be extended by subclasses to add additional subscriptions. * * Note: ALL subscriptions should push an unsubscribe function to the unsubscribers array, * for the lifecycle to work correctly and avoid memory leaks. */ protected subscribe(): void; /** * Unsubscribes from all stored subscriptions * * This method calls all stored unsubscribe functions to clean up event listeners * and prevent memory leaks. It should be called when the command is no longer * needed and is also called before setting up new subscriptions. */ private unsubscribe; protected unsubscribers: Unsubscribe[]; protected subscriptions: Callback>[]; protected abstract readonly cacheControlStrategyConfig: CacheControlStrategyConfig; /** * Compares two result values for equality to determine if a cache update should trigger a rerun * * This method is used to prevent unnecessary reruns when the cached data hasn't actually changed. * The default implementation uses deep equality comparison, but subclasses can override this * to provide more efficient or domain-specific comparison logic. * * @param result1 - The first result to compare * @param result2 - The second result to compare * @returns True if the results are equal, false otherwise * * @todo This should likely be abstract in v2. For v1, provide default comparison logic. */ protected equals(result1: ReturnData | undefined, result2: ReturnData | undefined): boolean; /** * Reads from the cache and returns the cache result or error * * In case of a missing or partial result, this should return either a DataNotFoundError or * DataIncompleteError, respectively, in the Error response * * Note that any subclass should JUST try to read the data from the cache here; it should * NOT try to take metadata (eg cache control semantics) into account while reading the * data. The CacheController is responsible for enforcing those semantics. * * @param cache source of cached data * @returns result or error from the cache */ abstract readFromCache(cache: ReadonlyCache): SyncOrAsync>; /** * Requests the resource from network, returning the result along with network metadata * * @returns network result including metadata */ abstract requestFromNetwork(): SyncOrAsync>; /** * Writes the given result to the cache * * @param cache source of cached data * @param networkResult network result with metadata to write to the cache. This param will * be the result of a previous requestFromNetwork call, if the CacheController determines * that the data should be cached */ abstract writeToCache(cache: Cache, networkResult: Result): SyncOrAsync; /** * Hook method called after a network request completes * * This method provides a point for subclasses to perform post-request operations * such as logging, metrics collection, or cleanup. The default implementation * is empty and can be overridden by subclasses as needed. * * @param _options - Request completion options * @param _options.statusCode - HTTP status code from the network response */ afterRequestHooks(_options: { statusCode: number; }): Promise; /** * Forces a refresh of the cached data by bypassing cache and fetching from network * * This method executes the command with a "no-cache" configuration, ensuring that * fresh data is fetched from the network regardless of cache state. It's useful * for scenarios where you need to ensure the most up-to-date data. * * @returns A refresh result indicating success or failure of the refresh operation */ refresh(): SyncOrAsync>; /** * Writes network result to cache and records the keys that were updated * * This method wraps the cache write operation with key tracking functionality. * It uses a recordable cache wrapper to capture which keys are modified during * the write operation, then updates the internal tracking of used and updated keys. * * @param cache - The cache instance to write to * @param networkResult - The network result containing data to write to cache * @returns A result indicating success or failure of the write operation */ writeToCacheAndRecordKeys(cache: Cache, networkResult: Result): SyncOrAsync>; /** * Builds a subscribable result by reading from cache and setting up subscriptions * * This method reads data from the cache and wraps the result in a subscribable * structure that allows consumers to subscribe to updates. It also tracks which * cache keys were read for future invalidation purposes. * * @param cache - The readonly cache to read from * @returns A subscribable result containing the cached data or error */ buildResultWithSubscribe(cache: ReadonlyCache): PromiseLike>): Unsubscribe; refresh(): SyncOrAsync>; } & { data: any; }, { subscribe(callback: Callback>): Unsubscribe; refresh(): SyncOrAsync>; } & { failure: any; }> | import("@luvio/utils").Err<{ subscribe(callback: Callback>): Unsubscribe; refresh(): SyncOrAsync>; } & { data: any; }, { subscribe(callback: Callback>): Unsubscribe; refresh(): SyncOrAsync>; } & { failure: any; }>>; /** * Builds a function that subscribes to cache changes via the pubsub service. Whenever * relevant cache updates occur, it re-reads the data and compares it against * the last known value. If a change is detected, the provided * callback is invoked. * * @returns an unsubscribe function to stop watching for updates */ protected buildSubscribe(): (consumerCallback: Callback>) => Unsubscribe; /** * Re-runs the command execution with optional overrides and notifies subscribers of changes * * This method is called internally when cache updates occur that affect the command's data. * It executes the command with the provided overrides and compares the result with the * last known result. If the data has changed, it notifies all subscribers with the new data. * * The method handles deduplication to prevent unnecessary notifications when the data * hasn't actually changed, and properly manages the internal execution state. * * @param overrides - Optional execution overrides for the rerun * @returns A promise that resolves to the execution result */ protected rerun(overrides?: ExecuteOverrides): PromiseLike>): Unsubscribe; refresh(): SyncOrAsync>; } & { data: ReturnData; }, { subscribe(callback: Callback>): Unsubscribe; refresh(): SyncOrAsync>; } & { failure: Error; }> | import("@luvio/utils").Err<{ subscribe(callback: Callback>): Unsubscribe; refresh(): SyncOrAsync>; } & { data: ReturnData; }, { subscribe(callback: Callback>): Unsubscribe; refresh(): SyncOrAsync>; } & { failure: Error; }>>; /** * Invokes all registered consumer callbacks with the provided data * * This private method safely calls all registered subscriber callbacks with the * provided result data. It includes error handling to prevent callback failures * from affecting other callbacks or the overall system. * * @param data - The result data to send to all subscribers */ private invokeConsumerCallbacks; } /** * Merges a base cache control strategy configuration with execution overrides * * This utility function combines a base cache control configuration with optional * execution overrides, handling the merging of nested configuration properties * and ensuring that override values take precedence over base values. * * @param baseConfig - The base cache control strategy configuration * @param overrides - Optional execution overrides to merge with the base config * @param overrides.now - Override timestamp for cache control calculations * @param overrides.cacheControlConfig - Override cache control strategy configuration * @returns A merged cache control strategy configuration */ export declare function mergeCacheControlConfigs(baseConfig: CacheControlStrategyConfig, overrides?: ExecuteOverrides): CacheControlStrategyConfig; export {};