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 +21 -0
- package/README.md +169 -0
- package/dist/100-199/index.d.ts +3 -0
- package/dist/100-199/index.js +27 -0
- package/dist/200-299/index.d.ts +3 -0
- package/dist/200-299/index.js +42 -0
- package/dist/300-399/index.d.ts +3 -0
- package/dist/300-399/index.js +41 -0
- package/dist/400-499/index.d.ts +3 -0
- package/dist/400-499/index.js +100 -0
- package/dist/500-599/index.d.ts +3 -0
- package/dist/500-599/index.js +46 -0
- package/dist/__tests__/response-helpers.test.d.ts +1 -0
- package/dist/__tests__/response-helpers.test.js +537 -0
- package/dist/enum.d.ts +73 -0
- package/dist/enum.js +81 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +30 -0
- package/dist/utils.d.ts +4 -0
- package/dist/utils.js +17 -0
- package/package.json +53 -0
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,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,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,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,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,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 = {}));
|
package/dist/index.d.ts
ADDED
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();
|
package/dist/utils.d.ts
ADDED
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
|
+
}
|