@ido_kawaz/server-framework 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/README.md +90 -0
- package/dist/__tests__/config.test.d.ts +2 -0
- package/dist/__tests__/config.test.d.ts.map +1 -0
- package/dist/__tests__/config.test.js +62 -0
- package/dist/__tests__/config.test.js.map +1 -0
- package/dist/__tests__/decorators.test.d.ts +2 -0
- package/dist/__tests__/decorators.test.d.ts.map +1 -0
- package/dist/__tests__/decorators.test.js +78 -0
- package/dist/__tests__/decorators.test.js.map +1 -0
- package/dist/__tests__/errors.test.d.ts +2 -0
- package/dist/__tests__/errors.test.d.ts.map +1 -0
- package/dist/__tests__/errors.test.js +33 -0
- package/dist/__tests__/errors.test.js.map +1 -0
- package/dist/config.d.ts +6 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +20 -0
- package/dist/config.js.map +1 -0
- package/dist/decorators.d.ts +4 -0
- package/dist/decorators.d.ts.map +1 -0
- package/dist/decorators.js +36 -0
- package/dist/decorators.js.map +1 -0
- package/dist/errors.d.ts +17 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +36 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +4 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +28 -0
- package/dist/server.js.map +1 -0
- package/dist/utils.d.ts +4 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +23 -0
- package/dist/utils.js.map +1 -0
- package/package.json +47 -0
package/README.md
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# server-framework
|
|
2
|
+
|
|
3
|
+
TypeScript server framework utilities for building Express-based APIs with consistent middleware, error handling, and request logging.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm i @ido_kawaz/server-framework
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Exports
|
|
12
|
+
|
|
13
|
+
- `startServer`
|
|
14
|
+
- `createServerConfig`
|
|
15
|
+
- `createRequestHandlerDecorator`
|
|
16
|
+
- Error classes:
|
|
17
|
+
- `ApiError`
|
|
18
|
+
- `BadRequestError`
|
|
19
|
+
- `UnauthorizedError`
|
|
20
|
+
- `NotFoundError`
|
|
21
|
+
- `InternalServerError`
|
|
22
|
+
- Express types re-exported:
|
|
23
|
+
- `Application`
|
|
24
|
+
- `Request`
|
|
25
|
+
- `Response`
|
|
26
|
+
- `NextFunction`
|
|
27
|
+
|
|
28
|
+
## Environment Variables
|
|
29
|
+
|
|
30
|
+
- `PORT` (required): server port number.
|
|
31
|
+
- `SECURED` (optional): internal secured flag in config (defaults to `false` when not set).
|
|
32
|
+
|
|
33
|
+
## Quick Start
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
import express from "express";
|
|
37
|
+
import {
|
|
38
|
+
createRequestHandlerDecorator,
|
|
39
|
+
createServerConfig,
|
|
40
|
+
startServer,
|
|
41
|
+
BadRequestError,
|
|
42
|
+
Request,
|
|
43
|
+
Response
|
|
44
|
+
} from "@ido_kawaz/server-framework";
|
|
45
|
+
|
|
46
|
+
const decorate = createRequestHandlerDecorator("orders-service");
|
|
47
|
+
|
|
48
|
+
const registerRoutes = () => (app: express.Express) => {
|
|
49
|
+
app.get(
|
|
50
|
+
"/health",
|
|
51
|
+
decorate("health", async (_req: Request, res: Response) => {
|
|
52
|
+
res.status(200).json({ ok: true });
|
|
53
|
+
})
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
app.get(
|
|
57
|
+
"/orders/:id",
|
|
58
|
+
decorate("getOrder", async (req: Request, res: Response) => {
|
|
59
|
+
if (!req.params.id) {
|
|
60
|
+
throw new BadRequestError("Missing order id");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
res.status(200).json({ id: req.params.id });
|
|
64
|
+
})
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
return app;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const config = createServerConfig();
|
|
71
|
+
void startServer(config, registerRoutes);
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Development Scripts
|
|
75
|
+
|
|
76
|
+
- `npm run build` - Compile TypeScript to `dist`.
|
|
77
|
+
- `npm run build:watch` - Compile in watch mode.
|
|
78
|
+
- `npm run clean` - Remove `dist`.
|
|
79
|
+
- `npm test` - Build and run Jest test suite.
|
|
80
|
+
- `npm run test:advanced` - Clean install, build, and run tests.
|
|
81
|
+
- `npm run package` - Run advanced tests and publish.
|
|
82
|
+
|
|
83
|
+
## Testing
|
|
84
|
+
|
|
85
|
+
Current test coverage includes:
|
|
86
|
+
|
|
87
|
+
- Server config parsing behavior.
|
|
88
|
+
- API error classes and status-code mapping.
|
|
89
|
+
- Request handler decorator success/error behavior.
|
|
90
|
+
- Centralized request error handler responses.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/config.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const config_1 = require("../config");
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
describe("createServerConfig", () => {
|
|
6
|
+
const originalEnv = process.env;
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
process.env = { ...originalEnv };
|
|
9
|
+
});
|
|
10
|
+
afterAll(() => {
|
|
11
|
+
process.env = originalEnv;
|
|
12
|
+
});
|
|
13
|
+
it("parses PORT from environment variables", () => {
|
|
14
|
+
process.env.PORT = "3000";
|
|
15
|
+
delete process.env.SECURED;
|
|
16
|
+
const config = (0, config_1.createServerConfig)();
|
|
17
|
+
expect(config).toEqual({ port: 3000, secured: false });
|
|
18
|
+
});
|
|
19
|
+
it("throws when PORT is missing", () => {
|
|
20
|
+
delete process.env.PORT;
|
|
21
|
+
delete process.env.SECURED;
|
|
22
|
+
const expectedError = new zod_1.ZodError([
|
|
23
|
+
{
|
|
24
|
+
code: "invalid_type",
|
|
25
|
+
expected: "number",
|
|
26
|
+
received: "nan",
|
|
27
|
+
path: ["PORT"],
|
|
28
|
+
message: "Expected number, received nan"
|
|
29
|
+
}
|
|
30
|
+
]);
|
|
31
|
+
try {
|
|
32
|
+
(0, config_1.createServerConfig)();
|
|
33
|
+
throw new Error("Expected createServerConfig to throw");
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
expect(error).toBeInstanceOf(zod_1.ZodError);
|
|
37
|
+
expect(error).toEqual(expectedError);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
it("throws when PORT is invalid", () => {
|
|
41
|
+
process.env.PORT = "not-a-number";
|
|
42
|
+
delete process.env.SECURED;
|
|
43
|
+
const expectedError = new zod_1.ZodError([
|
|
44
|
+
{
|
|
45
|
+
code: "invalid_type",
|
|
46
|
+
expected: "number",
|
|
47
|
+
received: "nan",
|
|
48
|
+
path: ["PORT"],
|
|
49
|
+
message: "Expected number, received nan"
|
|
50
|
+
}
|
|
51
|
+
]);
|
|
52
|
+
try {
|
|
53
|
+
(0, config_1.createServerConfig)();
|
|
54
|
+
throw new Error("Expected createServerConfig to throw");
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
expect(error).toBeInstanceOf(zod_1.ZodError);
|
|
58
|
+
expect(error).toEqual(expectedError);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
//# sourceMappingURL=config.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.test.js","sourceRoot":"","sources":["../../src/__tests__/config.test.ts"],"names":[],"mappings":";;AAAA,sCAA+C;AAC/C,6BAA+B;AAE/B,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;IAChC,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC;IAEhC,UAAU,CAAC,GAAG,EAAE;QACZ,OAAO,CAAC,GAAG,GAAG,EAAE,GAAG,WAAW,EAAE,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,GAAG,EAAE;QACV,OAAO,CAAC,GAAG,GAAG,WAAW,CAAC;IAC9B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAC9C,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,MAAM,CAAC;QAC1B,OAAO,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC;QAE3B,MAAM,MAAM,GAAG,IAAA,2BAAkB,GAAE,CAAC;QAEpC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACnC,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;QACxB,OAAO,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC;QAE3B,MAAM,aAAa,GAAG,IAAI,cAAQ,CAAC;YAC/B;gBACI,IAAI,EAAE,cAAc;gBACpB,QAAQ,EAAE,QAAQ;gBAClB,QAAQ,EAAE,KAAK;gBACf,IAAI,EAAE,CAAC,MAAM,CAAC;gBACd,OAAO,EAAE,+BAA+B;aAC3C;SACJ,CAAC,CAAC;QAEH,IAAI,CAAC;YACD,IAAA,2BAAkB,GAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;QAC5D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,MAAM,CAAC,KAAK,CAAC,CAAC,cAAc,CAAC,cAAQ,CAAC,CAAC;YACvC,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QACzC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACnC,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,cAAc,CAAC;QAClC,OAAO,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC;QAE3B,MAAM,aAAa,GAAG,IAAI,cAAQ,CAAC;YAC/B;gBACI,IAAI,EAAE,cAAc;gBACpB,QAAQ,EAAE,QAAQ;gBAClB,QAAQ,EAAE,KAAK;gBACf,IAAI,EAAE,CAAC,MAAM,CAAC;gBACd,OAAO,EAAE,+BAA+B;aAC3C;SACJ,CAAC,CAAC;QAEH,IAAI,CAAC;YACD,IAAA,2BAAkB,GAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;QAC5D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,MAAM,CAAC,KAAK,CAAC,CAAC,cAAc,CAAC,cAAQ,CAAC,CAAC;YACvC,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QACzC,CAAC;IACL,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decorators.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/decorators.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,78 @@
|
|
|
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 http_status_codes_1 = require("http-status-codes");
|
|
7
|
+
const pino_1 = __importDefault(require("pino"));
|
|
8
|
+
const decorators_1 = require("../decorators");
|
|
9
|
+
const errors_1 = require("../errors");
|
|
10
|
+
const infoMock = jest.fn();
|
|
11
|
+
const errorMock = jest.fn();
|
|
12
|
+
jest.mock("pino", () => ({
|
|
13
|
+
__esModule: true,
|
|
14
|
+
default: jest.fn(() => ({
|
|
15
|
+
info: infoMock,
|
|
16
|
+
error: errorMock
|
|
17
|
+
}))
|
|
18
|
+
}));
|
|
19
|
+
describe("createRequestHandlerDecorator", () => {
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
jest.clearAllMocks();
|
|
22
|
+
});
|
|
23
|
+
it("logs start and success for successful handlers", async () => {
|
|
24
|
+
const serviceName = "orders-service";
|
|
25
|
+
const functionName = "getOrders";
|
|
26
|
+
const baseHandler = jest.fn().mockResolvedValue(undefined);
|
|
27
|
+
const decoratedHandler = (0, decorators_1.createRequestHandlerDecorator)(serviceName)(functionName, baseHandler);
|
|
28
|
+
const request = { method: "GET" };
|
|
29
|
+
const response = {};
|
|
30
|
+
const next = jest.fn();
|
|
31
|
+
await decoratedHandler(request, response, next);
|
|
32
|
+
expect(pino_1.default).toHaveBeenCalledWith({ name: serviceName });
|
|
33
|
+
expect(baseHandler).toHaveBeenCalledWith(request, response, next);
|
|
34
|
+
expect(infoMock).toHaveBeenCalledTimes(2);
|
|
35
|
+
expect(errorMock).not.toHaveBeenCalled();
|
|
36
|
+
expect(next).not.toHaveBeenCalled();
|
|
37
|
+
});
|
|
38
|
+
it("logs error and forwards exception for failing handlers", async () => {
|
|
39
|
+
const functionName = "createOrder";
|
|
40
|
+
const failure = new Error("boom");
|
|
41
|
+
const baseHandler = jest.fn().mockRejectedValue(failure);
|
|
42
|
+
const decoratedHandler = (0, decorators_1.createRequestHandlerDecorator)("orders-service")(functionName, baseHandler);
|
|
43
|
+
const request = { method: "POST" };
|
|
44
|
+
const response = {};
|
|
45
|
+
const next = jest.fn();
|
|
46
|
+
await decoratedHandler(request, response, next);
|
|
47
|
+
expect(baseHandler).toHaveBeenCalledWith(request, response, next);
|
|
48
|
+
expect(errorMock).toHaveBeenCalledTimes(1);
|
|
49
|
+
expect(next).toHaveBeenCalledWith(failure);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
describe("RequestErrorHandler", () => {
|
|
53
|
+
it("responds with ApiError status code", () => {
|
|
54
|
+
const error = new errors_1.BadRequestError("invalid payload");
|
|
55
|
+
const statusMock = jest.fn();
|
|
56
|
+
const jsonMock = jest.fn();
|
|
57
|
+
statusMock.mockReturnValue({ json: jsonMock });
|
|
58
|
+
const request = {};
|
|
59
|
+
const response = { status: statusMock };
|
|
60
|
+
const next = jest.fn();
|
|
61
|
+
(0, decorators_1.RequestErrorHandler)(error, request, response, next);
|
|
62
|
+
expect(statusMock).toHaveBeenCalledWith(http_status_codes_1.StatusCodes.BAD_REQUEST);
|
|
63
|
+
expect(jsonMock).toHaveBeenCalledWith({ error: "invalid payload" });
|
|
64
|
+
});
|
|
65
|
+
it("responds with 500 for unknown errors", () => {
|
|
66
|
+
const error = new Error("unexpected");
|
|
67
|
+
const statusMock = jest.fn();
|
|
68
|
+
const jsonMock = jest.fn();
|
|
69
|
+
statusMock.mockReturnValue({ json: jsonMock });
|
|
70
|
+
const request = {};
|
|
71
|
+
const response = { status: statusMock };
|
|
72
|
+
const next = jest.fn();
|
|
73
|
+
(0, decorators_1.RequestErrorHandler)(error, request, response, next);
|
|
74
|
+
expect(statusMock).toHaveBeenCalledWith(http_status_codes_1.StatusCodes.INTERNAL_SERVER_ERROR);
|
|
75
|
+
expect(jsonMock).toHaveBeenCalledWith({ error: "unexpected" });
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
//# sourceMappingURL=decorators.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decorators.test.js","sourceRoot":"","sources":["../../src/__tests__/decorators.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/errors.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const http_status_codes_1 = require("http-status-codes");
|
|
4
|
+
const errors_1 = require("../errors");
|
|
5
|
+
describe("errors", () => {
|
|
6
|
+
it("creates ApiError with explicit status code", () => {
|
|
7
|
+
const error = new errors_1.ApiError(418, "teapot");
|
|
8
|
+
expect(error).toBeInstanceOf(Error);
|
|
9
|
+
expect(error.statusCode).toBe(418);
|
|
10
|
+
expect(error.message).toBe("teapot");
|
|
11
|
+
});
|
|
12
|
+
it("creates BadRequestError with BAD_REQUEST status", () => {
|
|
13
|
+
const error = new errors_1.BadRequestError("bad request");
|
|
14
|
+
expect(error.statusCode).toBe(http_status_codes_1.StatusCodes.BAD_REQUEST);
|
|
15
|
+
expect(error.message).toBe("bad request");
|
|
16
|
+
});
|
|
17
|
+
it("creates UnauthorizedError with UNAUTHORIZED status", () => {
|
|
18
|
+
const error = new errors_1.UnauthorizedError("unauthorized");
|
|
19
|
+
expect(error.statusCode).toBe(http_status_codes_1.StatusCodes.UNAUTHORIZED);
|
|
20
|
+
expect(error.message).toBe("unauthorized");
|
|
21
|
+
});
|
|
22
|
+
it("creates NotFoundError with NOT_FOUND status", () => {
|
|
23
|
+
const error = new errors_1.NotFoundError("not found");
|
|
24
|
+
expect(error.statusCode).toBe(http_status_codes_1.StatusCodes.NOT_FOUND);
|
|
25
|
+
expect(error.message).toBe("not found");
|
|
26
|
+
});
|
|
27
|
+
it("creates InternalServerError with INTERNAL_SERVER_ERROR status", () => {
|
|
28
|
+
const error = new errors_1.InternalServerError("internal");
|
|
29
|
+
expect(error.statusCode).toBe(http_status_codes_1.StatusCodes.INTERNAL_SERVER_ERROR);
|
|
30
|
+
expect(error.message).toBe("internal");
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
//# sourceMappingURL=errors.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.test.js","sourceRoot":"","sources":["../../src/__tests__/errors.test.ts"],"names":[],"mappings":";;AAAA,yDAAgD;AAChD,sCAMmB;AAEnB,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE;IACpB,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QAClD,MAAM,KAAK,GAAG,IAAI,iBAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QAE1C,MAAM,CAAC,KAAK,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;QACpC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACnC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACvD,MAAM,KAAK,GAAG,IAAI,wBAAe,CAAC,aAAa,CAAC,CAAC;QAEjD,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,+BAAW,CAAC,WAAW,CAAC,CAAC;QACvD,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;QAC1D,MAAM,KAAK,GAAG,IAAI,0BAAiB,CAAC,cAAc,CAAC,CAAC;QAEpD,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,+BAAW,CAAC,YAAY,CAAC,CAAC;QACxD,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACnD,MAAM,KAAK,GAAG,IAAI,sBAAa,CAAC,WAAW,CAAC,CAAC;QAE7C,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,+BAAW,CAAC,SAAS,CAAC,CAAC;QACrD,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+DAA+D,EAAE,GAAG,EAAE;QACrE,MAAM,KAAK,GAAG,IAAI,4BAAmB,CAAC,UAAU,CAAC,CAAC;QAElD,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,+BAAW,CAAC,qBAAqB,CAAC,CAAC;QACjE,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,YAAY;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CACpB;AAQD,eAAO,MAAM,kBAAkB,QAAO,YAMrC,CAAA"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
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
|
+
exports.createServerConfig = void 0;
|
|
7
|
+
const zod_1 = __importDefault(require("zod"));
|
|
8
|
+
const serverEnvSchema = zod_1.default.object({
|
|
9
|
+
PORT: zod_1.default.coerce.number(),
|
|
10
|
+
SECURED: zod_1.default.boolean().default(false)
|
|
11
|
+
});
|
|
12
|
+
const createServerConfig = () => {
|
|
13
|
+
const env = serverEnvSchema.parse(process.env);
|
|
14
|
+
return {
|
|
15
|
+
port: env.PORT,
|
|
16
|
+
secured: env.SECURED
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
exports.createServerConfig = createServerConfig;
|
|
20
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";;;;;;AAAA,8CAAoB;AAOpB,MAAM,eAAe,GAAG,aAAC,CAAC,MAAM,CAAC;IAC7B,IAAI,EAAE,aAAC,CAAC,MAAM,CAAC,MAAM,EAAE;IACvB,OAAO,EAAE,aAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;CACtC,CAAC,CAAC;AAGI,MAAM,kBAAkB,GAAG,GAAiB,EAAE;IACjD,MAAM,GAAG,GAAG,eAAe,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC/C,OAAO;QACH,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,OAAO,EAAE,GAAG,CAAC,OAAO;KACvB,CAAA;AACL,CAAC,CAAA;AANY,QAAA,kBAAkB,sBAM9B"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Request, Response, NextFunction, RequestHandler } from "express";
|
|
2
|
+
export declare const createRequestHandlerDecorator: (serviceName: string) => (functionName: string, handler: RequestHandler) => (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
3
|
+
export declare const RequestErrorHandler: (err: Error, _req: Request, res: Response, _next: NextFunction) => void;
|
|
4
|
+
//# sourceMappingURL=decorators.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAK1E,eAAO,MAAM,6BAA6B,GAAI,aAAa,MAAM,MAAM,cAAc,MAAM,EAAE,SAAS,cAAc,MACzG,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,kBAgBrD,CAAA;AAEL,eAAO,MAAM,mBAAmB,GAAI,KAAK,KAAK,EAAE,MAAM,OAAO,EAAE,KAAK,QAAQ,EAAE,OAAO,YAAY,SAOhG,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
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
|
+
exports.RequestErrorHandler = exports.createRequestHandlerDecorator = void 0;
|
|
7
|
+
const pino_1 = __importDefault(require("pino"));
|
|
8
|
+
const errors_1 = require("./errors");
|
|
9
|
+
const http_status_codes_1 = require("http-status-codes");
|
|
10
|
+
const createRequestHandlerDecorator = (serviceName) => (functionName, handler) => async (req, res, next) => {
|
|
11
|
+
const logger = (0, pino_1.default)({ name: serviceName });
|
|
12
|
+
const startTime = Date.now();
|
|
13
|
+
logger.info({ request: 'http', handler: functionName, method: req.method }, `Starting running ${functionName} handler`);
|
|
14
|
+
try {
|
|
15
|
+
await handler(req, res, next);
|
|
16
|
+
const duration = Date.now() - startTime;
|
|
17
|
+
logger.info({ request: 'http', handler: functionName, method: req.method, durationMs: duration }, `Finished running ${functionName} handler Successfully`);
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
const duration = Date.now() - startTime;
|
|
21
|
+
logger.error({ request: 'http', error: error.message, handler: functionName, method: req.method, durationMs: duration }, `Failed running ${functionName} handler`);
|
|
22
|
+
next(error);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
exports.createRequestHandlerDecorator = createRequestHandlerDecorator;
|
|
26
|
+
const RequestErrorHandler = (err, _req, res, _next) => {
|
|
27
|
+
const errorResponseBody = { error: err.message };
|
|
28
|
+
if (err instanceof errors_1.ApiError) {
|
|
29
|
+
res.status(err.statusCode).json(errorResponseBody);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
res.status(http_status_codes_1.StatusCodes.INTERNAL_SERVER_ERROR).json(errorResponseBody);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
exports.RequestErrorHandler = RequestErrorHandler;
|
|
36
|
+
//# sourceMappingURL=decorators.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decorators.js","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":";;;;;;AACA,gDAAwB;AACxB,qCAAoC;AACpC,yDAAgD;AAEzC,MAAM,6BAA6B,GAAG,CAAC,WAAmB,EAAE,EAAE,CAAC,CAAC,YAAoB,EAAE,OAAuB,EAAE,EAAE,CACpH,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE;IACtD,MAAM,MAAM,GAAG,IAAA,cAAI,EAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC;IAC3C,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,MAAM,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,EAAE,oBAAoB,YAAY,UAAU,CAAC,CAAC;IACxH,IAAI,CAAC;QACD,MAAM,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;QAC9B,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;QACxC,MAAM,CAAC,IAAI,CACP,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,EACpF,oBAAoB,YAAY,uBAAuB,CAC1D,CAAC;IACN,CAAC;IAAC,OAAO,KAAU,EAAE,CAAC;QAClB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;QACxC,MAAM,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,EAAE,kBAAkB,YAAY,UAAU,CAAC,CAAC;QACnK,IAAI,CAAC,KAAK,CAAC,CAAC;IAChB,CAAC;AACL,CAAC,CAAA;AAjBQ,QAAA,6BAA6B,iCAiBrC;AAEE,MAAM,mBAAmB,GAAG,CAAC,GAAU,EAAE,IAAa,EAAE,GAAa,EAAE,KAAmB,EAAE,EAAE;IACjG,MAAM,iBAAiB,GAAG,EAAE,KAAK,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC;IACjD,IAAI,GAAG,YAAY,iBAAQ,EAAE,CAAC;QAC1B,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;IACvD,CAAC;SAAM,CAAC;QACJ,GAAG,CAAC,MAAM,CAAC,+BAAW,CAAC,qBAAqB,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;IAC1E,CAAC;AACL,CAAC,CAAC;AAPW,QAAA,mBAAmB,uBAO9B"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export declare class ApiError extends Error {
|
|
2
|
+
readonly statusCode: number;
|
|
3
|
+
constructor(statusCode: number, message: string);
|
|
4
|
+
}
|
|
5
|
+
export declare class BadRequestError extends ApiError {
|
|
6
|
+
constructor(message: string);
|
|
7
|
+
}
|
|
8
|
+
export declare class UnauthorizedError extends ApiError {
|
|
9
|
+
constructor(message: string);
|
|
10
|
+
}
|
|
11
|
+
export declare class NotFoundError extends ApiError {
|
|
12
|
+
constructor(message: string);
|
|
13
|
+
}
|
|
14
|
+
export declare class InternalServerError extends ApiError {
|
|
15
|
+
constructor(message: string);
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAEA,qBAAa,QAAS,SAAQ,KAAK;aACH,UAAU,EAAE,MAAM;gBAAlB,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAGlE;AAED,qBAAa,eAAgB,SAAQ,QAAQ;gBAC7B,OAAO,EAAE,MAAM;CAG9B;AAED,qBAAa,iBAAkB,SAAQ,QAAQ;gBAC/B,OAAO,EAAE,MAAM;CAG9B;AAED,qBAAa,aAAc,SAAQ,QAAQ;gBAC3B,OAAO,EAAE,MAAM;CAG9B;AAED,qBAAa,mBAAoB,SAAQ,QAAQ;gBACjC,OAAO,EAAE,MAAM;CAG9B"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.InternalServerError = exports.NotFoundError = exports.UnauthorizedError = exports.BadRequestError = exports.ApiError = void 0;
|
|
4
|
+
const http_status_codes_1 = require("http-status-codes");
|
|
5
|
+
class ApiError extends Error {
|
|
6
|
+
constructor(statusCode, message) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.statusCode = statusCode;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
exports.ApiError = ApiError;
|
|
12
|
+
class BadRequestError extends ApiError {
|
|
13
|
+
constructor(message) {
|
|
14
|
+
super(http_status_codes_1.StatusCodes.BAD_REQUEST, message);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
exports.BadRequestError = BadRequestError;
|
|
18
|
+
class UnauthorizedError extends ApiError {
|
|
19
|
+
constructor(message) {
|
|
20
|
+
super(http_status_codes_1.StatusCodes.UNAUTHORIZED, message);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
exports.UnauthorizedError = UnauthorizedError;
|
|
24
|
+
class NotFoundError extends ApiError {
|
|
25
|
+
constructor(message) {
|
|
26
|
+
super(http_status_codes_1.StatusCodes.NOT_FOUND, message);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
exports.NotFoundError = NotFoundError;
|
|
30
|
+
class InternalServerError extends ApiError {
|
|
31
|
+
constructor(message) {
|
|
32
|
+
super(http_status_codes_1.StatusCodes.INTERNAL_SERVER_ERROR, message);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
exports.InternalServerError = InternalServerError;
|
|
36
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":";;;AAAA,yDAAgD;AAEhD,MAAa,QAAS,SAAQ,KAAK;IAC/B,YAA4B,UAAkB,EAAE,OAAe;QAC3D,KAAK,CAAC,OAAO,CAAC,CAAC;QADS,eAAU,GAAV,UAAU,CAAQ;IAE9C,CAAC;CACJ;AAJD,4BAIC;AAED,MAAa,eAAgB,SAAQ,QAAQ;IACzC,YAAY,OAAe;QACvB,KAAK,CAAC,+BAAW,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAC5C,CAAC;CACJ;AAJD,0CAIC;AAED,MAAa,iBAAkB,SAAQ,QAAQ;IAC3C,YAAY,OAAe;QACvB,KAAK,CAAC,+BAAW,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IAC7C,CAAC;CACJ;AAJD,8CAIC;AAED,MAAa,aAAc,SAAQ,QAAQ;IACvC,YAAY,OAAe;QACvB,KAAK,CAAC,+BAAW,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IAC1C,CAAC;CACJ;AAJD,sCAIC;AAED,MAAa,mBAAoB,SAAQ,QAAQ;IAC7C,YAAY,OAAe;QACvB,KAAK,CAAC,+BAAW,CAAC,qBAAqB,EAAE,OAAO,CAAC,CAAC;IACtD,CAAC;CACJ;AAJD,kDAIC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { Application, Request, Response, NextFunction } from "express";
|
|
2
|
+
export { startServer } from "./server";
|
|
3
|
+
export { createServerConfig, ServerConfig } from "./config";
|
|
4
|
+
export { createRequestHandlerDecorator } from "./decorators";
|
|
5
|
+
export * from "./errors";
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvE,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC5D,OAAO,EAAE,6BAA6B,EAAE,MAAM,cAAc,CAAC;AAC7D,cAAc,UAAU,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.createRequestHandlerDecorator = exports.createServerConfig = exports.startServer = void 0;
|
|
18
|
+
var server_1 = require("./server");
|
|
19
|
+
Object.defineProperty(exports, "startServer", { enumerable: true, get: function () { return server_1.startServer; } });
|
|
20
|
+
var config_1 = require("./config");
|
|
21
|
+
Object.defineProperty(exports, "createServerConfig", { enumerable: true, get: function () { return config_1.createServerConfig; } });
|
|
22
|
+
var decorators_1 = require("./decorators");
|
|
23
|
+
Object.defineProperty(exports, "createRequestHandlerDecorator", { enumerable: true, get: function () { return decorators_1.createRequestHandlerDecorator; } });
|
|
24
|
+
__exportStar(require("./errors"), exports);
|
|
25
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AACA,mCAAuC;AAA9B,qGAAA,WAAW,OAAA;AACpB,mCAA4D;AAAnD,4GAAA,kBAAkB,OAAA;AAC3B,2CAA6D;AAApD,2HAAA,6BAA6B,OAAA;AACtC,2CAAyB"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import { ServerConfig } from "./config";
|
|
3
|
+
export declare const startServer: <Args extends any[]>(config: ServerConfig, registerRoutes: (...args: Args) => (app: express.Express) => express.Express, ...args: Args) => Promise<void>;
|
|
4
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAG9B,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAKxC,eAAO,MAAM,WAAW,GAAS,IAAI,SAAS,GAAG,EAAE,EAC/C,QAAQ,YAAY,EACpB,gBAAgB,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,KAAK,OAAO,CAAC,OAAO,EAC5E,GAAG,MAAM,IAAI,kBAehB,CAAC"}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
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
|
+
exports.startServer = void 0;
|
|
7
|
+
const express_1 = __importDefault(require("express"));
|
|
8
|
+
const http_1 = __importDefault(require("http"));
|
|
9
|
+
const https_1 = __importDefault(require("https"));
|
|
10
|
+
const utils_1 = require("./utils");
|
|
11
|
+
const ramda_1 = require("ramda");
|
|
12
|
+
const startServer = async (config, registerRoutes, ...args) => {
|
|
13
|
+
const expressApp = (0, express_1.default)();
|
|
14
|
+
const serviceApp = (0, ramda_1.pipe)(utils_1.registerMiddlewares, registerRoutes(...args), utils_1.registerErrorHandling)(expressApp);
|
|
15
|
+
const { port, secured } = config;
|
|
16
|
+
const server = secured ? https_1.default.createServer(serviceApp) : http_1.default.createServer(serviceApp);
|
|
17
|
+
return new Promise((resolve, reject) => {
|
|
18
|
+
server.listen(port, () => {
|
|
19
|
+
console.log(`Server is running on port ${port}`);
|
|
20
|
+
resolve();
|
|
21
|
+
}).on("error", (error) => {
|
|
22
|
+
console.error("Error starting the server:", error);
|
|
23
|
+
reject(error);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
exports.startServer = startServer;
|
|
28
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":";;;;;;AAAA,sDAA8B;AAC9B,gDAAwB;AACxB,kDAA0B;AAE1B,mCAAqE;AACrE,iCAA6B;AAGtB,MAAM,WAAW,GAAG,KAAK,EAC5B,MAAoB,EACpB,cAA4E,EAC5E,GAAG,IAAU,EACf,EAAE;IACA,MAAM,UAAU,GAAG,IAAA,iBAAO,GAAE,CAAC;IAC7B,MAAM,UAAU,GAAG,IAAA,YAAI,EAAC,2BAAmB,EAAE,cAAc,CAAC,GAAG,IAAI,CAAC,EAAE,6BAAqB,CAAC,CAAC,UAAU,CAAC,CAAC;IACzG,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC;IACjC,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,eAAK,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,cAAI,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC;IACxF,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACzC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;YACrB,OAAO,CAAC,GAAG,CAAC,6BAA6B,IAAI,EAAE,CAAC,CAAC;YACjD,OAAO,EAAE,CAAC;QACd,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;YACrB,OAAO,CAAC,KAAK,CAAC,4BAA4B,EAAE,KAAK,CAAC,CAAC;YACnD,MAAM,CAAC,KAAK,CAAC,CAAC;QAClB,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;AACP,CAAC,CAAC;AAlBW,QAAA,WAAW,eAkBtB"}
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAEA,OAAO,OAAO,EAAE,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAG3C,eAAO,MAAM,mBAAmB,GAAI,KAAK,OAAO,oBAK/C,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAI,KAAK,OAAO,oBAGjD,CAAA"}
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
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
|
+
exports.registerErrorHandling = exports.registerMiddlewares = void 0;
|
|
7
|
+
const cors_1 = __importDefault(require("cors"));
|
|
8
|
+
const body_parser_1 = __importDefault(require("body-parser"));
|
|
9
|
+
const express_1 = __importDefault(require("express"));
|
|
10
|
+
const decorators_1 = require("./decorators");
|
|
11
|
+
const registerMiddlewares = (app) => {
|
|
12
|
+
app.use((0, cors_1.default)());
|
|
13
|
+
app.use(express_1.default.json());
|
|
14
|
+
app.use(body_parser_1.default.urlencoded({ extended: true }));
|
|
15
|
+
return app;
|
|
16
|
+
};
|
|
17
|
+
exports.registerMiddlewares = registerMiddlewares;
|
|
18
|
+
const registerErrorHandling = (app) => {
|
|
19
|
+
app.use(decorators_1.RequestErrorHandler);
|
|
20
|
+
return app;
|
|
21
|
+
};
|
|
22
|
+
exports.registerErrorHandling = registerErrorHandling;
|
|
23
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":";;;;;;AAAA,gDAAwB;AACxB,8DAAqC;AACrC,sDAA2C;AAC3C,6CAAmD;AAE5C,MAAM,mBAAmB,GAAG,CAAC,GAAY,EAAE,EAAE;IAChD,GAAG,CAAC,GAAG,CAAC,IAAA,cAAI,GAAE,CAAC,CAAC;IAChB,GAAG,CAAC,GAAG,CAAC,iBAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IACxB,GAAG,CAAC,GAAG,CAAC,qBAAU,CAAC,UAAU,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACnD,OAAO,GAAG,CAAC;AACf,CAAC,CAAC;AALW,QAAA,mBAAmB,uBAK9B;AAEK,MAAM,qBAAqB,GAAG,CAAC,GAAY,EAAE,EAAE;IAClD,GAAG,CAAC,GAAG,CAAC,gCAAmB,CAAC,CAAC;IAC7B,OAAO,GAAG,CAAC;AACf,CAAC,CAAA;AAHY,QAAA,qBAAqB,yBAGjC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ido_kawaz/server-framework",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Server framework library for Kawaz Plus services",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "npm run clean && tsc",
|
|
12
|
+
"build:watch": "tsc --watch",
|
|
13
|
+
"build:advanced": "npm run clean:advanced && npm i && tsc",
|
|
14
|
+
"test": "npm run build && jest --runInBand",
|
|
15
|
+
"test:advanced": "npm run build:advanced && jest --runInBand",
|
|
16
|
+
"clean": "rimraf dist",
|
|
17
|
+
"clean:advanced": "rimraf dist node_modules package-lock.json",
|
|
18
|
+
"package": "npm run test:advanced && npm publish --access public"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"server",
|
|
22
|
+
"express",
|
|
23
|
+
"http"
|
|
24
|
+
],
|
|
25
|
+
"author": "",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@types/cors": "^2.8.19",
|
|
29
|
+
"@types/express": "^5.0.6",
|
|
30
|
+
"@types/ramda": "^0.31.1",
|
|
31
|
+
"body-parser": "^2.2.2",
|
|
32
|
+
"cors": "^2.8.6",
|
|
33
|
+
"express": "^5.2.1",
|
|
34
|
+
"http-status-codes": "^2.3.0",
|
|
35
|
+
"pino": "^10.3.1",
|
|
36
|
+
"ramda": "^0.32.0",
|
|
37
|
+
"zod": "^3.24.2"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/jest": "^30.0.0",
|
|
41
|
+
"@types/node": "^25.2.3",
|
|
42
|
+
"jest": "^30.1.3",
|
|
43
|
+
"rimraf": "^6.0.0",
|
|
44
|
+
"ts-jest": "^29.4.1",
|
|
45
|
+
"typescript": "^5.9.3"
|
|
46
|
+
}
|
|
47
|
+
}
|