better-lock 3.0.0 → 3.2.0
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 +12 -3
- package/dist/better_lock.d.ts +13 -7
- package/dist/better_lock.js +80 -23
- package/dist/better_lock.js.map +1 -1
- package/dist/errors.d.ts +1 -0
- package/dist/errors.js +7 -1
- package/dist/errors.js.map +1 -1
- package/dist/options.d.ts +12 -0
- package/dist/types.d.ts +4 -0
- package/docs/assets/search.js +1 -1
- package/docs/classes/BetterLockError.html +3 -3
- package/docs/classes/BetterLockExecutionTimeoutError.html +6 -6
- package/docs/classes/BetterLockInternalError.html +3 -3
- package/docs/classes/BetterLockInvalidArgumentError.html +4 -4
- package/docs/classes/BetterLockJobAbortedError.html +5 -5
- package/docs/classes/BetterLockQueueOverflowError.html +8 -8
- package/docs/classes/BetterLockWaitTimeoutError.html +6 -6
- package/docs/classes/_internal_.BetterLock.html +189 -7
- package/docs/classes/_internal_.KeyQueue.html +4 -4
- package/docs/classes/_internal_.LockJob.html +13 -13
- package/docs/functions/_internal_.__home_panta_dev_pantas_better_lock_src_errors_.isAcquisitionFailureError.html +62 -0
- package/docs/index.html +13 -2
- package/docs/interfaces/BetterLockJobOptions.html +27 -4
- package/docs/interfaces/BetterLockOptions.html +26 -8
- package/docs/modules/_internal_.__home_panta_dev_pantas_better_lock_src_errors_.html +6 -1
- package/docs/types/BetterLockErrorName.html +1 -1
- package/docs/types/BetterLockExecutor.html +1 -1
- package/docs/types/BetterLockKey.html +1 -1
- package/docs/types/BetterLockQueueEjectionStrategy.html +1 -1
- package/docs/types/_internal_.ICallback.html +1 -1
- package/docs/types/_internal_.ICallbackExecutor.html +1 -1
- package/docs/types/_internal_.IErrorExports.html +1 -1
- package/docs/types/_internal_.IPromiseExecutor.html +1 -1
- package/docs/variables/BetterLock.html +1 -1
- package/package.json +1 -1
- package/spec/better_lock.spec.ts +160 -0
- package/src/better_lock.ts +115 -31
- package/src/errors.ts +14 -1
- package/src/options.ts +15 -0
- package/src/types.ts +5 -0
package/src/better_lock.ts
CHANGED
|
@@ -5,11 +5,12 @@ import {
|
|
|
5
5
|
BetterLockJobAbortedError,
|
|
6
6
|
BetterLockQueueOverflowError,
|
|
7
7
|
BetterLockWaitTimeoutError,
|
|
8
|
+
isAcquisitionFailureError,
|
|
8
9
|
} from './errors';
|
|
9
10
|
import { KeyQueue, LockJob } from './internals';
|
|
10
11
|
import { ILockJobOptions, IOptions } from './options';
|
|
11
12
|
import * as tools from './tools';
|
|
12
|
-
import { ICallback, ICallbackExecutor, ILockKey, IPromiseExecutor } from './types';
|
|
13
|
+
import { ICallback, ICallbackExecutor, ILockKey, IPromiseExecutor, IValidResult } from './types';
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Main better lock class.
|
|
@@ -80,30 +81,30 @@ export class BetterLock {
|
|
|
80
81
|
);
|
|
81
82
|
}
|
|
82
83
|
|
|
83
|
-
public acquire<TResult>(
|
|
84
|
+
public acquire<TResult extends IValidResult>(
|
|
84
85
|
executor: IPromiseExecutor<TResult>,
|
|
85
86
|
jobOptions?: ILockJobOptions
|
|
86
87
|
): Promise<TResult>;
|
|
87
|
-
public acquire<TResult>(
|
|
88
|
+
public acquire<TResult extends IValidResult>(
|
|
88
89
|
executor: ICallbackExecutor<TResult>,
|
|
89
90
|
jobOptions?: ILockJobOptions
|
|
90
91
|
): Promise<TResult>;
|
|
91
|
-
public acquire<TResult>(
|
|
92
|
+
public acquire<TResult extends IValidResult>(
|
|
92
93
|
executor: ICallbackExecutor<TResult>,
|
|
93
94
|
callback: ICallback<TResult>,
|
|
94
95
|
jobOptions?: ILockJobOptions
|
|
95
96
|
): void;
|
|
96
|
-
public acquire<TResult>(
|
|
97
|
+
public acquire<TResult extends IValidResult>(
|
|
97
98
|
key: ILockKey | Array<ILockKey>,
|
|
98
99
|
executor: IPromiseExecutor<TResult>,
|
|
99
100
|
jobOptions?: ILockJobOptions
|
|
100
101
|
): Promise<TResult>;
|
|
101
|
-
public acquire<TResult>(
|
|
102
|
+
public acquire<TResult extends IValidResult>(
|
|
102
103
|
key: ILockKey | Array<ILockKey>,
|
|
103
104
|
executor: ICallbackExecutor<TResult>,
|
|
104
105
|
jobOptions?: ILockJobOptions
|
|
105
106
|
): Promise<TResult>;
|
|
106
|
-
public acquire<TResult>(
|
|
107
|
+
public acquire<TResult extends IValidResult>(
|
|
107
108
|
key: ILockKey | Array<ILockKey>,
|
|
108
109
|
executor: ICallbackExecutor<TResult>,
|
|
109
110
|
callback: ICallback<TResult>,
|
|
@@ -125,7 +126,7 @@ export class BetterLock {
|
|
|
125
126
|
* @param jobOptions Options to be applied on this job only
|
|
126
127
|
*/
|
|
127
128
|
public acquire(key, executor?, callback?, jobOptions?) {
|
|
128
|
-
if (tools.isFunction(key)
|
|
129
|
+
if (tools.isFunction(key)) {
|
|
129
130
|
// Presume we weren't given a key (first form)
|
|
130
131
|
jobOptions = callback;
|
|
131
132
|
callback = executor;
|
|
@@ -133,23 +134,6 @@ export class BetterLock {
|
|
|
133
134
|
key = null;
|
|
134
135
|
}
|
|
135
136
|
|
|
136
|
-
// Repackage "key" into an array of keys
|
|
137
|
-
const keys = [];
|
|
138
|
-
if (Array.isArray(key)) {
|
|
139
|
-
const arrayOfKeys = key;
|
|
140
|
-
// Make sure we don't add duplicates
|
|
141
|
-
const seenKeys = new Set();
|
|
142
|
-
for (let i = 0; i < arrayOfKeys.length; i++) {
|
|
143
|
-
const normalizedKey = this.normalizeAndValidateKey(arrayOfKeys[i]);
|
|
144
|
-
if (!seenKeys.has(normalizedKey)) {
|
|
145
|
-
keys.push(normalizedKey);
|
|
146
|
-
seenKeys.add(normalizedKey);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
} else {
|
|
150
|
-
keys.push(this.normalizeAndValidateKey(key));
|
|
151
|
-
}
|
|
152
|
-
|
|
153
137
|
// Create callback wrapper for promise interface
|
|
154
138
|
if (!tools.isFunction(callback)) {
|
|
155
139
|
if (jobOptions === undefined) {
|
|
@@ -159,6 +143,32 @@ export class BetterLock {
|
|
|
159
143
|
callback = tools.callbackWithPromise();
|
|
160
144
|
}
|
|
161
145
|
|
|
146
|
+
// Prepare job options
|
|
147
|
+
const effectiveJobOptions = jobOptions
|
|
148
|
+
? tools.assign({} as IOptions, [this.options, jobOptions])
|
|
149
|
+
: // No need to create new object since these will be read-only and are subset of global options
|
|
150
|
+
this.options;
|
|
151
|
+
|
|
152
|
+
// Repackage "key" into an array of keys, but only if lock_condition allows it.
|
|
153
|
+
// Otherwise, it stays an array and gets executed immediately.
|
|
154
|
+
const keys = [];
|
|
155
|
+
if (!effectiveJobOptions.lock_condition || effectiveJobOptions.lock_condition(key)) {
|
|
156
|
+
if (Array.isArray(key)) {
|
|
157
|
+
const arrayOfKeys = key;
|
|
158
|
+
// Make sure we don't add duplicates
|
|
159
|
+
const seenKeys = new Set();
|
|
160
|
+
for (let i = 0; i < arrayOfKeys.length; i++) {
|
|
161
|
+
const normalizedKey = this.normalizeAndValidateKey(arrayOfKeys[i]);
|
|
162
|
+
if (!seenKeys.has(normalizedKey)) {
|
|
163
|
+
keys.push(normalizedKey);
|
|
164
|
+
seenKeys.add(normalizedKey);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
} else {
|
|
168
|
+
keys.push(this.normalizeAndValidateKey(key));
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
162
172
|
// Validate other options
|
|
163
173
|
if (!tools.isFunction(executor)) {
|
|
164
174
|
throw new BetterLockInvalidArgumentError(
|
|
@@ -177,12 +187,6 @@ export class BetterLock {
|
|
|
177
187
|
);
|
|
178
188
|
}
|
|
179
189
|
|
|
180
|
-
// Prepare job options
|
|
181
|
-
const effectiveJobOptions = jobOptions
|
|
182
|
-
? tools.assign({}, [this.options, jobOptions])
|
|
183
|
-
: // No need to create new object since these will be read-only and are subset of global options
|
|
184
|
-
this.options;
|
|
185
|
-
|
|
186
190
|
const job = new LockJob(keys, executor, callback, effectiveJobOptions);
|
|
187
191
|
|
|
188
192
|
// Set incoming stack
|
|
@@ -221,6 +225,86 @@ export class BetterLock {
|
|
|
221
225
|
return (callback as any).promise;
|
|
222
226
|
}
|
|
223
227
|
|
|
228
|
+
public acquireOr<TResult extends IValidResult>(
|
|
229
|
+
failureResult: TResult,
|
|
230
|
+
executor: IPromiseExecutor<TResult>,
|
|
231
|
+
jobOptions?: ILockJobOptions
|
|
232
|
+
): Promise<TResult>;
|
|
233
|
+
public acquireOr<TResult extends IValidResult>(
|
|
234
|
+
failureResult: TResult,
|
|
235
|
+
executor: ICallbackExecutor<TResult>,
|
|
236
|
+
jobOptions?: ILockJobOptions
|
|
237
|
+
): Promise<TResult>;
|
|
238
|
+
public acquireOr<TResult extends IValidResult>(
|
|
239
|
+
failureResult: TResult,
|
|
240
|
+
executor: ICallbackExecutor<TResult>,
|
|
241
|
+
callback: ICallback<TResult>,
|
|
242
|
+
jobOptions?: ILockJobOptions
|
|
243
|
+
): void;
|
|
244
|
+
public acquireOr<TResult extends IValidResult>(
|
|
245
|
+
key: ILockKey | Array<ILockKey>,
|
|
246
|
+
failureResult: TResult,
|
|
247
|
+
executor: IPromiseExecutor<TResult>,
|
|
248
|
+
jobOptions?: ILockJobOptions
|
|
249
|
+
): Promise<TResult>;
|
|
250
|
+
public acquireOr<TResult extends IValidResult>(
|
|
251
|
+
key: ILockKey | Array<ILockKey>,
|
|
252
|
+
failureResult: TResult,
|
|
253
|
+
executor: ICallbackExecutor<TResult>,
|
|
254
|
+
jobOptions?: ILockJobOptions
|
|
255
|
+
): Promise<TResult>;
|
|
256
|
+
public acquireOr<TResult extends IValidResult>(
|
|
257
|
+
key: ILockKey | Array<ILockKey>,
|
|
258
|
+
failureResult: TResult,
|
|
259
|
+
executor: ICallbackExecutor<TResult>,
|
|
260
|
+
callback: ICallback<TResult>,
|
|
261
|
+
jobOptions?: ILockJobOptions
|
|
262
|
+
): void;
|
|
263
|
+
/**
|
|
264
|
+
* The same as acquire, except if the lock can't be acquired (the executor never gets called), instead of throwing
|
|
265
|
+
* an error, the lock will resolve/callback with the provided failureResult value (for example, a `null`).
|
|
266
|
+
* @param key Named key or array of keys for this particular call. Calls with different keys will be run in parallel. Not required.
|
|
267
|
+
* @param failureResult The value to return if the executor never get called. This happens instead of rejecting.
|
|
268
|
+
* @param executor Function that will run inside the lock. Required.
|
|
269
|
+
* @param callback Function to be called after the executor finishes, or there is an execution timeout. Leave out to use promises.
|
|
270
|
+
* @param jobOptions Options to be applied on this job only
|
|
271
|
+
*/
|
|
272
|
+
public acquireOr(key, failureResult, executor?, callback?, jobOptions?) {
|
|
273
|
+
if (tools.isFunction(failureResult)) {
|
|
274
|
+
// There is no key given. Reshuffle.
|
|
275
|
+
jobOptions = callback;
|
|
276
|
+
callback = executor;
|
|
277
|
+
executor = failureResult;
|
|
278
|
+
failureResult = key;
|
|
279
|
+
key = null;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (tools.isFunction(callback)) {
|
|
283
|
+
const originalCallback = callback;
|
|
284
|
+
callback = (err, ...results) => {
|
|
285
|
+
if (isAcquisitionFailureError(err)) {
|
|
286
|
+
originalCallback(null, failureResult);
|
|
287
|
+
} else {
|
|
288
|
+
originalCallback(err, ...results);
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// noinspection JSVoidFunctionReturnValueUsed
|
|
294
|
+
let promiseResult = this.acquire(key, executor, callback, jobOptions) as any;
|
|
295
|
+
|
|
296
|
+
if (typeof promiseResult !== 'undefined') {
|
|
297
|
+
promiseResult = promiseResult.catch(err => {
|
|
298
|
+
if (isAcquisitionFailureError(err)) {
|
|
299
|
+
return failureResult;
|
|
300
|
+
}
|
|
301
|
+
throw err;
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return promiseResult;
|
|
306
|
+
}
|
|
307
|
+
|
|
224
308
|
/**
|
|
225
309
|
* Update state of given list of queues.
|
|
226
310
|
*/
|
package/src/errors.ts
CHANGED
|
@@ -51,7 +51,12 @@ export class BetterLockInternalError extends BetterLockError {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
export class BetterLockInvalidArgumentError extends BetterLockError {
|
|
54
|
-
constructor(
|
|
54
|
+
constructor(
|
|
55
|
+
lockName,
|
|
56
|
+
public argument,
|
|
57
|
+
expected,
|
|
58
|
+
actual
|
|
59
|
+
) {
|
|
55
60
|
super(lockName, `Argument "${argument}" must be ${expected} (got: "${actual}")`);
|
|
56
61
|
this.argument = argument;
|
|
57
62
|
}
|
|
@@ -125,3 +130,11 @@ export class BetterLockJobAbortedError extends BetterLockError {
|
|
|
125
130
|
this.keys = job.keys;
|
|
126
131
|
}
|
|
127
132
|
}
|
|
133
|
+
|
|
134
|
+
export function isAcquisitionFailureError(err) {
|
|
135
|
+
return (
|
|
136
|
+
(err && err instanceof BetterLockQueueOverflowError) ||
|
|
137
|
+
err instanceof BetterLockWaitTimeoutError ||
|
|
138
|
+
err instanceof BetterLockJobAbortedError
|
|
139
|
+
);
|
|
140
|
+
}
|
package/src/options.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { ILockKey } from './types';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Options that apply on LockJob
|
|
3
5
|
*/
|
|
@@ -16,6 +18,13 @@ export interface ILockJobOptions {
|
|
|
16
18
|
* In any error produced by BetterLock, extend the stack trace to include incoming trace. Overrides BetterLockOptions value.
|
|
17
19
|
*/
|
|
18
20
|
extend_stack_traces?: boolean;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Before acquiring lock, call this function with the given key. If condition is rejected, the executor is immediately called, without locking.
|
|
24
|
+
* This can be used to more conveniently opt out of locking, without duplicating or extracting the executor.
|
|
25
|
+
* Overrides BetterLockOptions value.
|
|
26
|
+
*/
|
|
27
|
+
lock_condition?: (key: ILockKey | Array<ILockKey>) => boolean;
|
|
19
28
|
}
|
|
20
29
|
|
|
21
30
|
export type IQueueEjectionStrategy = 'oldest' | 'newest';
|
|
@@ -45,6 +54,12 @@ export interface IOptions extends ILockJobOptions {
|
|
|
45
54
|
*/
|
|
46
55
|
queue_ejection_strategy?: IQueueEjectionStrategy;
|
|
47
56
|
|
|
57
|
+
/**
|
|
58
|
+
* Before acquiring lock, call this function with the given key. If condition is rejected, the executor is immediately called, without locking.
|
|
59
|
+
* This can be used to more conveniently opt out of locking, without duplicating or extracting the executor.
|
|
60
|
+
*/
|
|
61
|
+
lock_condition?: (key: ILockKey | Array<ILockKey>) => boolean;
|
|
62
|
+
|
|
48
63
|
/**
|
|
49
64
|
* Lock name. This will be written in all logs and error messages, to help you distinguish between different locks
|
|
50
65
|
*/
|
package/src/types.ts
CHANGED
|
@@ -27,3 +27,8 @@ export type IExecutor<TResult> = ICallbackExecutor<TResult> | IPromiseExecutor<T
|
|
|
27
27
|
* Key types that can be used for a lock.
|
|
28
28
|
*/
|
|
29
29
|
export type ILockKey = string | number | null | undefined;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Valid results are anything except functions
|
|
33
|
+
*/
|
|
34
|
+
export type IValidResult = Exclude<any, (...args: any[]) => any>;
|