alepha
Version:
Easy-to-use modern TypeScript framework for building many kind of applications.
637 lines • 20.1 kB
TypeScript
import { Alepha, MiddlewareMetadata, OPTIONS, Primitive, Service, Static } from "alepha";
import { DateTimeProvider, DurationLike, Timeout } from "alepha/datetime";
//#region ../../src/cache/core/providers/CacheProvider.d.ts
/**
* Cache provider interface.
*
* All methods are asynchronous and return promises.
* Values are stored as Uint8Array.
*/
declare abstract class CacheProvider {
protected encoder: TextEncoder;
protected decoder: TextDecoder;
protected codes: {
BINARY: number;
JSON: number;
STRING: number;
COMPRESSED: number;
};
/**
* Get the value of a key.
*
* @param name Cache name, used to group keys. Should be Redis-like "some:group:name" format.
* @param key The key of the value to get.
*
* @return The value of the key, or undefined if the key does not exist.
*/
abstract get(name: string, key: string): Promise<Uint8Array | undefined>;
/**
* Set the string value of a key.
*
* @param name Cache name, used to group keys. Should be Redis-like "some:group:name" format.
* @param key The key of the value to set.
* @param value The value to set.
* @param ttl The time-to-live of the key, in milliseconds.
*
* @return The value of the key.
*/
abstract set(name: string, key: string, value: Uint8Array, ttl?: number): Promise<Uint8Array>;
/**
* Remove the specified keys.
*
* @param name Cache name, used to group keys. Should be Redis-like "some:group:name" format.
* @param keys The keys to delete.
*/
abstract del(name: string, ...keys: string[]): Promise<void>;
abstract has(name: string, key: string): Promise<boolean>;
abstract keys(name: string, filter?: string): Promise<string[]>;
/**
* Remove all keys from all cache names.
*/
abstract clear(): Promise<void>;
/**
* Increment the integer value of a key by the given amount.
*
* If the key does not exist, it is set to 0 before performing the operation.
* This operation is atomic when using Redis.
*
* @param name Cache name, used to group keys.
* @param key The key to increment.
* @param amount The amount to increment by.
* @returns The new value after incrementing.
*/
abstract incr(name: string, key: string, amount: number): Promise<number>;
/**
* Set a typed value with automatic serialization and optional compression.
*/
setTyped(name: string, key: string, value: unknown, options?: {
ttl?: number;
compress?: boolean;
}): Promise<void>;
/**
* Get a typed value with automatic deserialization and optional decompression.
*/
getTyped<T>(name: string, key: string): Promise<T | undefined>;
/**
* Invalidate cache keys with wildcard support.
*
* Keys ending in `*` are expanded via `this.keys()`.
* Called with no keys, delegates to `this.del(name)` which clears the entire container.
*/
invalidateKeys(name: string, keys: string[]): Promise<void>;
/**
* Serialize a value to a typed Uint8Array with a leading type marker byte.
*/
protected serialize(value: unknown): Uint8Array;
/**
* Deserialize a typed Uint8Array back to the original value.
*/
protected deserialize<T>(uint8Array: Uint8Array): T;
/**
* Compress data with gzip, prepending a COMPRESSED marker byte.
*/
protected compress(data: Uint8Array): Promise<Uint8Array>;
/**
* Decompress gzipped data.
*/
protected decompress(data: Uint8Array): Promise<Uint8Array>;
}
//#endregion
//#region ../../src/cache/core/primitives/$cache.d.ts
/**
* Creates a cache primitive for caching with automatic management.
*
* **Middleware mode** (no `handler`) — usable in `use` arrays AND as a store:
* ```ts
* class UserService {
* userCache = $cache({ name: "users", ttl: [10, "minutes"] });
*
* fetchUser = $pipeline({
* use: [this.userCache],
* handler: async (userId: string) => this.repo.getById(userId),
* });
*
* async invalidateUser(userId: string) {
* await this.userCache.invalidate(userId);
* }
* }
* ```
*
* **Primitive mode** (with `handler`) — standalone callable:
* ```ts
* getUserData = $cache({
* name: "user-data",
* ttl: [10, "minutes"],
* handler: async (userId: string) => {
* return await database.users.findById(userId);
* }
* });
* ```
*/
declare function $cache<TReturn = string, TParameter extends any[] = any[]>(options: CachePrimitiveOptions<TReturn, TParameter> & {
handler: (...args: TParameter) => TReturn;
}): CachePrimitiveFn<TReturn, TParameter>;
declare function $cache<TReturn = any, TParameter extends any[] = any[]>(options?: CachePrimitiveOptions<TReturn, TParameter>): CacheMiddlewareFn<TReturn>;
/**
* Options for the in-memory L1 tier.
*/
interface CacheMemoryTierOptions {
/**
* TTL for the in-memory tier. Should be ≤ the remote `ttl`.
* Bounds the cross-isolate staleness window after invalidation.
*
* @default min(ttl, 30s)
*/
ttl?: DurationLike;
/**
* LRU bound — max entries kept in memory before eviction.
*
* @default 500
*/
max?: number;
/**
* Also cache provider misses (`undefined`) in memory for this duration.
* Prevents hammering the remote tier on cold/unknown keys.
*
* @default off
*/
negative?: DurationLike;
}
interface CachePrimitiveOptions<TReturn = any, TParameter extends any[] = any[]> {
/**
* The cache name. This is useful for invalidating multiple caches at once.
*
* Store key as `cache:$name:$key`.
*
* @default Name of the key of the class.
*/
name?: string;
/**
* Function which returns cached data.
*/
handler?: (...args: TParameter) => TReturn;
/**
* The key generator for the cache.
* If not provided, the arguments will be json.stringify().
*/
key?: (...args: TParameter) => string;
/**
* The store provider for the cache.
*
* Accepts:
* - `"memory"` — short-circuits to {@link MemoryCacheProvider} regardless
* of the default `CacheProvider` binding. Useful for caches that must
* stay process-local (e.g. ETag, HTTP client).
* - A {@link CacheProvider} class (concrete OR abstract) — resolved via
* `alepha.inject(...)` at primitive construction. Use this to opt a
* specific cache into a non-default backend (e.g.
* `provider: DatabaseCacheProvider` to keep one cache in SQL while the
* rest of the app uses Cloudflare KV).
* - `undefined` — falls back to whatever is bound to `CacheProvider` in
* the container. On Cloudflare workers this is
* {@link CloudflareKVProvider} by default; on Node it's
* {@link MemoryCacheProvider}.
*
* Note: passing an *abstract* class works because Alepha's DI resolves
* through substitutions, e.g. `alepha.with({ provide: CacheProvider, use:
* MyCacheProvider })`.
*/
provider?: Service<CacheProvider> | "memory";
/**
* The time-to-live for the cache in seconds.
* Set 0 to skip expiration.
*
* @default 300 (5 minutes).
*/
ttl?: DurationLike;
/**
* If the cache is disabled.
*/
disabled?: boolean;
/**
* Enable gzip compression for cached values.
* Reduces storage size by 60-80% for JSON payloads at the cost of CPU.
*/
compress?: boolean;
/**
* Add an in-process L1 memory tier in front of `provider`.
*
* Reads check memory first, fall back to the provider on miss. Writes go
* to both tiers (write-through), so own-writes are immediately visible.
*
* Caveats:
* - Per-process only. Each Worker isolate / Node process has its own L1.
* `invalidate()` clears the local L1 + the remote provider; other
* processes keep their L1 until its TTL expires.
* - Use a short L1 TTL to bound the cross-isolate staleness window.
*
* @default off
*/
memory?: true | CacheMemoryTierOptions;
/**
* Stale-while-revalidate window. After `ttl` expires, the cached value
* remains servable for `stale` longer; reads in this window return the
* stale value immediately and trigger ONE background refresh
* (single-flight per key).
*
* Requires a `handler` (primitive mode) OR middleware mode wrapping a
* handler — the cache needs to know how to recompute.
*
* @default off
*/
stale?: DurationLike;
}
/**
* Cache configuration atom.
*/
declare const cacheOptions: import("alepha").Atom<import("zod").ZodObject<{
enabled: import("zod").ZodDefault<import("zod").ZodBoolean>;
defaultTtl: import("zod").ZodDefault<import("zod").ZodNumber>;
}, import("zod/v4/core").$strip>, "alepha.cache.options">;
type CacheAtomOptions = Static<typeof cacheOptions.schema>;
declare module "alepha" {
interface State {
[cacheOptions.key]: CacheAtomOptions;
}
}
declare const SWR_MARKER: "__swr";
type SwrEnvelope = {
[SWR_MARKER]: 1;
v: unknown;
f: number;
};
type L1Entry<T> = {
value: T | undefined;
expiresAt: number;
negative: boolean;
};
type ReadResult<T> = {
value: T | undefined;
stale: boolean;
};
declare class CachePrimitive<TReturn = any, TParameter extends any[] = any[]> extends Primitive<CachePrimitiveOptions<TReturn, TParameter>> {
protected readonly settings: Readonly<{
enabled: boolean;
defaultTtl: number;
}>;
protected readonly dateTimeProvider: DateTimeProvider;
readonly provider: CacheProvider;
protected readonly memoryStore?: Map<string, L1Entry<TReturn>>;
protected readonly memoryMax: number;
protected readonly memoryTtlMs: number;
protected readonly negativeTtlMs: number;
protected readonly inflightRefreshes: Map<string, Promise<TReturn>>;
constructor(args: ConstructorParameters<typeof Primitive<CachePrimitiveOptions<TReturn, TParameter>>>[0]);
get container(): string;
run(...args: TParameter): Promise<TReturn>;
key(...args: TParameter): string;
incr(key: string, amount?: number): Promise<number>;
invalidate(...keys: string[]): Promise<void>;
set(key: string, value: TReturn, ttl?: DurationLike): Promise<void>;
get(key: string): Promise<TReturn | undefined>;
/**
* Internal read that also reports whether the value is stale (SWR
* grace window). Middleware and `run()` use this to decide whether to
* schedule a background refresh.
*/
read(key: string): Promise<ReadResult<TReturn>>;
/**
* Run a handler under single-flight: concurrent callers for the same
* key share one in-flight promise.
*/
runSingleFlight(key: string, handler: () => Promise<TReturn> | TReturn): Promise<TReturn>;
/**
* Schedule a background refresh for a stale key. At most one refresh
* per key is in-flight at any time; failures are swallowed (the stale
* value keeps being served until expiry).
*/
scheduleRefresh(key: string, handler: () => Promise<TReturn> | TReturn): void;
protected $provider(): CacheProvider;
protected isSwrEnvelope(value: unknown): value is SwrEnvelope;
protected setL1(key: string, entry: L1Entry<TReturn>): void;
protected delL1(key: string): void;
}
interface CachePrimitiveFn<TReturn = any, TParameter extends any[] = any[]> extends CachePrimitive<TReturn, TParameter> {
/**
* Run the cache primitive with the provided arguments.
*/
(...args: TParameter): Promise<TReturn>;
}
/**
* Cache middleware + store. Callable as middleware `(handler) => wrappedHandler`
* AND exposes store methods (`.get()`, `.set()`, `.invalidate()`, `.incr()`).
*/
interface CacheMiddlewareFn<TReturn = any> extends CachePrimitive<TReturn, any[]> {
<T extends (...args: any[]) => any>(handler: T): T;
[OPTIONS]?: MiddlewareMetadata;
}
//#endregion
//#region ../../src/cache/core/providers/CloudflareKVProvider.d.ts
/**
* KVNamespace interface matching Cloudflare's KV API.
*/
interface KVNamespace {
get(key: string, type: "arrayBuffer"): Promise<ArrayBuffer | null>;
get(key: string, type: "text"): Promise<string | null>;
get(key: string, type?: "text"): Promise<string | null>;
put(key: string, value: string | ArrayBuffer | ReadableStream, options?: KVPutOptions): Promise<void>;
delete(key: string): Promise<void>;
list(options?: KVListOptions): Promise<KVListResult>;
}
interface KVPutOptions {
expiration?: number;
expirationTtl?: number;
metadata?: unknown;
}
interface KVListOptions {
prefix?: string;
limit?: number;
cursor?: string;
}
interface KVListResult {
keys: KVListKey[];
list_complete: boolean;
cursor?: string;
}
interface KVListKey {
name: string;
expiration?: number;
metadata?: unknown;
}
/**
* Default KV binding name used in wrangler configuration.
*/
declare const KV_DEFAULT_BINDING = "KV_CACHE";
/**
* Cloudflare KV cache provider.
*
* Uses a KV namespace binding for all cache operations.
* Keys are stored as: `cache:{name}:{key}`
*
* **Required Cloudflare binding:**
* - `KV_CACHE` - A KV namespace binding in wrangler configuration
*
* @example
* ```toml
* # wrangler.toml - automatically generated by alepha build
* [[kv_namespaces]]
* binding = "KV_CACHE"
* id = "abc123"
* ```
*/
declare class CloudflareKVProvider extends CacheProvider {
protected readonly alepha: Alepha;
protected readonly log: import("alepha/logger").Logger;
protected kv?: KVNamespace;
protected readonly onStart: import("alepha").HookPrimitive<"start">;
get(name: string, key: string): Promise<Uint8Array | undefined>;
set(name: string, key: string, value: Uint8Array, ttl?: number): Promise<Uint8Array>;
del(name: string, ...keys: string[]): Promise<void>;
has(name: string, key: string): Promise<boolean>;
keys(name: string, filter?: string): Promise<string[]>;
clear(): Promise<void>;
incr(name: string, key: string, amount: number): Promise<number>;
/**
* Build the full KV key: `cache:{name}:{key}`
*/
protected prefix(...path: string[]): string;
protected getKV(): KVNamespace;
/**
* List all keys matching a prefix, handling pagination.
*/
protected listAllKeys(prefix: string): Promise<string[]>;
}
//#endregion
//#region ../../src/cache/core/providers/MemoryCacheProvider.d.ts
type CacheName = string;
type CacheKey = string;
type CacheValue = {
data?: Uint8Array;
timeout?: Timeout;
};
interface MemoryCacheCall {
name: string;
key: string;
timestamp: number;
}
interface MemoryCacheSetCall extends MemoryCacheCall {
value: Uint8Array;
ttl?: number;
}
interface MemoryCacheDelCall {
name: string;
keys: string[];
timestamp: number;
}
interface MemoryCacheStats {
hits: number;
misses: number;
sets: number;
deletes: number;
}
interface MemoryCacheProviderOptions {
/**
* Error to throw on get operations (for testing error handling)
*/
getError?: Error | null;
/**
* Error to throw on set operations (for testing error handling)
*/
setError?: Error | null;
/**
* Error to throw on del operations (for testing error handling)
*/
delError?: Error | null;
}
/**
* In-memory implementation of CacheProvider for testing.
*
* This provider stores all cache entries in memory, making it ideal for
* unit tests that need to verify cache operations without touching Redis or other backends.
*
* @example
* ```typescript
* // In tests, substitute the real CacheProvider with MemoryCacheProvider
* const alepha = Alepha.create().with({
* provide: CacheProvider,
* use: MemoryCacheProvider,
* });
*
* // Run code that uses caching
* const service = alepha.inject(MyService);
* await service.fetchWithCache("key");
*
* // Verify cache behavior
* const cache = alepha.inject(MemoryCacheProvider);
* expect(cache.stats().misses).toBe(1);
* await service.fetchWithCache("key");
* expect(cache.stats().hits).toBe(1);
* ```
*/
declare class MemoryCacheProvider extends CacheProvider {
protected readonly dateTimeProvider: DateTimeProvider;
protected readonly log: import("alepha/logger").Logger;
protected store: Record<CacheName, Record<CacheKey, CacheValue>>;
/**
* All recorded get calls.
*/
getCalls: MemoryCacheCall[];
/**
* All recorded set calls.
*/
setCalls: MemoryCacheSetCall[];
/**
* All recorded del calls.
*/
delCalls: MemoryCacheDelCall[];
/**
* Cache statistics.
*/
protected _stats: MemoryCacheStats;
/**
* Error to throw on get (for testing error handling)
*/
getError: Error | null;
/**
* Error to throw on set (for testing error handling)
*/
setError: Error | null;
/**
* Error to throw on del (for testing error handling)
*/
delError: Error | null;
constructor(options?: MemoryCacheProviderOptions);
get(name: string, key: string): Promise<Uint8Array | undefined>;
set(name: string, key: string, value: Uint8Array, ttl?: number): Promise<Uint8Array>;
del(name: string, ...keys: string[]): Promise<void>;
has(name: string, key: string): Promise<boolean>;
keys(name: string, filter?: string): Promise<string[]>;
clear(): Promise<void>;
incr(name: string, key: string, amount: number): Promise<number>;
/**
* Get cache statistics (hits, misses, sets, deletes).
*
* @example
* ```typescript
* expect(cache.stats().hits).toBe(1);
* expect(cache.stats().misses).toBe(0);
* ```
*/
stats(): MemoryCacheStats;
/**
* Check if a key was set during the test.
*
* @example
* ```typescript
* expect(cache.wasSet("my-cache", "user:123")).toBe(true);
* ```
*/
wasSet(name: string, key?: string): boolean;
/**
* Check if a key was retrieved during the test.
*
* @example
* ```typescript
* expect(cache.wasGet("my-cache", "user:123")).toBe(true);
* ```
*/
wasGet(name: string, key?: string): boolean;
/**
* Check if a key was deleted during the test.
*
* @example
* ```typescript
* expect(cache.wasDeleted("my-cache", "user:123")).toBe(true);
* ```
*/
wasDeleted(name: string, key?: string): boolean;
/**
* Get the number of cached entries for a specific cache name.
*
* @example
* ```typescript
* expect(cache.size("my-cache")).toBe(5);
* ```
*/
size(name?: string): number;
/**
* Get all cache names.
*
* @example
* ```typescript
* expect(cache.names()).toContain("my-cache");
* ```
*/
names(): string[];
/**
* Reset all in-memory state (useful between tests).
*
* @example
* ```typescript
* beforeEach(() => {
* cache.reset();
* });
* ```
*/
reset(): void;
}
//#endregion
//#region ../../src/cache/core/index.d.ts
declare module "alepha" {
interface Hooks {
/**
* Fires when a cache lookup finds a value.
*/
"cache:hit": {
container: string;
key: string;
};
/**
* Fires when a cache lookup does not find a value.
*/
"cache:miss": {
container: string;
key: string;
};
/**
* Fires when a value is written to the cache.
*/
"cache:set": {
container: string;
key: string;
ttlMs?: number;
};
/**
* Fires when a stale value (SWR grace window) is served and a
* background refresh is scheduled.
*/
"cache:stale": {
container: string;
key: string;
};
/**
* Fires when a background SWR refresh completes successfully and
* the value has been written back to the cache.
*/
"cache:revalidate": {
container: string;
key: string;
};
}
}
/**
* Type-safe caching with TTL support.
*
* **Features:**
* - Cached computations with type-safe keys and values
* - Configurable TTL
* - Cache invalidation
* - Automatic cache population
* - Providers: Memory (default), Redis
*
* @module alepha.cache
*/
declare const AlephaCache: import("alepha").Service<import("alepha").Module>;
//#endregion
export { $cache, AlephaCache, CacheAtomOptions, CacheMemoryTierOptions, CacheMiddlewareFn, CachePrimitive, CachePrimitiveFn, CachePrimitiveOptions, CacheProvider, CloudflareKVProvider, KVListKey, KVListOptions, KVListResult, KVNamespace, KVPutOptions, KV_DEFAULT_BINDING, MemoryCacheCall, MemoryCacheDelCall, MemoryCacheProvider, MemoryCacheProviderOptions, MemoryCacheSetCall, MemoryCacheStats, cacheOptions };
//# sourceMappingURL=index.d.ts.map