alepha
Version:
Easy-to-use modern TypeScript framework for building many kind of applications.
386 lines • 14 kB
TypeScript
import { AlephaError, AsyncFn, Middleware, Primitive, Static } from "alepha";
import { TopicProvider } from "alepha/topic";
import { DateTime, DateTimeProvider, DurationLike, Timeout } from "alepha/datetime";
//#region ../../src/lock/core/errors/LockAcquireError.d.ts
declare class LockAcquireError extends AlephaError {
constructor(name: string);
}
//#endregion
//#region ../../src/lock/core/providers/LockProvider.d.ts
/**
* Store Provider Interface
*/
declare abstract class LockProvider {
/**
* Set the string value of a key.
*
* @param key The key of the value to set.
* @param value The value to set.
* @param nx If set to true, the key will only be set if it does not already exist.
* @param px Set the specified expire time, in milliseconds.
*/
abstract set(key: string, value: string, nx?: boolean, px?: number): Promise<string>;
/**
* Remove the specified keys.
*
* @param keys The keys to delete.
*/
abstract del(...keys: string[]): Promise<void>;
}
//#endregion
//#region ../../src/lock/core/primitives/$lock.d.ts
/**
* Distributed lock middleware for `use` arrays in `$action`, `$job`, `$page`, `$pipeline`.
*
* Acquires a distributed lock before the handler runs and releases it after completion.
* Throws `LockAcquireError` if the lock cannot be acquired (unless `wait: true`).
*
* ```ts
* processOrder = $action({
* use: [$lock({ name: "process-order" })],
* handler: async ({ body }) => { ... },
* });
* ```
*/
declare const $lock: (options: LockMiddlewareOptions) => Middleware;
/**
* Options for $lock in middleware mode (no handler).
*/
interface LockMiddlewareOptions {
/**
* Lock key name. Required in middleware mode (no class context available).
* Can be a static string or a function that derives the key from handler args.
*/
name: string | ((...args: any[]) => string);
/**
* Whether to wait for the lock to become available.
*
* @default false
*/
wait?: boolean;
/**
* Maximum duration the lock can be held before automatic expiration.
*
* @default [5, "minutes"]
*/
maxDuration?: DurationLike;
}
interface LockPrimitiveOptions<TFunc extends AsyncFn> {
/**
* The function to execute when the lock is successfully acquired.
*
* This function:
* - Only executes on the instance that successfully acquires the lock
* - Has exclusive access to the protected resource during execution
* - Should contain the critical section logic that must not run concurrently
* - Can be async and perform any operations needed
* - Will automatically release the lock upon completion or error
* - Has access to the full Alepha dependency injection container
*
* **Handler Design Guidelines**:
* - Keep critical sections as short as possible to minimize lock contention
* - Include proper error handling to ensure locks are released
* - Use timeouts for external operations to prevent deadlocks
* - Log important operations for debugging and monitoring
* - Consider idempotency for handlers that might be retried
*
* @param ...args - The arguments passed to the lock execution
* @returns Promise that resolves when the protected operation is complete
*
* @example
* ```ts
* handler: async (batchId: string) => {
* console.log(`Processing batch ${batchId} - only one instance will run this`);
*
* const batch = await this.getBatchData(batchId);
* const results = await this.processBatchItems(batch.items);
* await this.saveBatchResults(batchId, results);
*
* console.log(`Batch ${batchId} completed successfully`);
* }
* ```
*/
handler: TFunc;
/**
* Whether the lock should wait for other instances to complete before giving up.
*
* **wait = false (default)**:
* - Non-blocking behavior - if lock is held, immediately return without executing
* - Perfect for scheduled tasks where you only want one execution per trigger
* - Use when multiple triggers are acceptable but concurrent execution is not
* - Examples: periodic cleanup, cron jobs, background maintenance
*
* **wait = true**:
* - Blocking behavior - wait for the current lock holder to finish
* - All instances will eventually execute (one after another)
* - Perfect for initialization tasks where all instances need the work completed
* - Examples: database migrations, cache warming, resource initialization
*
* **Trade-offs**:
* - Non-waiting: Better performance, may miss executions if timing is off
* - Waiting: Guaranteed execution order, slower overall throughput
*
* @default false
*
* @example
* ```ts
* // Scheduled task - don't wait, just skip if already running
* scheduledCleanup = $lock({
* wait: false, // Skip if cleanup already running
* handler: async () => { } // perform cleanup
* });
*
* // Migration - wait for completion before proceeding
* migration = $lock({
* wait: true, // All instances wait for migration to complete
* handler: async () => { } // perform migration
* });
* ```
*/
wait?: boolean;
/**
* The unique identifier for the lock.
*
* Can be either:
* - **Static string**: A fixed identifier for the lock
* - **Dynamic function**: A function that generates the lock key based on arguments
*
* **Dynamic Lock Keys**:
* - Enable per-resource locking (e.g., per-user, per-file, per-product)
* - Allow fine-grained concurrency control
* - Prevent unnecessary blocking between unrelated operations
*
* **Key Design Guidelines**:
* - Use descriptive names that indicate the protected resource
* - Include relevant identifiers for dynamic keys
* - Keep keys reasonably short but unique
* - Consider using hierarchical naming (e.g., "service:operation:resource")
*
* If not provided, defaults to `{serviceName}:{propertyKey}`.
*
* @example "user-migration"
* @example "daily-report-generation"
* @example (userId: string) => `user-profile-update:${userId}`
* @example (fileId: string, operation: string) => `file-${operation}:${fileId}`
*
* @example
* ```ts
* // Static lock key - all instances compete for the same lock
* globalCleanup = $lock({
* name: "system-cleanup",
* handler: async () => { } // perform cleanup
* });
*
* // Dynamic lock key - per-user locks, users don't block each other
* updateUserProfile = $lock({
* name: (userId: string) => `user-update:${userId}`,
* handler: async (userId: string, data: UserData) => {
* // Only one update per user at a time, but different users can update concurrently
* }
* });
* ```
*/
name?: string | ((...args: Parameters<TFunc>) => string);
/**
* Maximum duration the lock can be held before it expires automatically.
*
* This prevents deadlocks when a process dies while holding a lock or when
* operations take longer than expected. The lock will be automatically released
* after this duration, allowing other instances to proceed.
*
* **Duration Guidelines**:
* - Set based on expected operation duration plus safety margin
* - Too short: Operations may be interrupted by early expiration
* - Too long: Failed processes block others for extended periods
* - Consider worst-case scenarios and external dependency timeouts
*
* **Typical Values**:
* - Quick operations: 30 seconds - 2 minutes
* - Database operations: 5 - 15 minutes
* - File processing: 10 - 30 minutes
* - Large migrations: 30 minutes - 2 hours
*
* @default [5, "minutes"]
*
* @example [30, "seconds"] // Quick operations
* @example [10, "minutes"] // Database migrations
* @example [1, "hour"] // Long-running batch jobs
*
* @example
* ```ts
* quickTask = $lock({
* maxDuration: [2, "minutes"], // Quick timeout for fast operations
* handler: async () => { } // perform quick task
* });
*
* heavyProcessing = $lock({
* maxDuration: [30, "minutes"], // Longer timeout for heavy work
* handler: async () => { } // perform heavy processing
* });
* ```
*/
maxDuration?: DurationLike;
/**
* Additional time to keep the lock active after the handler completes successfully.
*
* This provides a "cooling off" period that can be useful for:
* - Preventing immediate re-execution of the same operation
* - Giving time for related systems to process the results
* - Avoiding race conditions with dependent operations
* - Providing a buffer for cleanup operations
*
* Can be either:
* - **Static duration**: Fixed grace period for all executions
* - **Dynamic function**: Grace period determined by execution arguments
* - **undefined**: No grace period, lock released immediately after completion
*
* **Grace Period Use Cases**:
* - File processing: Prevent immediate reprocessing of uploaded files
* - Cache updates: Allow time for cache propagation
* - Batch operations: Prevent overlapping batch processing
* - External API calls: Respect rate limiting requirements
*
* @default undefined (no grace period)
*
* @example [5, "minutes"] // Fixed 5-minute grace period
* @example [30, "seconds"] // Short grace for quick operations
* @example (userId: string) => userId.startsWith("premium") ? [10, "minutes"] : [2, "minutes"]
*
* @example
* ```ts
* fileProcessor = $lock({
* gracePeriod: [10, "minutes"], // Prevent reprocessing same file immediately
* handler: async (filePath: string) => {
* await this.processFile(filePath);
* }
* });
*
* userOperation = $lock({
* gracePeriod: (userId: string, operation: string) => {
* // Dynamic grace based on operation type
* return operation === 'migration' ? [30, "minutes"] : [5, "minutes"];
* },
* handler: async (userId: string, operation: string) => {
* await this.performUserOperation(userId, operation);
* }
* });
* ```
*/
gracePeriod?: ((...args: Parameters<TFunc>) => DurationLike | undefined) | DurationLike;
}
/**
* Lock configuration atom.
*/
declare const lockOptions: import("alepha").Atom<import("zod").ZodObject<{
prefixKey: import("zod").ZodString;
}, import("zod/v4/core").$strip>, "alepha.lock.options">;
type LockAtomOptions = Static<typeof lockOptions.schema>;
declare module "alepha" {
interface State {
[lockOptions.key]: LockAtomOptions;
}
}
declare class LockPrimitive<TFunc extends AsyncFn> extends Primitive<LockPrimitiveOptions<TFunc>> {
protected readonly log: import("alepha/logger").Logger;
protected readonly provider: LockProvider;
protected readonly settings: Readonly<{
prefixKey: string;
}>;
protected readonly dateTimeProvider: DateTimeProvider;
/**
* Lazy-initialized UUID to avoid calling crypto.randomUUID() in global scope.
* Cloudflare Workers doesn't allow random value generation during initialization.
*/
protected _id?: string;
protected get id(): string;
readonly maxDuration: import("alepha/datetime").Duration;
protected readonly topicLockEnd: import("alepha/topic").TopicPrimitive<{
payload: import("zod").ZodObject<{
name: import("zod").ZodString;
}, import("zod/v4/core").$strip>;
}>;
run(...args: Parameters<TFunc>): Promise<void>;
/**
* Set the lock for the given key.
*/
protected lock(key: string): Promise<LockResult>;
protected setGracePeriod(key: string, lock: LockResult, ...args: Parameters<TFunc>): Promise<void>;
protected wait(key: string, maxDuration: DurationLike): Promise<void>;
protected key(...args: Parameters<TFunc>): string;
protected parse(value: string): LockResult;
}
interface LockResult {
id: string;
createdAt: DateTime;
endedAt?: DateTime;
response?: string;
}
//#endregion
//#region ../../src/lock/core/providers/LockTopicProvider.d.ts
declare abstract class LockTopicProvider extends TopicProvider {}
//#endregion
//#region ../../src/lock/core/providers/MemoryLockProvider.d.ts
/**
* A simple in-memory store provider.
*/
declare class MemoryLockProvider implements LockProvider {
protected readonly dateTimeProvider: DateTimeProvider;
protected readonly log: import("alepha/logger").Logger;
/**
* The in-memory store.
*/
protected store: Record<string, string>;
/**
* Timeouts used to expire keys.
*/
protected storeTimeout: Record<string, Timeout>;
set(key: string, value: string, nx?: boolean, px?: number): Promise<string>;
del(...keys: string[]): Promise<void>;
protected ttl(key: string, ms: number): void;
}
//#endregion
//#region ../../src/lock/core/index.d.ts
declare module "alepha" {
interface Hooks {
/**
* Fires when a lock is successfully acquired.
*/
"lock:acquired": {
name: string;
id: string;
maxDurationMs: number;
};
/**
* Fires when a lock is released (handler completed or threw).
*/
"lock:released": {
name: string;
id: string;
heldMs: number;
};
/**
* Fires when a lock acquisition contends with another holder.
* Emitted whether the caller eventually acquires (`wait: true`) or fails.
*/
"lock:contended": {
name: string;
id: string;
};
}
}
/**
* Resource locking for distributed systems.
*
* **Features:**
* - Distributed locks with timeout
* - Time-based lock expiration
* - Automatic release on scope exit
* - Distributed coordination via Redis
* - Providers: Memory (dev), Redis (production)
*
* @module alepha.lock
*/
declare const AlephaLock: import("alepha").Service<import("alepha").Module>;
//#endregion
export { $lock, AlephaLock, LockAcquireError, LockAtomOptions, LockMiddlewareOptions, LockPrimitive, LockPrimitiveOptions, LockProvider, LockResult, LockTopicProvider, MemoryLockProvider, lockOptions };
//# sourceMappingURL=index.d.ts.map