commandkit 1.0.0-dev.20250622124751 → 1.0.0-dev.20250622135945
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/async-queue.cjs +9 -0
- package/async-queue.d.ts +1 -0
- package/dist/cli/information.js +1 -1
- package/dist/index.js +1 -1
- package/dist/utils/useful-stuff/async-queue.d.ts +84 -0
- package/dist/utils/useful-stuff/async-queue.js +136 -0
- package/dist/utils/useful-stuff/async-queue.js.map +1 -0
- package/dist/utils/useful-stuff/mutex.d.ts +188 -0
- package/dist/utils/useful-stuff/mutex.js +214 -0
- package/dist/utils/useful-stuff/mutex.js.map +1 -0
- package/dist/utils/useful-stuff/ratelimiter.d.ts +193 -0
- package/dist/utils/useful-stuff/ratelimiter.js +223 -0
- package/dist/utils/useful-stuff/ratelimiter.js.map +1 -0
- package/dist/utils/useful-stuff/semaphore.d.ts +227 -0
- package/dist/utils/useful-stuff/semaphore.js +267 -0
- package/dist/utils/useful-stuff/semaphore.js.map +1 -0
- package/dist/{version-CRg5idya.js → version-DfdnkNI2.js} +2 -2
- package/dist/{version-CRg5idya.js.map → version-DfdnkNI2.js.map} +1 -1
- package/dist/version.js +1 -1
- package/env.cjs +13 -0
- package/env.d.ts +6 -0
- package/mutex.cjs +21 -0
- package/mutex.d.ts +1 -0
- package/package.json +38 -3
- package/ratelimit.cjs +26 -0
- package/ratelimit.d.ts +1 -0
- package/semaphore.cjs +23 -0
- package/semaphore.d.ts +1 -0
package/async-queue.cjs
ADDED
package/async-queue.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './dist/utils/useful-stuff/async-queue.js';
|
package/dist/cli/information.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const require_chunk = require('../chunk-nOFOJqeH.js');
|
|
2
|
-
const require_version = require('../version-
|
|
2
|
+
const require_version = require('../version-DfdnkNI2.js');
|
|
3
3
|
const node_fs = require_chunk.__toESM(require("node:fs"));
|
|
4
4
|
const node_path = require_chunk.__toESM(require("node:path"));
|
|
5
5
|
const node_child_process = require_chunk.__toESM(require("node:child_process"));
|
package/dist/index.js
CHANGED
|
@@ -36,7 +36,7 @@ require('./store-CyzliDXj.js');
|
|
|
36
36
|
const require_helpers = require('./helpers-DfV6HlgI.js');
|
|
37
37
|
require('./app-gCenKq8k.js');
|
|
38
38
|
require('./ILogger-BMIMljYD.js');
|
|
39
|
-
const require_version = require('./version-
|
|
39
|
+
const require_version = require('./version-DfdnkNI2.js');
|
|
40
40
|
const require_feature_flags = require('./feature-flags-CFoqosTw.js');
|
|
41
41
|
const require_init = require('./init-muFynnQh.js');
|
|
42
42
|
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
//#region src/utils/useful-stuff/async-queue.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Async queue implementation for processing tasks sequentially or with limited concurrency.
|
|
4
|
+
* Useful for rate-limiting, batching, or controlling resource usage.
|
|
5
|
+
*/
|
|
6
|
+
interface AsyncQueueOptions {
|
|
7
|
+
/** Maximum number of concurrent tasks. Default: 1 (sequential) */
|
|
8
|
+
concurrency?: number;
|
|
9
|
+
/** Optional callback invoked when all tasks are completed */
|
|
10
|
+
onDrain?: () => void | Promise<void>;
|
|
11
|
+
/** Optional AbortSignal for cancelling the queue */
|
|
12
|
+
signal?: AbortSignal;
|
|
13
|
+
}
|
|
14
|
+
type AsyncQueueTask<T> = () => Promise<T>;
|
|
15
|
+
declare class AsyncQueue {
|
|
16
|
+
private readonly concurrency;
|
|
17
|
+
private readonly onDrain?;
|
|
18
|
+
private readonly signal?;
|
|
19
|
+
private running;
|
|
20
|
+
private paused;
|
|
21
|
+
private aborted;
|
|
22
|
+
private queue;
|
|
23
|
+
constructor(options?: AsyncQueueOptions);
|
|
24
|
+
/**
|
|
25
|
+
* Adds a task to the queue.
|
|
26
|
+
* @param task - The async function to execute.
|
|
27
|
+
* @param signal - Optional AbortSignal for cancelling this specific task.
|
|
28
|
+
* @returns Promise resolving to the result of the task.
|
|
29
|
+
*/
|
|
30
|
+
add<T>(task: AsyncQueueTask<T>, signal?: AbortSignal): Promise<T>;
|
|
31
|
+
/**
|
|
32
|
+
* Pauses the queue. No new tasks will be started until resumed.
|
|
33
|
+
*/
|
|
34
|
+
pause(): void;
|
|
35
|
+
/**
|
|
36
|
+
* Resumes the queue and processes pending tasks.
|
|
37
|
+
*/
|
|
38
|
+
resume(): void;
|
|
39
|
+
/**
|
|
40
|
+
* Aborts the queue, rejecting all pending tasks.
|
|
41
|
+
*/
|
|
42
|
+
abort(): void;
|
|
43
|
+
/**
|
|
44
|
+
* Clears all pending tasks from the queue.
|
|
45
|
+
*/
|
|
46
|
+
clear(): void;
|
|
47
|
+
/**
|
|
48
|
+
* Returns the number of running tasks.
|
|
49
|
+
*/
|
|
50
|
+
getRunning(): number;
|
|
51
|
+
/**
|
|
52
|
+
* Returns the number of pending tasks in the queue.
|
|
53
|
+
*/
|
|
54
|
+
getPending(): number;
|
|
55
|
+
/**
|
|
56
|
+
* Returns true if the queue is currently paused.
|
|
57
|
+
*/
|
|
58
|
+
isPaused(): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Returns true if the queue has been aborted.
|
|
61
|
+
*/
|
|
62
|
+
isAborted(): boolean;
|
|
63
|
+
private next;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Creates a new async queue instance with the specified configuration.
|
|
67
|
+
* @param options - Configuration options for the async queue.
|
|
68
|
+
* @returns New AsyncQueue instance.
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```typescript
|
|
72
|
+
* const controller = new AbortController();
|
|
73
|
+
* const queue = createAsyncQueue({
|
|
74
|
+
* concurrency: 2,
|
|
75
|
+
* signal: controller.signal
|
|
76
|
+
* });
|
|
77
|
+
* queue.add(async () => await doSomething());
|
|
78
|
+
* controller.abort(); // Aborts the queue
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
declare function createAsyncQueue(options?: AsyncQueueOptions): AsyncQueue;
|
|
82
|
+
//#endregion
|
|
83
|
+
export { AsyncQueue, AsyncQueueOptions, AsyncQueueTask, createAsyncQueue };
|
|
84
|
+
//# sourceMappingURL=async-queue.d.ts.map
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
|
|
2
|
+
//#region src/utils/useful-stuff/async-queue.ts
|
|
3
|
+
var AsyncQueue = class {
|
|
4
|
+
concurrency;
|
|
5
|
+
onDrain;
|
|
6
|
+
signal;
|
|
7
|
+
running = 0;
|
|
8
|
+
paused = false;
|
|
9
|
+
aborted = false;
|
|
10
|
+
queue = [];
|
|
11
|
+
constructor(options = {}) {
|
|
12
|
+
this.concurrency = options.concurrency ?? 1;
|
|
13
|
+
this.onDrain = options.onDrain;
|
|
14
|
+
this.signal = options.signal;
|
|
15
|
+
if (this.signal) this.signal.addEventListener("abort", () => {
|
|
16
|
+
this.abort();
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Adds a task to the queue.
|
|
21
|
+
* @param task - The async function to execute.
|
|
22
|
+
* @param signal - Optional AbortSignal for cancelling this specific task.
|
|
23
|
+
* @returns Promise resolving to the result of the task.
|
|
24
|
+
*/
|
|
25
|
+
add(task, signal) {
|
|
26
|
+
if (this.aborted) return Promise.reject(new Error("Queue has been aborted"));
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
const run = async () => {
|
|
29
|
+
if (this.paused || this.aborted) {
|
|
30
|
+
if (this.aborted) reject(new Error("Queue has been aborted"));
|
|
31
|
+
else this.queue.push(run);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
35
|
+
reject(new Error("Task was aborted"));
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
this.running++;
|
|
39
|
+
try {
|
|
40
|
+
const result = await task();
|
|
41
|
+
resolve(result);
|
|
42
|
+
} catch (err) {
|
|
43
|
+
reject(err);
|
|
44
|
+
} finally {
|
|
45
|
+
this.running--;
|
|
46
|
+
this.next();
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
if (this.running < this.concurrency && !this.paused && !this.aborted) run();
|
|
50
|
+
else this.queue.push(run);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Pauses the queue. No new tasks will be started until resumed.
|
|
55
|
+
*/
|
|
56
|
+
pause() {
|
|
57
|
+
this.paused = true;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Resumes the queue and processes pending tasks.
|
|
61
|
+
*/
|
|
62
|
+
resume() {
|
|
63
|
+
if (!this.paused) return;
|
|
64
|
+
this.paused = false;
|
|
65
|
+
this.next();
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Aborts the queue, rejecting all pending tasks.
|
|
69
|
+
*/
|
|
70
|
+
abort() {
|
|
71
|
+
this.aborted = true;
|
|
72
|
+
this.clear();
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Clears all pending tasks from the queue.
|
|
76
|
+
*/
|
|
77
|
+
clear() {
|
|
78
|
+
this.queue = [];
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Returns the number of running tasks.
|
|
82
|
+
*/
|
|
83
|
+
getRunning() {
|
|
84
|
+
return this.running;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Returns the number of pending tasks in the queue.
|
|
88
|
+
*/
|
|
89
|
+
getPending() {
|
|
90
|
+
return this.queue.length;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Returns true if the queue is currently paused.
|
|
94
|
+
*/
|
|
95
|
+
isPaused() {
|
|
96
|
+
return this.paused;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Returns true if the queue has been aborted.
|
|
100
|
+
*/
|
|
101
|
+
isAborted() {
|
|
102
|
+
return this.aborted;
|
|
103
|
+
}
|
|
104
|
+
next() {
|
|
105
|
+
if (this.paused || this.aborted) return;
|
|
106
|
+
while (this.running < this.concurrency && this.queue.length > 0) {
|
|
107
|
+
const fn = this.queue.shift();
|
|
108
|
+
if (fn) fn();
|
|
109
|
+
}
|
|
110
|
+
if (this.running === 0 && this.queue.length === 0 && this.onDrain) this.onDrain();
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
/**
|
|
114
|
+
* Creates a new async queue instance with the specified configuration.
|
|
115
|
+
* @param options - Configuration options for the async queue.
|
|
116
|
+
* @returns New AsyncQueue instance.
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* ```typescript
|
|
120
|
+
* const controller = new AbortController();
|
|
121
|
+
* const queue = createAsyncQueue({
|
|
122
|
+
* concurrency: 2,
|
|
123
|
+
* signal: controller.signal
|
|
124
|
+
* });
|
|
125
|
+
* queue.add(async () => await doSomething());
|
|
126
|
+
* controller.abort(); // Aborts the queue
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
function createAsyncQueue(options) {
|
|
130
|
+
return new AsyncQueue(options);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
//#endregion
|
|
134
|
+
exports.AsyncQueue = AsyncQueue;
|
|
135
|
+
exports.createAsyncQueue = createAsyncQueue;
|
|
136
|
+
//# sourceMappingURL=async-queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"async-queue.js","names":[],"sources":["../../../src/utils/useful-stuff/async-queue.ts"],"sourcesContent":["/**\n * Async queue implementation for processing tasks sequentially or with limited concurrency.\n * Useful for rate-limiting, batching, or controlling resource usage.\n */\n\nexport interface AsyncQueueOptions {\n /** Maximum number of concurrent tasks. Default: 1 (sequential) */\n concurrency?: number;\n /** Optional callback invoked when all tasks are completed */\n onDrain?: () => void | Promise<void>;\n /** Optional AbortSignal for cancelling the queue */\n signal?: AbortSignal;\n}\n\nexport type AsyncQueueTask<T> = () => Promise<T>;\n\nexport class AsyncQueue {\n private readonly concurrency: number;\n private readonly onDrain?: () => void | Promise<void>;\n private readonly signal?: AbortSignal;\n private running = 0;\n private paused = false;\n private aborted = false;\n private queue: Array<() => void> = [];\n\n constructor(options: AsyncQueueOptions = {}) {\n this.concurrency = options.concurrency ?? 1;\n this.onDrain = options.onDrain;\n this.signal = options.signal;\n\n if (this.signal) {\n this.signal.addEventListener('abort', () => {\n this.abort();\n });\n }\n }\n\n /**\n * Adds a task to the queue.\n * @param task - The async function to execute.\n * @param signal - Optional AbortSignal for cancelling this specific task.\n * @returns Promise resolving to the result of the task.\n */\n public add<T>(task: AsyncQueueTask<T>, signal?: AbortSignal): Promise<T> {\n if (this.aborted) {\n return Promise.reject(new Error('Queue has been aborted'));\n }\n\n return new Promise<T>((resolve, reject) => {\n const run = async () => {\n if (this.paused || this.aborted) {\n if (this.aborted) {\n reject(new Error('Queue has been aborted'));\n } else {\n this.queue.push(run);\n }\n return;\n }\n\n // Check if task-specific signal is aborted\n if (signal?.aborted) {\n reject(new Error('Task was aborted'));\n return;\n }\n\n this.running++;\n try {\n const result = await task();\n resolve(result);\n } catch (err) {\n reject(err);\n } finally {\n this.running--;\n this.next();\n }\n };\n\n if (this.running < this.concurrency && !this.paused && !this.aborted) {\n run();\n } else {\n this.queue.push(run);\n }\n });\n }\n\n /**\n * Pauses the queue. No new tasks will be started until resumed.\n */\n public pause() {\n this.paused = true;\n }\n\n /**\n * Resumes the queue and processes pending tasks.\n */\n public resume() {\n if (!this.paused) return;\n this.paused = false;\n this.next();\n }\n\n /**\n * Aborts the queue, rejecting all pending tasks.\n */\n public abort() {\n this.aborted = true;\n this.clear();\n }\n\n /**\n * Clears all pending tasks from the queue.\n */\n public clear() {\n this.queue = [];\n }\n\n /**\n * Returns the number of running tasks.\n */\n public getRunning(): number {\n return this.running;\n }\n\n /**\n * Returns the number of pending tasks in the queue.\n */\n public getPending(): number {\n return this.queue.length;\n }\n\n /**\n * Returns true if the queue is currently paused.\n */\n public isPaused(): boolean {\n return this.paused;\n }\n\n /**\n * Returns true if the queue has been aborted.\n */\n public isAborted(): boolean {\n return this.aborted;\n }\n\n private next() {\n if (this.paused || this.aborted) return;\n while (this.running < this.concurrency && this.queue.length > 0) {\n const fn = this.queue.shift();\n if (fn) fn();\n }\n if (this.running === 0 && this.queue.length === 0 && this.onDrain) {\n this.onDrain();\n }\n }\n}\n\n/**\n * Creates a new async queue instance with the specified configuration.\n * @param options - Configuration options for the async queue.\n * @returns New AsyncQueue instance.\n *\n * @example\n * ```typescript\n * const controller = new AbortController();\n * const queue = createAsyncQueue({\n * concurrency: 2,\n * signal: controller.signal\n * });\n * queue.add(async () => await doSomething());\n * controller.abort(); // Aborts the queue\n * ```\n */\nexport function createAsyncQueue(options?: AsyncQueueOptions): AsyncQueue {\n return new AsyncQueue(options);\n}\n"],"mappings":";;AAgBA,IAAa,aAAb,MAAwB;CACtB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAQ,UAAU;CAClB,AAAQ,SAAS;CACjB,AAAQ,UAAU;CAClB,AAAQ,QAA2B,CAAE;CAErC,YAAY,UAA6B,CAAE,GAAE;AAC3C,OAAK,cAAc,QAAQ,eAAe;AAC1C,OAAK,UAAU,QAAQ;AACvB,OAAK,SAAS,QAAQ;AAEtB,MAAI,KAAK,OACP,MAAK,OAAO,iBAAiB,SAAS,MAAM;AAC1C,QAAK,OAAO;EACb,EAAC;CAEN;;;;;;;CAQA,AAAO,IAAO,MAAyB,QAAkC;AACvE,MAAI,KAAK,QACP,QAAO,QAAQ,OAAO,IAAI,MAAM,0BAA0B;AAG5D,SAAO,IAAI,QAAW,CAAC,SAAS,WAAW;GACzC,MAAM,MAAM,YAAY;AACtB,QAAI,KAAK,UAAU,KAAK,SAAS;AAC/B,SAAI,KAAK,QACP,QAAO,IAAI,MAAM,0BAA0B;SAE3C,MAAK,MAAM,KAAK,IAAI;AAEtB;IACF;AAGA,wDAAI,OAAQ,SAAS;AACnB,YAAO,IAAI,MAAM,oBAAoB;AACrC;IACF;AAEA,SAAK;AACL,QAAI;KACF,MAAM,SAAS,MAAM,MAAM;AAC3B,aAAQ,OAAO;IAChB,SAAQ,KAAK;AACZ,YAAO,IAAI;IACZ,UAAS;AACR,UAAK;AACL,UAAK,MAAM;IACb;GACD;AAED,OAAI,KAAK,UAAU,KAAK,gBAAgB,KAAK,WAAW,KAAK,QAC3D,MAAK;OAEL,MAAK,MAAM,KAAK,IAAI;EAEvB;CACH;;;;CAKA,AAAO,QAAQ;AACb,OAAK,SAAS;CAChB;;;;CAKA,AAAO,SAAS;AACd,OAAK,KAAK,OAAQ;AAClB,OAAK,SAAS;AACd,OAAK,MAAM;CACb;;;;CAKA,AAAO,QAAQ;AACb,OAAK,UAAU;AACf,OAAK,OAAO;CACd;;;;CAKA,AAAO,QAAQ;AACb,OAAK,QAAQ,CAAE;CACjB;;;;CAKA,AAAO,aAAqB;AAC1B,SAAO,KAAK;CACd;;;;CAKA,AAAO,aAAqB;AAC1B,SAAO,KAAK,MAAM;CACpB;;;;CAKA,AAAO,WAAoB;AACzB,SAAO,KAAK;CACd;;;;CAKA,AAAO,YAAqB;AAC1B,SAAO,KAAK;CACd;CAEA,AAAQ,OAAO;AACb,MAAI,KAAK,UAAU,KAAK,QAAS;AACjC,SAAO,KAAK,UAAU,KAAK,eAAe,KAAK,MAAM,SAAS,GAAG;GAC/D,MAAM,KAAK,KAAK,MAAM,OAAO;AAC7B,OAAI,GAAI,KAAI;EACd;AACA,MAAI,KAAK,YAAY,KAAK,KAAK,MAAM,WAAW,KAAK,KAAK,QACxD,MAAK,SAAS;CAElB;AACF;;;;;;;;;;;;;;;;;AAkBA,SAAgB,iBAAiB,SAAyC;AACxE,QAAO,IAAI,WAAW;AACxB"}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
//#region src/utils/useful-stuff/mutex.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Async mutex implementation for coordinating access to shared resources.
|
|
4
|
+
* Provides mutual exclusion to ensure only one task can access a resource at a time.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Interface for mutex storage implementations.
|
|
8
|
+
* Provides methods to store, retrieve, and delete mutex lock data.
|
|
9
|
+
*/
|
|
10
|
+
interface MutexStorage {
|
|
11
|
+
/**
|
|
12
|
+
* Attempts to acquire a lock for a given key
|
|
13
|
+
* @param key - The unique identifier for the mutex lock
|
|
14
|
+
* @param timeout - Optional timeout in milliseconds for the lock
|
|
15
|
+
* @param signal - Optional AbortSignal for cancelling the acquisition
|
|
16
|
+
* @returns Promise resolving to true if lock was acquired, false if timeout or already locked
|
|
17
|
+
*/
|
|
18
|
+
acquire(key: string, timeout?: number, signal?: AbortSignal): Promise<boolean>;
|
|
19
|
+
/**
|
|
20
|
+
* Releases the lock for a given key
|
|
21
|
+
* @param key - The unique identifier for the mutex lock
|
|
22
|
+
* @returns Promise that resolves when the lock is released
|
|
23
|
+
*/
|
|
24
|
+
release(key: string): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* Checks if a lock is currently held for a given key
|
|
27
|
+
* @param key - The unique identifier for the mutex lock
|
|
28
|
+
* @returns Promise resolving to true if the lock is held, false otherwise
|
|
29
|
+
*/
|
|
30
|
+
isLocked(key: string): Promise<boolean>;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Configuration options for mutex
|
|
34
|
+
*/
|
|
35
|
+
interface MutexOptions {
|
|
36
|
+
/** Default timeout in milliseconds for lock acquisition. Default: 30000 */
|
|
37
|
+
timeout?: number;
|
|
38
|
+
/** Storage implementation for persisting mutex data. Default: {@link MemoryMutexStorage} */
|
|
39
|
+
storage?: MutexStorage;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* In-memory storage implementation for mutex locks.
|
|
43
|
+
* Suitable for single-instance applications.
|
|
44
|
+
*/
|
|
45
|
+
declare class MemoryMutexStorage implements MutexStorage {
|
|
46
|
+
private readonly locks;
|
|
47
|
+
/**
|
|
48
|
+
* Attempts to acquire a lock for a given key
|
|
49
|
+
* @param key - The unique identifier for the mutex lock
|
|
50
|
+
* @param timeout - Optional timeout in milliseconds for the lock
|
|
51
|
+
* @param signal - Optional AbortSignal for cancelling the acquisition
|
|
52
|
+
* @returns Promise resolving to true if lock was acquired, false if timeout or already locked
|
|
53
|
+
*/
|
|
54
|
+
acquire(key: string, timeout?: number, signal?: AbortSignal): Promise<boolean>;
|
|
55
|
+
/**
|
|
56
|
+
* Releases the lock for a given key
|
|
57
|
+
* @param key - The unique identifier for the mutex lock
|
|
58
|
+
* @returns Promise that resolves when the lock is released
|
|
59
|
+
*/
|
|
60
|
+
release(key: string): Promise<void>;
|
|
61
|
+
/**
|
|
62
|
+
* Checks if a lock is currently held for a given key
|
|
63
|
+
* @param key - The unique identifier for the mutex lock
|
|
64
|
+
* @returns Promise resolving to true if the lock is held, false otherwise
|
|
65
|
+
*/
|
|
66
|
+
isLocked(key: string): Promise<boolean>;
|
|
67
|
+
private generateHolderId;
|
|
68
|
+
private delay;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Async mutex implementation that provides mutual exclusion for shared resources.
|
|
72
|
+
* Ensures only one task can access a protected resource at a time.
|
|
73
|
+
*/
|
|
74
|
+
declare class Mutex {
|
|
75
|
+
private storage;
|
|
76
|
+
private readonly defaultTimeout;
|
|
77
|
+
/**
|
|
78
|
+
* Creates a new mutex instance
|
|
79
|
+
* @param options - Configuration options for the mutex
|
|
80
|
+
*/
|
|
81
|
+
constructor(options?: MutexOptions);
|
|
82
|
+
/**
|
|
83
|
+
* Sets the storage implementation for the mutex
|
|
84
|
+
* @param storage - The storage implementation to use
|
|
85
|
+
*/
|
|
86
|
+
setStorage(storage: MutexStorage): void;
|
|
87
|
+
/**
|
|
88
|
+
* Gets the storage implementation for the mutex
|
|
89
|
+
* @returns The storage implementation
|
|
90
|
+
*/
|
|
91
|
+
getStorage(): MutexStorage;
|
|
92
|
+
/**
|
|
93
|
+
* Acquires a lock for the given key
|
|
94
|
+
* @param key - The unique identifier for the mutex lock
|
|
95
|
+
* @param timeout - Optional timeout in milliseconds for lock acquisition
|
|
96
|
+
* @param signal - Optional AbortSignal for cancelling the acquisition
|
|
97
|
+
* @returns Promise resolving to true if lock was acquired, false if timeout
|
|
98
|
+
*/
|
|
99
|
+
acquire(key: string, timeout?: number, signal?: AbortSignal): Promise<boolean>;
|
|
100
|
+
/**
|
|
101
|
+
* Releases the lock for the given key
|
|
102
|
+
* @param key - The unique identifier for the mutex lock
|
|
103
|
+
* @returns Promise that resolves when the lock is released
|
|
104
|
+
*/
|
|
105
|
+
release(key: string): Promise<void>;
|
|
106
|
+
/**
|
|
107
|
+
* Checks if a lock is currently held for the given key
|
|
108
|
+
* @param key - The unique identifier for the mutex lock
|
|
109
|
+
* @returns Promise resolving to true if the lock is held, false otherwise
|
|
110
|
+
*/
|
|
111
|
+
isLocked(key: string): Promise<boolean>;
|
|
112
|
+
/**
|
|
113
|
+
* Executes a function with exclusive access to the resource
|
|
114
|
+
* @param key - The unique identifier for the mutex lock
|
|
115
|
+
* @param fn - The function to execute with exclusive access
|
|
116
|
+
* @param timeout - Optional timeout in milliseconds for lock acquisition
|
|
117
|
+
* @param signal - Optional AbortSignal for cancelling the lock acquisition
|
|
118
|
+
* @returns Promise resolving to the result of the function execution
|
|
119
|
+
* @throws Error if lock acquisition fails or function execution fails
|
|
120
|
+
*/
|
|
121
|
+
withLock<T>(key: string, fn: () => Promise<T> | T, timeout?: number, signal?: AbortSignal): Promise<T>;
|
|
122
|
+
/**
|
|
123
|
+
* Gets the current configuration of the mutex
|
|
124
|
+
* @returns Object containing the current timeout value
|
|
125
|
+
*/
|
|
126
|
+
getConfig(): Omit<MutexOptions, 'storage'>;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Default mutex instance for global use
|
|
130
|
+
*/
|
|
131
|
+
declare const defaultMutex: Mutex;
|
|
132
|
+
/**
|
|
133
|
+
* Convenience function to execute a function with exclusive access using the default mutex.
|
|
134
|
+
*
|
|
135
|
+
* @param key - The unique identifier for the mutex lock
|
|
136
|
+
* @param fn - The function to execute with exclusive access
|
|
137
|
+
* @param timeout - Optional timeout in milliseconds for lock acquisition
|
|
138
|
+
* @param signal - Optional AbortSignal for cancelling the lock acquisition
|
|
139
|
+
* @returns Promise resolving to the result of the function execution
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```typescript
|
|
143
|
+
* const controller = new AbortController();
|
|
144
|
+
* const result = await withMutex('shared-resource', async () => {
|
|
145
|
+
* // This code runs with exclusive access
|
|
146
|
+
* return await updateSharedResource();
|
|
147
|
+
* }, 30000, controller.signal);
|
|
148
|
+
* controller.abort(); // Cancels the lock acquisition
|
|
149
|
+
* ```
|
|
150
|
+
*/
|
|
151
|
+
declare function withMutex<T>(key: string, fn: () => Promise<T> | T, timeout?: number, signal?: AbortSignal): Promise<T>;
|
|
152
|
+
/**
|
|
153
|
+
* Creates a new mutex instance with the specified configuration
|
|
154
|
+
* @param options - Configuration options for the mutex
|
|
155
|
+
* @returns New Mutex instance
|
|
156
|
+
*
|
|
157
|
+
* @example
|
|
158
|
+
* ```typescript
|
|
159
|
+
* const mutex = createMutex({
|
|
160
|
+
* timeout: 60000,
|
|
161
|
+
* storage: new RedisMutexStorage()
|
|
162
|
+
* });
|
|
163
|
+
* ```
|
|
164
|
+
*/
|
|
165
|
+
declare function createMutex(options: MutexOptions): Mutex;
|
|
166
|
+
/**
|
|
167
|
+
* Acquires a lock using the default mutex
|
|
168
|
+
* @param key - The unique identifier for the mutex lock
|
|
169
|
+
* @param timeout - Optional timeout in milliseconds for lock acquisition
|
|
170
|
+
* @param signal - Optional AbortSignal for cancelling the acquisition
|
|
171
|
+
* @returns Promise resolving to true if lock was acquired, false if timeout
|
|
172
|
+
*/
|
|
173
|
+
declare function acquireLock(key: string, timeout?: number, signal?: AbortSignal): Promise<boolean>;
|
|
174
|
+
/**
|
|
175
|
+
* Releases a lock using the default mutex
|
|
176
|
+
* @param key - The unique identifier for the mutex lock
|
|
177
|
+
* @returns Promise that resolves when the lock is released
|
|
178
|
+
*/
|
|
179
|
+
declare function releaseLock(key: string): Promise<void>;
|
|
180
|
+
/**
|
|
181
|
+
* Checks if a lock is held using the default mutex
|
|
182
|
+
* @param key - The unique identifier for the mutex lock
|
|
183
|
+
* @returns Promise resolving to true if the lock is held, false otherwise
|
|
184
|
+
*/
|
|
185
|
+
declare function isLocked(key: string): Promise<boolean>;
|
|
186
|
+
//#endregion
|
|
187
|
+
export { MemoryMutexStorage, Mutex, MutexOptions, MutexStorage, acquireLock, createMutex, defaultMutex, isLocked, releaseLock, withMutex };
|
|
188
|
+
//# sourceMappingURL=mutex.d.ts.map
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
|
|
2
|
+
//#region src/utils/useful-stuff/mutex.ts
|
|
3
|
+
/**
|
|
4
|
+
* In-memory storage implementation for mutex locks.
|
|
5
|
+
* Suitable for single-instance applications.
|
|
6
|
+
*/
|
|
7
|
+
var MemoryMutexStorage = class {
|
|
8
|
+
locks = /* @__PURE__ */ new Map();
|
|
9
|
+
/**
|
|
10
|
+
* Attempts to acquire a lock for a given key
|
|
11
|
+
* @param key - The unique identifier for the mutex lock
|
|
12
|
+
* @param timeout - Optional timeout in milliseconds for the lock
|
|
13
|
+
* @param signal - Optional AbortSignal for cancelling the acquisition
|
|
14
|
+
* @returns Promise resolving to true if lock was acquired, false if timeout or already locked
|
|
15
|
+
*/
|
|
16
|
+
async acquire(key, timeout = 3e4, signal) {
|
|
17
|
+
const holder = this.generateHolderId();
|
|
18
|
+
const startTime = Date.now();
|
|
19
|
+
while (Date.now() - startTime < timeout) {
|
|
20
|
+
if (signal === null || signal === void 0 ? void 0 : signal.aborted) throw new Error("Lock acquisition was aborted");
|
|
21
|
+
if (!this.locks.has(key)) {
|
|
22
|
+
this.locks.set(key, {
|
|
23
|
+
holder,
|
|
24
|
+
acquiredAt: Date.now()
|
|
25
|
+
});
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
await this.delay(10);
|
|
29
|
+
}
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Releases the lock for a given key
|
|
34
|
+
* @param key - The unique identifier for the mutex lock
|
|
35
|
+
* @returns Promise that resolves when the lock is released
|
|
36
|
+
*/
|
|
37
|
+
async release(key) {
|
|
38
|
+
this.locks.delete(key);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Checks if a lock is currently held for a given key
|
|
42
|
+
* @param key - The unique identifier for the mutex lock
|
|
43
|
+
* @returns Promise resolving to true if the lock is held, false otherwise
|
|
44
|
+
*/
|
|
45
|
+
async isLocked(key) {
|
|
46
|
+
return this.locks.has(key);
|
|
47
|
+
}
|
|
48
|
+
generateHolderId() {
|
|
49
|
+
return `holder_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
50
|
+
}
|
|
51
|
+
delay(ms) {
|
|
52
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Async mutex implementation that provides mutual exclusion for shared resources.
|
|
57
|
+
* Ensures only one task can access a protected resource at a time.
|
|
58
|
+
*/
|
|
59
|
+
var Mutex = class {
|
|
60
|
+
storage;
|
|
61
|
+
defaultTimeout;
|
|
62
|
+
/**
|
|
63
|
+
* Creates a new mutex instance
|
|
64
|
+
* @param options - Configuration options for the mutex
|
|
65
|
+
*/
|
|
66
|
+
constructor(options = {}) {
|
|
67
|
+
this.storage = options.storage || new MemoryMutexStorage();
|
|
68
|
+
this.defaultTimeout = options.timeout || 3e4;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Sets the storage implementation for the mutex
|
|
72
|
+
* @param storage - The storage implementation to use
|
|
73
|
+
*/
|
|
74
|
+
setStorage(storage) {
|
|
75
|
+
this.storage = storage;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Gets the storage implementation for the mutex
|
|
79
|
+
* @returns The storage implementation
|
|
80
|
+
*/
|
|
81
|
+
getStorage() {
|
|
82
|
+
return this.storage;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Acquires a lock for the given key
|
|
86
|
+
* @param key - The unique identifier for the mutex lock
|
|
87
|
+
* @param timeout - Optional timeout in milliseconds for lock acquisition
|
|
88
|
+
* @param signal - Optional AbortSignal for cancelling the acquisition
|
|
89
|
+
* @returns Promise resolving to true if lock was acquired, false if timeout
|
|
90
|
+
*/
|
|
91
|
+
async acquire(key, timeout, signal) {
|
|
92
|
+
return this.storage.acquire(key, timeout || this.defaultTimeout, signal);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Releases the lock for the given key
|
|
96
|
+
* @param key - The unique identifier for the mutex lock
|
|
97
|
+
* @returns Promise that resolves when the lock is released
|
|
98
|
+
*/
|
|
99
|
+
async release(key) {
|
|
100
|
+
return this.storage.release(key);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Checks if a lock is currently held for the given key
|
|
104
|
+
* @param key - The unique identifier for the mutex lock
|
|
105
|
+
* @returns Promise resolving to true if the lock is held, false otherwise
|
|
106
|
+
*/
|
|
107
|
+
async isLocked(key) {
|
|
108
|
+
return this.storage.isLocked(key);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Executes a function with exclusive access to the resource
|
|
112
|
+
* @param key - The unique identifier for the mutex lock
|
|
113
|
+
* @param fn - The function to execute with exclusive access
|
|
114
|
+
* @param timeout - Optional timeout in milliseconds for lock acquisition
|
|
115
|
+
* @param signal - Optional AbortSignal for cancelling the lock acquisition
|
|
116
|
+
* @returns Promise resolving to the result of the function execution
|
|
117
|
+
* @throws Error if lock acquisition fails or function execution fails
|
|
118
|
+
*/
|
|
119
|
+
async withLock(key, fn, timeout, signal) {
|
|
120
|
+
const acquired = await this.acquire(key, timeout, signal);
|
|
121
|
+
if (!acquired) throw new Error(`Failed to acquire lock for key: ${key}`);
|
|
122
|
+
try {
|
|
123
|
+
return await fn();
|
|
124
|
+
} finally {
|
|
125
|
+
await this.release(key);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Gets the current configuration of the mutex
|
|
130
|
+
* @returns Object containing the current timeout value
|
|
131
|
+
*/
|
|
132
|
+
getConfig() {
|
|
133
|
+
return { timeout: this.defaultTimeout };
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
/**
|
|
137
|
+
* Default mutex instance for global use
|
|
138
|
+
*/
|
|
139
|
+
const defaultMutex = new Mutex();
|
|
140
|
+
/**
|
|
141
|
+
* Convenience function to execute a function with exclusive access using the default mutex.
|
|
142
|
+
*
|
|
143
|
+
* @param key - The unique identifier for the mutex lock
|
|
144
|
+
* @param fn - The function to execute with exclusive access
|
|
145
|
+
* @param timeout - Optional timeout in milliseconds for lock acquisition
|
|
146
|
+
* @param signal - Optional AbortSignal for cancelling the lock acquisition
|
|
147
|
+
* @returns Promise resolving to the result of the function execution
|
|
148
|
+
*
|
|
149
|
+
* @example
|
|
150
|
+
* ```typescript
|
|
151
|
+
* const controller = new AbortController();
|
|
152
|
+
* const result = await withMutex('shared-resource', async () => {
|
|
153
|
+
* // This code runs with exclusive access
|
|
154
|
+
* return await updateSharedResource();
|
|
155
|
+
* }, 30000, controller.signal);
|
|
156
|
+
* controller.abort(); // Cancels the lock acquisition
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
159
|
+
async function withMutex(key, fn, timeout, signal) {
|
|
160
|
+
return defaultMutex.withLock(key, fn, timeout, signal);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Creates a new mutex instance with the specified configuration
|
|
164
|
+
* @param options - Configuration options for the mutex
|
|
165
|
+
* @returns New Mutex instance
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* ```typescript
|
|
169
|
+
* const mutex = createMutex({
|
|
170
|
+
* timeout: 60000,
|
|
171
|
+
* storage: new RedisMutexStorage()
|
|
172
|
+
* });
|
|
173
|
+
* ```
|
|
174
|
+
*/
|
|
175
|
+
function createMutex(options) {
|
|
176
|
+
return new Mutex(options);
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Acquires a lock using the default mutex
|
|
180
|
+
* @param key - The unique identifier for the mutex lock
|
|
181
|
+
* @param timeout - Optional timeout in milliseconds for lock acquisition
|
|
182
|
+
* @param signal - Optional AbortSignal for cancelling the acquisition
|
|
183
|
+
* @returns Promise resolving to true if lock was acquired, false if timeout
|
|
184
|
+
*/
|
|
185
|
+
async function acquireLock(key, timeout, signal) {
|
|
186
|
+
return defaultMutex.acquire(key, timeout, signal);
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Releases a lock using the default mutex
|
|
190
|
+
* @param key - The unique identifier for the mutex lock
|
|
191
|
+
* @returns Promise that resolves when the lock is released
|
|
192
|
+
*/
|
|
193
|
+
async function releaseLock(key) {
|
|
194
|
+
return defaultMutex.release(key);
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Checks if a lock is held using the default mutex
|
|
198
|
+
* @param key - The unique identifier for the mutex lock
|
|
199
|
+
* @returns Promise resolving to true if the lock is held, false otherwise
|
|
200
|
+
*/
|
|
201
|
+
async function isLocked(key) {
|
|
202
|
+
return defaultMutex.isLocked(key);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
//#endregion
|
|
206
|
+
exports.MemoryMutexStorage = MemoryMutexStorage;
|
|
207
|
+
exports.Mutex = Mutex;
|
|
208
|
+
exports.acquireLock = acquireLock;
|
|
209
|
+
exports.createMutex = createMutex;
|
|
210
|
+
exports.defaultMutex = defaultMutex;
|
|
211
|
+
exports.isLocked = isLocked;
|
|
212
|
+
exports.releaseLock = releaseLock;
|
|
213
|
+
exports.withMutex = withMutex;
|
|
214
|
+
//# sourceMappingURL=mutex.js.map
|