mini-semaphore 1.4.2 → 1.5.1

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/README.md CHANGED
@@ -163,6 +163,69 @@ async function resolve(id: string | number): Promise<TType> {
163
163
  }
164
164
 
165
165
  ```
166
+
167
+ ---
168
+ > ## Abortable Semaphore
169
+
170
+ Starting from version **1.4.3**, `mini-semaphore` introduces support for abortable semaphores.
171
+ This feature allows you to cancel pending tasks and notify listeners when an abort event occurs.
172
+ This is particularly useful in scenarios where you need to terminate ongoing operations gracefully.
173
+
174
+ ### Key Features
175
+
176
+ - **Abort Method**: Immediately cancels all pending tasks and restores the semaphore's capacity.
177
+ - **Event Emission**: Emits an `abort` event to notify listeners of the cancellation.
178
+ - **Listener Management**: Provides `onAbort` and `offAbort` methods to register and remove event listeners.
179
+
180
+ ### Example Usage
181
+
182
+ ```typescript
183
+ import { createWithAbort } from "mini-semaphore";
184
+
185
+ // Create an abortable semaphore with a capacity of 3
186
+ const semaphore = createWithAbort(3);
187
+
188
+ // Register an abort event listener
189
+ semaphore.onAbort((reason) => {
190
+ console.log("Abort event received:", reason.message);
191
+ });
192
+
193
+ // Simulate tasks
194
+ const task = async (id: number) => {
195
+ try {
196
+ await semaphore.acquire();
197
+ console.log(`Task ${id} started`);
198
+ await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulate work
199
+ console.log(`Task ${id} completed`);
200
+ semaphore.release();
201
+ } catch (e) {
202
+ console.log(`Task ${id} aborted:`, e.message);
203
+ }
204
+ };
205
+
206
+ // Start tasks
207
+ const tasks = [1, 2, 3, 4, 5].map((id) => task(id));
208
+
209
+ // Abort all pending tasks after 2 seconds
210
+ setTimeout(() => {
211
+ semaphore.abort();
212
+ }, 2000);
213
+
214
+ // Wait for all tasks to settle
215
+ Promise.allSettled(tasks).then(() => {
216
+ console.log("All tasks settled");
217
+ });
218
+ ```
219
+
220
+ ### Explanation
221
+
222
+ 1. **Creating an Abortable Semaphore**: Use `createWithAbort` to create a semaphore with abort capabilities.
223
+ 2. **Registering Listeners**: Use `onAbort` to listen for abort events and handle cleanup or logging.
224
+ 3. **Aborting Tasks**: Call `abort` to cancel all pending tasks. Tasks that are already running will not be interrupted but will complete normally.
225
+ 4. **Graceful Cleanup**: Use `offAbort` to remove listeners when they are no longer needed.
226
+
227
+ This feature enhances the flexibility of `mini-semaphore`, making it suitable for more complex concurrency control scenarios.
228
+
166
229
  > ## Authors
167
230
 
168
231
  + **jeffy-g** - [jeffy-g](https://github.com/jeffy-g)
package/cjs/class.d.ts ADDED
@@ -0,0 +1,85 @@
1
+ /*!
2
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3
+ Copyright (C) 2020 jeffy-g <hirotom1107@gmail.com>
4
+ Released under the MIT license
5
+ https://opensource.org/licenses/mit-license.php
6
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
7
+ */
8
+ /**
9
+ * @file minimal implementation of semaphore (class implementation
10
+ * @author jeffy-g <hirotom1107@gmail.com>
11
+ * @version 1.0
12
+ */
13
+ import * as core from "./core";
14
+ import { Deque } from "./deque";
15
+ export type * from "./core";
16
+ /**
17
+ * #### Mini Semaphore
18
+ *
19
+ * + minimal implementation of semaphore
20
+ *
21
+ * @example
22
+ * import { MiniSemaphore } from "mini-semaphore";
23
+ *
24
+ * const s = new MiniSemaphore(10);
25
+ * async function fetchTypeData(type_id) {
26
+ * await s.acquire();
27
+ * try {
28
+ * return fetch(`https://esi.evetech.net/latest/universe/types/${type_id}/`);
29
+ * } finally {
30
+ * s.release();
31
+ * }
32
+ * }
33
+ *
34
+ * //
35
+ * // or automatic acquire/release
36
+ * //
37
+ * async function fetchTypeData(type_id) {
38
+ * return s.flow(async () => fetch(`https://esi.evetech.net/latest/universe/types/${type_id}/`));
39
+ * }
40
+ *
41
+ * @date 2020/2/7
42
+ * @version 1.0
43
+ */
44
+ export declare class MiniSemaphore implements core.TFlowableLock {
45
+ /**
46
+ * spare capacity
47
+ */
48
+ capacity: number;
49
+ /**
50
+ * limitation
51
+ */
52
+ limit: number;
53
+ /**
54
+ * queue of Promise's `resolve`
55
+ */
56
+ q: Deque<core.TVoidFunction>;
57
+ /**
58
+ * constructs a semaphore instance limited at `capacity`
59
+ *
60
+ * @param {number} capacity limitation of concurrent async by `capacity`
61
+ */
62
+ constructor(capacity: number);
63
+ /**
64
+ * If there is enough capacity, execute the `resolve` immediately
65
+ *
66
+ * If not, put it in a queue and wait for the currently pending process to execute `release`
67
+ *
68
+ * @param {boolean=} lazy
69
+ */
70
+ acquire(lazy?: boolean): Promise<void>;
71
+ release(): void;
72
+ /**
73
+ * @param {number} restriction
74
+ */
75
+ setRestriction(restriction: number): void;
76
+ get pending(): number;
77
+ /**
78
+ * automatic acquire/release
79
+ *
80
+ * @template {any} T description
81
+ * @param {() => Promise<T>} process
82
+ * @param {boolean=} lazy
83
+ */
84
+ flow<T>(process: () => Promise<T>, lazy?: boolean): Promise<T>;
85
+ }
package/cjs/core.d.ts ADDED
@@ -0,0 +1,107 @@
1
+ /*!
2
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3
+ Copyright (C) 2020 jeffy-g <hirotom1107@gmail.com>
4
+ Released under the MIT license
5
+ https://opensource.org/licenses/mit-license.php
6
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
7
+ */
8
+ /**
9
+ * @file minimal implementation of semaphore (core
10
+ * @author jeffy-g <hirotom1107@gmail.com>
11
+ * @version 1.0
12
+ */
13
+ import type { Deque } from "./deque";
14
+ /**
15
+ * basic of simplified lock interface
16
+ */
17
+ export interface ISimplifiedLock {
18
+ /**
19
+ * acquire the process rights
20
+ *
21
+ * @param {boolean} lazy Whether the privilege acquisition process is deffer. default `true`
22
+ */
23
+ acquire(lazy?: boolean): Promise<void>;
24
+ /**
25
+ * release the pending of one
26
+ */
27
+ release(): void;
28
+ /**
29
+ * Change sharing restrictions to the value of `restriction`
30
+ * @param {number} restriction
31
+ */
32
+ setRestriction(restriction: number): void;
33
+ /**
34
+ * Get the number of currently pending processes
35
+ * @type {number}
36
+ */
37
+ readonly pending: number;
38
+ /**
39
+ * limitation
40
+ * @type {number}
41
+ */
42
+ limit: number;
43
+ /**
44
+ * capacity
45
+ * @type {number}
46
+ */
47
+ capacity: number;
48
+ }
49
+ /**
50
+ * extention of `ISimplifiedLock` interface
51
+ */
52
+ export interface IFlowableLock extends ISimplifiedLock {
53
+ /**
54
+ * combination of acquire/release
55
+ *
56
+ * + acquire/release is automatic
57
+ *
58
+ * @param lazy Whether the privilege acquisition process is deffer. default `true`
59
+ */
60
+ flow<T>(f: () => Promise<T>, lazy?: boolean): Promise<T>;
61
+ }
62
+ /**
63
+ * internal type for `createMiniSemaphore`
64
+ */
65
+ export type TFlowableLock<T = TVoidFunction> = IFlowableLock & {
66
+ /**
67
+ * pending
68
+ */
69
+ readonly q: Deque<T>;
70
+ };
71
+ export type TResolver = {
72
+ resolve: () => void;
73
+ reject: (reason: any) => void;
74
+ };
75
+ export type TAbortListener = (reason: IProcessAbortedError) => void;
76
+ export type TFlowableLockWithAbort = IFlowableLock & {
77
+ readonly q: Deque<TResolver>;
78
+ abort(): void;
79
+ onAbort(listener: TAbortListener): void;
80
+ offAbort(listener: TAbortListener): void;
81
+ };
82
+ export declare interface IProcessAbortedError {
83
+ readonly message: "Process Aborted";
84
+ }
85
+ export type TVoidFunction = () => void;
86
+ /**
87
+ *
88
+ * @param {TFlowableLock} dis
89
+ * @param {boolean} [lazy] default: true
90
+ * @returns {Promise<void>}
91
+ */
92
+ export declare const acquire: (dis: TFlowableLock, lazy?: boolean) => Promise<void>;
93
+ /**
94
+ * @param {TFlowableLock} dis
95
+ * @returns {void}
96
+ */
97
+ export declare const release: (dis: TFlowableLock) => void;
98
+ /**
99
+ * @param {TFlowableLockWithAbort} dis
100
+ * @returns {Promise<void>}
101
+ */
102
+ export declare const acquireWithAbort: (dis: TFlowableLockWithAbort) => Promise<void>;
103
+ /**
104
+ * @param {TFlowableLockWithAbort} dis
105
+ * @returns {void}
106
+ */
107
+ export declare const releaseWithAbort: (dis: TFlowableLockWithAbort) => void;
package/cjs/core.js CHANGED
@@ -2,13 +2,16 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.releaseWithAbort = exports.acquireWithAbort = exports.release = exports.acquire = void 0;
4
4
  /**
5
- * @typedef {import("./index").TVoidFunction} TVoidFunction
6
- * @typedef {import("./index").Deque<TVoidFunction>} Deque
7
- * @typedef {import("./index").TFlowableLockWithAbort} TFlowableLockWithAbort
8
- * @typedef {import("./index").Deque<TResolver>} DequeWithAbort
9
- * @typedef {import("./index").ISimplifiedLock} ISimplifiedLock
10
- * @typedef {import("./index").TFlowableLock} TFlowableLock
11
- * @typedef {import("./index").IFlowableLock} IFlowableLock
5
+ * @import {
6
+ * TResolver,
7
+ * TVoidFunction,
8
+ * TFlowableLockWithAbort,
9
+ * ISimplifiedLock,
10
+ * TFlowableLock,
11
+ * IFlowableLock,
12
+ * Deque,
13
+ * } from "./index.mjs";
14
+ * @typedef {Deque<TResolver>} DequeWithAbort
12
15
  */
