async-queue-runner 0.19.0 → 1.0.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/_cjs/action.js CHANGED
@@ -7,6 +7,13 @@ class Action {
7
7
  if (opts.delay)
8
8
  this.delay = opts.delay;
9
9
  }
10
+ async onError(error, context) {
11
+ const logger = context.logger;
12
+ if (typeof logger?.error === 'function') {
13
+ logger.error(error);
14
+ }
15
+ context.abort();
16
+ }
10
17
  }
11
18
  exports.Action = Action;
12
19
  class LockingAction extends Action {
package/_cjs/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.util = exports.lockingClassFactory = exports.Action = exports.AsyncQueue = exports.QueueRunner = void 0;
3
+ exports.util = exports.LockManager = exports.lockingClassFactory = exports.Action = exports.AsyncQueue = exports.QueueRunner = void 0;
4
4
  var runner_js_1 = require("./runner.js");
5
5
  Object.defineProperty(exports, "QueueRunner", { enumerable: true, get: function () { return runner_js_1.QueueRunner; } });
6
6
  var queue_js_1 = require("./queue.js");
@@ -8,5 +8,7 @@ Object.defineProperty(exports, "AsyncQueue", { enumerable: true, get: function (
8
8
  var action_js_1 = require("./action.js");
9
9
  Object.defineProperty(exports, "Action", { enumerable: true, get: function () { return action_js_1.Action; } });
10
10
  Object.defineProperty(exports, "lockingClassFactory", { enumerable: true, get: function () { return action_js_1.lockingClassFactory; } });
11
+ var locking_js_1 = require("./locking.js");
12
+ Object.defineProperty(exports, "LockManager", { enumerable: true, get: function () { return locking_js_1.LockManager; } });
11
13
  var utils_js_1 = require("./utils.js");
12
14
  Object.defineProperty(exports, "util", { enumerable: true, get: function () { return utils_js_1.util; } });
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.assertValidScope = exports.isLockingAction = exports.LockManager = void 0;
4
+ function reversePromiseFactory() {
5
+ let resolve = () => { };
6
+ const promise = new Promise((res) => {
7
+ resolve = res;
8
+ });
9
+ return {
10
+ promise,
11
+ resolve,
12
+ };
13
+ }
14
+ class LockManager {
15
+ lockedScopes = new Map();
16
+ isLocked(scope) {
17
+ return this.lockedScopes.has(scope);
18
+ }
19
+ lock(scope) {
20
+ if (this.lockedScopes.has(scope)) {
21
+ throw new Error(`scope "${scope}" is already locked`);
22
+ }
23
+ this.lockedScopes.set(scope, reversePromiseFactory());
24
+ }
25
+ unlock(scope) {
26
+ if (!this.lockedScopes.has(scope)) {
27
+ return;
28
+ }
29
+ this.lockedScopes.get(scope).resolve();
30
+ this.lockedScopes.delete(scope);
31
+ }
32
+ wait(scope) {
33
+ if (!this.lockedScopes.has(scope)) {
34
+ return Promise.resolve();
35
+ }
36
+ return this.lockedScopes.get(scope).promise;
37
+ }
38
+ async runWithLock(scope, fn) {
39
+ if (this.isLocked(scope)) {
40
+ await this.wait(scope);
41
+ }
42
+ this.lock(scope);
43
+ try {
44
+ await fn();
45
+ }
46
+ finally {
47
+ this.unlock(scope);
48
+ }
49
+ }
50
+ }
51
+ exports.LockManager = LockManager;
52
+ function isLockingAction(action) {
53
+ return action.locking === true;
54
+ }
55
+ exports.isLockingAction = isLockingAction;
56
+ function assertValidScope(scope) {
57
+ if (typeof scope !== 'string' || scope.trim().length === 0) {
58
+ throw new TypeError('Lock scope must be a non-empty string');
59
+ }
60
+ return scope;
61
+ }
62
+ exports.assertValidScope = assertValidScope;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.QueueContextImpl = void 0;
4
+ class QueueContextImpl {
5
+ pushFn;
6
+ nameFn;
7
+ abortFn;
8
+ constructor(opts) {
9
+ this.pushFn = opts.push;
10
+ this.nameFn = opts.name;
11
+ this.abortFn = opts.abort;
12
+ }
13
+ push = (actions) => {
14
+ this.pushFn(actions);
15
+ };
16
+ extend = (obj) => {
17
+ Object.assign(this, obj);
18
+ };
19
+ name = () => {
20
+ return this.nameFn();
21
+ };
22
+ abort = () => {
23
+ this.abortFn();
24
+ };
25
+ initialize = (context) => {
26
+ Object.assign(this, context);
27
+ };
28
+ }
29
+ exports.QueueContextImpl = QueueContextImpl;
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.QueueExecutor = void 0;
4
+ const locking_js_1 = require("./locking.js");
5
+ class QueueExecutor {
6
+ opts;
7
+ constructor(opts) {
8
+ this.opts = opts;
9
+ }
10
+ async run(context) {
11
+ this.opts.context.initialize(context);
12
+ try {
13
+ while (true) {
14
+ if (this.opts.queue.length === 0) {
15
+ this.opts.logger.info(`Queue(${this.opts.name}): stopped`);
16
+ break;
17
+ }
18
+ const item = this.opts.queue.shift();
19
+ const action = this.opts.processQueueItem(item);
20
+ const actionName = action.constructor.name || 'some undefined';
21
+ this.opts.logger.setContext(actionName);
22
+ this.opts.logger.info(`Queue(${this.opts.name}): running action`);
23
+ const runAction = async () => {
24
+ try {
25
+ await this.opts.iterate(action);
26
+ }
27
+ catch (e) {
28
+ await this.opts.handleActionError(action, e);
29
+ }
30
+ };
31
+ if ((0, locking_js_1.isLockingAction)(action)) {
32
+ const scope = (0, locking_js_1.assertValidScope)(action.scope);
33
+ if (this.opts.lockManager.isLocked(scope)) {
34
+ this.opts.logger.info(`Queue(${this.opts.name}): waiting for scope to unlock`);
35
+ }
36
+ await this.opts.lockManager.runWithLock(scope, runAction);
37
+ }
38
+ else {
39
+ await runAction();
40
+ }
41
+ }
42
+ this.opts.end();
43
+ }
44
+ catch (e) {
45
+ this.opts.logger.info(`Queue(${this.opts.name}) failed`);
46
+ this.opts.logger.error(e);
47
+ }
48
+ }
49
+ }
50
+ exports.QueueExecutor = QueueExecutor;
package/_cjs/queue.js CHANGED
@@ -2,6 +2,8 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.AsyncQueue = void 0;
4
4
  const action_js_1 = require("./action.js");
5
+ const queue_context_js_1 = require("./queue-context.js");
6
+ const queue_executor_js_1 = require("./queue-executor.js");
5
7
  const logger = {
6
8
  info(message) {
7
9
  console.log(message);
@@ -14,69 +16,56 @@ class AsyncQueue {
14
16
  queue = [];
15
17
  end;
16
18
  logger;
17
- locker;
18
- loopAction = false;
19
- context = {
20
- push: (actions) => this.push(actions),
21
- stop: () => { this.loopAction = false; },
22
- extend: (obj) => Object.assign(this.context, obj),
23
- name: () => this.name,
24
- abort: () => {
25
- while (this.queue.length > 0) {
26
- this.queue.pop();
27
- }
28
- },
29
- };
19
+ lockManager;
20
+ context;
30
21
  constructor(opts) {
31
22
  this.queue = opts.actions;
32
23
  this.name = opts.name;
33
24
  this.end = opts.end || (() => { });
34
25
  this.logger = opts.logger || logger;
35
- this.locker = opts.lockingContext;
26
+ this.lockManager = opts.lockingContext;
27
+ this.context = new queue_context_js_1.QueueContextImpl({
28
+ push: (actions) => this.push(actions),
29
+ name: () => this.name,
30
+ abort: () => {
31
+ while (this.queue.length > 0) {
32
+ this.queue.pop();
33
+ }
34
+ },
35
+ });
36
36
  }
37
37
  async delay(timeout) {
38
38
  return new Promise((res) => setTimeout(res, timeout));
39
39
  }
40
40
  async run(context) {
41
- this.loopAction = true;
42
- Object.assign(this.context, context);
43
- try {
44
- while (this.loopAction) {
45
- if (this.queue.length === 0) {
46
- this.loopAction = false;
47
- this.logger.info(`Queue(${this.name}): stopped`);
48
- break;
49
- }
50
- const item = this.queue.shift();
51
- const action = this.processQueueItem(item);
52
- const actionName = action.constructor.name || 'some undefined';
53
- this.logger.setContext(actionName);
54
- const isLocking = action.locking;
55
- const scope = action.scope;
56
- if (isLocking) {
57
- if (this.locker.isLocked(scope)) {
58
- this.logger.info(`Queue(${this.name}): waiting for scope to unlock`);
59
- await this.locker.wait(scope);
60
- }
61
- this.locker.lock(scope);
62
- }
63
- this.logger.info(`Queue(${this.name}): running action`);
64
- await this.iterate(action);
65
- if (isLocking) {
66
- this.locker.unlock(scope);
67
- }
68
- }
69
- this.end();
70
- }
71
- catch (e) {
72
- this.logger.info(`Queue(${this.name}) failed`);
73
- this.logger.error(e);
74
- }
41
+ const executor = new queue_executor_js_1.QueueExecutor({
42
+ name: this.name,
43
+ queue: this.queue,
44
+ logger: this.logger,
45
+ lockManager: this.lockManager,
46
+ context: this.context,
47
+ end: this.end,
48
+ processQueueItem: (item) => this.processQueueItem(item),
49
+ iterate: (action) => this.iterate(action),
50
+ handleActionError: (action, error) => this.handleActionError(action, error),
51
+ });
52
+ await executor.run(context);
75
53
  }
76
54
  async iterate(action) {
77
55
  await action.execute(this.context);
78
56
  await this.delay(action.delay);
79
57
  }
58
+ async handleActionError(action, error) {
59
+ const err = error instanceof Error ? error : new Error(String(error));
60
+ try {
61
+ await action.onError(err, this.context);
62
+ }
63
+ catch (handlerError) {
64
+ this.logger.info(`Queue(${this.name}) onError failed`);
65
+ this.logger.error(handlerError);
66
+ this.context.abort();
67
+ }
68
+ }
80
69
  push(actions) {
81
70
  this.queue.unshift(...actions);
82
71
  }
package/_cjs/runner.js CHANGED
@@ -2,16 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.QueueRunner = void 0;
4
4
  const queue_js_1 = require("./queue.js");
5
- function reversePromiseFactory() {
6
- let resolve = (value) => { };
7
- const promise = new Promise((res) => {
8
- resolve = res;
9
- });
10
- return {
11
- promise,
12
- resolve,
13
- };
14
- }
5
+ const locking_js_1 = require("./locking.js");
15
6
  class QueueRunner {
16
7
  queues = new Map();
17
8
  listeners = [];
@@ -24,35 +15,7 @@ class QueueRunner {
24
15
  this.locking = this.preparteLockingContext();
25
16
  }
26
17
  preparteLockingContext() {
27
- const lockedScopes = new Map();
28
- const unlock = (scope) => {
29
- if (!lockedScopes.has(scope)) {
30
- return;
31
- }
32
- lockedScopes.get(scope).resolve();
33
- lockedScopes.delete(scope);
34
- };
35
- const wait = (scope) => {
36
- if (!lockedScopes.has(scope)) {
37
- return Promise.resolve();
38
- }
39
- return lockedScopes.get(scope).promise;
40
- };
41
- const lock = (scope) => {
42
- if (lockedScopes.has(scope)) {
43
- throw new Error(`scope "${scope}" is already locked`);
44
- }
45
- lockedScopes.set(scope, reversePromiseFactory());
46
- };
47
- const isLocked = (scope) => {
48
- return lockedScopes.has(scope);
49
- };
50
- return {
51
- isLocked,
52
- lock,
53
- unlock,
54
- wait,
55
- };
18
+ return new locking_js_1.LockManager();
56
19
  }
57
20
  add(actions, context = {}, name = this.getName()) {
58
21
  const queue = new queue_js_1.AsyncQueue({
package/_esm/action.js CHANGED
@@ -4,6 +4,13 @@ export class Action {
4
4
  if (opts.delay)
5
5
  this.delay = opts.delay;
6
6
  }
7
+ async onError(error, context) {
8
+ const logger = context.logger;
9
+ if (typeof logger?.error === 'function') {
10
+ logger.error(error);
11
+ }
12
+ context.abort();
13
+ }
7
14
  }
8
15
  export class LockingAction extends Action {
9
16
  locking = true;
package/_esm/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export { QueueRunner } from './runner.js';
2
2
  export { AsyncQueue } from './queue.js';
3
3
  export { Action, lockingClassFactory } from './action.js';
4
+ export { LockManager } from './locking.js';
4
5
  export { util } from './utils.js';
@@ -0,0 +1,56 @@
1
+ function reversePromiseFactory() {
2
+ let resolve = () => { };
3
+ const promise = new Promise((res) => {
4
+ resolve = res;
5
+ });
6
+ return {
7
+ promise,
8
+ resolve,
9
+ };
10
+ }
11
+ export class LockManager {
12
+ lockedScopes = new Map();
13
+ isLocked(scope) {
14
+ return this.lockedScopes.has(scope);
15
+ }
16
+ lock(scope) {
17
+ if (this.lockedScopes.has(scope)) {
18
+ throw new Error(`scope "${scope}" is already locked`);
19
+ }
20
+ this.lockedScopes.set(scope, reversePromiseFactory());
21
+ }
22
+ unlock(scope) {
23
+ if (!this.lockedScopes.has(scope)) {
24
+ return;
25
+ }
26
+ this.lockedScopes.get(scope).resolve();
27
+ this.lockedScopes.delete(scope);
28
+ }
29
+ wait(scope) {
30
+ if (!this.lockedScopes.has(scope)) {
31
+ return Promise.resolve();
32
+ }
33
+ return this.lockedScopes.get(scope).promise;
34
+ }
35
+ async runWithLock(scope, fn) {
36
+ if (this.isLocked(scope)) {
37
+ await this.wait(scope);
38
+ }
39
+ this.lock(scope);
40
+ try {
41
+ await fn();
42
+ }
43
+ finally {
44
+ this.unlock(scope);
45
+ }
46
+ }
47
+ }
48
+ export function isLockingAction(action) {
49
+ return action.locking === true;
50
+ }
51
+ export function assertValidScope(scope) {
52
+ if (typeof scope !== 'string' || scope.trim().length === 0) {
53
+ throw new TypeError('Lock scope must be a non-empty string');
54
+ }
55
+ return scope;
56
+ }
@@ -0,0 +1,25 @@
1
+ export class QueueContextImpl {
2
+ pushFn;
3
+ nameFn;
4
+ abortFn;
5
+ constructor(opts) {
6
+ this.pushFn = opts.push;
7
+ this.nameFn = opts.name;
8
+ this.abortFn = opts.abort;
9
+ }
10
+ push = (actions) => {
11
+ this.pushFn(actions);
12
+ };
13
+ extend = (obj) => {
14
+ Object.assign(this, obj);
15
+ };
16
+ name = () => {
17
+ return this.nameFn();
18
+ };
19
+ abort = () => {
20
+ this.abortFn();
21
+ };
22
+ initialize = (context) => {
23
+ Object.assign(this, context);
24
+ };
25
+ }
@@ -0,0 +1,46 @@
1
+ import { assertValidScope, isLockingAction } from './locking.js';
2
+ export class QueueExecutor {
3
+ opts;
4
+ constructor(opts) {
5
+ this.opts = opts;
6
+ }
7
+ async run(context) {
8
+ this.opts.context.initialize(context);
9
+ try {
10
+ while (true) {
11
+ if (this.opts.queue.length === 0) {
12
+ this.opts.logger.info(`Queue(${this.opts.name}): stopped`);
13
+ break;
14
+ }
15
+ const item = this.opts.queue.shift();
16
+ const action = this.opts.processQueueItem(item);
17
+ const actionName = action.constructor.name || 'some undefined';
18
+ this.opts.logger.setContext(actionName);
19
+ this.opts.logger.info(`Queue(${this.opts.name}): running action`);
20
+ const runAction = async () => {
21
+ try {
22
+ await this.opts.iterate(action);
23
+ }
24
+ catch (e) {
25
+ await this.opts.handleActionError(action, e);
26
+ }
27
+ };
28
+ if (isLockingAction(action)) {
29
+ const scope = assertValidScope(action.scope);
30
+ if (this.opts.lockManager.isLocked(scope)) {
31
+ this.opts.logger.info(`Queue(${this.opts.name}): waiting for scope to unlock`);
32
+ }
33
+ await this.opts.lockManager.runWithLock(scope, runAction);
34
+ }
35
+ else {
36
+ await runAction();
37
+ }
38
+ }
39
+ this.opts.end();
40
+ }
41
+ catch (e) {
42
+ this.opts.logger.info(`Queue(${this.opts.name}) failed`);
43
+ this.opts.logger.error(e);
44
+ }
45
+ }
46
+ }
package/_esm/queue.js CHANGED
@@ -1,4 +1,6 @@
1
1
  import { Action } from './action.js';
2
+ import { QueueContextImpl } from './queue-context.js';
3
+ import { QueueExecutor } from './queue-executor.js';
2
4
  const logger = {
3
5
  info(message) {
4
6
  console.log(message);
@@ -11,69 +13,56 @@ export class AsyncQueue {
11
13
  queue = [];
12
14
  end;
13
15
  logger;
14
- locker;
15
- loopAction = false;
16
- context = {
17
- push: (actions) => this.push(actions),
18
- stop: () => { this.loopAction = false; },
19
- extend: (obj) => Object.assign(this.context, obj),
20
- name: () => this.name,
21
- abort: () => {
22
- while (this.queue.length > 0) {
23
- this.queue.pop();
24
- }
25
- },
26
- };
16
+ lockManager;
17
+ context;
27
18
  constructor(opts) {
28
19
  this.queue = opts.actions;
29
20
  this.name = opts.name;
30
21
  this.end = opts.end || (() => { });
31
22
  this.logger = opts.logger || logger;
32
- this.locker = opts.lockingContext;
23
+ this.lockManager = opts.lockingContext;
24
+ this.context = new QueueContextImpl({
25
+ push: (actions) => this.push(actions),
26
+ name: () => this.name,
27
+ abort: () => {
28
+ while (this.queue.length > 0) {
29
+ this.queue.pop();
30
+ }
31
+ },
32
+ });
33
33
  }
34
34
  async delay(timeout) {
35
35
  return new Promise((res) => setTimeout(res, timeout));
36
36
  }
37
37
  async run(context) {
38
- this.loopAction = true;
39
- Object.assign(this.context, context);
40
- try {
41
- while (this.loopAction) {
42
- if (this.queue.length === 0) {
43
- this.loopAction = false;
44
- this.logger.info(`Queue(${this.name}): stopped`);
45
- break;
46
- }
47
- const item = this.queue.shift();
48
- const action = this.processQueueItem(item);
49
- const actionName = action.constructor.name || 'some undefined';
50
- this.logger.setContext(actionName);
51
- const isLocking = action.locking;
52
- const scope = action.scope;
53
- if (isLocking) {
54
- if (this.locker.isLocked(scope)) {
55
- this.logger.info(`Queue(${this.name}): waiting for scope to unlock`);
56
- await this.locker.wait(scope);
57
- }
58
- this.locker.lock(scope);
59
- }
60
- this.logger.info(`Queue(${this.name}): running action`);
61
- await this.iterate(action);
62
- if (isLocking) {
63
- this.locker.unlock(scope);
64
- }
65
- }
66
- this.end();
67
- }
68
- catch (e) {
69
- this.logger.info(`Queue(${this.name}) failed`);
70
- this.logger.error(e);
71
- }
38
+ const executor = new QueueExecutor({
39
+ name: this.name,
40
+ queue: this.queue,
41
+ logger: this.logger,
42
+ lockManager: this.lockManager,
43
+ context: this.context,
44
+ end: this.end,
45
+ processQueueItem: (item) => this.processQueueItem(item),
46
+ iterate: (action) => this.iterate(action),
47
+ handleActionError: (action, error) => this.handleActionError(action, error),
48
+ });
49
+ await executor.run(context);
72
50
  }
73
51
  async iterate(action) {
74
52
  await action.execute(this.context);
75
53
  await this.delay(action.delay);
76
54
  }
55
+ async handleActionError(action, error) {
56
+ const err = error instanceof Error ? error : new Error(String(error));
57
+ try {
58
+ await action.onError(err, this.context);
59
+ }
60
+ catch (handlerError) {
61
+ this.logger.info(`Queue(${this.name}) onError failed`);
62
+ this.logger.error(handlerError);
63
+ this.context.abort();
64
+ }
65
+ }
77
66
  push(actions) {
78
67
  this.queue.unshift(...actions);
79
68
  }
package/_esm/runner.js CHANGED
@@ -1,14 +1,5 @@
1
1
  import { AsyncQueue } from "./queue.js";
2
- function reversePromiseFactory() {
3
- let resolve = (value) => { };
4
- const promise = new Promise((res) => {
5
- resolve = res;
6
- });
7
- return {
8
- promise,
9
- resolve,
10
- };
11
- }
2
+ import { LockManager } from "./locking.js";
12
3
  export class QueueRunner {
13
4
  queues = new Map();
14
5
  listeners = [];
@@ -21,35 +12,7 @@ export class QueueRunner {
21
12
  this.locking = this.preparteLockingContext();
22
13
  }
23
14
  preparteLockingContext() {
24
- const lockedScopes = new Map();
25
- const unlock = (scope) => {
26
- if (!lockedScopes.has(scope)) {
27
- return;
28
- }
29
- lockedScopes.get(scope).resolve();
30
- lockedScopes.delete(scope);
31
- };
32
- const wait = (scope) => {
33
- if (!lockedScopes.has(scope)) {
34
- return Promise.resolve();
35
- }
36
- return lockedScopes.get(scope).promise;
37
- };
38
- const lock = (scope) => {
39
- if (lockedScopes.has(scope)) {
40
- throw new Error(`scope "${scope}" is already locked`);
41
- }
42
- lockedScopes.set(scope, reversePromiseFactory());
43
- };
44
- const isLocked = (scope) => {
45
- return lockedScopes.has(scope);
46
- };
47
- return {
48
- isLocked,
49
- lock,
50
- unlock,
51
- wait,
52
- };
15
+ return new LockManager();
53
16
  }
54
17
  add(actions, context = {}, name = this.getName()) {
55
18
  const queue = new AsyncQueue({
@@ -6,6 +6,7 @@ export declare abstract class Action<C> implements IAction {
6
6
  delay: number;
7
7
  constructor(opts?: Options);
8
8
  abstract execute(context: C & QueueContext): Promise<void>;
9
+ onError(error: Error, context: QueueContext): Promise<void>;
9
10
  }
10
11
  export declare abstract class LockingAction<C> extends Action<C> implements ILockingAction {
11
12
  locking: boolean;
@@ -16,4 +17,5 @@ export declare function lockingClassFactory<C>(scope: string): abstract new (opt
16
17
  locking: boolean;
17
18
  delay: number;
18
19
  execute(context: C & QueueContext): Promise<void>;
20
+ onError(error: Error, context: QueueContext): Promise<void>;
19
21
  };
package/_types/index.d.ts CHANGED
@@ -2,4 +2,5 @@ export { IAction, ActionClass, QueueAction, Branches, QueueContext } from './typ
2
2
  export { QueueRunner, EndListener } from './runner.js';
3
3
  export { AsyncQueue, QueueOpts } from './queue.js';
4
4
  export { Action, Options, lockingClassFactory } from './action.js';
5
+ export { LockManager, LockingContext } from './locking.js';
5
6
  export { util } from './utils.js';
@@ -0,0 +1,18 @@
1
+ import { IAction, ILockingAction } from './types.js';
2
+ export type LockingContext = {
3
+ isLocked: (scope: string) => boolean;
4
+ lock: (scope: string) => void;
5
+ unlock: (scope: string) => void;
6
+ wait: (scope: string) => Promise<void>;
7
+ runWithLock: (scope: string, fn: () => Promise<void>) => Promise<void>;
8
+ };
9
+ export declare class LockManager implements LockingContext {
10
+ private lockedScopes;
11
+ isLocked(scope: string): boolean;
12
+ lock(scope: string): void;
13
+ unlock(scope: string): void;
14
+ wait(scope: string): Promise<void>;
15
+ runWithLock(scope: string, fn: () => Promise<void>): Promise<void>;
16
+ }
17
+ export declare function isLockingAction(action: IAction): action is IAction & ILockingAction;
18
+ export declare function assertValidScope(scope: unknown): string;
@@ -0,0 +1,18 @@
1
+ import { QueueAction, QueueContext } from './types.js';
2
+ type QueueContextOptions = {
3
+ push: (actions: QueueAction[]) => void;
4
+ name: () => string;
5
+ abort: () => void;
6
+ };
7
+ export declare class QueueContextImpl implements QueueContext {
8
+ private readonly pushFn;
9
+ private readonly nameFn;
10
+ private readonly abortFn;
11
+ constructor(opts: QueueContextOptions);
12
+ push: (actions: QueueAction[]) => void;
13
+ extend: (obj: Partial<object>) => void;
14
+ name: () => string;
15
+ abort: () => void;
16
+ initialize: (context: object) => void;
17
+ }
18
+ export {};
@@ -0,0 +1,26 @@
1
+ import { LockingContext } from './locking.js';
2
+ import { IAction, QueueAction, QueueContext } from './types.js';
3
+ type QueueContextRuntime = QueueContext & {
4
+ initialize: (context: object) => void;
5
+ };
6
+ type QueueExecutorOptions = {
7
+ name: string;
8
+ queue: QueueAction[];
9
+ logger: {
10
+ info: (message: string) => void;
11
+ setContext: (context: string) => void;
12
+ error: (e: Error) => void;
13
+ };
14
+ lockManager: LockingContext;
15
+ context: QueueContextRuntime;
16
+ end: () => void;
17
+ processQueueItem: (item: QueueAction) => IAction;
18
+ iterate: (action: IAction) => Promise<void>;
19
+ handleActionError: (action: IAction, error: unknown) => Promise<void>;
20
+ };
21
+ export declare class QueueExecutor {
22
+ private readonly opts;
23
+ constructor(opts: QueueExecutorOptions);
24
+ run(context: object): Promise<void>;
25
+ }
26
+ export {};
package/_types/queue.d.ts CHANGED
@@ -1,5 +1,6 @@
1
- import { LockingContext } from './runner.js';
2
- import { IAction, QueueAction, QueueContext } from './types.js';
1
+ import { LockingContext } from './locking.js';
2
+ import { QueueContextImpl } from './queue-context.js';
3
+ import { IAction, QueueAction } from './types.js';
3
4
  export interface ILogger {
4
5
  info: (message: string) => void;
5
6
  setContext: (context: string) => void;
@@ -17,13 +18,13 @@ export declare class AsyncQueue {
17
18
  queue: QueueAction[];
18
19
  end: () => void;
19
20
  logger: ILogger;
20
- locker: LockingContext;
21
- loopAction: boolean;
22
- context: QueueContext;
21
+ lockManager: LockingContext;
22
+ context: QueueContextImpl;
23
23
  constructor(opts: QueueOpts);
24
24
  delay(timeout: number): Promise<unknown>;
25
25
  run(context: object): Promise<void>;
26
26
  iterate(action: IAction): Promise<void>;
27
+ handleActionError(action: IAction, error: unknown): Promise<void>;
27
28
  push(actions: QueueAction[]): void;
28
29
  processQueueItem(item: QueueAction): IAction;
29
30
  }
@@ -1,15 +1,11 @@
1
1
  import { ILogger } from "./queue.js";
2
+ import { LockingContext as LockingContextType } from "./locking.js";
2
3
  import { QueueAction } from "./types.js";
3
4
  export type EndListener = (name: string, size: number) => void;
4
5
  export type RunnerOpts = {
5
6
  logger?: ILogger;
6
7
  };
7
- export type LockingContext = {
8
- isLocked: (scope: string) => boolean;
9
- lock: (scope: string) => void;
10
- unlock: (scope: string) => void;
11
- wait: (scope: string) => Promise<unknown>;
12
- };
8
+ export type LockingContext = LockingContextType;
13
9
  export declare class QueueRunner {
14
10
  queues: Map<any, any>;
15
11
  listeners: EndListener[];
package/_types/types.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export interface IAction {
2
2
  delay: number;
3
3
  execute: (context: any) => Promise<void>;
4
+ onError: (error: Error, context: QueueContext) => Promise<void> | void;
4
5
  }
5
6
  export interface ILockingAction {
6
7
  locking: boolean;
@@ -15,7 +16,6 @@ export type Branches = {
15
16
  export type QueueContext = {
16
17
  push: (actions: QueueAction[]) => void;
17
18
  extend: (obj: Partial<object>) => void;
18
- stop: () => void;
19
19
  name: () => string;
20
20
  abort: () => void;
21
21
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "async-queue-runner",
3
- "version": "0.19.0",
3
+ "version": "1.0.0",
4
4
  "description": "Library to run in parallel extendable queue of tasks",
5
5
  "scripts": {
6
6
  "compile": "tsc --project ./tsconfig.json",