clever-queue 0.1.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.
Files changed (69) hide show
  1. package/.gitlab-ci.yml +13 -0
  2. package/.prettierrc.json +4 -0
  3. package/README.md +15 -0
  4. package/dist/engine/index.d.ts +21 -0
  5. package/dist/engine/index.js +187 -0
  6. package/dist/engine/index.js.map +1 -0
  7. package/dist/engine/interfaces.d.ts +28 -0
  8. package/dist/engine/interfaces.js +9 -0
  9. package/dist/engine/interfaces.js.map +1 -0
  10. package/dist/errors.d.ts +13 -0
  11. package/dist/errors.js +42 -0
  12. package/dist/errors.js.map +1 -0
  13. package/dist/helpers/errors.d.ts +7 -0
  14. package/dist/helpers/errors.js +20 -0
  15. package/dist/helpers/errors.js.map +1 -0
  16. package/dist/helpers/index.d.ts +3 -0
  17. package/dist/helpers/index.js +30 -0
  18. package/dist/helpers/index.js.map +1 -0
  19. package/dist/helpers/logs.d.ts +75 -0
  20. package/dist/helpers/logs.js +32 -0
  21. package/dist/helpers/logs.js.map +1 -0
  22. package/dist/helpers/traces.d.ts +1 -0
  23. package/dist/helpers/traces.js +24 -0
  24. package/dist/helpers/traces.js.map +1 -0
  25. package/dist/index.d.ts +7 -0
  26. package/dist/index.js +37 -0
  27. package/dist/index.js.map +1 -0
  28. package/dist/queue/index.d.ts +19 -0
  29. package/dist/queue/index.js +110 -0
  30. package/dist/queue/index.js.map +1 -0
  31. package/dist/queue/interfaces.d.ts +27 -0
  32. package/dist/queue/interfaces.js +18 -0
  33. package/dist/queue/interfaces.js.map +1 -0
  34. package/dist/runner/index.d.ts +14 -0
  35. package/dist/runner/index.js +83 -0
  36. package/dist/runner/index.js.map +1 -0
  37. package/dist/runner/interfaces.d.ts +19 -0
  38. package/dist/runner/interfaces.js +13 -0
  39. package/dist/runner/interfaces.js.map +1 -0
  40. package/dist/task/index.d.ts +14 -0
  41. package/dist/task/index.js +81 -0
  42. package/dist/task/index.js.map +1 -0
  43. package/dist/task/interfaces.d.ts +17 -0
  44. package/dist/task/interfaces.js +19 -0
  45. package/dist/task/interfaces.js.map +1 -0
  46. package/eslint.config.mjs +68 -0
  47. package/exemples/index01.js +116 -0
  48. package/exemples/index01.ts +98 -0
  49. package/exemples/index02.mjs +13 -0
  50. package/exemples/tsconfig.json +24 -0
  51. package/package.json +55 -0
  52. package/src/engine/index.ts +166 -0
  53. package/src/engine/interfaces.ts +33 -0
  54. package/src/helpers/errors.ts +16 -0
  55. package/src/helpers/index.ts +3 -0
  56. package/src/helpers/logs.ts +91 -0
  57. package/src/helpers/traces.ts +24 -0
  58. package/src/index.ts +13 -0
  59. package/src/queue/index.ts +93 -0
  60. package/src/queue/interfaces.ts +38 -0
  61. package/src/runner/index.ts +64 -0
  62. package/src/runner/interfaces.ts +24 -0
  63. package/src/task/index.ts +65 -0
  64. package/src/task/interfaces.ts +34 -0
  65. package/src/tsconfig.json +27 -0
  66. package/test/miscellaneous/test.mjs +92 -0
  67. package/test/units/engine.mjs +35 -0
  68. package/test/units/queue.mjs +34 -0
  69. package/test/units/task.mjs +50 -0