13
16
  /**
14
17
  * Throws an error indicating an inconsistent state in mini-semaphore.
@@ -51,7 +54,7 @@ exports.acquire = acquire;
51
54
  * @returns {void}
52
55
  */
53
56
  const release = (dis) => {
54
- /** @type {Deque} */
57
+ /** @type {Deque<TVoidFunction>} */
55
58
  let dq;
56
59
  if ((dq = dis.q).length) {
57
60
  (dq.shift() || /* istanbul ignore next */ THROW)();
@@ -91,13 +94,9 @@ const releaseWithAbort = (dis) => {
91
94
  let dq;
92
95
  if ((dq = dis.q).length) {
93
96
  const resolver = dq.shift();
97
+ resolver && resolver.resolve() || (
94
98
  /* istanbul ignore next */
95
- if (!resolver) {
96
- THROW();
97
- }
98
- else {
99
- resolver.resolve();
100
- }
99
+ THROW());
101
100
  }
102
101
  else {
103
102
  if (dis.capacity < dis.limit) {
package/cjs/deque.d.ts ADDED
@@ -0,0 +1,28 @@
1
+ /**
2
+ * ### Implementation restricted to FIFO
3
+ *
4
+ * This class uses a ring buffer for efficient FIFO operations.
5
+ * The maximum buffer size is **`1073741824`**.
6
+ *
7
+ * This class is based on https://github.com/petkaantonov/deque/blob/master/js/deque.js
8
+ * Released under the MIT License: https://github.com/petkaantonov/deque/blob/master/LICENSE
9
+ *
10
+ * @template {any} T
11
+ */
12
+ export declare class Deque<T extends any> {
13
+ _c: number;
14
+ _l: number;
15
+ _f: number;
16
+ _a: T[];
17
+ length: number;
18
+ /**
19
+ * default capacity `16`
20
+ * @param {number=} ic initial capacity
21
+ */
22
+ constructor(ic?: number);
23
+ /**
24
+ * @param {T} s subject
25
+ */
26
+ push(s: T): void;
27
+ shift(): T | undefined;
28
+ }
@@ -0,0 +1,21 @@
1
+ import type { IFlowableLock } from "./class";
2
+ /**
3
+ * @internal
4
+ * @date 2020/6/18
5
+ */
6
+ type TFlowableLockWithTimeStamp = IFlowableLock & {
7
+ last?: number;
8
+ };
9
+ /**
10
+ * @typedef {import("./index").IFlowableLock & { last?: number }} TFlowableLockWithTimeStamp
11
+ */
12
+ /**
13
+ * Flow Restriction
14
+ */
15
+ export declare const restrictor: {
16
+ getLockByKey: (key: PropertyKey) => Promise<TFlowableLockWithTimeStamp>;
17
+ cleanup: (timeSpan: number, debug?: true) => Promise<number>;
18
+ multi: <T>(key: PropertyKey, restriction: number, pb: () => Promise<T>) => Promise<T>;
19
+ one: <T>(key: PropertyKey, pb: () => Promise<T>) => Promise<T>;
20
+ };
21
+ export {};
package/cjs/index.d.ts CHANGED
@@ -100,9 +100,15 @@ export declare type TResolver = {
100
100
  resolve: () => void;
101
101
  reject: (reason: any) => void;
102
102
  };
103
+ export declare interface IProcessAbortedError {
104
+ readonly message: "Process Aborted";
105
+ }
106
+ export type TAbortListener = (reason: IProcessAbortedError) => void;
103
107
  export declare type TFlowableLockWithAbort = IFlowableLock & {
104
108
  readonly q: Deque<TResolver>;
105
109
  abort(): void;
110
+ onAbort(listener: TAbortListener): void;
111
+ offAbort(listener: TAbortListener): void;
106
112
  };
107
113
 
108
114
  /**
package/cjs/index.js CHANGED
@@ -10,4 +10,4 @@ var deque_1 = require("./deque");
10
10
  Object.defineProperty(exports, "Deque", { enumerable: true, get: function () { return deque_1.Deque; } });
11
11
  var flow_restrictor_1 = require("./flow-restrictor");
12
12
  Object.defineProperty(exports, "restrictor", { enumerable: true, get: function () { return flow_restrictor_1.restrictor; } });
13
- exports.version = "v1.4.2";
13
+ exports.version = "v1.5.1";
@@ -0,0 +1,86 @@
1
+ /*!
2
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3
+ Copyright (C) 2020 jeffy-g <hirotom1107@gmail.com>
4
+ Released under the MIT license
5
+ https://opensource.org/licenses/mit-license.php
6
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
7
+ */
8
+ /**
9
+ * @file minimal implementation of semaphore (object implementation
10
+ * @author jeffy-g <hirotom1107@gmail.com>
11
+ * @version 1.0
12
+ */
13
+ import * as core from "./core";
14
+ import { Deque } from "./deque";
15
+ export type * from "./core";
16
+ /**
17
+ * object implementation of `IFlowableLock`
18
+ *
19
+ * + constructs a semaphore object limited at `capacity`
20
+ *
21
+ * @param {number} capacity limitation of concurrent async by `capacity`
22
+ * @date 2020/2/7
23
+ * @version 1.0
24
+ */
25
+ export declare const create: (capacity: number) => {
26
+ pending: number;
27
+ /**
28
+ *
29
+ * @param {boolean} [lazy]
30
+ * @returns {Promise<void>}
31
+ */
32
+ acquire(lazy?: boolean): Promise<void>;
33
+ release(): void;
34
+ /**
35
+ * @template {any} T
36
+ * @param {() => Promise<T>} process
37
+ * @param {boolean} [lazy]
38
+ * @returns {Promise<T>}
39
+ */
40
+ flow<T>(process: () => Promise<T>, lazy?: boolean): Promise<T>;
41
+ setRestriction(restriction: number): void;
42
+ limit: number;
43
+ capacity: number;
44
+ q: Deque<core.TVoidFunction>;
45
+ };
46
+ /**
47
+ * object implementation of `TFlowableLockWithAbort`
48
+ *
49
+ * + constructs a semaphore object limited at `capacity`
50
+ *
51
+ * @param {number} capacity limitation of concurrent async by `capacity`
52
+ * @date 2025/5/12
53
+ * @version 1.4
54
+ */
55
+ export declare const createWithAbort: (capacity: number) => {
56
+ pending: number;
57
+ /**
58
+ * @returns {Promise<void>}
59
+ */
60
+ acquire(): Promise<void>;
61
+ release(): void;
62
+ /**
63
+ * @template {any} T
64
+ * @param {() => Promise<T>} process
65
+ * @returns {Promise<T>}
66
+ */
67
+ flow<T>(process: () => Promise<T>): Promise<T>;
68
+ /**
69
+ * @throws {AggregateError} description
70
+ */
71
+ abort(): void;
72
+ /**
73
+ * Registers an event listener for the "abort" event.
74
+ * @param {TAbortListener} listener
75
+ */
76
+ onAbort(listener: core.TAbortListener): void;
77
+ /**
78
+ * Removes an event listener for the "abort" event.
79
+ * @param {TAbortListener} listener
80
+ */
81
+ offAbort(listener: core.TAbortListener): void;
82
+ setRestriction(restriction: number): void;
83
+ limit: number;
84
+ capacity: number;
85
+ q: Deque<core.TResolver>;
86
+ };
package/cjs/object.js CHANGED
@@ -20,7 +20,17 @@ const r = core.release;
20
20
  const aa = core.acquireWithAbort;
21
21
  const ra = core.releaseWithAbort;
22
22
  /**
23
- * @template {core.IFlowableLock & { q: Deque<unknown>}} T
23
+ * @import {
24
+ * TResolver,
25
+ * TFlowableLock,
26
+ * IFlowableLock,
27
+ * TAbortListener,
28
+ * IProcessAbortedError,
29
+ * TFlowableLockWithAbort,
30
+ * } from "./index.mjs";
31
+ */
32
+ /**
33
+ * @template {IFlowableLock & { q: Deque<unknown>}} T
24
34
  * @param {number} capacity
25
35
  * @returns
26
36
  */
@@ -47,9 +57,9 @@ const createBase = (capacity) => {
47
57
  * @version 1.0
48
58
  */
49
59
  const create = (capacity) => {
50
- /** @type {core.TFlowableLock} */
60
+ /** @type {TFlowableLock} */
51
61
  const base = createBase(capacity);
52
- return /** @satisfies {core.TFlowableLock} */ ({
62
+ return /** @satisfies {TFlowableLock} */ ({
53
63
  ...base,
54
64
  get pending() {
55
65
  return this.q.length;
@@ -93,9 +103,11 @@ exports.create = create;
93
103
  * @version 1.4
94
104
  */
95
105
  const createWithAbort = (capacity) => {
96
- /** @type {core.TFlowableLockWithAbort} */
106
+ /** @type {TFlowableLockWithAbort} */
97
107
  const base = createBase(capacity);
98
- return /** @satisfies {core.TFlowableLockWithAbort} */ ({
108
+ /** @type {TAbortListener[]} */
109
+ const abortListeners = [];
110
+ return /** @satisfies {TFlowableLockWithAbort} */ ({
99
111
  ...base,
100
112
  get pending() {
101
113
  return this.q.length;
@@ -115,27 +127,51 @@ const createWithAbort = (capacity) => {
115
127
  * @returns {Promise<T>}
116
128
  */
117
129
  async flow(process) {
118
- await aa(this);
130
+ /** @type {T | undefined} */
131
+ let result;
119
132
  try {
120
- return await process();
133
+ await aa(this);
134
+ result = await process();
135
+ ra(this);
121
136
  }
122
137
  finally {
123
- ra(this);
138
+ return /** @type {T} */ (result);
124
139
  }
125
140
  },
126
141
  /**
127
142
  * @throws {AggregateError} description
128
143
  */
129
144
  abort() {
130
- const dq = this.q;
131
- let resolver;
145
+ /** @type {IProcessAbortedError} */
132
146
  const reason = {
133
147
  message: "Process Aborted"
134
148
  };
149
+ abortListeners.forEach(listener => listener(reason));
150
+ const dq = this.q;
151
+ let resolver;
135
152
  while (resolver = dq.shift()) {
136
153
  resolver.reject(reason);
137
154
  }
138
155
  this.capacity = this.limit;
156
+ },
157
+ /**
158
+ * Registers an event listener for the "abort" event.
159
+ * @param {TAbortListener} listener
160
+ */
161
+ onAbort(listener) {
162
+ if (!abortListeners.includes(listener)) {
163
+ abortListeners.push(listener);
164
+ }
165
+ },
166
+ /**
167
+ * Removes an event listener for the "abort" event.
168
+ * @param {TAbortListener} listener
169
+ */
170
+ offAbort(listener) {
171
+ const idx = abortListeners.findIndex(ls => listener === ls);
172
+ if (idx !== -1) {
173
+ /*return */ abortListeners.splice(idx, 1);
174
+ }
139
175
  }
140
176
  });
141
177
  };
package/esm/core.mjs CHANGED
@@ -1,11 +1,14 @@
1
1
  /**
2
- * @typedef {import("./index.mjs").TVoidFunction} TVoidFunction
3
- * @typedef {import("./index.mjs").Deque<TVoidFunction>} Deque
4
- * @typedef {import("./index.mjs").TFlowableLockWithAbort} TFlowableLockWithAbort
5
- * @typedef {import("./index.mjs").Deque<TResolver>} DequeWithAbort
6
- * @typedef {import("./index.mjs").ISimplifiedLock} ISimplifiedLock
7
- * @typedef {import("./index.mjs").TFlowableLock} TFlowableLock
8
- * @typedef {import("./index.mjs").IFlowableLock} IFlowableLock
2
+ * @import {
3
+ * TResolver,
4
+ * TVoidFunction,
5
+ * TFlowableLockWithAbort,
6
+ * ISimplifiedLock,
7
+ * TFlowableLock,
8
+ * IFlowableLock,
9
+ * Deque,
10
+ * } from "./index.mjs";
11
+ * @typedef {Deque<TResolver>} DequeWithAbort
9
12
  */
10
13
  /**
11
14
  * Throws an error indicating an inconsistent state in mini-semaphore.
@@ -47,7 +50,7 @@ export const acquire = (dis, lazy = true) => {
47
50
  * @returns {void}
48
51
  */
49
52
  export const release = (dis) => {
50
- /** @type {Deque} */
53
+ /** @type {Deque<TVoidFunction>} */
51
54
  let dq;
52
55
  if ((dq = dis.q).length) {
53
56
  (dq.shift() || /* istanbul ignore next */ THROW)();
@@ -85,13 +88,9 @@ export const releaseWithAbort = (dis) => {
85
88
  let dq;
86
89
  if ((dq = dis.q).length) {
87
90
  const resolver = dq.shift();
91
+ resolver && resolver.resolve() || (
88
92
  /* istanbul ignore next */
89
- if (!resolver) {
90
- THROW();
91
- }
92
- else {
93
- resolver.resolve();
94
- }
93
+ THROW());
95
94
  }
96
95
  else {
97
96
  if (dis.capacity < dis.limit) {
package/esm/index.d.mts CHANGED
@@ -100,9 +100,15 @@ export declare type TResolver = {
100
100
  resolve: () => void;
101
101
  reject: (reason: any) => void;
102
102
  };
103
+ export declare interface IProcessAbortedError {
104
+ readonly message: "Process Aborted";
105
+ }
106
+ export type TAbortListener = (reason: IProcessAbortedError) => void;
103
107
  export declare type TFlowableLockWithAbort = IFlowableLock & {
104
108
  readonly q: Deque<TResolver>;
105
109
  abort(): void;
110
+ onAbort(listener: TAbortListener): void;
111
+ offAbort(listener: TAbortListener): void;
106
112
  };
107
113
 
108
114
  /**
package/esm/index.mjs CHANGED
@@ -2,4 +2,4 @@ export { MiniSemaphore } from "./class.mjs";
2
2
  export { create, createWithAbort } from "./object.mjs";
3
3
  export { Deque } from "./deque.mjs";
4
4
  export { restrictor } from "./flow-restrictor.mjs";
5
- export const version = "v1.4.2";
5
+ export const version = "v1.5.1";
package/esm/object.mjs CHANGED
@@ -17,7 +17,17 @@ const r = core.release;
17
17
  const aa = core.acquireWithAbort;
18
18
  const ra = core.releaseWithAbort;
19
19
  /**
20
- * @template {core.IFlowableLock & { q: Deque<unknown>}} T
20
+ * @import {
21
+ * TResolver,
22
+ * TFlowableLock,
23
+ * IFlowableLock,
24
+ * TAbortListener,
25
+ * IProcessAbortedError,
26
+ * TFlowableLockWithAbort,
27
+ * } from "./index.mjs";
28
+ */
29
+ /**
30
+ * @template {IFlowableLock & { q: Deque<unknown>}} T
21
31
  * @param {number} capacity
22
32
  * @returns
23
33
  */
@@ -44,9 +54,9 @@ const createBase = (capacity) => {
44
54
  * @version 1.0
45
55
  */
46
56
  export const create = (capacity) => {
47
- /** @type {core.TFlowableLock} */
57
+ /** @type {TFlowableLock} */
48
58
  const base = createBase(capacity);
49
- return /** @satisfies {core.TFlowableLock} */ ({
59
+ return /** @satisfies {TFlowableLock} */ ({
50
60
  ...base,
51
61
  get pending() {
52
62
  return this.q.length;
@@ -89,9 +99,11 @@ export const create = (capacity) => {
89
99
  * @version 1.4
90
100
  */
91
101
  export const createWithAbort = (capacity) => {
92
- /** @type {core.TFlowableLockWithAbort} */
102
+ /** @type {TFlowableLockWithAbort} */
93
103
  const base = createBase(capacity);
94
- return /** @satisfies {core.TFlowableLockWithAbort} */ ({
104
+ /** @type {TAbortListener[]} */
105
+ const abortListeners = [];
106
+ return /** @satisfies {TFlowableLockWithAbort} */ ({
95
107
  ...base,
96
108
  get pending() {
97
109
  return this.q.length;
@@ -111,27 +123,51 @@ export const createWithAbort = (capacity) => {
111
123
  * @returns {Promise<T>}
112
124
  */
113
125
  async flow(process) {
114
- await aa(this);
126
+ /** @type {T | undefined} */
127
+ let result;
115
128
  try {
116
- return await process();
129
+ await aa(this);
130
+ result = await process();
131
+ ra(this);
117
132
  }
118
133
  finally {
119
- ra(this);
134
+ return /** @type {T} */ (result);
120
135
  }
121
136
  },
122
137
  /**
123
138
  * @throws {AggregateError} description
124
139
  */
125
140
  abort() {
126
- const dq = this.q;
127
- let resolver;
141
+ /** @type {IProcessAbortedError} */
128
142
  const reason = {
129
143
  message: "Process Aborted"
130
144
  };
145
+ abortListeners.forEach(listener => listener(reason));
146
+ const dq = this.q;
147
+ let resolver;
131
148
  while (resolver = dq.shift()) {
132
149
  resolver.reject(reason);
133
150
  }
134
151
  this.capacity = this.limit;
152
+ },
153
+ /**
154
+ * Registers an event listener for the "abort" event.
155
+ * @param {TAbortListener} listener
156
+ */
157
+ onAbort(listener) {
158
+ if (!abortListeners.includes(listener)) {
159
+ abortListeners.push(listener);
160
+ }
161
+ },
162
+ /**
163
+ * Removes an event listener for the "abort" event.
164
+ * @param {TAbortListener} listener
165
+ */
166
+ offAbort(listener) {
167
+ const idx = abortListeners.findIndex(ls => listener === ls);
168
+ if (idx !== -1) {
169
+ /*return */ abortListeners.splice(idx, 1);
170
+ }
135
171
  }
136
172
  });
137
173
  };
package/index.d.ts CHANGED
@@ -100,9 +100,15 @@ export declare type TResolver = {
100
100
  resolve: () => void;
101
101
  reject: (reason: any) => void;
102
102
  };
103
+ export declare interface IProcessAbortedError {
104
+ readonly message: "Process Aborted";
105
+ }
106
+ export type TAbortListener = (reason: IProcessAbortedError) => void;
103
107
  export declare type TFlowableLockWithAbort = IFlowableLock & {
104
108
  readonly q: Deque<TResolver>;
105
109
  abort(): void;
110
+ onAbort(listener: TAbortListener): void;
111
+ offAbort(listener: TAbortListener): void;
106
112
  };
107
113
 
108
114
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mini-semaphore",
3
- "version": "1.4.2",
3
+ "version": "1.5.1",
4
4
  "description": "A lightweight version of Semaphore",
5
5
  "private": false,
6
6
  "main": "./cjs/index.js",
@@ -8,6 +8,43 @@
8
8
  "unpkg": "./umd/index.js",
9
9
  "sideEffects": false,
10
10
  "types": "./index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./index.d.ts",
14
+ "import": "./esm/index.mjs",
15
+ "require": "./cjs/index.js"
16
+ },
17
+ "./class": {
18
+ "import": "./esm/class.mjs",
19
+ "require": "./cjs/class.js"
20
+ },
21
+ "./object": {
22
+ "import": "./esm/object.mjs",
23
+ "require": "./cjs/object.js"
24
+ },
25
+ "./deque": {
26
+ "import": "./esm/deque.mjs",
27
+ "require": "./cjs/deque.js"
28
+ },
29
+ "./flow-restrictor": {
30
+ "import": "./esm/flow-restrictor.mjs",
31
+ "require": "./cjs/flow-restrictor.js"
32
+ },
33
+ "./umd": {
34
+ "import": "./umd/index.js",
35
+ "require": "./umd/index.js",
36
+ "types": "./umd/index.d.ts"
37
+ },
38
+ "./webpack": {
39
+ "import": "./webpack/index.js",
40
+ "require": "./webpack/index.js",
41
+ "types": "./webpack/index.d.ts"
42
+ },
43
+ "./webpack-esm": {
44
+ "import": "./webpack-esm/index.mjs",
45
+ "types": "./webpack-esm/index.d.mts"
46
+ }
47
+ },
11
48
  "author": "jeffy-g",
12
49
  "license": "MIT",
13
50
  "bugs": {
package/umd/index.d.ts CHANGED
@@ -100,9 +100,15 @@ export declare type TResolver = {
100
100
  resolve: () => void;
101
101
  reject: (reason: any) => void;
102
102
  };
103
+ export declare interface IProcessAbortedError {
104
+ readonly message: "Process Aborted";
105
+ }
106
+ export type TAbortListener = (reason: IProcessAbortedError) => void;
103
107
  export declare type TFlowableLockWithAbort = IFlowableLock & {
104
108
  readonly q: Deque<TResolver>;
105
109
  abort(): void;
110
+ onAbort(listener: TAbortListener): void;
111
+ offAbort(listener: TAbortListener): void;
106
112
  };
107
113
 
108
114
  /**
package/umd/index.js CHANGED
@@ -1,7 +1,8 @@
1
1
  /*! For license information please see index.js.LICENSE.txt */
2
- ((e,t)=>{'object'==typeof exports&&'object'==typeof module?module.exports=t():'function'==typeof define&&define.amd?define([],t):'object'==typeof exports?exports.MiniSema=t():e.MiniSema=t()})(globalThis,(()=>(()=>{"use strict";var e={139:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.createWithAbort=t.create=void 0;const r=i(461),n=i(761),s=r.acquire,a=r.release,o=r.acquireWithAbort,c=r.releaseWithAbort,l=e=>({capacity:e,limit:e,q:new n.Deque(e),setRestriction(e){this.limit=this.capacity=e}});t.create=e=>({...l(e),get pending(){return this.q.length},acquire(e){return s(this,e)},release(){a(this)},async flow(e,t){await s(this,t);try{return await e()}finally{a(this)}}});t.createWithAbort=e=>({...l(e),get pending(){return this.q.length},acquire(){return o(this)},release(){
3
- c(this)},async flow(e){await o(this);try{return await e()}finally{c(this)}},abort(){const e=this.q;let t;const i={message:"Process Aborted"};for(;t=e.shift();)t.reject(i);this.capacity=this.limit}})},461:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.releaseWithAbort=t.acquireWithAbort=t.release=t.acquire=void 0;const i=()=>{throw new Error("mini-semaphore: Detected an inconsistent state, possibly due to a logic error or unexpected behavior.")},r=(e,t)=>{e.capacity>0?(e.capacity--,t()):e.q.push(t)};t.acquire=(e,t=!0)=>new Promise((i=>{t?setTimeout((()=>r(e,i)),4):r(e,i)}));t.release=e=>{let t;(t=e.q).length?(t.shift()||i)():e.capacity++,e.capacity>e.limit&&(console.warn("inconsistent release!"),e.capacity=e.limit)};t.acquireWithAbort=e=>new Promise(((t,i)=>{
4
- e.capacity>0?(e.capacity--,t()):e.q.push({resolve:t,reject:i})}));t.releaseWithAbort=e=>{let t;if((t=e.q).length){const e=t.shift();e?e.resolve():i()}else e.capacity<e.limit&&e.capacity++}},464:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.restrictor=void 0;const r=i(518);t.restrictor=(()=>{const{MiniSemaphore:e}=r,t=new e(1);let i=Object.create(null);async function n(r,n,s){const a=await(async(r,n)=>{await t.acquire(!1);let s=i[r];if(s||(i[r]=s=new e(n)),s.limit!==n)throw t.release(),new ReferenceError(`Cannot get object with different restriction: key: '${String(r)}', lock.limit: ${s.limit} <-> restriction: ${n},`);return t.release(),s})(r,n),o=a.flow(s);return a.last=Date.now(),o}return{getLockByKey:async e=>{await t.acquire(!1);const r=i[e];return t.release(),r},
5
- cleanup:async(e,r)=>{await t.acquire(!1);const n=i,s=Object.create(null),a=Object.keys(n);let o,c=0;!e&&(e=1),e*=1e3,r&&(o=[]);for(let t=0,i=a.length;t<i;){const i=a[t++],l=n[i];l.last&&Date.now()-l.last>=e?(c++,r&&o.push(i)):s[i]=l}return i=s,t.release(),r&&console.log(`eliminated: [\n${o.join(",\n")}\n]\nlived: [\n${Object.keys(s).join(",\n")}\n]`),c},multi:n,one:async function(e,t){return n(e,1,t)}}})()},518:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.MiniSemaphore=void 0;const r=i(461),n=i(761),s=r.acquire,a=r.release;t.MiniSemaphore=class{constructor(e){this.limit=this.capacity=e,this.q=new n.Deque(e)}acquire(e){return s(this,e)}release(){a(this)}setRestriction(e){this.limit=this.capacity=e}get pending(){return this.q.length}async flow(e,t){await s(this,t);try{
6
- return await e()}finally{a(this)}}}},761:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.Deque=void 0;const i=e=>(e=>(e>>>=0,e--,e|=e>>1,e|=e>>2,e|=e>>4,e|=e>>8,1+(e|=e>>16)))(Math.min(Math.max(16,0|e),1073741824));t.Deque=class{constructor(e){this._c=i(e||16),this._l=0,this._f=0,this._a=[],this.length=0}push(e){const t=this._l;this._c<t+1&&r(this,i(1.5*this._c+16));const n=this._f+t&this._c-1;this._a[n]=e,this.length=this._l=t+1}shift(){const e=this._l;if(0===e)return;const t=this._f,i=this._a[t];return this._a[t]=void 0,this._f=t+1&this._c-1,this.length=this._l=e-1,i}};const r=(e,t)=>{const i=e._c;e._c=t;const r=e._f+e._l;if(r>i){const t=r&i-1;((e,t,i,r,n)=>{for(let s=0;s<n;++s)i[s+r]=e[s+t],e[s+t]=void 0})(e._a,0,e._a,i,t)}}}},t={};function i(r){var n=t[r]
7
- ;if(void 0!==n)return n.exports;var s=t[r]={exports:{}};return e[r](s,s.exports,i),s.exports}var r={};return(()=>{var e=r;Object.defineProperty(e,"__esModule",{value:!0}),e.version=e.restrictor=e.Deque=e.createWithAbort=e.create=e.MiniSemaphore=void 0;var t=i(518);Object.defineProperty(e,"MiniSemaphore",{enumerable:!0,get:function(){return t.MiniSemaphore}});var n=i(139);Object.defineProperty(e,"create",{enumerable:!0,get:function(){return n.create}}),Object.defineProperty(e,"createWithAbort",{enumerable:!0,get:function(){return n.createWithAbort}});var s=i(761);Object.defineProperty(e,"Deque",{enumerable:!0,get:function(){return s.Deque}});var a=i(464);Object.defineProperty(e,"restrictor",{enumerable:!0,get:function(){return a.restrictor}}),e.version="v1.4.2"})(),r})()));
2
+ ((e,t)=>{'object'==typeof exports&&'object'==typeof module?module.exports=t():'function'==typeof define&&define.amd?define([],t):'object'==typeof exports?exports.MiniSema=t():e.MiniSema=t()})(globalThis,()=>(()=>{"use strict";var e=[,,(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.MiniSemaphore=void 0;const r=i(6),n=i(4),s=r.acquire,a=r.release;t.MiniSemaphore=class{constructor(e){this.limit=this.capacity=e,this.q=new n.Deque(e)}acquire(e){return s(this,e)}release(){a(this)}setRestriction(e){this.limit=this.capacity=e}get pending(){return this.q.length}async flow(e,t){await s(this,t);try{return await e()}finally{a(this)}}}},(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.createWithAbort=t.create=void 0
3
+ ;const r=i(6),n=i(4),s=r.acquire,a=r.release,o=r.acquireWithAbort,c=r.releaseWithAbort,l=e=>({capacity:e,limit:e,q:new n.Deque(e),setRestriction(e){this.limit=this.capacity=e}});t.create=e=>({...l(e),get pending(){return this.q.length},acquire(e){return s(this,e)},release(){a(this)},async flow(e,t){await s(this,t);try{return await e()}finally{a(this)}}});t.createWithAbort=e=>{const t=l(e),i=[];return{...t,get pending(){return this.q.length},acquire(){return o(this)},release(){c(this)},async flow(e){let t;try{await o(this),t=await e(),c(this)}finally{return t}},abort(){const e={message:"Process Aborted"};i.forEach(t=>t(e));const t=this.q;let r;for(;r=t.shift();)r.reject(e);this.capacity=this.limit},onAbort(e){i.includes(e)||i.push(e)},offAbort(e){const t=i.findIndex(t=>e===t)
4
+ ;-1!==t&&i.splice(t,1)}}}},(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.Deque=void 0;const i=e=>(e=>(e>>>=0,e--,e|=e>>1,e|=e>>2,e|=e>>4,e|=e>>8,1+(e|=e>>16)))(Math.min(Math.max(16,0|e),1073741824));t.Deque=class{constructor(e){this._c=i(e||16),this._l=0,this._f=0,this._a=[],this.length=0}push(e){const t=this._l;this._c<t+1&&r(this,i(1.5*this._c+16));const n=this._f+t&this._c-1;this._a[n]=e,this.length=this._l=t+1}shift(){const e=this._l;if(0===e)return;const t=this._f,i=this._a[t];return this._a[t]=void 0,this._f=t+1&this._c-1,this.length=this._l=e-1,i}};const r=(e,t)=>{const i=e._c;e._c=t;const r=e._f+e._l;if(r>i){const t=r&i-1;((e,t,i,r,n)=>{for(let s=0;s<n;++s)i[s+r]=e[s+t],e[s+t]=void 0})(e._a,0,e._a,i,t)}}},(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),
5
+ t.restrictor=void 0;const r=i(2);t.restrictor=(()=>{const{MiniSemaphore:e}=r,t=new e(1);let i=Object.create(null);async function n(r,n,s){const a=await(async(r,n)=>{await t.acquire(!1);let s=i[r];if(s||(i[r]=s=new e(n)),s.limit!==n)throw t.release(),new ReferenceError(`Cannot get object with different restriction: key: '${String(r)}', lock.limit: ${s.limit} <-> restriction: ${n},`);return t.release(),s})(r,n),o=a.flow(s);return a.last=Date.now(),o}return{getLockByKey:async e=>{await t.acquire(!1);const r=i[e];return t.release(),r},cleanup:async(e,r)=>{await t.acquire(!1);const n=i,s=Object.create(null),a=Object.keys(n);let o,c=0;!e&&(e=1),e*=1e3,r&&(o=[]);for(let t=0,i=a.length;t<i;){const i=a[t++],l=n[i];l.last&&Date.now()-l.last>=e?(c++,r&&o.push(i)):s[i]=l}return i=s,t.release(),
6
+ r&&console.log(`eliminated: [\n${o.join(",\n")}\n]\nlived: [\n${Object.keys(s).join(",\n")}\n]`),c},multi:n,one:async function(e,t){return n(e,1,t)}}})()},(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.releaseWithAbort=t.acquireWithAbort=t.release=t.acquire=void 0;const i=()=>{throw new Error("mini-semaphore: Detected an inconsistent state, possibly due to a logic error or unexpected behavior.")},r=(e,t)=>{e.capacity>0?(e.capacity--,t()):e.q.push(t)};t.acquire=(e,t=!0)=>new Promise(i=>{t?setTimeout(()=>r(e,i),4):r(e,i)});t.release=e=>{let t;(t=e.q).length?(t.shift()||i)():e.capacity++,e.capacity>e.limit&&(console.warn("inconsistent release!"),e.capacity=e.limit)};t.acquireWithAbort=e=>new Promise((t,i)=>{e.capacity>0?(e.capacity--,t()):e.q.push({resolve:t,reject:i})})
7
+ ;t.releaseWithAbort=e=>{let t;if((t=e.q).length){const e=t.shift();e&&e.resolve()||i()}else e.capacity<e.limit&&e.capacity++}}],t={};function i(r){var n=t[r];if(void 0!==n)return n.exports;var s=t[r]={exports:{}};return e[r](s,s.exports,i),s.exports}var r={};return(()=>{var e=r;Object.defineProperty(e,"__esModule",{value:!0}),e.version=e.restrictor=e.Deque=e.createWithAbort=e.create=e.MiniSemaphore=void 0;var t=i(2);Object.defineProperty(e,"MiniSemaphore",{enumerable:!0,get:function(){return t.MiniSemaphore}});var n=i(3);Object.defineProperty(e,"create",{enumerable:!0,get:function(){return n.create}}),Object.defineProperty(e,"createWithAbort",{enumerable:!0,get:function(){return n.createWithAbort}});var s=i(4);Object.defineProperty(e,"Deque",{enumerable:!0,get:function(){return s.Deque}})
8
+ ;var a=i(5);Object.defineProperty(e,"restrictor",{enumerable:!0,get:function(){return a.restrictor}}),e.version="v1.5.1"})(),r})());
@@ -100,9 +100,15 @@ export declare type TResolver = {
100
100
  resolve: () => void;
101
101
  reject: (reason: any) => void;
102
102
  };
103
+ export declare interface IProcessAbortedError {
104
+ readonly message: "Process Aborted";
105
+ }
106
+ export type TAbortListener = (reason: IProcessAbortedError) => void;
103
107
  export declare type TFlowableLockWithAbort = IFlowableLock & {
104
108
  readonly q: Deque<TResolver>;
105
109
  abort(): void;
110
+ onAbort(listener: TAbortListener): void;
111
+ offAbort(listener: TAbortListener): void;
106
112
  };
107
113
 
108
114
  /**
package/webpack/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /*! For license information please see index.js.LICENSE.txt */
2
- (()=>{"use strict";var e={139:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.createWithAbort=t.create=void 0;const r=i(461),n=i(761),s=r.acquire,a=r.release,c=r.acquireWithAbort,o=r.releaseWithAbort,l=e=>({capacity:e,limit:e,q:new n.Deque(e),setRestriction(e){this.limit=this.capacity=e}});t.create=e=>({...l(e),get pending(){return this.q.length},acquire(e){return s(this,e)},release(){a(this)},async flow(e,t){await s(this,t);try{return await e()}finally{a(this)}}});t.createWithAbort=e=>({...l(e),get pending(){return this.q.length},acquire(){return c(this)},release(){o(this)},async flow(e){await c(this);try{return await e()}finally{o(this)}},abort(){const e=this.q;let t;const i={message:"Process Aborted"};for(;t=e.shift();)t.reject(i);this.capacity=this.limit}})},461:(e,t)=>{
3
- Object.defineProperty(t,"__esModule",{value:!0}),t.releaseWithAbort=t.acquireWithAbort=t.release=t.acquire=void 0;const i=()=>{throw new Error("mini-semaphore: Detected an inconsistent state, possibly due to a logic error or unexpected behavior.")},r=(e,t)=>{e.capacity>0?(e.capacity--,t()):e.q.push(t)};t.acquire=(e,t=!0)=>new Promise((i=>{t?setTimeout((()=>r(e,i)),4):r(e,i)}));t.release=e=>{let t;(t=e.q).length?(t.shift()||i)():e.capacity++,e.capacity>e.limit&&(console.warn("inconsistent release!"),e.capacity=e.limit)};t.acquireWithAbort=e=>new Promise(((t,i)=>{e.capacity>0?(e.capacity--,t()):e.q.push({resolve:t,reject:i})}));t.releaseWithAbort=e=>{let t;if((t=e.q).length){const e=t.shift();e?e.resolve():i()}else e.capacity<e.limit&&e.capacity++}},464:(e,t,i)=>{
4
- Object.defineProperty(t,"__esModule",{value:!0}),t.restrictor=void 0;const r=i(518);t.restrictor=(()=>{const{MiniSemaphore:e}=r,t=new e(1);let i=Object.create(null);async function n(r,n,s){const a=await(async(r,n)=>{await t.acquire(!1);let s=i[r];if(s||(i[r]=s=new e(n)),s.limit!==n)throw t.release(),new ReferenceError(`Cannot get object with different restriction: key: '${String(r)}', lock.limit: ${s.limit} <-> restriction: ${n},`);return t.release(),s})(r,n),c=a.flow(s);return a.last=Date.now(),c}return{getLockByKey:async e=>{await t.acquire(!1);const r=i[e];return t.release(),r},cleanup:async(e,r)=>{await t.acquire(!1);const n=i,s=Object.create(null),a=Object.keys(n);let c,o=0;!e&&(e=1),e*=1e3,r&&(c=[]);for(let t=0,i=a.length;t<i;){const i=a[t++],l=n[i];l.last&&Date.now()-l.last>=e?(o++,
5
- r&&c.push(i)):s[i]=l}return i=s,t.release(),r&&console.log(`eliminated: [\n${c.join(",\n")}\n]\nlived: [\n${Object.keys(s).join(",\n")}\n]`),o},multi:n,one:async function(e,t){return n(e,1,t)}}})()},518:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.MiniSemaphore=void 0;const r=i(461),n=i(761),s=r.acquire,a=r.release;t.MiniSemaphore=class{constructor(e){this.limit=this.capacity=e,this.q=new n.Deque(e)}acquire(e){return s(this,e)}release(){a(this)}setRestriction(e){this.limit=this.capacity=e}get pending(){return this.q.length}async flow(e,t){await s(this,t);try{return await e()}finally{a(this)}}}},761:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.Deque=void 0;const i=e=>(e=>(e>>>=0,e--,e|=e>>1,e|=e>>2,e|=e>>4,e|=e>>8,
6
- 1+(e|=e>>16)))(Math.min(Math.max(16,0|e),1073741824));t.Deque=class{constructor(e){this._c=i(e||16),this._l=0,this._f=0,this._a=[],this.length=0}push(e){const t=this._l;this._c<t+1&&r(this,i(1.5*this._c+16));const n=this._f+t&this._c-1;this._a[n]=e,this.length=this._l=t+1}shift(){const e=this._l;if(0===e)return;const t=this._f,i=this._a[t];return this._a[t]=void 0,this._f=t+1&this._c-1,this.length=this._l=e-1,i}};const r=(e,t)=>{const i=e._c;e._c=t;const r=e._f+e._l;if(r>i){const t=r&i-1;((e,t,i,r,n)=>{for(let s=0;s<n;++s)i[s+r]=e[s+t],e[s+t]=void 0})(e._a,0,e._a,i,t)}}}},t={};function i(r){var n=t[r];if(void 0!==n)return n.exports;var s=t[r]={exports:{}};return e[r](s,s.exports,i),s.exports}var r={};(()=>{var e=r;Object.defineProperty(e,"__esModule",{value:!0}),
7
- e.version=e.restrictor=e.Deque=e.createWithAbort=e.create=e.MiniSemaphore=void 0;var t=i(518);Object.defineProperty(e,"MiniSemaphore",{enumerable:!0,get:function(){return t.MiniSemaphore}});var n=i(139);Object.defineProperty(e,"create",{enumerable:!0,get:function(){return n.create}}),Object.defineProperty(e,"createWithAbort",{enumerable:!0,get:function(){return n.createWithAbort}});var s=i(761);Object.defineProperty(e,"Deque",{enumerable:!0,get:function(){return s.Deque}});var a=i(464);Object.defineProperty(e,"restrictor",{enumerable:!0,get:function(){return a.restrictor}}),e.version="v1.4.2"})(),module.exports=r})();
2
+ (()=>{"use strict";var e=[,,(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.MiniSemaphore=void 0;const r=i(6),n=i(4),s=r.acquire,a=r.release;t.MiniSemaphore=class{constructor(e){this.limit=this.capacity=e,this.q=new n.Deque(e)}acquire(e){return s(this,e)}release(){a(this)}setRestriction(e){this.limit=this.capacity=e}get pending(){return this.q.length}async flow(e,t){await s(this,t);try{return await e()}finally{a(this)}}}},(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.createWithAbort=t.create=void 0;const r=i(6),n=i(4),s=r.acquire,a=r.release,c=r.acquireWithAbort,o=r.releaseWithAbort,l=e=>({capacity:e,limit:e,q:new n.Deque(e),setRestriction(e){this.limit=this.capacity=e}});t.create=e=>({...l(e),get pending(){return this.q.length},acquire(e){return s(this,e)},
3
+ release(){a(this)},async flow(e,t){await s(this,t);try{return await e()}finally{a(this)}}});t.createWithAbort=e=>{const t=l(e),i=[];return{...t,get pending(){return this.q.length},acquire(){return c(this)},release(){o(this)},async flow(e){let t;try{await c(this),t=await e(),o(this)}finally{return t}},abort(){const e={message:"Process Aborted"};i.forEach(t=>t(e));const t=this.q;let r;for(;r=t.shift();)r.reject(e);this.capacity=this.limit},onAbort(e){i.includes(e)||i.push(e)},offAbort(e){const t=i.findIndex(t=>e===t);-1!==t&&i.splice(t,1)}}}},(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.Deque=void 0;const i=e=>(e=>(e>>>=0,e--,e|=e>>1,e|=e>>2,e|=e>>4,e|=e>>8,1+(e|=e>>16)))(Math.min(Math.max(16,0|e),1073741824));t.Deque=class{constructor(e){this._c=i(e||16),this._l=0,this._f=0,
4
+ this._a=[],this.length=0}push(e){const t=this._l;this._c<t+1&&r(this,i(1.5*this._c+16));const n=this._f+t&this._c-1;this._a[n]=e,this.length=this._l=t+1}shift(){const e=this._l;if(0===e)return;const t=this._f,i=this._a[t];return this._a[t]=void 0,this._f=t+1&this._c-1,this.length=this._l=e-1,i}};const r=(e,t)=>{const i=e._c;e._c=t;const r=e._f+e._l;if(r>i){const t=r&i-1;((e,t,i,r,n)=>{for(let s=0;s<n;++s)i[s+r]=e[s+t],e[s+t]=void 0})(e._a,0,e._a,i,t)}}},(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.restrictor=void 0;const r=i(2);t.restrictor=(()=>{const{MiniSemaphore:e}=r,t=new e(1);let i=Object.create(null);async function n(r,n,s){const a=await(async(r,n)=>{await t.acquire(!1);let s=i[r];if(s||(i[r]=s=new e(n)),s.limit!==n)throw t.release(),
5
+ new ReferenceError(`Cannot get object with different restriction: key: '${String(r)}', lock.limit: ${s.limit} <-> restriction: ${n},`);return t.release(),s})(r,n),c=a.flow(s);return a.last=Date.now(),c}return{getLockByKey:async e=>{await t.acquire(!1);const r=i[e];return t.release(),r},cleanup:async(e,r)=>{await t.acquire(!1);const n=i,s=Object.create(null),a=Object.keys(n);let c,o=0;!e&&(e=1),e*=1e3,r&&(c=[]);for(let t=0,i=a.length;t<i;){const i=a[t++],l=n[i];l.last&&Date.now()-l.last>=e?(o++,r&&c.push(i)):s[i]=l}return i=s,t.release(),r&&console.log(`eliminated: [\n${c.join(",\n")}\n]\nlived: [\n${Object.keys(s).join(",\n")}\n]`),o},multi:n,one:async function(e,t){return n(e,1,t)}}})()},(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),
6
+ t.releaseWithAbort=t.acquireWithAbort=t.release=t.acquire=void 0;const i=()=>{throw new Error("mini-semaphore: Detected an inconsistent state, possibly due to a logic error or unexpected behavior.")},r=(e,t)=>{e.capacity>0?(e.capacity--,t()):e.q.push(t)};t.acquire=(e,t=!0)=>new Promise(i=>{t?setTimeout(()=>r(e,i),4):r(e,i)});t.release=e=>{let t;(t=e.q).length?(t.shift()||i)():e.capacity++,e.capacity>e.limit&&(console.warn("inconsistent release!"),e.capacity=e.limit)};t.acquireWithAbort=e=>new Promise((t,i)=>{e.capacity>0?(e.capacity--,t()):e.q.push({resolve:t,reject:i})});t.releaseWithAbort=e=>{let t;if((t=e.q).length){const e=t.shift();e&&e.resolve()||i()}else e.capacity<e.limit&&e.capacity++}}],t={};function i(r){var n=t[r];if(void 0!==n)return n.exports;var s=t[r]={exports:{}}
7
+ ;return e[r](s,s.exports,i),s.exports}var r={};(()=>{var e=r;Object.defineProperty(e,"__esModule",{value:!0}),e.version=e.restrictor=e.Deque=e.createWithAbort=e.create=e.MiniSemaphore=void 0;var t=i(2);Object.defineProperty(e,"MiniSemaphore",{enumerable:!0,get:function(){return t.MiniSemaphore}});var n=i(3);Object.defineProperty(e,"create",{enumerable:!0,get:function(){return n.create}}),Object.defineProperty(e,"createWithAbort",{enumerable:!0,get:function(){return n.createWithAbort}});var s=i(4);Object.defineProperty(e,"Deque",{enumerable:!0,get:function(){return s.Deque}});var a=i(5);Object.defineProperty(e,"restrictor",{enumerable:!0,get:function(){return a.restrictor}}),e.version="v1.5.1"})(),module.exports=r})();
@@ -100,9 +100,15 @@ export declare type TResolver = {
100
100
  resolve: () => void;
101
101
  reject: (reason: any) => void;
102
102
  };
103
+ export declare interface IProcessAbortedError {
104
+ readonly message: "Process Aborted";
105
+ }
106
+ export type TAbortListener = (reason: IProcessAbortedError) => void;
103
107
  export declare type TFlowableLockWithAbort = IFlowableLock & {
104
108
  readonly q: Deque<TResolver>;
105
109
  abort(): void;
110
+ onAbort(listener: TAbortListener): void;
111
+ offAbort(listener: TAbortListener): void;
106
112
  };
107
113
 
108
114
  /**
@@ -1,7 +1,8 @@
1
1
  /*! For license information please see index.js.LICENSE.txt */
2
- var e={139:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.createWithAbort=t.create=void 0;const r=i(461),s=i(761),n=r.acquire,a=r.release,c=r.acquireWithAbort,o=r.releaseWithAbort,l=e=>({capacity:e,limit:e,q:new s.Deque(e),setRestriction(e){this.limit=this.capacity=e}});t.create=e=>({...l(e),get pending(){return this.q.length},acquire(e){return n(this,e)},release(){a(this)},async flow(e,t){await n(this,t);try{return await e()}finally{a(this)}}});t.createWithAbort=e=>({...l(e),get pending(){return this.q.length},acquire(){return c(this)},release(){o(this)},async flow(e){await c(this);try{return await e()}finally{o(this)}},abort(){const e=this.q;let t;const i={message:"Process Aborted"};for(;t=e.shift();)t.reject(i);this.capacity=this.limit}})},461:(e,t)=>{
3
- Object.defineProperty(t,"__esModule",{value:!0}),t.releaseWithAbort=t.acquireWithAbort=t.release=t.acquire=void 0;const i=()=>{throw new Error("mini-semaphore: Detected an inconsistent state, possibly due to a logic error or unexpected behavior.")},r=(e,t)=>{e.capacity>0?(e.capacity--,t()):e.q.push(t)};t.acquire=(e,t=!0)=>new Promise((i=>{t?setTimeout((()=>r(e,i)),4):r(e,i)}));t.release=e=>{let t;(t=e.q).length?(t.shift()||i)():e.capacity++,e.capacity>e.limit&&(console.warn("inconsistent release!"),e.capacity=e.limit)};t.acquireWithAbort=e=>new Promise(((t,i)=>{e.capacity>0?(e.capacity--,t()):e.q.push({resolve:t,reject:i})}));t.releaseWithAbort=e=>{let t;if((t=e.q).length){const e=t.shift();e?e.resolve():i()}else e.capacity<e.limit&&e.capacity++}},464:(e,t,i)=>{
4
- Object.defineProperty(t,"__esModule",{value:!0}),t.restrictor=void 0;const r=i(518);t.restrictor=(()=>{const{MiniSemaphore:e}=r,t=new e(1);let i=Object.create(null);async function s(r,s,n){const a=await(async(r,s)=>{await t.acquire(!1);let n=i[r];if(n||(i[r]=n=new e(s)),n.limit!==s)throw t.release(),new ReferenceError(`Cannot get object with different restriction: key: '${String(r)}', lock.limit: ${n.limit} <-> restriction: ${s},`);return t.release(),n})(r,s),c=a.flow(n);return a.last=Date.now(),c}return{getLockByKey:async e=>{await t.acquire(!1);const r=i[e];return t.release(),r},cleanup:async(e,r)=>{await t.acquire(!1);const s=i,n=Object.create(null),a=Object.keys(s);let c,o=0;!e&&(e=1),e*=1e3,r&&(c=[]);for(let t=0,i=a.length;t<i;){const i=a[t++],l=s[i];l.last&&Date.now()-l.last>=e?(o++,
5
- r&&c.push(i)):n[i]=l}return i=n,t.release(),r&&console.log(`eliminated: [\n${c.join(",\n")}\n]\nlived: [\n${Object.keys(n).join(",\n")}\n]`),o},multi:s,one:async function(e,t){return s(e,1,t)}}})()},518:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.MiniSemaphore=void 0;const r=i(461),s=i(761),n=r.acquire,a=r.release;t.MiniSemaphore=class{constructor(e){this.limit=this.capacity=e,this.q=new s.Deque(e)}acquire(e){return n(this,e)}release(){a(this)}setRestriction(e){this.limit=this.capacity=e}get pending(){return this.q.length}async flow(e,t){await n(this,t);try{return await e()}finally{a(this)}}}},761:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.Deque=void 0;const i=e=>(e=>(e>>>=0,e--,e|=e>>1,e|=e>>2,e|=e>>4,e|=e>>8,
6
- 1+(e|=e>>16)))(Math.min(Math.max(16,0|e),1073741824));t.Deque=class{constructor(e){this._c=i(e||16),this._l=0,this._f=0,this._a=[],this.length=0}push(e){const t=this._l;this._c<t+1&&r(this,i(1.5*this._c+16));const s=this._f+t&this._c-1;this._a[s]=e,this.length=this._l=t+1}shift(){const e=this._l;if(0===e)return;const t=this._f,i=this._a[t];return this._a[t]=void 0,this._f=t+1&this._c-1,this.length=this._l=e-1,i}};const r=(e,t)=>{const i=e._c;e._c=t;const r=e._f+e._l;if(r>i){const t=r&i-1;((e,t,i,r,s)=>{for(let n=0;n<s;++n)i[n+r]=e[n+t],e[n+t]=void 0})(e._a,0,e._a,i,t)}}}},t={};function i(r){var s=t[r];if(void 0!==s)return s.exports;var n=t[r]={exports:{}};return e[r](n,n.exports,i),n.exports}var r={};(()=>{var e=r;Object.defineProperty(e,"BJ",{value:!0}),e.rE=e.Ws=e.Jj=e.Xz=e.vt=e.C=void 0
7
- ;var t=i(518);Object.defineProperty(e,"C",{enumerable:!0,get:function(){return t.MiniSemaphore}});var s=i(139);Object.defineProperty(e,"vt",{enumerable:!0,get:function(){return s.create}}),Object.defineProperty(e,"Xz",{enumerable:!0,get:function(){return s.createWithAbort}});var n=i(761);Object.defineProperty(e,"Jj",{enumerable:!0,get:function(){return n.Deque}});var a=i(464);Object.defineProperty(e,"Ws",{enumerable:!0,get:function(){return a.restrictor}}),e.rE="v1.4.2"})();const s=r.Jj,n=r.C,a=r.BJ,c=r.vt,o=r.Xz,l=r.Ws,h=r.rE;export{s as Deque,n as MiniSemaphore,a as __esModule,c as create,o as createWithAbort,l as restrictor,h as version};
2
+ var e=[,,(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.MiniSemaphore=void 0;const i=r(6),s=r(4),n=i.acquire,a=i.release;t.MiniSemaphore=class{constructor(e){this.limit=this.capacity=e,this.q=new s.Deque(e)}acquire(e){return n(this,e)}release(){a(this)}setRestriction(e){this.limit=this.capacity=e}get pending(){return this.q.length}async flow(e,t){await n(this,t);try{return await e()}finally{a(this)}}}},(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.createWithAbort=t.create=void 0;const i=r(6),s=r(4),n=i.acquire,a=i.release,c=i.acquireWithAbort,o=i.releaseWithAbort,l=e=>({capacity:e,limit:e,q:new s.Deque(e),setRestriction(e){this.limit=this.capacity=e}});t.create=e=>({...l(e),get pending(){return this.q.length},acquire(e){return n(this,e)},release(){a(this)},
3
+ async flow(e,t){await n(this,t);try{return await e()}finally{a(this)}}});t.createWithAbort=e=>{const t=l(e),r=[];return{...t,get pending(){return this.q.length},acquire(){return c(this)},release(){o(this)},async flow(e){let t;try{await c(this),t=await e(),o(this)}finally{return t}},abort(){const e={message:"Process Aborted"};r.forEach(t=>t(e));const t=this.q;let i;for(;i=t.shift();)i.reject(e);this.capacity=this.limit},onAbort(e){r.includes(e)||r.push(e)},offAbort(e){const t=r.findIndex(t=>e===t);-1!==t&&r.splice(t,1)}}}},(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.Deque=void 0;const r=e=>(e=>(e>>>=0,e--,e|=e>>1,e|=e>>2,e|=e>>4,e|=e>>8,1+(e|=e>>16)))(Math.min(Math.max(16,0|e),1073741824));t.Deque=class{constructor(e){this._c=r(e||16),this._l=0,this._f=0,this._a=[],
4
+ this.length=0}push(e){const t=this._l;this._c<t+1&&i(this,r(1.5*this._c+16));const s=this._f+t&this._c-1;this._a[s]=e,this.length=this._l=t+1}shift(){const e=this._l;if(0===e)return;const t=this._f,r=this._a[t];return this._a[t]=void 0,this._f=t+1&this._c-1,this.length=this._l=e-1,r}};const i=(e,t)=>{const r=e._c;e._c=t;const i=e._f+e._l;if(i>r){const t=i&r-1;((e,t,r,i,s)=>{for(let n=0;n<s;++n)r[n+i]=e[n+t],e[n+t]=void 0})(e._a,0,e._a,r,t)}}},(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.restrictor=void 0;const i=r(2);t.restrictor=(()=>{const{MiniSemaphore:e}=i,t=new e(1);let r=Object.create(null);async function s(i,s,n){const a=await(async(i,s)=>{await t.acquire(!1);let n=r[i];if(n||(r[i]=n=new e(s)),n.limit!==s)throw t.release(),
5
+ new ReferenceError(`Cannot get object with different restriction: key: '${String(i)}', lock.limit: ${n.limit} <-> restriction: ${s},`);return t.release(),n})(i,s),c=a.flow(n);return a.last=Date.now(),c}return{getLockByKey:async e=>{await t.acquire(!1);const i=r[e];return t.release(),i},cleanup:async(e,i)=>{await t.acquire(!1);const s=r,n=Object.create(null),a=Object.keys(s);let c,o=0;!e&&(e=1),e*=1e3,i&&(c=[]);for(let t=0,r=a.length;t<r;){const r=a[t++],l=s[r];l.last&&Date.now()-l.last>=e?(o++,i&&c.push(r)):n[r]=l}return r=n,t.release(),i&&console.log(`eliminated: [\n${c.join(",\n")}\n]\nlived: [\n${Object.keys(n).join(",\n")}\n]`),o},multi:s,one:async function(e,t){return s(e,1,t)}}})()},(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),
6
+ t.releaseWithAbort=t.acquireWithAbort=t.release=t.acquire=void 0;const r=()=>{throw new Error("mini-semaphore: Detected an inconsistent state, possibly due to a logic error or unexpected behavior.")},i=(e,t)=>{e.capacity>0?(e.capacity--,t()):e.q.push(t)};t.acquire=(e,t=!0)=>new Promise(r=>{t?setTimeout(()=>i(e,r),4):i(e,r)});t.release=e=>{let t;(t=e.q).length?(t.shift()||r)():e.capacity++,e.capacity>e.limit&&(console.warn("inconsistent release!"),e.capacity=e.limit)};t.acquireWithAbort=e=>new Promise((t,r)=>{e.capacity>0?(e.capacity--,t()):e.q.push({resolve:t,reject:r})});t.releaseWithAbort=e=>{let t;if((t=e.q).length){const e=t.shift();e&&e.resolve()||r()}else e.capacity<e.limit&&e.capacity++}}],t={};function r(i){var s=t[i];if(void 0!==s)return s.exports;var n=t[i]={exports:{}}
7
+ ;return e[i](n,n.exports,r),n.exports}var i={};(()=>{var e=i;Object.defineProperty(e,"__esModule",{value:!0}),e.version=e.restrictor=e.Deque=e.createWithAbort=e.create=e.MiniSemaphore=void 0;var t=r(2);Object.defineProperty(e,"MiniSemaphore",{enumerable:!0,get:function(){return t.MiniSemaphore}});var s=r(3);Object.defineProperty(e,"create",{enumerable:!0,get:function(){return s.create}}),Object.defineProperty(e,"createWithAbort",{enumerable:!0,get:function(){return s.createWithAbort}});var n=r(4);Object.defineProperty(e,"Deque",{enumerable:!0,get:function(){return n.Deque}});var a=r(5);Object.defineProperty(e,"restrictor",{enumerable:!0,get:function(){return a.restrictor}}),e.version="v1.5.1"})()
8
+ ;const s=i.Deque,n=i.MiniSemaphore,a=i.__esModule,c=i.create,o=i.createWithAbort,l=i.restrictor,h=i.version;export{s as Deque,n as MiniSemaphore,a as __esModule,c as create,o as createWithAbort,l as restrictor,h as version,i as default};