@avleon/core 0.0.40 → 0.0.43

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 (52) hide show
  1. package/README.md +37 -5
  2. package/dist/cache.test.d.ts +1 -0
  3. package/dist/cache.test.js +36 -0
  4. package/dist/controller.d.ts +2 -0
  5. package/dist/controller.js +13 -0
  6. package/dist/controller.test.d.ts +1 -0
  7. package/dist/controller.test.js +111 -0
  8. package/dist/environment-variables.test.d.ts +1 -0
  9. package/dist/environment-variables.test.js +70 -0
  10. package/dist/exceptions/http-exceptions.d.ts +1 -0
  11. package/dist/exceptions/http-exceptions.js +3 -1
  12. package/dist/file-storage.d.ts +44 -9
  13. package/dist/file-storage.js +209 -59
  14. package/dist/file-storage.test.d.ts +1 -0
  15. package/dist/file-storage.test.js +104 -0
  16. package/dist/helpers.test.d.ts +1 -0
  17. package/dist/helpers.test.js +95 -0
  18. package/dist/icore.d.ts +8 -5
  19. package/dist/icore.js +225 -81
  20. package/dist/icore.test.d.ts +1 -0
  21. package/dist/icore.test.js +14 -0
  22. package/dist/index.d.ts +24 -0
  23. package/dist/index.js +8 -1
  24. package/dist/kenx-provider.test.d.ts +1 -0
  25. package/dist/kenx-provider.test.js +36 -0
  26. package/dist/logger.test.d.ts +1 -0
  27. package/dist/logger.test.js +42 -0
  28. package/dist/middleware.test.d.ts +1 -0
  29. package/dist/middleware.test.js +121 -0
  30. package/dist/multipart.test.d.ts +1 -0
  31. package/dist/multipart.test.js +87 -0
  32. package/dist/openapi.test.d.ts +1 -0
  33. package/dist/openapi.test.js +111 -0
  34. package/dist/params.test.d.ts +1 -0
  35. package/dist/params.test.js +83 -0
  36. package/dist/queue.d.ts +16 -14
  37. package/dist/queue.js +76 -63
  38. package/dist/queue.test.d.ts +1 -0
  39. package/dist/queue.test.js +79 -0
  40. package/dist/route-methods.test.d.ts +1 -0
  41. package/dist/route-methods.test.js +129 -0
  42. package/dist/swagger-schema.d.ts +42 -0
  43. package/dist/swagger-schema.js +331 -58
  44. package/dist/swagger-schema.test.d.ts +1 -0
  45. package/dist/swagger-schema.test.js +105 -0
  46. package/dist/validation.d.ts +7 -0
  47. package/dist/validation.js +2 -0
  48. package/dist/validation.test.d.ts +1 -0
  49. package/dist/validation.test.js +61 -0
  50. package/dist/websocket.test.d.ts +1 -0
  51. package/dist/websocket.test.js +27 -0
  52. package/package.json +11 -9