@@ -0,0 +1,98 @@
1
+ import * as CleverQueue from "../dist/index.js";
2
+
3
+ /* openTelemetry */
4
+ /*
5
+ import * as OTL from "@opentelemetry/api";
6
+ import * as OTLTrace from "@opentelemetry/sdk-trace-node";
7
+ import * as OTLTraceBase from "@opentelemetry/sdk-trace-base";
8
+
9
+ const { trace, context } = require("@opentelemetry/api");
10
+
11
+ const activeSpan = OTL.trace.getSpan(OTL.context.active());
12
+
13
+ const traceId = activeSpan.spanContext().traceId;
14
+ const spanId = activeSpan.spanContext().spanId;
15
+ const traceFlag = activeSpan.spanContext().traceFlags;
16
+
17
+ console.log(traceId, spanId, traceFlag);
18
+
19
+ /*
20
+ import { NodeSDK as OTLNodeSDK } from "@opentelemetry/sdk-node";
21
+
22
+ const otl = new OTLNodeSDK({
23
+ traceExporter: new OTLConsoleSpanExporter(),
24
+ });
25
+
26
+ otl.start();
27
+ */
28
+ /*
29
+ OTL.diag.setLogger(new OTL.DiagConsoleLogger(), OTL.DiagLogLevel.DEBUG);
30
+ const tracerProvider = new OTLTrace.NodeTracerProvider();
31
+ const exporter = new OTLTraceBase.ConsoleSpanExporter();
32
+ const processor = new OTLTraceBase.SimpleSpanProcessor(exporter);
33
+ tracerProvider.addSpanProcessor(processor);
34
+ tracerProvider.register();
35
+
36
+ const tracer = OTL.trace.getTracer("esm-tracer");
37
+ tracer.startActiveSpan("manual", (span) => {
38
+ span.addEvent("myEvent");
39
+ span.addEvent("myEvent2");
40
+ span.end();
41
+ });
42
+ */
43
+
44
+ const myFunction: CleverQueue.Task.FunctionToExecute = function (message: string) {
45
+ // eslint-disable-next-line no-console
46
+ console.log("Executing", message);
47
+ };
48
+
49
+ /*
50
+ const myPromise: CleverQueue.Task.FunctionToExecute = function (message: string): Promise<string> {
51
+ console.log(message);
52
+ await new Promise((resolve) => setTimeout(resolve, 2000));
53
+ console.log(message);
54
+ return message;
55
+ };
56
+ */
57
+
58
+ const myAsync: CleverQueue.Task.FunctionToExecute = async function (message: string) {
59
+ logFunction({ body: "Executing " + message });
60
+ await new Promise((resolve) => setTimeout(resolve, 2000));
61
+ return message;
62
+ };
63
+
64
+ const logFunction: CleverQueue.Engine.LogFunction = function (logRecord) {
65
+ // eslint-disable-next-line no-console
66
+ console.log(JSON.stringify(logRecord));
67
+ };
68
+
69
+ async function run(): Promise<void> {
70
+ logFunction({ body: "01 - Creating Engine" });
71
+ const engine = CleverQueue.createEngine({ logFunction: logFunction, bestEffortRunners: 2 });
72
+ logFunction({ body: "02 - Creating Queue" });
73
+ const queue = engine.createQueue({ priority: CleverQueue.Queue.Priorities.Standard, weight: 50 });
74
+ logFunction({ body: "03 - Creating Tasks" });
75
+ const task1 = engine.createTask(() => myAsync("myAsync01"), {});
76
+ const task2 = engine.createTask(() => myAsync("myAsync02"), {});
77
+ const task3 = engine.createTask(() => myAsync("myAsync03"), {});
78
+ logFunction({ body: "04 - Enqueue Task" });
79
+ const result = await queue.enqueue(task1);
80
+ queue.enqueue(task2);
81
+ queue.enqueue(task3);
82
+ logFunction({ body: JSON.stringify(engine.statistics()) });
83
+ logFunction({ body: result as string });
84
+ logFunction({ body: "05 - Stopping Engine" });
85
+ logFunction({ body: JSON.stringify(engine.statistics()) });
86
+ engine.stop();
87
+ logFunction({ body: JSON.stringify(engine.statistics()) });
88
+ while (engine.statistics().engine.tasks > 0) {
89
+ await new Promise((resolve) => setTimeout(resolve, 100));
90
+ }
91
+ logFunction({ body: JSON.stringify(engine.statistics()) });
92
+ await new Promise((resolve) => setTimeout(resolve, 2000));
93
+ logFunction({ body: JSON.stringify(engine.statistics()) });
94
+ logFunction({ body: { task1: task1.result as string, task2: task2.result as string, task3: task3.result as string } });
95
+ }
96
+
97
+ // eslint-disable-next-line unicorn/prefer-top-level-await
98
+ run();
@@ -0,0 +1,13 @@
1
+ import * as CleverQueue from "../dist/index.js";
2
+
3
+ async function run() {
4
+ console.log("01");
5
+ const engine = CleverQueue.createEngine({ bestEffortRunners: 2 });
6
+ const queue = engine.createQueue({ priority: CleverQueue.Queue.Priorities.Standard, weight: 50 });
7
+ console.log("02");
8
+ // engine.run();
9
+ console.log("03");
10
+ queue.createTask("aaaa");
11
+ }
12
+
13
+ run();
@@ -0,0 +1,24 @@
1
+ {
2
+ "compilerOptions": {
3
+ "module": "CommonJS",
4
+ "esModuleInterop": true,
5
+ "target": "es2022",
6
+ "moduleResolution": "node",
7
+ "lib": [
8
+ "es2022",
9
+ ],
10
+ "strict": true,
11
+ "useUnknownInCatchVariables": true,
12
+ "noImplicitAny": true,
13
+ "strictNullChecks": true,
14
+ "strictFunctionTypes": true,
15
+ "strictBindCallApply": true,
16
+ "strictPropertyInitialization": true,
17
+ "noImplicitThis": true,
18
+ "alwaysStrict": true,
19
+ "forceConsistentCasingInFileNames": true
20
+ },
21
+ "$schema": "https://json.schemastore.org/tsconfig",
22
+ "display": "Recommended"
23
+ }
24
+
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "clever-queue",
3
+ "version": "0.1.0",
4
+ "description": "Queuing system for promises that handle concurring, throttling, weighting and prioritizing in a clever fashion.",
5
+ "main": "dist/index.js",
6
+ "scripts": {
7
+ "build": "npx tsc -p src/ --declaration",
8
+ "build:watch": "npx tsc -p src/ -w",
9
+ "prestart": "npx npm run build",
10
+ "start": "node dist/index.js",
11
+ "debug": "node --nolazy --inspect-brk=9229 dist/index.js",
12
+ "test": "node --test ./test/**/*.mjs",
13
+ "test:unit:watch": "node --test --watch ./test/units/*.mjs"
14
+ },
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://gitlab.com/job-so/clever-queue.git"
18
+ },
19
+ "keywords": [
20
+ "queue",
21
+ "clever",
22
+ "concurrency",
23
+ "throttling",
24
+ "priority",
25
+ "weight",
26
+ "promise"
27
+ ],
28
+ "author": "Olivier GROSJEANNE",
29
+ "license": "ISC",
30
+ "bugs": {
31
+ "url": "https://gitlab.com/job-so/clever-queue/issues"
32
+ },
33
+ "homepage": "https://gitlab.com/job-so/clever-queue#readme",
34
+ "devDependencies": {
35
+ "@eslint/eslintrc": "^3.1",
36
+ "@eslint/js": "^9.8",
37
+ "@opentelemetry/api": "^1.9.0",
38
+ "@opentelemetry/sdk-metrics": "^1.26",
39
+ "@opentelemetry/sdk-node": "^0.53",
40
+ "@opentelemetry/sdk-trace-node": "^1.26",
41
+ "@types/eslint__js": "^8.42",
42
+ "@types/node": "^22.1",
43
+ "eslint": "^9.8",
44
+ "eslint-config-prettier": "^9.1",
45
+ "eslint-plugin-node": "^11.1",
46
+ "eslint-plugin-prettier": "^5.1",
47
+ "eslint-plugin-promise": "^6.4",
48
+ "eslint-plugin-security": "^3.0",
49
+ "eslint-plugin-unicorn": "^54.0",
50
+ "globals": "^15.9",
51
+ "node": "^22.4",
52
+ "typescript": "^5.5",
53
+ "typescript-eslint": "^8.0"
54
+ }
55
+ }
@@ -0,0 +1,166 @@
1
+ // clever-queue
2
+
3
+ import * as _ from "../helpers";
4
+ import * as Queue from "../queue";
5
+ import * as Runner from "../runner";
6
+ import * as Task from "../task";
7
+
8
+ type Status = "unknown" | "initialiazing" | "running" | "stopping" | "destroying";
9
+
10
+ const TICK_START = 42;
11
+
12
+ import { Options, Statistics, ErrorsList } from "./interfaces";
13
+
14
+ const defaultOptions: Options = {
15
+ bestEffortRunners: 1,
16
+ autostart: true,
17
+ logFunction: undefined,
18
+ };
19
+
20
+ class Engine {
21
+ status: Status = "unknown";
22
+ readonly runners: Runner.Runner[] = [];
23
+ readonly queues: Queue.Queue[] = [];
24
+
25
+ constructor(opt: Options) {
26
+ this.status = "initialiazing";
27
+ const options = { ...defaultOptions, ...opt };
28
+ this.#checkOptionsConsistancy(options);
29
+
30
+ // create runners
31
+ for (let index = 0; index < (options.bestEffortRunners ?? 0); index++) this.createRunner({ priority: Runner.Priorities.BestEffort });
32
+ if (options.logFunction) options.logFunction({ body: "engine has been initialized" });
33
+ if (options.autostart) this.start();
34
+ }
35
+
36
+ start(): void {
37
+ this.status = "running";
38
+ }
39
+
40
+ stop(): void {
41
+ this.status = "stopping";
42
+ for (const queue of this.queues) queue.stop();
43
+ for (const runner of this.runners) runner.stop();
44
+ }
45
+
46
+ createQueue(options: Queue.Options): Queue.Queue {
47
+ const queue = new Queue.Queue({ logFunction: options.logFunction, ...options });
48
+ this.queues.push(queue);
49
+ this.#resetLastTick(options.priority);
50
+ return queue;
51
+ }
52
+
53
+ createRunner(options: Runner.Options): Runner.Runner {
54
+ const runner = new Runner.Runner({ logFunction: options.logFunction, ...options }, this);
55
+ this.runners.push(runner);
56
+ return runner;
57
+ }
58
+
59
+ createTask(function_: Task.FunctionToExecute, options: Task.Options): Task.Task {
60
+ return new Task.Task(function_, { logFunction: options.logFunction, ...options });
61
+ }
62
+
63
+ #checkOptionsConsistancy(options: Options): void {
64
+ if (!options) throw new _.Errors.CQError(ErrorsList.NoOptionsOnEngineInitialization);
65
+ if (!options.bestEffortRunners || options.bestEffortRunners < 1) throw new _.Errors.CQError(ErrorsList.bestEffortRunnersOptionMustBeGreaterThanZero);
66
+ }
67
+
68
+ #getSumWeightFunction: Queue.GetSumWeightFunction = (priority: number): number => {
69
+ let result = 0;
70
+ for (const q of this.queues) if (q.options.priority === priority) result += q.options.weight;
71
+ return result;
72
+ };
73
+
74
+ #resetLastTick(priority: number): void {
75
+ for (const q of this.queues) if (q.options.priority === priority) q.tick = TICK_START;
76
+ }
77
+
78
+ #getNextWeightedQueue(priority: number): Queue.Queue | undefined {
79
+ let minTick = Infinity;
80
+ let result: Queue.Queue | undefined = undefined;
81
+ for (const q of this.queues)
82
+ if (q.options.priority === priority && q.tick <= minTick) {
83
+ minTick = q.tick;
84
+ result = q;
85
+ }
86
+ return result;
87
+ }
88
+
89
+ #howMuchTasksWaitingInQueues(priority: number): number {
90
+ let result: number = 0;
91
+ for (const q of this.queues) if (q.options.priority === priority) result += q.tasks.length;
92
+ return result;
93
+ }
94
+ /*
95
+ async getNextTask(priorityGreaterOrEqualTo: number): Promise<Task.Task> {
96
+ for (let priority = Queue.Priorities.Absolute; priority <= priorityGreaterOrEqualTo; priority++) {
97
+ // Iterate on Each PriorityLevel From Absolute to param function
98
+ while (this.#howMuchTasksWaitingInQueues(priority) > 0) {
99
+ let task: Task.Task | undefined;
100
+ while (!task) {
101
+ const nextPriorityQueue = this.#getNextQueue(priority); // Find Next Priority Queue to process
102
+ if (nextPriorityQueue) {
103
+ nextPriorityQueue.tick += this.#getSumWeightFunction(priority) / nextPriorityQueue.options.weight; // Update tick with related weight
104
+ if (nextPriorityQueue.tasks.length > 0) {
105
+ task = nextPriorityQueue.tasks.shift(); // If queue is not empty, this is our next Task to process
106
+ if (task) task.runner();
107
+ console.log(nextPriorityQueue.options.name, nextPriorityQueue.tick);
108
+ }
109
+ }
110
+ }
111
+ }
112
+ }
113
+ }
114
+ */
115
+
116
+ async getNextTask(priorityGreaterOrEqualTo: number): Promise<Task.Task | undefined> {
117
+ let task: Task.Task | undefined = undefined;
118
+ // Iterate on Each PriorityLevel From Absolute to parameter
119
+ for (let priority = Queue.Priorities.Absolute; priority <= priorityGreaterOrEqualTo; priority++) {
120
+ // Check if there is task waiting of this priority level
121
+ while (this.#howMuchTasksWaitingInQueues(priority) > 0) {
122
+ // Find Next Weighted Queue of this priority level to check
123
+ const nextPriorityQueue = this.#getNextWeightedQueue(priority);
124
+ if (nextPriorityQueue) {
125
+ // update Tick (next check) according to queue related weigth
126
+ nextPriorityQueue.tick += this.#getSumWeightFunction(priority) / nextPriorityQueue.options.weight;
127
+ // If queue is not empty, we've got our next Task to process
128
+ if (nextPriorityQueue.tasks.length > 0) {
129
+ // dequeue task from queue
130
+ task = nextPriorityQueue.tasks.shift();
131
+ if (task) return task;
132
+ }
133
+ }
134
+ }
135
+ }
136
+ return undefined;
137
+ }
138
+
139
+ statistics(): Statistics {
140
+ let taskCount = 0;
141
+ const runners: Runner.Statistics[] = [];
142
+ const queues: Queue.Statistics[] = [];
143
+ for (const runner of this.runners) {
144
+ if (runner.status == "running") taskCount += 1;
145
+ runners.push(runner.statistics());
146
+ }
147
+ for (const queue of this.queues) {
148
+ taskCount += queue.tasks.length;
149
+ queues.push(queue.statistics());
150
+ }
151
+ const stat: Statistics = {
152
+ engine: {
153
+ status: this.status,
154
+ runners: this.runners.length,
155
+ queues: this.queues.length,
156
+ tasks: taskCount,
157
+ },
158
+ runners,
159
+ queues,
160
+ };
161
+ return stat;
162
+ }
163
+ }
164
+
165
+ export { Options, Statistics, ErrorsList } from "./interfaces";
166
+ export { Engine };
@@ -0,0 +1,33 @@
1
+ import * as _ from "../helpers";
2
+
3
+ import * as Runner from "../runner/interfaces";
4
+ import * as Queue from "../runner/interfaces";
5
+
6
+ interface Options {
7
+ autostart?: boolean;
8
+ bestEffortRunners?: number;
9
+ logFunction?: _.Logs.LogFunction;
10
+ }
11
+
12
+ interface Statistics {
13
+ engine: {
14
+ status: string;
15
+ runners: number;
16
+ queues: number;
17
+ tasks: number;
18
+ };
19
+ runners: Runner.Statistics[];
20
+ queues: Queue.Statistics[];
21
+ }
22
+ type ErrorListKeys = "NoOptionsOnEngineInitialization" | "bestEffortRunnersOptionMustBeGreaterThanZero";
23
+
24
+ const ErrorsList: {
25
+ [index in ErrorListKeys]: { name: string; message: string };
26
+ } = {
27
+ NoOptionsOnEngineInitialization: { name: "NoOptionsOnEngineInitialization", message: "You must provide an options parameter to Engine" },
28
+ bestEffortRunnersOptionMustBeGreaterThanZero: { name: "bestEffortRunnersOptionMustBeGreaterThanZero", message: "bestEffortRunners Option must be equal or superior to 1" },
29
+ };
30
+
31
+ export { LogFunction } from "../helpers/logs";
32
+ export { TraceFunction } from "../helpers/traces";
33
+ export { Options, Statistics, ErrorsList };
@@ -0,0 +1,16 @@
1
+ class CQError extends Error {
2
+ constructor(error: { name: string; message: string }, context?: object) {
3
+ if (!error) {
4
+ error = {
5
+ name: "undocumentedError",
6
+ message: "Undocumented Error, please raise an issue to developpers",
7
+ };
8
+ }
9
+ super(error.message);
10
+ this.name = error.name;
11
+ this.message = error.message;
12
+ if (context) this.cause = JSON.stringify(context);
13
+ }
14
+ }
15
+
16
+ export { CQError };
@@ -0,0 +1,3 @@
1
+ export * as Errors from "./errors";
2
+ export * as Logs from "./logs";
3
+ export * as Traces from "./traces";
@@ -0,0 +1,91 @@
1
+ export declare type HrTime = [number, number];
2
+ export declare type TimeInput = HrTime | number | Date;
3
+
4
+ export type AnyValueScalar = string | number | boolean;
5
+ export type AnyValueArray = Array<AnyValue>;
6
+ /**
7
+ * AnyValueMap is a map from string to AnyValue (attribute value or a nested map)
8
+ */
9
+ export interface AnyValueMap {
10
+ [attributeKey: string]: AnyValue;
11
+ }
12
+
13
+ /**
14
+ * AnyValue can be one of the following:
15
+ * - a scalar value
16
+ * - a byte array
17
+ * - array of any value
18
+ * - map from string to any value
19
+ * - empty value
20
+ */
21
+ export type AnyValue = AnyValueScalar | Uint8Array | AnyValueArray | AnyValueMap | null | undefined;
22
+
23
+ export type LogBody = AnyValue;
24
+ export type LogAttributes = AnyValueMap;
25
+
26
+ export enum SeverityNumber {
27
+ UNSPECIFIED = 0,
28
+ TRACE = 1,
29
+ TRACE2 = 2,
30
+ TRACE3 = 3,
31
+ TRACE4 = 4,
32
+ DEBUG = 5,
33
+ DEBUG2 = 6,
34
+ DEBUG3 = 7,
35
+ DEBUG4 = 8,
36
+ INFO = 9,
37
+ INFO2 = 10,
38
+ INFO3 = 11,
39
+ INFO4 = 12,
40
+ WARN = 13,
41
+ WARN2 = 14,
42
+ WARN3 = 15,
43
+ WARN4 = 16,
44
+ ERROR = 17,
45
+ ERROR2 = 18,
46
+ ERROR3 = 19,
47
+ ERROR4 = 20,
48
+ FATAL = 21,
49
+ FATAL2 = 22,
50
+ FATAL3 = 23,
51
+ FATAL4 = 24,
52
+ }
53
+
54
+ export interface LogRecord {
55
+ /**
56
+ * The time when the log record occurred as UNIX Epoch time in nanoseconds.
57
+ */
58
+ timestamp?: TimeInput;
59
+
60
+ /**
61
+ * Time when the event was observed by the collection system.
62
+ */
63
+ observedTimestamp?: TimeInput;
64
+
65
+ /**
66
+ * Numerical value of the severity.
67
+ */
68
+ severityNumber?: SeverityNumber;
69
+
70
+ /**
71
+ * The severity text.
72
+ */
73
+ severityText?: string;
74
+
75
+ /**
76
+ * A value containing the body of the log record.
77
+ */
78
+ body?: LogBody;
79
+
80
+ /**
81
+ * Attributes that define the log record.
82
+ */
83
+ attributes?: LogAttributes;
84
+
85
+ /**
86
+ * The Context associated with the LogRecord.
87
+ */
88
+ // context?: Context;
89
+ }
90
+
91
+ export type LogFunction = (logRecord: LogRecord) => void;
@@ -0,0 +1,24 @@
1
+ /*
2
+ interface EventRecord {
3
+ // https://opentelemetry.io/docs/specs/otel/logs/data-model/
4
+ timestamp?: number; // Time when the event occurred.
5
+ observedTimestamp: number; // Time when the event was observed.
6
+ traceId?: string; // Request trace id.
7
+ spanId?: string; // Request span id.
8
+ traceFlags?: number; // W3C trace flag.
9
+ SeverityText; // The severity text (also known as log level).
10
+ SeverityNumber; // Numerical value of the severity.
11
+ Body; // The body of the log record.
12
+ Resource; // Describes the source of the log.
13
+ InstrumentationScope; // Describes the scope that emitted the log.
14
+ Attributes; // Additional information about the event.
15
+ }
16
+
17
+ declare type AttributeValue = string | number | boolean | Array<null | undefined | string> | Array<null | undefined | number> | Array<null | undefined | boolean>;
18
+ interface Attributes {
19
+ [attributeKey: string]: AttributeValue | undefined;
20
+ }
21
+ */
22
+
23
+ // export type TraceFunction = (message: string, attributes: Attributes) => void;
24
+ export type TraceFunction = () => void;
package/src/index.ts ADDED
@@ -0,0 +1,13 @@
1
+ // clever-queue
2
+ import * as Engine from "./engine";
3
+
4
+ function createEngine(options: Engine.Options): Engine.Engine {
5
+ return new Engine.Engine(options);
6
+ }
7
+
8
+ export * as Engine from "./engine/interfaces";
9
+ export * as Runner from "./runner/interfaces";
10
+ export * as Queue from "./queue/interfaces";
11
+ export * as Task from "./task/interfaces";
12
+
13
+ export { createEngine };
@@ -0,0 +1,93 @@
1
+ // clever-queue
2
+
3
+ import * as _ from "../helpers";
4
+ import * as Task from "../task";
5
+ import { Options, Statistics, ErrorsList, Priorities } from "./interfaces";
6
+
7
+ type GetSumWeightFunction = (priority: number) => number;
8
+
9
+ type Status = "unknown" | "initialiazing" | "running" | "stopping" | "destroying";
10
+
11
+ const defaultOptions: Options = {
12
+ autostart: true,
13
+ priority: Priorities.Standard,
14
+ weight: 128,
15
+ logFunction: undefined,
16
+ };
17
+
18
+ class Queue {
19
+ status: Status = "unknown";
20
+ readonly options: Options;
21
+ tick: number = 0;
22
+ tasks: Task.Task[] = [];
23
+
24
+ constructor(opt: Options) {
25
+ this.status = "initialiazing";
26
+ this.options = this.#checkOptionsConsistancy({ ...defaultOptions, ...opt });
27
+ if (this.options.autostart) this.start();
28
+ }
29
+
30
+ #checkOptionsConsistancy(options: Options): Options {
31
+ if (!options) throw new _.Errors.CQError(ErrorsList.NoOptionsOnQueueInitialization);
32
+ if (!(typeof options.priority === "number") || options.priority < 0 || options.priority > 255)
33
+ throw new _.Errors.CQError(ErrorsList.BadPriorityOptionsOnQueueInitialization, options);
34
+ if (!options.weight) throw new _.Errors.CQError(ErrorsList.NoWeightOptionsOnQueueInitialization);
35
+ return options;
36
+ }
37
+
38
+ start(): void {
39
+ this.status = "running";
40
+ }
41
+
42
+ stop(): void {
43
+ this.status = "stopping";
44
+ }
45
+
46
+ /*
47
+ async createTask(function_: Task._function): Promise<unknown> {
48
+ if (typeof function_ !== "function") {
49
+ console.error("raisong Task.ErrorsList.FunctionIsNotAFunction", Task.ErrorsList.FunctionIsNotAFunction);
50
+ throw new _.Errors.CQError(Task.ErrorsList.FunctionIsNotAFunction);
51
+ }
52
+ return new Promise((resolve, reject) => {
53
+ try {
54
+ const taskRunner = async (): Promise<void> => {
55
+ console.log("running Task");
56
+ const result = await function_();
57
+ console.log("task runned", result);
58
+ resolve(result);
59
+ };
60
+ const task = new Task.Task(taskRunner, {});
61
+ this.tasks.push(task);
62
+ } catch (error) {
63
+ reject(error);
64
+ }
65
+ });
66
+ }
67
+ */
68
+ async createTaskAndEnqueue(function_: Task.FunctionToExecute, options: Task.Options): Promise<unknown> {
69
+ const task = new Task.Task(function_, options);
70
+ return this.enqueue(task);
71
+ }
72
+
73
+ async enqueue(task: Task.Task): Promise<unknown> {
74
+ return new Promise((resolve, reject) => {
75
+ task.resolver = resolve;
76
+ task.rejecter = reject;
77
+ this.tasks.push(task);
78
+ });
79
+ }
80
+
81
+ statistics(): Statistics {
82
+ const stat: Statistics = {
83
+ status: this.status,
84
+ priority: this.options.priority,
85
+ weight: this.options.weight,
86
+ tasks: this.tasks.length,
87
+ };
88
+ return stat;
89
+ }
90
+ }
91
+
92
+ export { Options, Statistics, ErrorsList, Priorities } from "./interfaces";
93
+ export { Queue, GetSumWeightFunction };
@@ -0,0 +1,38 @@
1
+ import * as _ from "../helpers";
2
+
3
+ const Priorities = { Absolute: 0, Standard: 128, BestEffort: 255 };
4
+
5
+ interface Options {
6
+ name?: string;
7
+ priority: number;
8
+ weight: number;
9
+ autostart?: boolean;
10
+ logFunction?: _.Logs.LogFunction;
11
+ }
12
+
13
+ interface Statistics {
14
+ status: string;
15
+ priority: number;
16
+ weight: number;
17
+ tasks: number;
18
+ }
19
+
20
+ type ErrorListKeys = "BadPriorityOptionsOnQueueInitialization" | "NoWeightOptionsOnQueueInitialization" | "NoOptionsOnQueueInitialization";
21
+
22
+ const ErrorsList: {
23
+ [index in ErrorListKeys]: { name: string; message: string };
24
+ } = {
25
+ BadPriorityOptionsOnQueueInitialization: {
26
+ name: "BadPriorityOptionsOnQueueInitialization",
27
+ message:
28
+ "You must provide a 'priority' parameter for Queue initialization from 0 (absolute priority) to 255 (best effort priority), you may use CleverQueue.Priorities constants",
29
+ },
30
+ NoWeightOptionsOnQueueInitialization: {
31
+ name: "NoWeightOptionsOnQueueInitialization",
32
+ message: "You must provide a 'weight' parameter for Queue initialization",
33
+ },
34
+
35
+ NoOptionsOnQueueInitialization: { name: "NoOptionsOnQueueInitialization", message: "You must provide an options parameter to Queue" },
36
+ };
37
+
38
+ export { Options, Statistics, ErrorsList, Priorities };