@asterflow/response 1.0.10 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -175
- package/dist/cjs/index.cjs +66 -27
- package/dist/mjs/index.js +51 -13
- package/dist/types/controllers/Response.d.ts +19 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/utils/sniffContentType.d.ts +8 -0
- package/package.json +16 -1
package/README.md
CHANGED
|
@@ -10,202 +10,52 @@
|
|
|
10
10
|
|
|
11
11
|
</div>
|
|
12
12
|
|
|
13
|
-
>
|
|
13
|
+
> Type-safe HTTP response builder with status-code helpers, header/cookie management, and binary file responses.
|
|
14
14
|
|
|
15
15
|
## 📦 Installation
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
|
-
npm install @asterflow/response
|
|
19
|
-
# or
|
|
20
18
|
bun install @asterflow/response
|
|
21
19
|
```
|
|
22
20
|
|
|
23
|
-
|
|
21
|
+
### ✨ Features
|
|
24
22
|
|
|
25
|
-
|
|
23
|
+
- **Typed status codes** — `response.status(code)` narrows the accepted body type to what that code expects
|
|
24
|
+
- **Shorthand helpers** — `success`, `created`, `noContent`, `badRequest`, `unauthorized`, `forbidden`, `notFound`, `validationError`, `internalServerError`
|
|
25
|
+
- **JSON responses** — `.json(data)` sends the body and sets `Content-Type: application/json`
|
|
26
|
+
- **Binary/file responses** — `.file(data, contentType?)` sends a `Uint8Array` as raw bytes; without a `contentType`, it's detected from the data's magic bytes (PNG, JPEG, GIF, PDF, BMP, ICO, GZIP, ZIP, WEBP), falling back to `application/octet-stream`
|
|
27
|
+
- **Headers and cookies** — `setHeader`/`setCookie`, each chainable and reflected in the response's type
|
|
28
|
+
- **Runtime conversion** — `toResponse()` for the standard Web `Response`, `toServerResponse(res)` for Node's `http.ServerResponse`
|
|
26
29
|
|
|
27
|
-
##
|
|
30
|
+
## ❓ How to Use
|
|
28
31
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
- **Header Management:** Type-safe header manipulation with method chaining
|
|
32
|
-
- **Cookie Support:** Integrated cookie management system
|
|
33
|
-
- **Content Type Detection:** Automatic content type detection and JSON serialization
|
|
34
|
-
- **Multi-Runtime Support:** Compatible with standard Response and Node.js ServerResponse
|
|
35
|
-
- **Immutable Design:** Response objects are immutable, promoting safer code patterns
|
|
36
|
-
- **Method Chaining:** Fluent API for building complex responses
|
|
32
|
+
```ts
|
|
33
|
+
import { AsterResponse } from '@asterflow/response'
|
|
37
34
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
### Basic Response
|
|
41
|
-
|
|
42
|
-
```typescript
|
|
43
|
-
import { Response } from '@asterflow/response'
|
|
44
|
-
|
|
45
|
-
// Simple text response
|
|
46
|
-
const response = new Response()
|
|
47
|
-
.success({ message: 'Hello World!' })
|
|
48
|
-
|
|
49
|
-
// With status code
|
|
50
|
-
const response = new Response()
|
|
35
|
+
const response = new AsterResponse()
|
|
51
36
|
.status(201)
|
|
52
|
-
.
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
### Status Code Helpers
|
|
56
|
-
|
|
57
|
-
```typescript
|
|
58
|
-
import { Response } from '@asterflow/response'
|
|
59
|
-
|
|
60
|
-
// Success responses
|
|
61
|
-
const success = new Response().success({ data: 'Success!' })
|
|
62
|
-
const created = new Response().created({ id: 1 })
|
|
63
|
-
const noContent = new Response().noContent()
|
|
64
|
-
|
|
65
|
-
// Error responses
|
|
66
|
-
const badRequest = new Response().badRequest({ error: 'Invalid input' })
|
|
67
|
-
const unauthorized = new Response().unauthorized({ error: 'Not authenticated' })
|
|
68
|
-
const forbidden = new Response().forbidden({ error: 'Access denied' })
|
|
69
|
-
const notFound = new Response().notFound({ error: 'Resource not found' })
|
|
70
|
-
const zodError = new Response().zodError({ errors: ['Validation failed'] })
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
### Headers and Cookies
|
|
74
|
-
|
|
75
|
-
```typescript
|
|
76
|
-
import { Response } from '@asterflow/response'
|
|
77
|
-
|
|
78
|
-
const response = new Response()
|
|
79
|
-
.success({ message: 'Hello!' })
|
|
80
|
-
.setHeader('X-Custom-Header', 'custom-value')
|
|
81
|
-
.setHeader('Cache-Control', 'no-cache')
|
|
82
|
-
.setCookie('session', 'abc123')
|
|
83
|
-
.setCookie('theme', 'dark')
|
|
84
|
-
|
|
85
|
-
// Headers and cookies are fully typed
|
|
86
|
-
console.log(response.header) // Map with typed entries
|
|
87
|
-
console.log(response.cookies) // Map with typed entries
|
|
37
|
+
.setHeader('X-Request-Id', 'abc123')
|
|
38
|
+
.json({ id: 1, name: 'User' })
|
|
88
39
|
```
|
|
89
40
|
|
|
90
|
-
|
|
41
|
+
Sending a file works the same way — pass the bytes, and the content type is sniffed automatically if you don't already know it:
|
|
91
42
|
|
|
92
|
-
```
|
|
93
|
-
import {
|
|
43
|
+
```ts
|
|
44
|
+
import { readFile } from 'fs/promises'
|
|
45
|
+
import { AsterResponse } from '@asterflow/response'
|
|
94
46
|
|
|
95
|
-
|
|
96
|
-
const
|
|
97
|
-
.json({
|
|
98
|
-
users: [
|
|
99
|
-
{ id: 1, name: 'John' },
|
|
100
|
-
{ id: 2, name: 'Jane' }
|
|
101
|
-
]
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
// Content-Type automatically set to application/json
|
|
47
|
+
const data = await readFile('avatar.png')
|
|
48
|
+
const response = new AsterResponse().file(data) // Content-Type: image/png
|
|
105
49
|
```
|
|
106
50
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
#### Standard Web API
|
|
110
|
-
|
|
111
|
-
```typescript
|
|
112
|
-
import { Response } from '@asterflow/response'
|
|
113
|
-
|
|
114
|
-
const response = new Response()
|
|
115
|
-
.success({ message: 'Hello World!' })
|
|
116
|
-
|
|
117
|
-
// Convert to standard Response
|
|
118
|
-
const webResponse = response.toResponse()
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
#### Node.js HTTP Server
|
|
122
|
-
|
|
123
|
-
```typescript
|
|
124
|
-
import { Response } from '@asterflow/response'
|
|
125
|
-
import { createServer } from 'http'
|
|
126
|
-
|
|
127
|
-
createServer((req, res) => {
|
|
128
|
-
const response = new Response()
|
|
129
|
-
.success({ message: 'Hello from Node.js!' })
|
|
130
|
-
|
|
131
|
-
// Convert to ServerResponse
|
|
132
|
-
response.toServerResponse(res)
|
|
133
|
-
})
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
### Advanced Type Safety
|
|
137
|
-
|
|
138
|
-
```typescript
|
|
139
|
-
import { Response } from '@asterflow/response'
|
|
140
|
-
|
|
141
|
-
// Define custom response types
|
|
142
|
-
type MyResponders = {
|
|
143
|
-
200: { message: string; data: unknown }
|
|
144
|
-
201: { id: number; message: string }
|
|
145
|
-
400: { error: string; details?: string[] }
|
|
146
|
-
404: { error: string }
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
const response = new Response<MyResponders>()
|
|
150
|
-
.success({ message: 'Success!', data: { id: 1 } }) // Fully typed
|
|
151
|
-
|
|
152
|
-
// TypeScript will enforce the correct shape for each status code
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
### Method Chaining
|
|
156
|
-
|
|
157
|
-
```typescript
|
|
158
|
-
import { Response } from '@asterflow/response'
|
|
159
|
-
|
|
160
|
-
const response = new Response()
|
|
161
|
-
.status(201)
|
|
162
|
-
.setHeader('Location', '/users/123')
|
|
163
|
-
.setHeader('X-Request-ID', 'req-123')
|
|
164
|
-
.setCookie('last-action', 'create-user')
|
|
165
|
-
.json({
|
|
166
|
-
id: 123,
|
|
167
|
-
message: 'User created successfully'
|
|
168
|
-
})
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
## 🔧 API Reference
|
|
172
|
-
|
|
173
|
-
### Core Methods
|
|
174
|
-
|
|
175
|
-
- `status(code)` - Set HTTP status code
|
|
176
|
-
- `getStatus()` - Get current status code
|
|
177
|
-
- `send(data)` - Send response with data
|
|
178
|
-
- `json(data)` - Send JSON response with proper Content-Type
|
|
179
|
-
|
|
180
|
-
### Status Code Helpers
|
|
181
|
-
|
|
182
|
-
- `success(data)` - 200 OK
|
|
183
|
-
- `created(data)` - 201 Created
|
|
184
|
-
- `noContent(data)` - 204 No Content
|
|
185
|
-
- `badRequest(data)` - 400 Bad Request
|
|
186
|
-
- `unauthorized(data)` - 401 Unauthorized
|
|
187
|
-
- `forbidden(data)` - 403 Forbidden
|
|
188
|
-
- `notFound(data)` - 404 Not Found
|
|
189
|
-
- `zodError(data)` - 422 Unprocessable Entity
|
|
190
|
-
|
|
191
|
-
### Header and Cookie Management
|
|
192
|
-
|
|
193
|
-
- `setHeader(name, value)` - Add/update header
|
|
194
|
-
- `setCookie(name, value)` - Add/update cookie
|
|
195
|
-
|
|
196
|
-
### Runtime Conversion
|
|
197
|
-
|
|
198
|
-
- `toResponse()` - Convert to standard Web API Response
|
|
199
|
-
- `toServerResponse(serverRes)` - Write to Node.js ServerResponse
|
|
51
|
+
Convert the finished response for whichever runtime you're on: `response.toResponse()` or `response.toServerResponse(res)`.
|
|
200
52
|
|
|
201
53
|
## 🔗 Related Packages
|
|
202
54
|
|
|
203
|
-
- [asterflow](https://www.npmjs.com/package/
|
|
204
|
-
- [@asterflow/
|
|
205
|
-
- [
|
|
206
|
-
- [@asterflow/request](https://www.npmjs.com/package/@asterflow/request) - Unified HTTP request system
|
|
207
|
-
- [@asterflow/plugin](https://www.npmjs.com/package/@asterflow/plugin) - A modular and typed plugin system
|
|
55
|
+
- Depended on by [@asterflow/adapter](https://www.npmjs.com/package/@asterflow/adapter) — builds error responses and passes `AsterResponse` to its runtime adapters
|
|
56
|
+
- Depended on by [@asterflow/multipart](https://www.npmjs.com/package/@asterflow/multipart) — returns `AsterResponse` errors for malformed multipart requests
|
|
57
|
+
- Depended on by [asterflow](https://www.npmjs.com/package/asterflow) — the core framework's request handler works with `AsterResponse`
|
|
208
58
|
|
|
209
59
|
## 📄 License
|
|
210
60
|
|
|
211
|
-
|
|
61
|
+
This project is licensed under the [MIT License](../../LICENSE).
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -1,26 +1,53 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var
|
|
3
|
-
var
|
|
4
|
-
var
|
|
5
|
-
var
|
|
6
|
-
var
|
|
2
|
+
var i = Object.defineProperty;
|
|
3
|
+
var c = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var x = Object.getOwnPropertyNames;
|
|
5
|
+
var m = Object.prototype.hasOwnProperty;
|
|
6
|
+
var u = (s, e) => {
|
|
7
7
|
for (var t in e)
|
|
8
|
-
|
|
9
|
-
},
|
|
8
|
+
i(s, t, { get: e[t], enumerable: !0 });
|
|
9
|
+
}, S = (s, e, t, o) => {
|
|
10
10
|
if (e && typeof e == "object" || typeof e == "function")
|
|
11
|
-
for (let
|
|
12
|
-
!
|
|
13
|
-
return
|
|
11
|
+
for (let n of x(e))
|
|
12
|
+
!m.call(s, n) && n !== t && i(s, n, { get: () => e[n], enumerable: !(o = c(e, n)) || o.enumerable });
|
|
13
|
+
return s;
|
|
14
14
|
};
|
|
15
|
-
var
|
|
15
|
+
var C = (s) => S(i({}, "__esModule", { value: !0 }), s);
|
|
16
16
|
// packages/response/src/index.ts
|
|
17
|
-
var
|
|
18
|
-
|
|
19
|
-
AsterResponse: () =>
|
|
17
|
+
var B = {};
|
|
18
|
+
u(B, {
|
|
19
|
+
AsterResponse: () => y,
|
|
20
|
+
sniffContentType: () => d
|
|
20
21
|
});
|
|
21
|
-
module.exports =
|
|
22
|
+
module.exports = C(B);
|
|
23
|
+
// packages/response/src/utils/sniffContentType.ts
|
|
24
|
+
var R = [
|
|
25
|
+
{ mimeType: "image/png", bytes: [137, 80, 78, 71, 13, 10, 26, 10] },
|
|
26
|
+
{ mimeType: "image/jpeg", bytes: [255, 216, 255] },
|
|
27
|
+
{ mimeType: "image/gif", bytes: [71, 73, 70, 56] },
|
|
28
|
+
{ mimeType: "application/pdf", bytes: [37, 80, 68, 70] },
|
|
29
|
+
{ mimeType: "image/bmp", bytes: [66, 77] },
|
|
30
|
+
{ mimeType: "image/x-icon", bytes: [0, 0, 1, 0] },
|
|
31
|
+
{ mimeType: "application/gzip", bytes: [31, 139] },
|
|
32
|
+
{ mimeType: "application/zip", bytes: [80, 75, 3, 4] },
|
|
33
|
+
{ mimeType: "image/webp", bytes: [87, 69, 66, 80], offset: 8 }
|
|
34
|
+
], f = [82, 73, 70, 70];
|
|
35
|
+
function h(s, e, t = 0) {
|
|
36
|
+
if (s.length < t + e.length) return !1;
|
|
37
|
+
for (let o = 0; o < e.length; o++)
|
|
38
|
+
if (s[t + o] !== e[o]) return !1;
|
|
39
|
+
return !0;
|
|
40
|
+
}
|
|
41
|
+
function d(s) {
|
|
42
|
+
for (let e of R)
|
|
43
|
+
if (!(e.mimeType === "image/webp" && !h(s, f)) && h(s, e.bytes, e.offset))
|
|
44
|
+
return e.mimeType;
|
|
45
|
+
}
|
|
22
46
|
// packages/response/src/controllers/Response.ts
|
|
23
|
-
|
|
47
|
+
function p(s) {
|
|
48
|
+
return s instanceof Uint8Array;
|
|
49
|
+
}
|
|
50
|
+
var y = class s {
|
|
24
51
|
_status;
|
|
25
52
|
body;
|
|
26
53
|
context;
|
|
@@ -48,6 +75,15 @@ var r = class o {
|
|
|
48
75
|
}
|
|
49
76
|
}, this;
|
|
50
77
|
}
|
|
78
|
+
file(e, t) {
|
|
79
|
+
return this.send(e), this.context = {
|
|
80
|
+
...this.context,
|
|
81
|
+
header: {
|
|
82
|
+
...this.context.header,
|
|
83
|
+
"Content-Type": t ?? d(e) ?? "application/octet-stream"
|
|
84
|
+
}
|
|
85
|
+
}, this;
|
|
86
|
+
}
|
|
51
87
|
success(e) {
|
|
52
88
|
return this.status(200), this.send(e), this;
|
|
53
89
|
}
|
|
@@ -96,11 +132,12 @@ var r = class o {
|
|
|
96
132
|
toResponse() {
|
|
97
133
|
let e = new Headers();
|
|
98
134
|
e.set("Content-Type", "text/plain");
|
|
99
|
-
for (let [n,
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
135
|
+
for (let [n, a] of Object.entries(this.context.header)) e.set(n, a);
|
|
136
|
+
let t = p(this.body);
|
|
137
|
+
t ? "Content-Type" in this.context.header || e.set("Content-Type", "application/octet-stream") : (typeof this.body == "object" || Array.isArray(this.body)) && e.set("Content-Type", "application/json");
|
|
138
|
+
for (let [n, a] of Object.entries(this.context.cookies)) e.append("Set-Cookie", `${n}=${a}`);
|
|
139
|
+
let o = t ? this.body : e.get("Content-Type") === "application/json" ? JSON.stringify(this.body) : String(this.body);
|
|
140
|
+
return new globalThis.Response(o, {
|
|
104
141
|
status: this._status,
|
|
105
142
|
headers: e
|
|
106
143
|
});
|
|
@@ -108,16 +145,18 @@ var r = class o {
|
|
|
108
145
|
toServerResponse(e) {
|
|
109
146
|
let t = new Headers();
|
|
110
147
|
t.set("Content-Type", "text/plain");
|
|
111
|
-
for (let [
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
148
|
+
for (let [a, r] of Object.entries(this.context.header)) t.set(a, r);
|
|
149
|
+
let o = p(this.body);
|
|
150
|
+
o ? "Content-Type" in this.context.header || t.set("Content-Type", "application/octet-stream") : (typeof this.body == "object" || Array.isArray(this.body)) && t.set("Content-Type", "application/json");
|
|
151
|
+
for (let [a, r] of Object.entries(this.context.cookies)) t.append("Set-Cookie", `${a}=${r}`);
|
|
152
|
+
let n = o ? this.body : t.get("Content-Type") === "application/json" ? JSON.stringify(this.body) : String(this.body);
|
|
115
153
|
e.writeHead(this._status, Object.fromEntries(t)), e.end(n);
|
|
116
154
|
}
|
|
117
155
|
static create() {
|
|
118
|
-
return new
|
|
156
|
+
return new s();
|
|
119
157
|
}
|
|
120
158
|
};
|
|
121
159
|
0 && (module.exports = {
|
|
122
|
-
AsterResponse
|
|
160
|
+
AsterResponse,
|
|
161
|
+
sniffContentType
|
|
123
162
|
});
|
package/dist/mjs/index.js
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
|
+
// packages/response/src/utils/sniffContentType.ts
|
|
2
|
+
var p = [
|
|
3
|
+
{ mimeType: "image/png", bytes: [137, 80, 78, 71, 13, 10, 26, 10] },
|
|
4
|
+
{ mimeType: "image/jpeg", bytes: [255, 216, 255] },
|
|
5
|
+
{ mimeType: "image/gif", bytes: [71, 73, 70, 56] },
|
|
6
|
+
{ mimeType: "application/pdf", bytes: [37, 80, 68, 70] },
|
|
7
|
+
{ mimeType: "image/bmp", bytes: [66, 77] },
|
|
8
|
+
{ mimeType: "image/x-icon", bytes: [0, 0, 1, 0] },
|
|
9
|
+
{ mimeType: "application/gzip", bytes: [31, 139] },
|
|
10
|
+
{ mimeType: "application/zip", bytes: [80, 75, 3, 4] },
|
|
11
|
+
{ mimeType: "image/webp", bytes: [87, 69, 66, 80], offset: 8 }
|
|
12
|
+
], c = [82, 73, 70, 70];
|
|
13
|
+
function i(s, e, t = 0) {
|
|
14
|
+
if (s.length < t + e.length) return !1;
|
|
15
|
+
for (let o = 0; o < e.length; o++)
|
|
16
|
+
if (s[t + o] !== e[o]) return !1;
|
|
17
|
+
return !0;
|
|
18
|
+
}
|
|
19
|
+
function d(s) {
|
|
20
|
+
for (let e of p)
|
|
21
|
+
if (!(e.mimeType === "image/webp" && !i(s, c)) && i(s, e.bytes, e.offset))
|
|
22
|
+
return e.mimeType;
|
|
23
|
+
}
|
|
1
24
|
// packages/response/src/controllers/Response.ts
|
|
2
|
-
|
|
25
|
+
function y(s) {
|
|
26
|
+
return s instanceof Uint8Array;
|
|
27
|
+
}
|
|
28
|
+
var h = class s {
|
|
3
29
|
_status;
|
|
4
30
|
body;
|
|
5
31
|
context;
|
|
@@ -27,6 +53,15 @@ var a = class d {
|
|
|
27
53
|
}
|
|
28
54
|
}, this;
|
|
29
55
|
}
|
|
56
|
+
file(e, t) {
|
|
57
|
+
return this.send(e), this.context = {
|
|
58
|
+
...this.context,
|
|
59
|
+
header: {
|
|
60
|
+
...this.context.header,
|
|
61
|
+
"Content-Type": t ?? d(e) ?? "application/octet-stream"
|
|
62
|
+
}
|
|
63
|
+
}, this;
|
|
64
|
+
}
|
|
30
65
|
success(e) {
|
|
31
66
|
return this.status(200), this.send(e), this;
|
|
32
67
|
}
|
|
@@ -75,11 +110,12 @@ var a = class d {
|
|
|
75
110
|
toResponse() {
|
|
76
111
|
let e = new Headers();
|
|
77
112
|
e.set("Content-Type", "text/plain");
|
|
78
|
-
for (let [
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
113
|
+
for (let [a, n] of Object.entries(this.context.header)) e.set(a, n);
|
|
114
|
+
let t = y(this.body);
|
|
115
|
+
t ? "Content-Type" in this.context.header || e.set("Content-Type", "application/octet-stream") : (typeof this.body == "object" || Array.isArray(this.body)) && e.set("Content-Type", "application/json");
|
|
116
|
+
for (let [a, n] of Object.entries(this.context.cookies)) e.append("Set-Cookie", `${a}=${n}`);
|
|
117
|
+
let o = t ? this.body : e.get("Content-Type") === "application/json" ? JSON.stringify(this.body) : String(this.body);
|
|
118
|
+
return new globalThis.Response(o, {
|
|
83
119
|
status: this._status,
|
|
84
120
|
headers: e
|
|
85
121
|
});
|
|
@@ -87,16 +123,18 @@ var a = class d {
|
|
|
87
123
|
toServerResponse(e) {
|
|
88
124
|
let t = new Headers();
|
|
89
125
|
t.set("Content-Type", "text/plain");
|
|
90
|
-
for (let [
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
126
|
+
for (let [n, r] of Object.entries(this.context.header)) t.set(n, r);
|
|
127
|
+
let o = y(this.body);
|
|
128
|
+
o ? "Content-Type" in this.context.header || t.set("Content-Type", "application/octet-stream") : (typeof this.body == "object" || Array.isArray(this.body)) && t.set("Content-Type", "application/json");
|
|
129
|
+
for (let [n, r] of Object.entries(this.context.cookies)) t.append("Set-Cookie", `${n}=${r}`);
|
|
130
|
+
let a = o ? this.body : t.get("Content-Type") === "application/json" ? JSON.stringify(this.body) : String(this.body);
|
|
131
|
+
e.writeHead(this._status, Object.fromEntries(t)), e.end(a);
|
|
95
132
|
}
|
|
96
133
|
static create() {
|
|
97
|
-
return new
|
|
134
|
+
return new s();
|
|
98
135
|
}
|
|
99
136
|
};
|
|
100
137
|
export {
|
|
101
|
-
|
|
138
|
+
h as AsterResponse,
|
|
139
|
+
d as sniffContentType
|
|
102
140
|
};
|
|
@@ -16,6 +16,25 @@ export declare class AsterResponse<RawResponder extends Responders = Responders,
|
|
|
16
16
|
}>;
|
|
17
17
|
cookies: Context["cookies"];
|
|
18
18
|
}>;
|
|
19
|
+
/**
|
|
20
|
+
* Sends a raw `Buffer`/`Uint8Array` body (e.g. a file read from disk).
|
|
21
|
+
* Unlike `send`/`json`, the body is written to the response exactly as
|
|
22
|
+
* given - never `JSON.stringify`'d or `String()`'d - so the client
|
|
23
|
+
* receives just the file's bytes, no wrapping object.
|
|
24
|
+
*
|
|
25
|
+
* `contentType` is optional: pass it when you already know the MIME type
|
|
26
|
+
* (cheapest - skips detection entirely). Omitted, it's detected straight
|
|
27
|
+
* from `data`'s leading magic bytes via `sniffContentType` - O(1)
|
|
28
|
+
* relative to the payload size, it inspects at most ~12 bytes and never
|
|
29
|
+
* scans the buffer. Falls back to `application/octet-stream` only if
|
|
30
|
+
* nothing matches.
|
|
31
|
+
*/
|
|
32
|
+
file(data: Uint8Array, contentType?: string): AsterResponse<RawResponder, BodySchema, StatusCode, {
|
|
33
|
+
header: Prettify<Context["header"] & {
|
|
34
|
+
"Content-Type": string;
|
|
35
|
+
}>;
|
|
36
|
+
cookies: Context["cookies"];
|
|
37
|
+
}>;
|
|
19
38
|
success(data: BodySchema[200]): AsterResponse<RawResponder, BodySchema, 200, Context>;
|
|
20
39
|
created(data: BodySchema[201]): AsterResponse<RawResponder, BodySchema, 201, Context>;
|
|
21
40
|
noContent(data: BodySchema[204]): AsterResponse<RawResponder, BodySchema, 204, Context>;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detects a binary payload's MIME type from its leading magic bytes -
|
|
3
|
+
* O(1) relative to payload size (inspects at most ~12 bytes, never scans
|
|
4
|
+
* the buffer), synchronous, no dependency. Covers the formats an upload
|
|
5
|
+
* endpoint commonly deals with; returns `undefined` when nothing matches so
|
|
6
|
+
* callers can fall back to `application/octet-stream` themselves.
|
|
7
|
+
*/
|
|
8
|
+
export declare function sniffContentType(data: Uint8Array): string | undefined;
|
package/package.json
CHANGED
|
@@ -1,12 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@asterflow/response",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "Type-safe HTTP response builder with status-code helpers, header/cookie management, and binary file responses.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"asterflow",
|
|
7
|
+
"response",
|
|
8
|
+
"http"
|
|
9
|
+
],
|
|
4
10
|
"main": "dist/cjs/index.cjs",
|
|
5
11
|
"module": "dist/mjs/index.js",
|
|
6
12
|
"types": "dist/types/index.d.ts",
|
|
7
13
|
"typings": "dist/types/index.d.ts",
|
|
8
14
|
"type": "module",
|
|
9
15
|
"license": "MIT",
|
|
16
|
+
"author": "Ashu11-A",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/AsterFlow/AsterFlow.git"
|
|
20
|
+
},
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/AsterFlow/AsterFlow/issues"
|
|
23
|
+
},
|
|
24
|
+
"homepage": "https://github.com/AsterFlow/AsterFlow",
|
|
10
25
|
"exports": {
|
|
11
26
|
".": {
|
|
12
27
|
"types": "./dist/types/index.d.ts",
|