express-response-kit 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ahmad Nairat
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,169 @@
1
+ # express-response-kit
2
+
3
+ A lightweight Express.js extension that adds **semantic HTTP response helpers** and enforces a **consistent response structure** across your API.
4
+
5
+ `express-response-kit` augments `Express.Response` with intuitive methods like `res.ok()`, `res.badRequest()`, `res.internalServerError()`, etc., while automatically normalizing response bodies for success and error responses.
6
+
7
+ ---
8
+
9
+ ## ✨ Features
10
+
11
+ - ✅ Semantic response helpers for **all HTTP status codes**
12
+ - ✅ Consistent response structure for **1xx, 2xx, 4xx, 5xx**
13
+ - ✅ No middleware required
14
+ - ✅ No runtime configuration
15
+ - ✅ TypeScript-first (full type augmentation)
16
+ - ✅ Deprecated HTTP statuses are supported (with warnings)
17
+ - ✅ Zero breaking changes to Express behavior
18
+
19
+ ---
20
+
21
+ ## 📦 Installation
22
+
23
+ ```bash
24
+ npm install express-response-kit
25
+ ```
26
+
27
+ or
28
+
29
+ ```bash
30
+ pnpm add express-response-kit
31
+ ```
32
+
33
+ ---
34
+
35
+ ## 🚀 Usage
36
+
37
+ Import the package **once** in your app entry point:
38
+
39
+ ```ts
40
+ import "express-response-kit";
41
+ ```
42
+
43
+ All helpers are now available on `res`.
44
+
45
+ ---
46
+
47
+ ## 🧱 Default Response Structure (v1)
48
+
49
+ For the following HTTP status ranges:
50
+
51
+ - **1xx** – Informational
52
+ - **2xx** – Success
53
+ - **4xx** – Client errors
54
+ - **5xx** – Server errors
55
+
56
+ responses are automatically wrapped using this structure:
57
+
58
+ ```ts
59
+ {
60
+ success: boolean;
61
+ data: unknown;
62
+ }
63
+ ```
64
+
65
+ ---
66
+
67
+ ## ✅ Success Responses (1xx, 2xx)
68
+
69
+ - `success` is always `true`
70
+ - `data` is the value passed to the method
71
+ - If no value is passed, `data` is `null`
72
+
73
+ ### Examples
74
+
75
+ | Code | Result |
76
+ |------|--------|
77
+ | `res.ok("hey")` | `{ "success": true, "data": "hey" }` |
78
+ | `res.ok({ msg: "hey" })` | `{ "success": true, "data": { "msg": "hey" } }` |
79
+ | `res.ok()` | `{ "success": true, "data": null }` |
80
+ | `res.created({ id: 1 })` | `{ "success": true, "data": { "id": 1 } }` |
81
+
82
+ ---
83
+
84
+ ## ❌ Error Responses (4xx, 5xx)
85
+
86
+ - `success` is always `false`
87
+ - `data` is the error payload (if provided)
88
+ - If no payload is provided, `data` is `null`
89
+
90
+ ### Examples
91
+
92
+ | Code | Result |
93
+ |------|--------|
94
+ | `res.badRequest("Invalid input")` | `{ "success": false, "data": "Invalid input" }` |
95
+ | `res.notFound()` | `{ "success": false, "data": null }` |
96
+ | `res.internalServerError({ reason: "DB down" })` | `{ "success": false, "data": { "reason": "DB down" } }` |
97
+
98
+ ---
99
+
100
+ ## 🔁 Redirection Responses (3xx)
101
+
102
+ Redirection responses **do not follow the `{ success, data }` structure**.
103
+
104
+ They rely on standard HTTP semantics and the `Location` header.
105
+
106
+ ### Examples
107
+
108
+ | Code | Behavior |
109
+ |------|----------|
110
+ | `res.movedPermanently("/new-url")` | `301` + `Location: /new-url` |
111
+ | `res.found("/login")` | `302` + redirect |
112
+ | `res.temporaryRedirect("/retry")` | `307` + redirect |
113
+
114
+ ---
115
+
116
+ ## ⚠️ Deprecated Status Codes
117
+
118
+ Some HTTP status codes are officially deprecated but are still supported to **respect developer intent**.
119
+
120
+ Examples:
121
+ - `305 Use Proxy`
122
+ - `306 Switch Proxy`
123
+
124
+ These methods:
125
+ - Are clearly marked as `@deprecated`
126
+ - Still work correctly
127
+ - Are not blocked or altered
128
+
129
+ ```ts
130
+ res.useProxy("http://proxy.example.com");
131
+ ```
132
+
133
+ ---
134
+
135
+ ## 🧠 Design Philosophy
136
+
137
+ - Keep controllers clean and expressive
138
+ - Avoid repeating response boilerplate
139
+ - Enforce a predictable API contract
140
+ - Respect HTTP standards without being opinionated
141
+ - Extend Express without changing its core behavior
142
+
143
+ ---
144
+
145
+ ## 🧪 Testing
146
+
147
+ - Uses **Jest + Supertest**
148
+ - Runs against an in-memory Express app
149
+ - No real network calls
150
+ - All response helpers are covered
151
+
152
+ ```bash
153
+ npm test
154
+ ```
155
+
156
+ ---
157
+
158
+ ## 🔮 Roadmap
159
+
160
+ ### v2 (planned)
161
+
162
+ - Configurable response structure
163
+ - paginated response helpers (e.g. res.paginated(data))
164
+
165
+ ---
166
+
167
+ ## 📄 License
168
+
169
+ MIT © 2026
@@ -0,0 +1,3 @@
1
+ import "./index.d";
2
+ import { Response } from "express";
3
+ export declare const patch1xx: (res: Response) => void;
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.patch1xx = void 0;
4
+ const enum_1 = require("../enum");
5
+ const utils_1 = require("../utils");
6
+ require("./index.d");
7
+ const patch1xx = (res) => {
8
+ const getResBody = (data) => ({
9
+ success: true,
10
+ data: (0, utils_1.normalize)(data),
11
+ });
12
+ res.continue = (data) => {
13
+ return res.status(enum_1.HttpInfoStatus.CONTINUE).json(getResBody(data));
14
+ };
15
+ res.switchingProtocols = (data) => {
16
+ return res
17
+ .status(enum_1.HttpInfoStatus.SWITCHING_PROTOCOLS)
18
+ .json(getResBody(data));
19
+ };
20
+ res.processing = (data) => {
21
+ return res.status(enum_1.HttpInfoStatus.PROCESSING).json(getResBody(data));
22
+ };
23
+ res.earlyHints = (data) => {
24
+ return res.status(enum_1.HttpInfoStatus.EARLY_HINTS).json(getResBody(data));
25
+ };
26
+ };
27
+ exports.patch1xx = patch1xx;
@@ -0,0 +1,3 @@
1
+ import "./index.d";
2
+ import { Response } from "express";
3
+ export declare function patch2xx(res: Response): void;
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.patch2xx = patch2xx;
4
+ const enum_1 = require("../enum");
5
+ const utils_1 = require("../utils");
6
+ require("./index.d");
7
+ function patch2xx(res) {
8
+ const getResBody = (data) => ({
9
+ success: true,
10
+ data: (0, utils_1.normalize)(data),
11
+ });
12
+ res.ok = function (data) {
13
+ return this.status(enum_1.HttpSuccessStatus.OK).json(getResBody(data));
14
+ };
15
+ res.created = function (data) {
16
+ return this.status(enum_1.HttpSuccessStatus.CREATED).json(getResBody(data));
17
+ };
18
+ res.accepted = function (data) {
19
+ return this.status(enum_1.HttpSuccessStatus.ACCEPTED).json(getResBody(data));
20
+ };
21
+ res.nonAuthoritativeInformation = function (data) {
22
+ return this.status(enum_1.HttpSuccessStatus.NON_AUTHORITATIVE_INFORMATION).json(getResBody(data));
23
+ };
24
+ res.noContent = function () {
25
+ return this.status(enum_1.HttpSuccessStatus.NO_CONTENT).send();
26
+ };
27
+ res.resetContent = function () {
28
+ return this.status(enum_1.HttpSuccessStatus.RESET_CONTENT).send();
29
+ };
30
+ res.partialContent = function (data) {
31
+ return this.status(enum_1.HttpSuccessStatus.PARTIAL_CONTENT).json(getResBody(data));
32
+ };
33
+ res.multiStatus = function (data) {
34
+ return this.status(enum_1.HttpSuccessStatus.MULTI_STATUS).json(getResBody(data));
35
+ };
36
+ res.alreadyReported = function (data) {
37
+ return this.status(enum_1.HttpSuccessStatus.ALREADY_REPORTED).json(getResBody(data));
38
+ };
39
+ res.imUsed = function (data) {
40
+ return this.status(enum_1.HttpSuccessStatus.IM_USED).json(getResBody(data));
41
+ };
42
+ }
@@ -0,0 +1,3 @@
1
+ import { Response } from "express";
2
+ import "./index.d";
3
+ export declare const patch3xx: (res: Response) => void;
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.patch3xx = void 0;
4
+ require("./index.d");
5
+ const enum_1 = require("../enum");
6
+ const patch3xx = (res) => {
7
+ res.multipleChoices = function (url) {
8
+ return this.redirect(enum_1.HttpRedirectionStatus.MULTIPLE_CHOICES, url);
9
+ };
10
+ res.movedPermanently = function (url) {
11
+ return this.redirect(enum_1.HttpRedirectionStatus.MOVED_PERMANENTLY, url);
12
+ };
13
+ res.found = function (url) {
14
+ return this.redirect(enum_1.HttpRedirectionStatus.FOUND, url);
15
+ };
16
+ res.seeOther = function (url) {
17
+ return this.redirect(enum_1.HttpRedirectionStatus.SEE_OTHER, url);
18
+ };
19
+ res.notModified = function () {
20
+ return this.sendStatus(enum_1.HttpRedirectionStatus.NOT_MODIFIED);
21
+ };
22
+ res.useProxy = function (url, _data) {
23
+ if (process.env.NODE_ENV !== "production" && console?.warn) {
24
+ console.warn("Warning: HTTP 305 Use Proxy is deprecated and should be avoided.");
25
+ }
26
+ return this.redirect(enum_1.HttpRedirectionStatus.USE_PROXY, url);
27
+ };
28
+ res.switchProxy = function (url, _data) {
29
+ if (process.env.NODE_ENV !== "production" && console?.warn) {
30
+ console.warn("Warning: HTTP 306 Switch Proxy is deprecated and should be avoided.");
31
+ }
32
+ return this.redirect(enum_1.HttpRedirectionStatus.SWITCH_PROXY, url);
33
+ };
34
+ res.temporaryRedirect = function (url) {
35
+ return this.redirect(enum_1.HttpRedirectionStatus.TEMPORARY_REDIRECT, url);
36
+ };
37
+ res.permanentRedirect = function (url) {
38
+ return this.redirect(enum_1.HttpRedirectionStatus.PERMANENT_REDIRECT, url);
39
+ };
40
+ };
41
+ exports.patch3xx = patch3xx;
@@ -0,0 +1,3 @@
1
+ import "./index.d";
2
+ import { Response } from "express";
3
+ export declare const patch4xx: (res: Response) => void;
@@ -0,0 +1,100 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.patch4xx = void 0;
4
+ require("./index.d");
5
+ const enum_1 = require("../enum");
6
+ const utils_1 = require("../utils");
7
+ const patch4xx = (res) => {
8
+ const getResBody = (data) => ({
9
+ success: false,
10
+ data: (0, utils_1.normalize)(data),
11
+ });
12
+ res.badRequest = function (data) {
13
+ return this.status(enum_1.HttpClientErrorStatus.BAD_REQUEST).json(getResBody(data));
14
+ };
15
+ res.unauthorized = function (data) {
16
+ return this.status(enum_1.HttpClientErrorStatus.UNAUTHORIZED).json(getResBody(data));
17
+ };
18
+ res.paymentRequired = function (data) {
19
+ return this.status(enum_1.HttpClientErrorStatus.PAYMENT_REQUIRED).json(getResBody(data));
20
+ };
21
+ res.forbidden = function (data) {
22
+ return this.status(enum_1.HttpClientErrorStatus.FORBIDDEN).json(getResBody(data));
23
+ };
24
+ res.notFound = function (data) {
25
+ return this.status(enum_1.HttpClientErrorStatus.NOT_FOUND).json(getResBody(data));
26
+ };
27
+ res.methodNotAllowed = function (data) {
28
+ return this.status(enum_1.HttpClientErrorStatus.METHOD_NOT_ALLOWED).json(getResBody(data));
29
+ };
30
+ res.notAcceptable = function (data) {
31
+ return this.status(enum_1.HttpClientErrorStatus.NOT_ACCEPTABLE).json(getResBody(data));
32
+ };
33
+ res.proxyAuthenticationRequired = function (data) {
34
+ return this.status(enum_1.HttpClientErrorStatus.PROXY_AUTHENTICATION_REQUIRED).json(getResBody(data));
35
+ };
36
+ res.requestTimeout = function (data) {
37
+ return this.status(enum_1.HttpClientErrorStatus.REQUEST_TIMEOUT).json(getResBody(data));
38
+ };
39
+ res.conflict = function (data) {
40
+ return this.status(enum_1.HttpClientErrorStatus.CONFLICT).json(getResBody(data));
41
+ };
42
+ res.gone = function (data) {
43
+ return this.status(enum_1.HttpClientErrorStatus.GONE).json(getResBody(data));
44
+ };
45
+ res.lengthRequired = function (data) {
46
+ return this.status(enum_1.HttpClientErrorStatus.LENGTH_REQUIRED).json(getResBody(data));
47
+ };
48
+ res.preconditionFailed = function (data) {
49
+ return this.status(enum_1.HttpClientErrorStatus.PRECONDITION_FAILED).json(getResBody(data));
50
+ };
51
+ res.contentTooLarge = function (data) {
52
+ return this.status(enum_1.HttpClientErrorStatus.CONTENT_TOO_LARGE).json(getResBody(data));
53
+ };
54
+ res.uriTooLong = function (data) {
55
+ return this.status(enum_1.HttpClientErrorStatus.URI_TOO_LONG).json(getResBody(data));
56
+ };
57
+ res.unsupportedMediaType = function (data) {
58
+ return this.status(enum_1.HttpClientErrorStatus.UNSUPPORTED_MEDIA_TYPE).json(getResBody(data));
59
+ };
60
+ res.rangeNotSatisfiable = function (data) {
61
+ return this.status(enum_1.HttpClientErrorStatus.RANGE_NOT_SATISFIABLE).json(getResBody(data));
62
+ };
63
+ res.expectationFailed = function (data) {
64
+ return this.status(enum_1.HttpClientErrorStatus.EXPECTATION_FAILED).json(getResBody(data));
65
+ };
66
+ res.imATeapot = function (data) {
67
+ return this.status(enum_1.HttpClientErrorStatus.IM_A_TEAPOT).json(getResBody(data));
68
+ };
69
+ res.misdirectedRequest = function (data) {
70
+ return this.status(enum_1.HttpClientErrorStatus.MISDIRECTED_REQUEST).json(getResBody(data));
71
+ };
72
+ res.unprocessableEntity = function (data) {
73
+ return this.status(enum_1.HttpClientErrorStatus.UNPROCESSABLE_ENTITY).json(getResBody(data));
74
+ };
75
+ res.locked = function (data) {
76
+ return this.status(enum_1.HttpClientErrorStatus.LOCKED).json(getResBody(data));
77
+ };
78
+ res.failedDependency = function (data) {
79
+ return this.status(enum_1.HttpClientErrorStatus.FAILED_DEPENDENCY).json(getResBody(data));
80
+ };
81
+ res.tooEarly = function (data) {
82
+ return this.status(enum_1.HttpClientErrorStatus.TOO_EARLY).json(getResBody(data));
83
+ };
84
+ res.upgradeRequired = function (data) {
85
+ return this.status(enum_1.HttpClientErrorStatus.UPGRADE_REQUIRED).json(getResBody(data));
86
+ };
87
+ res.preconditionRequired = function (data) {
88
+ return this.status(enum_1.HttpClientErrorStatus.PRECONDITION_REQUIRED).json(getResBody(data));
89
+ };
90
+ res.tooManyRequests = function (data) {
91
+ return this.status(enum_1.HttpClientErrorStatus.TOO_MANY_REQUESTS).json(getResBody(data));
92
+ };
93
+ res.requestHeaderFieldsTooLarge = function (data) {
94
+ return this.status(enum_1.HttpClientErrorStatus.REQUEST_HEADER_FIELDS_TOO_LARGE).json(getResBody(data));
95
+ };
96
+ res.unavailableForLegalReasons = function (data) {
97
+ return this.status(enum_1.HttpClientErrorStatus.UNAVAILABLE_FOR_LEGAL_REASONS).json(getResBody(data));
98
+ };
99
+ };
100
+ exports.patch4xx = patch4xx;
@@ -0,0 +1,3 @@
1
+ import "./index.d";
2
+ import { Response } from "express";
3
+ export declare const patch5xx: (res: Response) => void;
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.patch5xx = void 0;
4
+ require("./index.d");
5
+ const enum_1 = require("../enum");
6
+ const utils_1 = require("../utils");
7
+ const patch5xx = (res) => {
8
+ const getResBody = (data) => ({
9
+ success: false,
10
+ data: (0, utils_1.normalize)(data),
11
+ });
12
+ res.internalServerError = function (data) {
13
+ return this.status(enum_1.HttpServerErrorStatus.INTERNAL_SERVER_ERROR).json(getResBody(data));
14
+ };
15
+ res.notImplemented = function (data) {
16
+ return this.status(enum_1.HttpServerErrorStatus.NOT_IMPLEMENTED).json(getResBody(data));
17
+ };
18
+ res.badGateway = function (data) {
19
+ return this.status(enum_1.HttpServerErrorStatus.BAD_GATEWAY).json(getResBody(data));
20
+ };
21
+ res.serviceUnavailable = function (data) {
22
+ return this.status(enum_1.HttpServerErrorStatus.SERVICE_UNAVAILABLE).json(getResBody(data));
23
+ };
24
+ res.gatewayTimeout = function (data) {
25
+ return this.status(enum_1.HttpServerErrorStatus.GATEWAY_TIMEOUT).json(getResBody(data));
26
+ };
27
+ res.httpVersionNotSupported = function (data) {
28
+ return this.status(enum_1.HttpServerErrorStatus.HTTP_VERSION_NOT_SUPPORTED).json(getResBody(data));
29
+ };
30
+ res.variantAlsoNegotiates = function (data) {
31
+ return this.status(enum_1.HttpServerErrorStatus.VARIANT_ALSO_NEGOTIATES).json(getResBody(data));
32
+ };
33
+ res.insufficientStorage = function (data) {
34
+ return this.status(enum_1.HttpServerErrorStatus.INSUFFICIENT_STORAGE).json(getResBody(data));
35
+ };
36
+ res.loopDetected = function (data) {
37
+ return this.status(enum_1.HttpServerErrorStatus.LOOP_DETECTED).json(getResBody(data));
38
+ };
39
+ res.notExtended = function (data) {
40
+ return this.status(enum_1.HttpServerErrorStatus.NOT_EXTENDED).json(getResBody(data));
41
+ };
42
+ res.networkAuthenticationRequired = function (data) {
43
+ return this.status(enum_1.HttpServerErrorStatus.NETWORK_AUTHENTICATION_REQUIRED).json(getResBody(data));
44
+ };
45
+ };
46
+ exports.patch5xx = patch5xx;
@@ -0,0 +1 @@
1
+ import "..";
@@ -0,0 +1,537 @@
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 express_1 = __importDefault(require("express"));
7
+ const supertest_1 = __importDefault(require("supertest"));
8
+ require(".."); // Auto-patches Express.Response
9
+ describe("Express Response Helper Methods", () => {
10
+ let app;
11
+ beforeEach(() => {
12
+ app = (0, express_1.default)();
13
+ });
14
+ /**
15
+ * Automatically discovers all custom response helper methods
16
+ * by filtering out native Express methods
17
+ */
18
+ function getCustomHelperMethods() {
19
+ const nativeExpressMethods = new Set([
20
+ // Express methods
21
+ "status",
22
+ "send",
23
+ "json",
24
+ "jsonp",
25
+ "sendStatus",
26
+ "sendFile",
27
+ "sendfile",
28
+ "download",
29
+ "contentType",
30
+ "type",
31
+ "format",
32
+ "attachment",
33
+ "append",
34
+ "header",
35
+ "set",
36
+ "get",
37
+ "clearCookie",
38
+ "cookie",
39
+ "location",
40
+ "redirect",
41
+ "vary",
42
+ "render",
43
+ "links",
44
+ "end",
45
+ "write",
46
+ "writeContinue",
47
+ "writeHead",
48
+ "_patched",
49
+ // Node.js HTTP/Stream methods
50
+ "addListener",
51
+ "addTrailers",
52
+ "appendHeader",
53
+ "assignSocket",
54
+ "cork",
55
+ "destroy",
56
+ "detachSocket",
57
+ "emit",
58
+ "eventNames",
59
+ "flushHeaders",
60
+ "getHeader",
61
+ "getHeaderNames",
62
+ "getHeaders",
63
+ "getMaxListeners",
64
+ "getRawHeaderNames",
65
+ "hasHeader",
66
+ "listenerCount",
67
+ "listeners",
68
+ "off",
69
+ "on",
70
+ "once",
71
+ "pipe",
72
+ "prependListener",
73
+ "prependOnceListener",
74
+ "rawListeners",
75
+ "removeAllListeners",
76
+ "removeHeader",
77
+ "removeListener",
78
+ "setHeader",
79
+ "setHeaders",
80
+ "setMaxListeners",
81
+ "setTimeout",
82
+ "uncork",
83
+ "writeEarlyHints",
84
+ "writeHeader",
85
+ "writeProcessing",
86
+ ]);
87
+ const res = express_1.default.response;
88
+ const allMethods = [];
89
+ // Collect all methods directly from the response object
90
+ for (const prop in res) {
91
+ if (typeof res[prop] === "function" &&
92
+ !nativeExpressMethods.has(prop) &&
93
+ !prop.startsWith("_") &&
94
+ !allMethods.includes(prop)) {
95
+ allMethods.push(prop);
96
+ }
97
+ }
98
+ // Also check own properties
99
+ Object.getOwnPropertyNames(res).forEach((prop) => {
100
+ if (typeof res[prop] === "function" &&
101
+ !nativeExpressMethods.has(prop) &&
102
+ !prop.startsWith("_") &&
103
+ !allMethods.includes(prop)) {
104
+ allMethods.push(prop);
105
+ }
106
+ });
107
+ return allMethods.sort();
108
+ }
109
+ /**
110
+ * Method metadata: maps each custom method to its expected behavior
111
+ */
112
+ const methodMetadata = {
113
+ // 1xx Informational
114
+ continue: { status: 100, type: "json", success: true, testData: { info: "continue" } },
115
+ switchingProtocols: { status: 101, type: "json", success: true, testData: { protocol: "ws" } },
116
+ processing: { status: 102, type: "json", success: true, testData: { task: "processing" } },
117
+ earlyHints: { status: 103, type: "json", success: true, testData: { hint: "preload" } },
118
+ // 2xx Success
119
+ ok: { status: 200, type: "json", success: true, testData: { message: "OK" } },
120
+ created: { status: 201, type: "json", success: true, testData: { id: 1 } },
121
+ accepted: { status: 202, type: "json", success: true, testData: { taskId: 123 } },
122
+ nonAuthoritativeInformation: {
123
+ status: 203,
124
+ type: "json",
125
+ success: true,
126
+ testData: { info: "cached" },
127
+ },
128
+ noContent: { status: 204, type: "empty" },
129
+ resetContent: { status: 205, type: "empty" },
130
+ partialContent: { status: 206, type: "json", success: true, testData: { chunk: 1 } },
131
+ multiStatus: { status: 207, type: "json", success: true, testData: [{ status: 200 }] },
132
+ alreadyReported: { status: 208, type: "json", success: true, testData: { reported: true } },
133
+ imUsed: { status: 226, type: "json", success: true, testData: { delta: "applied" } },
134
+ // 3xx Redirection
135
+ multipleChoices: { status: 300, type: "redirect", redirectUrl: "/choices" },
136
+ movedPermanently: { status: 301, type: "redirect", redirectUrl: "/new-location" },
137
+ found: { status: 302, type: "redirect", redirectUrl: "/found" },
138
+ seeOther: { status: 303, type: "redirect", redirectUrl: "/other" },
139
+ notModified: { status: 304, type: "empty" },
140
+ useProxy: { status: 305, type: "redirect", redirectUrl: "/proxy" },
141
+ switchProxy: { status: 306, type: "redirect", redirectUrl: "/switch" },
142
+ temporaryRedirect: { status: 307, type: "redirect", redirectUrl: "/temp" },
143
+ permanentRedirect: { status: 308, type: "redirect", redirectUrl: "/permanent" },
144
+ // 4xx Client Errors
145
+ badRequest: { status: 400, type: "json", success: false, testData: { error: "Bad request" } },
146
+ unauthorized: {
147
+ status: 401,
148
+ type: "json",
149
+ success: false,
150
+ testData: { error: "Unauthorized" },
151
+ },
152
+ paymentRequired: {
153
+ status: 402,
154
+ type: "json",
155
+ success: false,
156
+ testData: { error: "Payment required" },
157
+ },
158
+ forbidden: { status: 403, type: "json", success: false, testData: { error: "Forbidden" } },
159
+ notFound: { status: 404, type: "json", success: false, testData: { error: "Not found" } },
160
+ methodNotAllowed: {
161
+ status: 405,
162
+ type: "json",
163
+ success: false,
164
+ testData: { error: "Method not allowed" },
165
+ },
166
+ notAcceptable: {
167
+ status: 406,
168
+ type: "json",
169
+ success: false,
170
+ testData: { error: "Not acceptable" },
171
+ },
172
+ proxyAuthenticationRequired: {
173
+ status: 407,
174
+ type: "json",
175
+ success: false,
176
+ testData: { error: "Proxy auth required" },
177
+ },
178
+ requestTimeout: {
179
+ status: 408,
180
+ type: "json",
181
+ success: false,
182
+ testData: { error: "Request timeout" },
183
+ },
184
+ conflict: { status: 409, type: "json", success: false, testData: { error: "Conflict" } },
185
+ gone: { status: 410, type: "json", success: false, testData: { error: "Gone" } },
186
+ lengthRequired: {
187
+ status: 411,
188
+ type: "json",
189
+ success: false,
190
+ testData: { error: "Length required" },
191
+ },
192
+ preconditionFailed: {
193
+ status: 412,
194
+ type: "json",
195
+ success: false,
196
+ testData: { error: "Precondition failed" },
197
+ },
198
+ contentTooLarge: {
199
+ status: 413,
200
+ type: "json",
201
+ success: false,
202
+ testData: { error: "Content too large" },
203
+ },
204
+ uriTooLong: {
205
+ status: 414,
206
+ type: "json",
207
+ success: false,
208
+ testData: { error: "URI too long" },
209
+ },
210
+ unsupportedMediaType: {
211
+ status: 415,
212
+ type: "json",
213
+ success: false,
214
+ testData: { error: "Unsupported media type" },
215
+ },
216
+ rangeNotSatisfiable: {
217
+ status: 416,
218
+ type: "json",
219
+ success: false,
220
+ testData: { error: "Range not satisfiable" },
221
+ },
222
+ expectationFailed: {
223
+ status: 417,
224
+ type: "json",
225
+ success: false,
226
+ testData: { error: "Expectation failed" },
227
+ },
228
+ imATeapot: { status: 418, type: "json", success: false, testData: { error: "I'm a teapot" } },
229
+ misdirectedRequest: {
230
+ status: 421,
231
+ type: "json",
232
+ success: false,
233
+ testData: { error: "Misdirected request" },
234
+ },
235
+ unprocessableEntity: {
236
+ status: 422,
237
+ type: "json",
238
+ success: false,
239
+ testData: { error: "Unprocessable entity" },
240
+ },
241
+ locked: { status: 423, type: "json", success: false, testData: { error: "Locked" } },
242
+ failedDependency: {
243
+ status: 424,
244
+ type: "json",
245
+ success: false,
246
+ testData: { error: "Failed dependency" },
247
+ },
248
+ tooEarly: { status: 425, type: "json", success: false, testData: { error: "Too early" } },
249
+ upgradeRequired: {
250
+ status: 426,
251
+ type: "json",
252
+ success: false,
253
+ testData: { error: "Upgrade required" },
254
+ },
255
+ preconditionRequired: {
256
+ status: 428,
257
+ type: "json",
258
+ success: false,
259
+ testData: { error: "Precondition required" },
260
+ },
261
+ tooManyRequests: {
262
+ status: 429,
263
+ type: "json",
264
+ success: false,
265
+ testData: { error: "Too many requests" },
266
+ },
267
+ requestHeaderFieldsTooLarge: {
268
+ status: 431,
269
+ type: "json",
270
+ success: false,
271
+ testData: { error: "Headers too large" },
272
+ },
273
+ unavailableForLegalReasons: {
274
+ status: 451,
275
+ type: "json",
276
+ success: false,
277
+ testData: { error: "Legal reasons" },
278
+ },
279
+ // 5xx Server Errors
280
+ internalServerError: {
281
+ status: 500,
282
+ type: "json",
283
+ success: false,
284
+ testData: { error: "Internal server error" },
285
+ },
286
+ notImplemented: {
287
+ status: 501,
288
+ type: "json",
289
+ success: false,
290
+ testData: { error: "Not implemented" },
291
+ },
292
+ badGateway: {
293
+ status: 502,
294
+ type: "json",
295
+ success: false,
296
+ testData: { error: "Bad gateway" },
297
+ },
298
+ serviceUnavailable: {
299
+ status: 503,
300
+ type: "json",
301
+ success: false,
302
+ testData: { error: "Service unavailable" },
303
+ },
304
+ gatewayTimeout: {
305
+ status: 504,
306
+ type: "json",
307
+ success: false,
308
+ testData: { error: "Gateway timeout" },
309
+ },
310
+ httpVersionNotSupported: {
311
+ status: 505,
312
+ type: "json",
313
+ success: false,
314
+ testData: { error: "HTTP version not supported" },
315
+ },
316
+ variantAlsoNegotiates: {
317
+ status: 506,
318
+ type: "json",
319
+ success: false,
320
+ testData: { error: "Variant also negotiates" },
321
+ },
322
+ insufficientStorage: {
323
+ status: 507,
324
+ type: "json",
325
+ success: false,
326
+ testData: { error: "Insufficient storage" },
327
+ },
328
+ loopDetected: {
329
+ status: 508,
330
+ type: "json",
331
+ success: false,
332
+ testData: { error: "Loop detected" },
333
+ },
334
+ notExtended: {
335
+ status: 510,
336
+ type: "json",
337
+ success: false,
338
+ testData: { error: "Not extended" },
339
+ },
340
+ networkAuthenticationRequired: {
341
+ status: 511,
342
+ type: "json",
343
+ success: false,
344
+ testData: { error: "Network auth required" },
345
+ },
346
+ };
347
+ describe("Method Discovery", () => {
348
+ it("should discover all custom helper methods", () => {
349
+ const customMethods = getCustomHelperMethods();
350
+ expect(customMethods.length).toBeGreaterThan(0);
351
+ console.log(`Discovered ${customMethods.length} custom helper methods:`, customMethods);
352
+ });
353
+ it("should have metadata for all discovered methods", () => {
354
+ const customMethods = getCustomHelperMethods();
355
+ const unmappedMethods = customMethods.filter((method) => !methodMetadata[method]);
356
+ if (unmappedMethods.length > 0) {
357
+ console.warn("Methods without metadata:", unmappedMethods);
358
+ }
359
+ expect(unmappedMethods).toEqual([]);
360
+ });
361
+ });
362
+ describe("Helper Method Tests", () => {
363
+ const customMethods = Object.keys(methodMetadata).sort();
364
+ customMethods.forEach((methodName) => {
365
+ const metadata = methodMetadata[methodName];
366
+ if (metadata.type === "json") {
367
+ it(`${methodName}() should return ${metadata.status} with JSON body`, async () => {
368
+ app.get("/test", (req, res) => {
369
+ res[methodName](metadata.testData);
370
+ });
371
+ const response = await (0, supertest_1.default)(app).get("/test");
372
+ // Note: 1xx status codes (100-103) are informational and may not work properly with HTTP responses
373
+ // Express/Node.js may convert them to 500 errors. We test that the method exists and doesn't crash.
374
+ if (metadata.status >= 100 && metadata.status < 200) {
375
+ expect(response.status).toBeGreaterThanOrEqual(100);
376
+ // Just verify the method executed without crashing
377
+ return;
378
+ }
379
+ expect(response.status).toBe(metadata.status);
380
+ expect(response.body).toHaveProperty("success", metadata.success);
381
+ expect(response.body).toHaveProperty("data");
382
+ // Verify data normalization
383
+ const expectedData = typeof metadata.testData === "string" ||
384
+ typeof metadata.testData === "number" ||
385
+ typeof metadata.testData === "boolean"
386
+ ? { value: metadata.testData }
387
+ : metadata.testData;
388
+ expect(response.body.data).toEqual(expectedData);
389
+ });
390
+ }
391
+ else if (metadata.type === "redirect") {
392
+ it(`${methodName}() should redirect to ${metadata.redirectUrl} with ${metadata.status}`, async () => {
393
+ app.get("/test", (req, res) => {
394
+ res[methodName](metadata.redirectUrl);
395
+ });
396
+ const response = await (0, supertest_1.default)(app).get("/test").redirects(0);
397
+ expect(response.status).toBe(metadata.status);
398
+ expect(response.headers.location).toBe(metadata.redirectUrl);
399
+ });
400
+ }
401
+ else if (metadata.type === "empty") {
402
+ it(`${methodName}() should return ${metadata.status} with no body`, async () => {
403
+ app.get("/test", (req, res) => {
404
+ res[methodName]();
405
+ });
406
+ const response = await (0, supertest_1.default)(app).get("/test");
407
+ expect(response.status).toBe(metadata.status);
408
+ expect(response.text).toBe("");
409
+ });
410
+ }
411
+ });
412
+ });
413
+ describe("Data Normalization", () => {
414
+ it("should normalize undefined to null in data field", async () => {
415
+ app.get("/test", (req, res) => {
416
+ res.ok(undefined);
417
+ });
418
+ const response = await (0, supertest_1.default)(app).get("/test");
419
+ expect(response.body.data).toBeNull();
420
+ });
421
+ it("should wrap primitive strings in { value: string }", async () => {
422
+ app.get("/test", (req, res) => {
423
+ res.ok("Hello");
424
+ });
425
+ const response = await (0, supertest_1.default)(app).get("/test");
426
+ expect(response.body.data).toEqual({ value: "Hello" });
427
+ });
428
+ it("should wrap primitive numbers in { value: number }", async () => {
429
+ app.get("/test", (req, res) => {
430
+ res.ok(42);
431
+ });
432
+ const response = await (0, supertest_1.default)(app).get("/test");
433
+ expect(response.body.data).toEqual({ value: 42 });
434
+ });
435
+ it("should wrap primitive booleans in { value: boolean }", async () => {
436
+ app.get("/test", (req, res) => {
437
+ res.ok(true);
438
+ });
439
+ const response = await (0, supertest_1.default)(app).get("/test");
440
+ expect(response.body.data).toEqual({ value: true });
441
+ });
442
+ it("should not wrap objects", async () => {
443
+ app.get("/test", (req, res) => {
444
+ res.ok({ custom: "data" });
445
+ });
446
+ const response = await (0, supertest_1.default)(app).get("/test");
447
+ expect(response.body.data).toEqual({ custom: "data" });
448
+ });
449
+ it("should not wrap arrays", async () => {
450
+ app.get("/test", (req, res) => {
451
+ res.ok([1, 2, 3]);
452
+ });
453
+ const response = await (0, supertest_1.default)(app).get("/test");
454
+ expect(response.body.data).toEqual([1, 2, 3]);
455
+ });
456
+ });
457
+ describe("Success Flag", () => {
458
+ it("should set success: true for 1xx responses (if supported)", async () => {
459
+ app.get("/test", (req, res) => {
460
+ res.continue({ info: "test" });
461
+ });
462
+ const response = await (0, supertest_1.default)(app).get("/test");
463
+ // 1xx responses may not work properly in HTTP, so we skip strict validation
464
+ expect([100, 500]).toContain(response.status);
465
+ });
466
+ it("should set success: true for 2xx responses", async () => {
467
+ app.get("/test", (req, res) => {
468
+ res.ok({ data: "test" });
469
+ });
470
+ const response = await (0, supertest_1.default)(app).get("/test");
471
+ expect(response.body.success).toBe(true);
472
+ });
473
+ it("should set success: false for 4xx responses", async () => {
474
+ app.get("/test", (req, res) => {
475
+ res.badRequest({ error: "test" });
476
+ });
477
+ const response = await (0, supertest_1.default)(app).get("/test");
478
+ expect(response.body.success).toBe(false);
479
+ });
480
+ it("should set success: false for 5xx responses", async () => {
481
+ app.get("/test", (req, res) => {
482
+ res.internalServerError({ error: "test" });
483
+ });
484
+ const response = await (0, supertest_1.default)(app).get("/test");
485
+ expect(response.body.success).toBe(false);
486
+ });
487
+ });
488
+ describe("Deprecated Methods", () => {
489
+ it("useProxy() should work despite deprecation", async () => {
490
+ const originalWarn = console.warn;
491
+ const warnMock = jest.fn();
492
+ console.warn = warnMock;
493
+ app.get("/test", (req, res) => {
494
+ res.useProxy("/proxy-url");
495
+ });
496
+ const response = await (0, supertest_1.default)(app).get("/test").redirects(0);
497
+ expect(response.status).toBe(305);
498
+ expect(response.headers.location).toBe("/proxy-url");
499
+ console.warn = originalWarn;
500
+ });
501
+ it("switchProxy() should work despite deprecation", async () => {
502
+ const originalWarn = console.warn;
503
+ const warnMock = jest.fn();
504
+ console.warn = warnMock;
505
+ app.get("/test", (req, res) => {
506
+ res.switchProxy("/switch-url");
507
+ });
508
+ const response = await (0, supertest_1.default)(app).get("/test").redirects(0);
509
+ expect(response.status).toBe(306);
510
+ expect(response.headers.location).toBe("/switch-url");
511
+ console.warn = originalWarn;
512
+ });
513
+ });
514
+ describe("Edge Cases", () => {
515
+ it("should handle null data", async () => {
516
+ app.get("/test", (req, res) => {
517
+ res.ok(null);
518
+ });
519
+ const response = await (0, supertest_1.default)(app).get("/test");
520
+ expect(response.body.data).toBeNull();
521
+ });
522
+ it("should handle empty object", async () => {
523
+ app.get("/test", (req, res) => {
524
+ res.ok({});
525
+ });
526
+ const response = await (0, supertest_1.default)(app).get("/test");
527
+ expect(response.body.data).toEqual({});
528
+ });
529
+ it("should handle nested objects", async () => {
530
+ app.get("/test", (req, res) => {
531
+ res.ok({ level1: { level2: { level3: "deep" } } });
532
+ });
533
+ const response = await (0, supertest_1.default)(app).get("/test");
534
+ expect(response.body.data).toEqual({ level1: { level2: { level3: "deep" } } });
535
+ });
536
+ });
537
+ });
package/dist/enum.d.ts ADDED
@@ -0,0 +1,73 @@
1
+ export declare enum HttpInfoStatus {
2
+ CONTINUE = 100,
3
+ SWITCHING_PROTOCOLS = 101,
4
+ PROCESSING = 102,
5
+ EARLY_HINTS = 103
6
+ }
7
+ export declare enum HttpSuccessStatus {
8
+ OK = 200,
9
+ CREATED = 201,
10
+ ACCEPTED = 202,
11
+ NON_AUTHORITATIVE_INFORMATION = 203,
12
+ NO_CONTENT = 204,
13
+ RESET_CONTENT = 205,
14
+ PARTIAL_CONTENT = 206,
15
+ MULTI_STATUS = 207,
16
+ ALREADY_REPORTED = 208,
17
+ IM_USED = 226
18
+ }
19
+ export declare enum HttpRedirectionStatus {
20
+ MULTIPLE_CHOICES = 300,
21
+ MOVED_PERMANENTLY = 301,
22
+ FOUND = 302,
23
+ SEE_OTHER = 303,
24
+ NOT_MODIFIED = 304,
25
+ USE_PROXY = 305,
26
+ SWITCH_PROXY = 306,
27
+ TEMPORARY_REDIRECT = 307,
28
+ PERMANENT_REDIRECT = 308
29
+ }
30
+ export declare enum HttpClientErrorStatus {
31
+ BAD_REQUEST = 400,
32
+ UNAUTHORIZED = 401,
33
+ PAYMENT_REQUIRED = 402,
34
+ FORBIDDEN = 403,
35
+ NOT_FOUND = 404,
36
+ METHOD_NOT_ALLOWED = 405,
37
+ NOT_ACCEPTABLE = 406,
38
+ PROXY_AUTHENTICATION_REQUIRED = 407,
39
+ REQUEST_TIMEOUT = 408,
40
+ CONFLICT = 409,
41
+ GONE = 410,
42
+ LENGTH_REQUIRED = 411,
43
+ PRECONDITION_FAILED = 412,
44
+ CONTENT_TOO_LARGE = 413,
45
+ URI_TOO_LONG = 414,
46
+ UNSUPPORTED_MEDIA_TYPE = 415,
47
+ RANGE_NOT_SATISFIABLE = 416,
48
+ EXPECTATION_FAILED = 417,
49
+ IM_A_TEAPOT = 418,
50
+ MISDIRECTED_REQUEST = 421,
51
+ UNPROCESSABLE_ENTITY = 422,
52
+ LOCKED = 423,
53
+ FAILED_DEPENDENCY = 424,
54
+ TOO_EARLY = 425,
55
+ UPGRADE_REQUIRED = 426,
56
+ PRECONDITION_REQUIRED = 428,
57
+ TOO_MANY_REQUESTS = 429,
58
+ REQUEST_HEADER_FIELDS_TOO_LARGE = 431,
59
+ UNAVAILABLE_FOR_LEGAL_REASONS = 451
60
+ }
61
+ export declare enum HttpServerErrorStatus {
62
+ INTERNAL_SERVER_ERROR = 500,
63
+ NOT_IMPLEMENTED = 501,
64
+ BAD_GATEWAY = 502,
65
+ SERVICE_UNAVAILABLE = 503,
66
+ GATEWAY_TIMEOUT = 504,
67
+ HTTP_VERSION_NOT_SUPPORTED = 505,
68
+ VARIANT_ALSO_NEGOTIATES = 506,
69
+ INSUFFICIENT_STORAGE = 507,
70
+ LOOP_DETECTED = 508,
71
+ NOT_EXTENDED = 510,
72
+ NETWORK_AUTHENTICATION_REQUIRED = 511
73
+ }
package/dist/enum.js ADDED
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HttpServerErrorStatus = exports.HttpClientErrorStatus = exports.HttpRedirectionStatus = exports.HttpSuccessStatus = exports.HttpInfoStatus = void 0;
4
+ var HttpInfoStatus;
5
+ (function (HttpInfoStatus) {
6
+ HttpInfoStatus[HttpInfoStatus["CONTINUE"] = 100] = "CONTINUE";
7
+ HttpInfoStatus[HttpInfoStatus["SWITCHING_PROTOCOLS"] = 101] = "SWITCHING_PROTOCOLS";
8
+ HttpInfoStatus[HttpInfoStatus["PROCESSING"] = 102] = "PROCESSING";
9
+ HttpInfoStatus[HttpInfoStatus["EARLY_HINTS"] = 103] = "EARLY_HINTS";
10
+ })(HttpInfoStatus || (exports.HttpInfoStatus = HttpInfoStatus = {}));
11
+ var HttpSuccessStatus;
12
+ (function (HttpSuccessStatus) {
13
+ HttpSuccessStatus[HttpSuccessStatus["OK"] = 200] = "OK";
14
+ HttpSuccessStatus[HttpSuccessStatus["CREATED"] = 201] = "CREATED";
15
+ HttpSuccessStatus[HttpSuccessStatus["ACCEPTED"] = 202] = "ACCEPTED";
16
+ HttpSuccessStatus[HttpSuccessStatus["NON_AUTHORITATIVE_INFORMATION"] = 203] = "NON_AUTHORITATIVE_INFORMATION";
17
+ HttpSuccessStatus[HttpSuccessStatus["NO_CONTENT"] = 204] = "NO_CONTENT";
18
+ HttpSuccessStatus[HttpSuccessStatus["RESET_CONTENT"] = 205] = "RESET_CONTENT";
19
+ HttpSuccessStatus[HttpSuccessStatus["PARTIAL_CONTENT"] = 206] = "PARTIAL_CONTENT";
20
+ HttpSuccessStatus[HttpSuccessStatus["MULTI_STATUS"] = 207] = "MULTI_STATUS";
21
+ HttpSuccessStatus[HttpSuccessStatus["ALREADY_REPORTED"] = 208] = "ALREADY_REPORTED";
22
+ HttpSuccessStatus[HttpSuccessStatus["IM_USED"] = 226] = "IM_USED";
23
+ })(HttpSuccessStatus || (exports.HttpSuccessStatus = HttpSuccessStatus = {}));
24
+ var HttpRedirectionStatus;
25
+ (function (HttpRedirectionStatus) {
26
+ HttpRedirectionStatus[HttpRedirectionStatus["MULTIPLE_CHOICES"] = 300] = "MULTIPLE_CHOICES";
27
+ HttpRedirectionStatus[HttpRedirectionStatus["MOVED_PERMANENTLY"] = 301] = "MOVED_PERMANENTLY";
28
+ HttpRedirectionStatus[HttpRedirectionStatus["FOUND"] = 302] = "FOUND";
29
+ HttpRedirectionStatus[HttpRedirectionStatus["SEE_OTHER"] = 303] = "SEE_OTHER";
30
+ HttpRedirectionStatus[HttpRedirectionStatus["NOT_MODIFIED"] = 304] = "NOT_MODIFIED";
31
+ HttpRedirectionStatus[HttpRedirectionStatus["USE_PROXY"] = 305] = "USE_PROXY";
32
+ HttpRedirectionStatus[HttpRedirectionStatus["SWITCH_PROXY"] = 306] = "SWITCH_PROXY";
33
+ HttpRedirectionStatus[HttpRedirectionStatus["TEMPORARY_REDIRECT"] = 307] = "TEMPORARY_REDIRECT";
34
+ HttpRedirectionStatus[HttpRedirectionStatus["PERMANENT_REDIRECT"] = 308] = "PERMANENT_REDIRECT";
35
+ })(HttpRedirectionStatus || (exports.HttpRedirectionStatus = HttpRedirectionStatus = {}));
36
+ var HttpClientErrorStatus;
37
+ (function (HttpClientErrorStatus) {
38
+ HttpClientErrorStatus[HttpClientErrorStatus["BAD_REQUEST"] = 400] = "BAD_REQUEST";
39
+ HttpClientErrorStatus[HttpClientErrorStatus["UNAUTHORIZED"] = 401] = "UNAUTHORIZED";
40
+ HttpClientErrorStatus[HttpClientErrorStatus["PAYMENT_REQUIRED"] = 402] = "PAYMENT_REQUIRED";
41
+ HttpClientErrorStatus[HttpClientErrorStatus["FORBIDDEN"] = 403] = "FORBIDDEN";
42
+ HttpClientErrorStatus[HttpClientErrorStatus["NOT_FOUND"] = 404] = "NOT_FOUND";
43
+ HttpClientErrorStatus[HttpClientErrorStatus["METHOD_NOT_ALLOWED"] = 405] = "METHOD_NOT_ALLOWED";
44
+ HttpClientErrorStatus[HttpClientErrorStatus["NOT_ACCEPTABLE"] = 406] = "NOT_ACCEPTABLE";
45
+ HttpClientErrorStatus[HttpClientErrorStatus["PROXY_AUTHENTICATION_REQUIRED"] = 407] = "PROXY_AUTHENTICATION_REQUIRED";
46
+ HttpClientErrorStatus[HttpClientErrorStatus["REQUEST_TIMEOUT"] = 408] = "REQUEST_TIMEOUT";
47
+ HttpClientErrorStatus[HttpClientErrorStatus["CONFLICT"] = 409] = "CONFLICT";
48
+ HttpClientErrorStatus[HttpClientErrorStatus["GONE"] = 410] = "GONE";
49
+ HttpClientErrorStatus[HttpClientErrorStatus["LENGTH_REQUIRED"] = 411] = "LENGTH_REQUIRED";
50
+ HttpClientErrorStatus[HttpClientErrorStatus["PRECONDITION_FAILED"] = 412] = "PRECONDITION_FAILED";
51
+ HttpClientErrorStatus[HttpClientErrorStatus["CONTENT_TOO_LARGE"] = 413] = "CONTENT_TOO_LARGE";
52
+ HttpClientErrorStatus[HttpClientErrorStatus["URI_TOO_LONG"] = 414] = "URI_TOO_LONG";
53
+ HttpClientErrorStatus[HttpClientErrorStatus["UNSUPPORTED_MEDIA_TYPE"] = 415] = "UNSUPPORTED_MEDIA_TYPE";
54
+ HttpClientErrorStatus[HttpClientErrorStatus["RANGE_NOT_SATISFIABLE"] = 416] = "RANGE_NOT_SATISFIABLE";
55
+ HttpClientErrorStatus[HttpClientErrorStatus["EXPECTATION_FAILED"] = 417] = "EXPECTATION_FAILED";
56
+ HttpClientErrorStatus[HttpClientErrorStatus["IM_A_TEAPOT"] = 418] = "IM_A_TEAPOT";
57
+ HttpClientErrorStatus[HttpClientErrorStatus["MISDIRECTED_REQUEST"] = 421] = "MISDIRECTED_REQUEST";
58
+ HttpClientErrorStatus[HttpClientErrorStatus["UNPROCESSABLE_ENTITY"] = 422] = "UNPROCESSABLE_ENTITY";
59
+ HttpClientErrorStatus[HttpClientErrorStatus["LOCKED"] = 423] = "LOCKED";
60
+ HttpClientErrorStatus[HttpClientErrorStatus["FAILED_DEPENDENCY"] = 424] = "FAILED_DEPENDENCY";
61
+ HttpClientErrorStatus[HttpClientErrorStatus["TOO_EARLY"] = 425] = "TOO_EARLY";
62
+ HttpClientErrorStatus[HttpClientErrorStatus["UPGRADE_REQUIRED"] = 426] = "UPGRADE_REQUIRED";
63
+ HttpClientErrorStatus[HttpClientErrorStatus["PRECONDITION_REQUIRED"] = 428] = "PRECONDITION_REQUIRED";
64
+ HttpClientErrorStatus[HttpClientErrorStatus["TOO_MANY_REQUESTS"] = 429] = "TOO_MANY_REQUESTS";
65
+ HttpClientErrorStatus[HttpClientErrorStatus["REQUEST_HEADER_FIELDS_TOO_LARGE"] = 431] = "REQUEST_HEADER_FIELDS_TOO_LARGE";
66
+ HttpClientErrorStatus[HttpClientErrorStatus["UNAVAILABLE_FOR_LEGAL_REASONS"] = 451] = "UNAVAILABLE_FOR_LEGAL_REASONS";
67
+ })(HttpClientErrorStatus || (exports.HttpClientErrorStatus = HttpClientErrorStatus = {}));
68
+ var HttpServerErrorStatus;
69
+ (function (HttpServerErrorStatus) {
70
+ HttpServerErrorStatus[HttpServerErrorStatus["INTERNAL_SERVER_ERROR"] = 500] = "INTERNAL_SERVER_ERROR";
71
+ HttpServerErrorStatus[HttpServerErrorStatus["NOT_IMPLEMENTED"] = 501] = "NOT_IMPLEMENTED";
72
+ HttpServerErrorStatus[HttpServerErrorStatus["BAD_GATEWAY"] = 502] = "BAD_GATEWAY";
73
+ HttpServerErrorStatus[HttpServerErrorStatus["SERVICE_UNAVAILABLE"] = 503] = "SERVICE_UNAVAILABLE";
74
+ HttpServerErrorStatus[HttpServerErrorStatus["GATEWAY_TIMEOUT"] = 504] = "GATEWAY_TIMEOUT";
75
+ HttpServerErrorStatus[HttpServerErrorStatus["HTTP_VERSION_NOT_SUPPORTED"] = 505] = "HTTP_VERSION_NOT_SUPPORTED";
76
+ HttpServerErrorStatus[HttpServerErrorStatus["VARIANT_ALSO_NEGOTIATES"] = 506] = "VARIANT_ALSO_NEGOTIATES";
77
+ HttpServerErrorStatus[HttpServerErrorStatus["INSUFFICIENT_STORAGE"] = 507] = "INSUFFICIENT_STORAGE";
78
+ HttpServerErrorStatus[HttpServerErrorStatus["LOOP_DETECTED"] = 508] = "LOOP_DETECTED";
79
+ HttpServerErrorStatus[HttpServerErrorStatus["NOT_EXTENDED"] = 510] = "NOT_EXTENDED";
80
+ HttpServerErrorStatus[HttpServerErrorStatus["NETWORK_AUTHENTICATION_REQUIRED"] = 511] = "NETWORK_AUTHENTICATION_REQUIRED";
81
+ })(HttpServerErrorStatus || (exports.HttpServerErrorStatus = HttpServerErrorStatus = {}));
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Extend Express Response prototype with all custom helper methods
3
+ * Call this once in your app before using custom response methods.
4
+ */
5
+ export declare function extendResponse(): void;
6
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,30 @@
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.extendResponse = extendResponse;
7
+ // src/index.ts
8
+ const express_1 = __importDefault(require("express"));
9
+ // Import all patch functions
10
+ const _100_199_1 = require("./100-199");
11
+ const _200_299_1 = require("./200-299");
12
+ const _300_399_1 = require("./300-399");
13
+ const _400_499_1 = require("./400-499");
14
+ const _500_599_1 = require("./500-599");
15
+ /**
16
+ * Extend Express Response prototype with all custom helper methods
17
+ * Call this once in your app before using custom response methods.
18
+ */
19
+ function extendResponse() {
20
+ const res = express_1.default.response;
21
+ if (res._patched)
22
+ return;
23
+ res._patched = true;
24
+ (0, _100_199_1.patch1xx)(res);
25
+ (0, _200_299_1.patch2xx)(res);
26
+ (0, _300_399_1.patch3xx)(res);
27
+ (0, _400_499_1.patch4xx)(res);
28
+ (0, _500_599_1.patch5xx)(res);
29
+ }
30
+ extendResponse();
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Normalize data for response to ensure consistent structure
3
+ */
4
+ export declare const normalize: (data: unknown) => {} | null;
package/dist/utils.js ADDED
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalize = void 0;
4
+ /**
5
+ * Normalize data for response to ensure consistent structure
6
+ */
7
+ const normalize = (data) => {
8
+ if (data === undefined)
9
+ return null;
10
+ if (typeof data === "string" ||
11
+ typeof data === "number" ||
12
+ typeof data === "boolean") {
13
+ return { value: data };
14
+ }
15
+ return data;
16
+ };
17
+ exports.normalize = normalize;
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "express-response-kit",
3
+ "version": "1.0.0",
4
+ "description": "Opinionated response helpers that extend Express.js response with clean, reusable, and type-safe methods.",
5
+ "author": "ahmad-nairat",
6
+ "main": "dist/index.js",
7
+ "types": "dist/types.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/types.d.ts",
11
+ "default": "./dist/index.js"
12
+ }
13
+ },
14
+ "scripts": {
15
+ "build": "tsc",
16
+ "release": "npm run build",
17
+ "test": "jest",
18
+ "test:watch": "jest --watch",
19
+ "test:coverage": "jest --coverage"
20
+ },
21
+ "keywords": [
22
+ "express",
23
+ "express-response",
24
+ "response-kit",
25
+ "nodejs",
26
+ "nodejs-response"
27
+ ],
28
+ "files": [
29
+ "dist"
30
+ ],
31
+ "peerDependencies": {
32
+ "express": ">=4"
33
+ },
34
+ "devDependencies": {
35
+ "@types/express": "^5.0.6",
36
+ "@types/jest": "^29.5.14",
37
+ "@types/supertest": "^6.0.3",
38
+ "express": "^4.22.1",
39
+ "jest": "^29.7.0",
40
+ "supertest": "^6.3.4",
41
+ "ts-jest": "^29.4.6",
42
+ "typescript": "^5.3.0"
43
+ },
44
+ "license": "MIT",
45
+ "repository": {
46
+ "type": "git",
47
+ "url": "git+https://github.com/ahmad-nairat/express-response-kit.git"
48
+ },
49
+ "bugs": {
50
+ "url": "https://github.com/ahmad-nairat/express-response-kit/issues"
51
+ },
52
+ "homepage": "https://github.com/ahmad-nairat/express-response-kit#readme"
53
+ }