@@ -0,0 +1,121 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ require("reflect-metadata");
13
+ const middleware_1 = require("./middleware");
14
+ describe("AvleonMiddleware", () => {
15
+ class TestMiddleware extends middleware_1.AvleonMiddleware {
16
+ async invoke(req, res) {
17
+ return req;
18
+ }
19
+ }
20
+ it("should allow AppMiddleware decorator on valid class", () => {
21
+ expect(() => (0, middleware_1.AppMiddleware)(TestMiddleware)).not.toThrow();
22
+ });
23
+ it("should throw error if AppMiddleware is used on class without invoke", () => {
24
+ class InvalidMiddleware {
25
+ }
26
+ expect(() => (0, middleware_1.AppMiddleware)(InvalidMiddleware)).toThrow(/must implement an "invoke" method/);
27
+ });
28
+ });
29
+ describe("UseMiddleware", () => {
30
+ class MW1 extends middleware_1.AvleonMiddleware {
31
+ async invoke(req) {
32
+ return req;
33
+ }
34
+ }
35
+ class MW2 extends middleware_1.AvleonMiddleware {
36
+ async invoke(req) {
37
+ return req;
38
+ }
39
+ }
40
+ it("should attach middleware to class", () => {
41
+ let TestController = class TestController {
42
+ };
43
+ TestController = __decorate([
44
+ (0, middleware_1.UseMiddleware)([MW1, MW2])
45
+ ], TestController);
46
+ const middlewares = Reflect.getMetadata("controller:middleware", TestController);
47
+ expect(middlewares).toHaveLength(2);
48
+ expect(middlewares[0]).toBeInstanceOf(MW1);
49
+ expect(middlewares[1]).toBeInstanceOf(MW2);
50
+ });
51
+ it("should attach middleware to method", () => {
52
+ class TestController {
53
+ testMethod() { }
54
+ }
55
+ __decorate([
56
+ (0, middleware_1.UseMiddleware)(MW1),
57
+ __metadata("design:type", Function),
58
+ __metadata("design:paramtypes", []),
59
+ __metadata("design:returntype", void 0)
60
+ ], TestController.prototype, "testMethod", null);
61
+ const middlewares = Reflect.getMetadata("route:middleware", TestController.prototype, "testMethod");
62
+ expect(middlewares).toHaveLength(1);
63
+ expect(middlewares[0]).toBeInstanceOf(MW1);
64
+ });
65
+ });
66
+ // describe("Authorized decorator", () => {
67
+ // it("should define metadata on class", () => {
68
+ // @Authorized({ roles: ["admin"] })
69
+ // class TestClass {}
70
+ // const meta = Reflect.getMetadata("AUTHORIZATION_META_KEY", TestClass);
71
+ // expect(meta).toEqual({ authorize: true, options: { roles: ["admin"] } });
72
+ // });
73
+ // it("should define metadata on method", () => {
74
+ // class TestClass {
75
+ // @Authorized({ roles: ["user"] })
76
+ // testMethod() {}
77
+ // }
78
+ // const meta = Reflect.getMetadata(
79
+ // "AUTHORIZATION_META_KEY",
80
+ // TestClass.constructor,
81
+ // "testMethod",
82
+ // );
83
+ // expect(meta).toEqual({ authorize: true, options: { roles: ["user"] } });
84
+ // });
85
+ describe("CanAuthorize and AppAuthorization", () => {
86
+ class ValidAuthorize {
87
+ authorize(req, options) {
88
+ return req;
89
+ }
90
+ }
91
+ it("should not throw for valid CanAuthorize", () => {
92
+ expect(() => (0, middleware_1.CanAuthorize)(ValidAuthorize)).not.toThrow();
93
+ });
94
+ it("should throw for invalid CanAuthorize", () => {
95
+ class InvalidAuthorize {
96
+ }
97
+ expect(() => (0, middleware_1.CanAuthorize)(InvalidAuthorize)).toThrow(/must implement an "authorize" method/);
98
+ });
99
+ it("should not throw for valid AppAuthorization", () => {
100
+ expect(() => (0, middleware_1.AppAuthorization)(ValidAuthorize)).not.toThrow();
101
+ });
102
+ it("should throw for invalid AppAuthorization", () => {
103
+ class InvalidAuthorize {
104
+ }
105
+ expect(() => (0, middleware_1.AppAuthorization)(InvalidAuthorize)).toThrow(/must implement an "authorize" method/);
106
+ });
107
+ });
108
+ describe("AuthorizeMiddleware", () => {
109
+ class TestAuthorizeMiddleware extends middleware_1.AuthorizeMiddleware {
110
+ authorize(roles) {
111
+ return (req) => req;
112
+ }
113
+ }
114
+ it("should implement authorize method", () => {
115
+ const mw = new TestAuthorizeMiddleware();
116
+ const handler = mw.authorize(["admin"]);
117
+ expect(typeof handler).toBe("function");
118
+ const req = {};
119
+ expect(handler(req)).toBe(req);
120
+ });
121
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const multipart_1 = require("./multipart");
7
+ const exceptions_1 = require("./exceptions");
8
+ const fs_1 = __importDefault(require("fs"));
9
+ jest.mock("fs");
10
+ jest.mock("stream/promises", () => ({
11
+ pipeline: jest.fn(() => Promise.resolve()),
12
+ }));
13
+ const mockFileStream = {};
14
+ const mockReq = (fileData) => ({
15
+ params: {},
16
+ query: {},
17
+ body: {},
18
+ headers: {},
19
+ method: "POST",
20
+ url: "/upload",
21
+ file: jest.fn().mockResolvedValue(fileData),
22
+ // Add any other required IRequest properties as needed for type compatibility
23
+ });
24
+ describe("UploadFileFromRequest", () => {
25
+ beforeEach(() => {
26
+ jest.clearAllMocks();
27
+ fs_1.default.existsSync.mockReturnValue(false);
28
+ });
29
+ // it("should save file to default location", async () => {
30
+ // const fileData = {
31
+ // file: mockFileStream,
32
+ // filename: "test.txt",
33
+ // };
34
+ // const req = mockReq(fileData);
35
+ // const result = await UploadFileFromRequest(req);
36
+ // expect(fs.existsSync).toHaveBeenCalled();
37
+ // expect(pipeline).toHaveBeenCalledWith(mockFileStream, expect.anything());
38
+ // expect(result).toMatchObject({
39
+ // ...fileData,
40
+ // filename: "test.txt",
41
+ // });
42
+ // });
43
+ // it("should save file to custom location with saveAs", async () => {
44
+ // const fileData = {
45
+ // file: mockFileStream,
46
+ // filename: "original.txt",
47
+ // };
48
+ // const req = mockReq(fileData);
49
+ // const options = { saveAs: "custom.txt" };
50
+ // const result = await UploadFileFromRequest(req, options);
51
+ // expect(fs.existsSync).toHaveBeenCalled();
52
+ // expect(pipeline).toHaveBeenCalledWith(mockFileStream, expect.anything());
53
+ // expect(result).toMatchObject({
54
+ // ...fileData,
55
+ // filename: "custom.txt",
56
+ // });
57
+ // });
58
+ // it("should save file to custom dest", async () => {
59
+ // const fileData = {
60
+ // file: mockFileStream,
61
+ // filename: "file.txt",
62
+ // };
63
+ // const req = mockReq(fileData);
64
+ // const options:any = { dest: "/tmp" };
65
+ // const result = await UploadFileFromRequest(req, options);
66
+ // expect(fs.existsSync).toHaveBeenCalled();
67
+ // expect(pipeline).toHaveBeenCalledWith(mockFileStream, expect.anything());
68
+ // expect(result).toMatchObject({
69
+ // ...fileData,
70
+ // filename: "file.txt",
71
+ // });
72
+ // });
73
+ it("should throw error if file already exists", async () => {
74
+ fs_1.default.existsSync.mockReturnValue(true);
75
+ const fileData = {
76
+ file: mockFileStream,
77
+ filename: "exists.txt",
78
+ };
79
+ const req = mockReq(fileData);
80
+ await expect((0, multipart_1.UploadFileFromRequest)(req)).rejects.toThrow(exceptions_1.InternalErrorException);
81
+ });
82
+ it("should return undefined if no file is present", async () => {
83
+ const req = mockReq(null);
84
+ const result = await (0, multipart_1.UploadFileFromRequest)(req);
85
+ expect(result).toBeUndefined();
86
+ });
87
+ });
@@ -0,0 +1 @@
1
+ import 'reflect-metadata';
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ require("reflect-metadata");
13
+ const openapi_1 = require("./openapi");
14
+ describe('OpenApi Decorator', () => {
15
+ it('should define metadata on class', () => {
16
+ let TestController = class TestController {
17
+ };
18
+ TestController = __decorate([
19
+ (0, openapi_1.OpenApi)({ description: 'Test Controller' })
20
+ ], TestController);
21
+ const metadata = Reflect.getMetadata('controller:openapi', TestController);
22
+ expect(metadata).toEqual({ description: 'Test Controller' });
23
+ });
24
+ it('should define metadata on method', () => {
25
+ class TestController {
26
+ testMethod() { }
27
+ }
28
+ __decorate([
29
+ (0, openapi_1.OpenApi)({ description: 'Test Route' }),
30
+ __metadata("design:type", Function),
31
+ __metadata("design:paramtypes", []),
32
+ __metadata("design:returntype", void 0)
33
+ ], TestController.prototype, "testMethod", null);
34
+ const metadata = Reflect.getMetadata('route:openapi', TestController.prototype, 'testMethod');
35
+ expect(metadata).toEqual({ description: 'Test Route' });
36
+ });
37
+ it('should define metadata on property', () => {
38
+ class TestController {
39
+ constructor() {
40
+ this.testProperty = '';
41
+ }
42
+ }
43
+ __decorate([
44
+ (0, openapi_1.OpenApi)({ description: 'Test Property' }),
45
+ __metadata("design:type", String)
46
+ ], TestController.prototype, "testProperty", void 0);
47
+ const metadata = Reflect.getMetadata('property:openapi', TestController.prototype, 'testProperty');
48
+ expect(metadata).toEqual({ description: 'Test Property' });
49
+ });
50
+ });
51
+ describe('Type Definitions', () => {
52
+ it('InfoObject should allow optional description', () => {
53
+ const info = {
54
+ title: 'API',
55
+ version: '1.0.0',
56
+ description: 'API Description',
57
+ };
58
+ expect(info.description).toBe('API Description');
59
+ });
60
+ it('ContactObject should allow optional fields', () => {
61
+ const contact = {
62
+ name: 'John Doe',
63
+ email: 'john@example.com',
64
+ };
65
+ expect(contact.name).toBe('John Doe');
66
+ expect(contact.email).toBe('john@example.com');
67
+ });
68
+ it('LicenseObject should require name', () => {
69
+ const license = {
70
+ name: 'MIT',
71
+ };
72
+ expect(license.name).toBe('MIT');
73
+ });
74
+ it('ServerObject should allow variables', () => {
75
+ var _a;
76
+ const server = {
77
+ url: 'https://api.example.com',
78
+ variables: {
79
+ version: {
80
+ default: 'v1',
81
+ enum: ['v1', 'v2'],
82
+ },
83
+ },
84
+ };
85
+ expect((_a = server.variables) === null || _a === void 0 ? void 0 : _a.version.default).toBe('v1');
86
+ });
87
+ it('SchemaObject should allow array and non-array types', () => {
88
+ const arraySchema = {
89
+ type: 'array',
90
+ items: { type: 'string' },
91
+ };
92
+ expect(arraySchema.type).toBe('array');
93
+ const nonArraySchema = {
94
+ type: 'string',
95
+ };
96
+ expect(nonArraySchema.type).toBe('string');
97
+ });
98
+ it('ComponentsObject should allow schemas and responses', () => {
99
+ var _a, _b;
100
+ const components = {
101
+ schemas: {
102
+ User: { type: 'object' },
103
+ },
104
+ responses: {
105
+ NotFound: { description: 'Not found' },
106
+ },
107
+ };
108
+ expect((_a = components.schemas) === null || _a === void 0 ? void 0 : _a.User).toBeDefined();
109
+ expect(((_b = components.responses) === null || _b === void 0 ? void 0 : _b.NotFound).description).toBe('Not found');
110
+ });
111
+ });
@@ -0,0 +1 @@
1
+ import "reflect-metadata";
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ require("reflect-metadata");
16
+ const params_1 = require("./params");
17
+ const container_1 = require("./container");
18
+ describe("Parameter Decorators", () => {
19
+ class DummyValidator {
20
+ }
21
+ class TestController {
22
+ testMethod(id, search, body, token, user) { }
23
+ }
24
+ __decorate([
25
+ __param(0, (0, params_1.Param)("id")),
26
+ __param(1, (0, params_1.Query)("search")),
27
+ __param(2, (0, params_1.Body)()),
28
+ __param(3, (0, params_1.Header)("x-token")),
29
+ __param(4, (0, params_1.AuthUser)()),
30
+ __metadata("design:type", Function),
31
+ __metadata("design:paramtypes", [String, String, DummyValidator, String, Object]),
32
+ __metadata("design:returntype", void 0)
33
+ ], TestController.prototype, "testMethod", null);
34
+ it("should define metadata for Param decorator", () => {
35
+ const meta = Reflect.getMetadata(container_1.PARAM_META_KEY, TestController.prototype, "testMethod");
36
+ expect(meta).toBeDefined();
37
+ expect(meta[0].key).toBe("id");
38
+ expect(meta[0].type).toBe("route:param");
39
+ });
40
+ it("should define metadata for Query decorator", () => {
41
+ const meta = Reflect.getMetadata(container_1.QUERY_META_KEY, TestController.prototype, "testMethod");
42
+ expect(meta).toBeDefined();
43
+ expect(meta[1].key).toBe("search");
44
+ expect(meta[1].type).toBe("route:query");
45
+ });
46
+ it("should define metadata for Body decorator", () => {
47
+ const meta = Reflect.getMetadata(container_1.REQUEST_BODY_META_KEY, TestController.prototype, "testMethod");
48
+ expect(meta).toBeDefined();
49
+ expect(meta[2].key).toBe("all");
50
+ expect(meta[2].type).toBe("route:body");
51
+ });
52
+ it("should define metadata for Header decorator", () => {
53
+ const meta = Reflect.getMetadata(container_1.REQUEST_HEADER_META_KEY, TestController.prototype, "testMethod");
54
+ expect(meta).toBeDefined();
55
+ expect(meta[3].key).toBe("x-token");
56
+ expect(meta[3].type).toBe("route:header");
57
+ });
58
+ it("should define metadata for AuthUser decorator", () => {
59
+ const meta = Reflect.getMetadata(container_1.REQUEST_USER_META_KEY, TestController.prototype, "testMethod");
60
+ expect(meta).toBeDefined();
61
+ expect(meta[4].key).toBe("all");
62
+ expect(meta[4].type).toBe("route:user");
63
+ });
64
+ it("should set required and validate options to true by default", () => {
65
+ const meta = Reflect.getMetadata(container_1.PARAM_META_KEY, TestController.prototype, "testMethod");
66
+ expect(meta[0].required).toBe(true);
67
+ expect(meta[0].validate).toBe(true);
68
+ });
69
+ it("should allow overriding required and validate options", () => {
70
+ class AnotherController {
71
+ test(id) { }
72
+ }
73
+ __decorate([
74
+ __param(0, (0, params_1.Param)("id", { required: false, validate: false })),
75
+ __metadata("design:type", Function),
76
+ __metadata("design:paramtypes", [String]),
77
+ __metadata("design:returntype", void 0)
78
+ ], AnotherController.prototype, "test", null);
79
+ const meta = Reflect.getMetadata(container_1.PARAM_META_KEY, AnotherController.prototype, "test");
80
+ expect(meta[0].required).toBe(false);
81
+ expect(meta[0].validate).toBe(false);
82
+ });
83
+ });
package/dist/queue.d.ts CHANGED
@@ -1,12 +1,9 @@
1
- /**
2
- * @copyright 2024
3
- * @author Tareq Hossain
4
- * @email xtrinsic96@gmail.com
5
- * @url https://github.com/xtareq
6
- */
1
+ import { EventEmitter } from "events";
7
2
  interface Job {
8
3
  id: string;
9
4
  data: any;
5
+ runAt?: number;
6
+ status?: "pending" | "running" | "failed" | "completed";
10
7
  }
11
8
  interface QueueAdapter {
12
9
  loadJobs(): Promise<Job[]>;
@@ -18,19 +15,24 @@ export declare class FileQueueAdapter implements QueueAdapter {
18
15
  loadJobs(): Promise<Job[]>;
19
16
  saveJobs(jobs: Job[]): Promise<void>;
20
17
  }
21
- declare class SimpleQueue {
18
+ export declare class AvleonQueue extends EventEmitter {
19
+ private name;
22
20
  private processing;
21
+ private stopped;
23
22
  private jobHandler;
24
23
  private adapter;
25
- constructor(adapter: QueueAdapter, jobHandler: (job: Job) => Promise<void>);
26
- addJob(data: any): Promise<void>;
24
+ constructor(name: string, adapter?: QueueAdapter, jobHandler?: (job: Job) => Promise<void>);
25
+ private defaultHandler;
26
+ addJob(data: any, options?: {
27
+ delay?: number;
28
+ }): Promise<void>;
27
29
  private processNext;
30
+ onDone(cb: (job: Job) => void): Promise<void>;
31
+ onFailed(cb: (error: any, job: Job) => void): Promise<void>;
32
+ getJobs(): Promise<Job[]>;
33
+ stop(): Promise<void>;
28
34
  }
29
35
  export declare class QueueManager {
30
- private static instance;
31
- private adapter;
32
- private constructor();
33
- static getInstance(adapter: QueueAdapter): QueueManager;
34
- createQueue(jobHandler: (job: Job) => Promise<void>): SimpleQueue;
36
+ from(name: string, jobHandler?: (job: Job) => Promise<void>): Promise<AvleonQueue>;
35
37
  }
36
38
  export {};
package/dist/queue.js CHANGED
@@ -1,15 +1,17 @@
1
1
  "use strict";
2
- /**
3
- * @copyright 2024
4
- * @author Tareq Hossain
5
- * @email xtrinsic96@gmail.com
6
- * @url https://github.com/xtareq
7
- */
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.QueueManager = exports.FileQueueAdapter = void 0;
9
+ exports.QueueManager = exports.AvleonQueue = exports.FileQueueAdapter = void 0;
10
10
  const fs_1 = require("fs");
11
11
  const path_1 = require("path");
12
- const helpers_1 = require("./helpers");
12
+ const crypto_1 = require("crypto");
13
+ const events_1 = require("events");
14
+ const decorators_1 = require("./decorators");
13
15
  class FileQueueAdapter {
14
16
  constructor(queueName) {
15
17
  this.queueFile = (0, path_1.join)(__dirname, `${queueName}.json`);
@@ -19,7 +21,7 @@ class FileQueueAdapter {
19
21
  const data = await fs_1.promises.readFile(this.queueFile, "utf-8");
20
22
  return JSON.parse(data);
21
23
  }
22
- catch (error) {
24
+ catch (_a) {
23
25
  return [];
24
26
  }
25
27
  }
@@ -28,76 +30,87 @@ class FileQueueAdapter {
28
30
  }
29
31
  }
30
32
  exports.FileQueueAdapter = FileQueueAdapter;
31
- // class RedisQueueAdapter implements QueueAdapter {
32
- // private client = createClient();
33
- // private queueKey: string;
34
- //
35
- // constructor(queueName: string) {
36
- // this.queueKey = `queue:${queueName}`;
37
- // this.client.connect();
38
- // }
39
- //
40
- // async loadJobs(): Promise<Job[]> {
41
- // const jobs = await this.client.lRange(this.queueKey, 0, -1);
42
- // return jobs.map((job) => JSON.parse(job));
43
- // }
44
- //
45
- // async saveJobs(jobs: Job[]) {
46
- // await this.client.del(this.queueKey);
47
- // if (jobs.length > 0) {
48
- // await this.client.rPush(this.queueKey, ...jobs.map(job => JSON.stringify(job)));
49
- // }
50
- // }
51
- // }
52
- class SimpleQueue {
53
- constructor(adapter, jobHandler) {
33
+ class AvleonQueue extends events_1.EventEmitter {
34
+ constructor(name, adapter, jobHandler) {
35
+ super();
54
36
  this.processing = false;
55
- this.adapter = adapter;
56
- this.jobHandler = jobHandler;
37
+ this.stopped = false;
38
+ this.name = name;
39
+ this.adapter = adapter ? adapter : new FileQueueAdapter(name);
40
+ this.jobHandler = jobHandler || this.defaultHandler.bind(this);
41
+ this.setMaxListeners(10);
57
42
  }
58
- async addJob(data) {
59
- const job = { id: helpers_1.uuid, data };
43
+ async defaultHandler(job) {
44
+ if (typeof job.data === "function") {
45
+ await job.data();
46
+ }
47
+ }
48
+ async addJob(data, options) {
49
+ const job = {
50
+ id: (0, crypto_1.randomUUID)(),
51
+ data,
52
+ runAt: Date.now() + ((options === null || options === void 0 ? void 0 : options.delay) || 0),
53
+ status: "pending",
54
+ };
60
55
  const jobs = await this.adapter.loadJobs();
61
56
  jobs.push(job);
62
57
  await this.adapter.saveJobs(jobs);
63
- this.processNext();
58
+ if (!this.processing)
59
+ this.processNext();
64
60
  }
65
61
  async processNext() {
66
- if (this.processing)
62
+ if (this.processing || this.stopped)
67
63
  return;
68
64
  this.processing = true;
69
- const jobs = await this.adapter.loadJobs();
70
- if (jobs.length === 0) {
71
- this.processing = false;
72
- return;
73
- }
74
- const job = jobs.shift();
75
- if (job) {
76
- try {
77
- await this.jobHandler(job);
65
+ while (!this.stopped) {
66
+ const jobs = await this.adapter.loadJobs();
67
+ const nextJob = jobs.find((j) => j.status === "pending");
68
+ if (!nextJob) {
69
+ this.processing = false;
70
+ return;
78
71
  }
79
- catch (error) {
80
- console.error(`Error processing job ${job.id}:`, error);
81
- jobs.unshift(job);
72
+ const now = Date.now();
73
+ if (nextJob.runAt && nextJob.runAt > now) {
74
+ const delay = nextJob.runAt - now;
75
+ await new Promise((res) => setTimeout(res, delay));
82
76
  }
77
+ nextJob.status = "running";
83
78
  await this.adapter.saveJobs(jobs);
84
- this.processing = false;
85
- this.processNext();
79
+ this.emit("start", nextJob);
80
+ try {
81
+ await this.jobHandler(nextJob);
82
+ nextJob.status = "completed";
83
+ this.emit("done", nextJob);
84
+ }
85
+ catch (err) {
86
+ nextJob.status = "failed";
87
+ this.emit("failed", err, nextJob);
88
+ }
89
+ await this.adapter.saveJobs(jobs.filter((j) => j.id !== nextJob.id));
86
90
  }
91
+ this.processing = false;
87
92
  }
88
- }
89
- class QueueManager {
90
- constructor(adapter) {
91
- this.adapter = adapter;
93
+ async onDone(cb) {
94
+ this.on("done", cb);
92
95
  }
93
- static getInstance(adapter) {
94
- if (!QueueManager.instance) {
95
- QueueManager.instance = new QueueManager(adapter);
96
- }
97
- return QueueManager.instance;
96
+ async onFailed(cb) {
97
+ this.on("failed", cb);
98
98
  }
99
- createQueue(jobHandler) {
100
- return new SimpleQueue(this.adapter, jobHandler);
99
+ async getJobs() {
100
+ return this.adapter.loadJobs();
101
+ }
102
+ async stop() {
103
+ this.stopped = true;
101
104
  }
102
105
  }
106
+ exports.AvleonQueue = AvleonQueue;
107
+ let QueueManager = class QueueManager {
108
+ async from(name, jobHandler) {
109
+ const q = new AvleonQueue(name, new FileQueueAdapter(name), jobHandler);
110
+ return q;
111
+ }
112
+ };
103
113
  exports.QueueManager = QueueManager;
114
+ exports.QueueManager = QueueManager = __decorate([
115
+ decorators_1.AppService
116
+ ], QueueManager);
@@ -0,0 +1 @@
1
+ export {};