@tripod311/currents 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +257 -0
- package/dist/binaryBody.d.ts +3 -0
- package/dist/binaryBody.d.ts.map +1 -0
- package/dist/binaryBody.js +14 -0
- package/dist/binaryBody.js.map +1 -0
- package/dist/body/binaryBody.d.ts +3 -0
- package/dist/body/binaryBody.d.ts.map +1 -0
- package/dist/body/binaryBody.js +14 -0
- package/dist/body/binaryBody.js.map +1 -0
- package/dist/body/formBody.d.ts +3 -0
- package/dist/body/formBody.d.ts.map +1 -0
- package/dist/body/formBody.js +15 -0
- package/dist/body/formBody.js.map +1 -0
- package/dist/body/jsonBody.d.ts +3 -0
- package/dist/body/jsonBody.d.ts.map +1 -0
- package/dist/body/jsonBody.js +7 -0
- package/dist/body/jsonBody.js.map +1 -0
- package/dist/body/multipartBody.d.ts +3 -0
- package/dist/body/multipartBody.d.ts.map +1 -0
- package/dist/body/multipartBody.js +115 -0
- package/dist/body/multipartBody.js.map +1 -0
- package/dist/body/textBody.d.ts +3 -0
- package/dist/body/textBody.d.ts.map +1 -0
- package/dist/body/textBody.js +7 -0
- package/dist/body/textBody.js.map +1 -0
- package/dist/context.d.ts +43 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +129 -0
- package/dist/context.js.map +1 -0
- package/dist/cookie.d.ts +13 -0
- package/dist/cookie.d.ts.map +1 -0
- package/dist/cookie.js +61 -0
- package/dist/cookie.js.map +1 -0
- package/dist/cors.d.ts +9 -0
- package/dist/cors.d.ts.map +1 -0
- package/dist/cors.js +34 -0
- package/dist/cors.js.map +1 -0
- package/dist/currents.d.ts +41 -0
- package/dist/currents.d.ts.map +1 -0
- package/dist/currents.js +172 -0
- package/dist/currents.js.map +1 -0
- package/dist/formBody.d.ts +2 -0
- package/dist/formBody.d.ts.map +1 -0
- package/dist/formBody.js +2 -0
- package/dist/formBody.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/jsonBody.d.ts +2 -0
- package/dist/jsonBody.d.ts.map +1 -0
- package/dist/jsonBody.js +2 -0
- package/dist/jsonBody.js.map +1 -0
- package/dist/multipartBody.d.ts +2 -0
- package/dist/multipartBody.d.ts.map +1 -0
- package/dist/multipartBody.js +2 -0
- package/dist/multipartBody.js.map +1 -0
- package/dist/router.d.ts +24 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +113 -0
- package/dist/router.js.map +1 -0
- package/dist/security.d.ts +16 -0
- package/dist/security.d.ts.map +1 -0
- package/dist/security.js +34 -0
- package/dist/security.js.map +1 -0
- package/dist/static.d.ts +9 -0
- package/dist/static.d.ts.map +1 -0
- package/dist/static.js +71 -0
- package/dist/static.js.map +1 -0
- package/dist/textBody.d.ts +3 -0
- package/dist/textBody.d.ts.map +1 -0
- package/dist/textBody.js +4 -0
- package/dist/textBody.js.map +1 -0
- package/package.json +31 -0
package/README.md
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
# Currents
|
|
2
|
+
|
|
3
|
+
**Currents** is a lightweight pipeline-based API constructor, designed to cover 90% of API use cases.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
* 🚦 Routing
|
|
8
|
+
* 🌍 CORS headers
|
|
9
|
+
* 🍪 Cookie management
|
|
10
|
+
* 🛡️ Security headers
|
|
11
|
+
* 📂 Static file delivery
|
|
12
|
+
* 📦 Parsing of popular request bodies (JSON, multipart, etc.)
|
|
13
|
+
* ⚡ HTTP/2 support
|
|
14
|
+
|
|
15
|
+
## Concept
|
|
16
|
+
|
|
17
|
+
Currents is **pipeline-based**: every route is treated as a chain of handlers. Instead of plugin registration, you just add or remove handlers from the chain. This gives you full control over every route.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install @tripod311/currents
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Example
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import { Currents, Context, JsonBody, MultipartBody, ServeStatic } from "@tripod311/currents"
|
|
33
|
+
|
|
34
|
+
const app = Currents.fromOptions({});
|
|
35
|
+
|
|
36
|
+
app.get("/", [
|
|
37
|
+
(ctx: Context) => {
|
|
38
|
+
ctx.text("Hello world");
|
|
39
|
+
}
|
|
40
|
+
]);
|
|
41
|
+
|
|
42
|
+
app.post('/table/:id', [
|
|
43
|
+
JsonBody,
|
|
44
|
+
(ctx: Context) => {
|
|
45
|
+
const body = ctx.body as { some: "body" };
|
|
46
|
+
ctx.json({ hello: "world" });
|
|
47
|
+
}
|
|
48
|
+
]);
|
|
49
|
+
|
|
50
|
+
app.put('/images/*', [
|
|
51
|
+
MultipartBody,
|
|
52
|
+
(ctx: Context) => {
|
|
53
|
+
// some operations with body
|
|
54
|
+
ctx.binary(result_buffer);
|
|
55
|
+
}
|
|
56
|
+
]);
|
|
57
|
+
|
|
58
|
+
app.get('/images/', [
|
|
59
|
+
ServeStatic({
|
|
60
|
+
basePath: '/images',
|
|
61
|
+
rootDir: '/home/me/images',
|
|
62
|
+
cacheControl: ["public", "max-age=0"],
|
|
63
|
+
fallback: 'default.png'
|
|
64
|
+
})
|
|
65
|
+
])
|
|
66
|
+
|
|
67
|
+
app.server.listen({ port: 80 });
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## API
|
|
73
|
+
|
|
74
|
+
### Currents
|
|
75
|
+
|
|
76
|
+
The main application class. Holds the server and provides the interface for routing.
|
|
77
|
+
|
|
78
|
+
#### Constructor
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
Currents.fromOptions({
|
|
82
|
+
forceHttpVersion?: 1 | 2,
|
|
83
|
+
certificates: {
|
|
84
|
+
cert: "path-to-cert",
|
|
85
|
+
key: "path-to-key",
|
|
86
|
+
ca?: "path-to-ca"
|
|
87
|
+
}
|
|
88
|
+
})
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
* Creates the app and sets up the server with given configuration.
|
|
92
|
+
* If you don’t force HTTP version: with certificates → defaults to **HTTP/2**, without → defaults to **HTTP/1**.
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
Currents.fromServer(server: HttpServer | Http2Server)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
* Create Currents app with an already configured server.
|
|
99
|
+
|
|
100
|
+
#### Methods
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
app.get(route: string, handlers: RouteHandler[])
|
|
104
|
+
app.post(route: string, handlers: RouteHandler[])
|
|
105
|
+
app.put(route: string, handlers: RouteHandler[])
|
|
106
|
+
app.delete(route: string, handlers: RouteHandler[])
|
|
107
|
+
app.any(route: string, handlers: RouteHandler[])
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
#### Properties
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
app.notFoundHandler(chain: RouteHandler[])
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Custom 404 handler. By default returns: `Cannot find ${route}`.
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
app.errorHandler(handler: (err: any, ctx: Context) => void)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Custom error handler.
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
### Context
|
|
127
|
+
|
|
128
|
+
Context represents a single request/response.
|
|
129
|
+
|
|
130
|
+
#### Properties
|
|
131
|
+
|
|
132
|
+
* `ctx.method: string` → Request method
|
|
133
|
+
* `ctx.path: string` → URL-decoded request path
|
|
134
|
+
* `ctx.headers: Record<string, string>` → Request headers
|
|
135
|
+
* `ctx.cookies: Record<string, string>` → Filled if you use `ParseCookies`
|
|
136
|
+
* `ctx.params: Record<string, string>` → Route params (`/table/:id`)
|
|
137
|
+
* `ctx.query: UrlSearchParams` → Query parameters
|
|
138
|
+
* `ctx.body: any` → Filled if body parsers are used
|
|
139
|
+
* `ctx.locals: Record<string, any>` → Custom data (pass between handlers)
|
|
140
|
+
* `ctx.finished: boolean` → Response already sent
|
|
141
|
+
* `ctx.notFound: boolean` → NotFound chain should be executed
|
|
142
|
+
|
|
143
|
+
#### Methods
|
|
144
|
+
|
|
145
|
+
* `ctx.responseHeader(header, value)` → set response header
|
|
146
|
+
* `ctx.status(code)` → set HTTP status
|
|
147
|
+
* `ctx.json(value)` → send JSON with proper header
|
|
148
|
+
* `ctx.text(value)` → send text response
|
|
149
|
+
* `ctx.binary(value)` → send buffer response
|
|
150
|
+
* `ctx.send(data, contentLength?)` → send custom buffer or stream
|
|
151
|
+
* `ctx.end()` → end response with empty body
|
|
152
|
+
* `ctx.redirect(to)` → 307 redirect
|
|
153
|
+
* `ctx.callNotFound()` → call default notFound chain
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
### Body parsers
|
|
158
|
+
|
|
159
|
+
Default handlers to populate `ctx.body`:
|
|
160
|
+
|
|
161
|
+
```ts
|
|
162
|
+
import { BinaryBody, TextBody, JsonBody, FormBody, MultipartBody } from "@tripod311/currents"
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
* **BinaryBody** → dumps buffer into body
|
|
166
|
+
* **TextBody** → UTF-8 string body
|
|
167
|
+
* **JsonBody** → parses JSON
|
|
168
|
+
* **FormBody** → parses `application/x-www-form-urlencoded`
|
|
169
|
+
* **MultipartBody** → parses multipart bodies
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
### Cookies
|
|
174
|
+
|
|
175
|
+
```ts
|
|
176
|
+
import { Context, ParseCookies, SetCookie } from "@tripod311/currents"
|
|
177
|
+
|
|
178
|
+
app.get('/', [
|
|
179
|
+
ParseCookies(),
|
|
180
|
+
(ctx: Context) => {
|
|
181
|
+
ctx.cookies; // filled with cookies
|
|
182
|
+
SetCookie(ctx, 'myCookie', 'myValue', {
|
|
183
|
+
maxAge?: number,
|
|
184
|
+
expires?: Date,
|
|
185
|
+
httpOnly?: boolean,
|
|
186
|
+
secure?: boolean,
|
|
187
|
+
sameSite?: "Strict" | "Lax" | "None",
|
|
188
|
+
path?: string,
|
|
189
|
+
domain?: string
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
])
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
You can provide a secret string to `ParseCookies` if you want signed cookies.
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
### Static files
|
|
200
|
+
|
|
201
|
+
```ts
|
|
202
|
+
import { ServeStatic } from "@tripod311/currents"
|
|
203
|
+
|
|
204
|
+
app.get('/*', [
|
|
205
|
+
ServeStatic({
|
|
206
|
+
basePath: '/',
|
|
207
|
+
rootDir: '/home/me/site',
|
|
208
|
+
cacheControl: ["public", "max-age=0"],
|
|
209
|
+
fallback: 'index.html'
|
|
210
|
+
})
|
|
211
|
+
])
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
* **cacheControl** and **fallback** are optional
|
|
215
|
+
* **basePath** defines which part of path should be cut before file lookup
|
|
216
|
+
* **fallback** is useful for SPA
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
### CORS
|
|
221
|
+
|
|
222
|
+
```ts
|
|
223
|
+
import { Cors } from "@tripod311/currents"
|
|
224
|
+
|
|
225
|
+
app.get('/*', [
|
|
226
|
+
Cors({
|
|
227
|
+
allowedOrigin: string | string[],
|
|
228
|
+
allowedMethods?: string[],
|
|
229
|
+
allowedHeaders?: string[],
|
|
230
|
+
credentials?: boolean
|
|
231
|
+
})
|
|
232
|
+
])
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
### Security headers
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
import { SecurityHeaders } from "@tripod311/currents"
|
|
241
|
+
|
|
242
|
+
app.get('/*', [
|
|
243
|
+
SecurityHeaders({
|
|
244
|
+
contentTypeOptions?: boolean,
|
|
245
|
+
xFrameOptions?: 'DENY' | 'SAMEORIGIN',
|
|
246
|
+
referrerPolicy?: "no-referrer" | "no-referrer-when-downgrade" | "origin" | "origin-when-cross-origin" | "same-origin" | "strict-origin" | "strict-origin-when-cross-origin" | "unsafe-url",
|
|
247
|
+
transportSecurity?: {
|
|
248
|
+
maxAge: number,
|
|
249
|
+
includeSubDomains?: boolean,
|
|
250
|
+
preload?: boolean
|
|
251
|
+
},
|
|
252
|
+
contentSecurityPolicy?: string,
|
|
253
|
+
crossOriginResourcePolicy?: 'same-origin' | 'same-site' | 'cross-origin',
|
|
254
|
+
permissionsPolicy?: string
|
|
255
|
+
})
|
|
256
|
+
])
|
|
257
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"binaryBody.d.ts","sourceRoot":"","sources":["../src/binaryBody.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAEtC,MAAM,CAAC,OAAO,UAAU,UAAU,CAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAc/D"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Context } from "./context.js";
|
|
2
|
+
export default function BinaryBody(ctx) {
|
|
3
|
+
return new Promise((resolve, reject) => {
|
|
4
|
+
ctx.body = Buffer.alloc(0);
|
|
5
|
+
const stream = ctx.raw.httpVersion === 1 ? ctx.raw.req : ctx.raw.stream;
|
|
6
|
+
stream.on('data', (chunk) => {
|
|
7
|
+
ctx.body = Buffer.concat([ctx.body, chunk]);
|
|
8
|
+
});
|
|
9
|
+
stream.on('end', () => {
|
|
10
|
+
resolve();
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=binaryBody.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"binaryBody.js","sourceRoot":"","sources":["../src/binaryBody.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAEtC,MAAM,CAAC,OAAO,UAAU,UAAU,CAAE,GAAY;IAC/C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACtC,GAAG,CAAC,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAE3B,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,WAAW,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC;QAExE,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;YAC3B,GAAG,CAAC,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;QAC7C,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;YACrB,OAAO,EAAE,CAAC;QACX,CAAC,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"binaryBody.d.ts","sourceRoot":"","sources":["../../src/body/binaryBody.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAEvC,MAAM,CAAC,OAAO,UAAU,UAAU,CAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAc/D"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Context } from "../context.js";
|
|
2
|
+
export default function BinaryBody(ctx) {
|
|
3
|
+
return new Promise((resolve, reject) => {
|
|
4
|
+
ctx.body = Buffer.alloc(0);
|
|
5
|
+
const stream = ctx.raw.httpVersion === 1 ? ctx.raw.req : ctx.raw.stream;
|
|
6
|
+
stream.on('data', (chunk) => {
|
|
7
|
+
ctx.body = Buffer.concat([ctx.body, chunk]);
|
|
8
|
+
});
|
|
9
|
+
stream.on('end', () => {
|
|
10
|
+
resolve();
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=binaryBody.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"binaryBody.js","sourceRoot":"","sources":["../../src/body/binaryBody.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAEvC,MAAM,CAAC,OAAO,UAAU,UAAU,CAAE,GAAY;IAC/C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACtC,GAAG,CAAC,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAE3B,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,WAAW,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC;QAExE,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;YAC3B,GAAG,CAAC,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;QAC7C,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;YACrB,OAAO,EAAE,CAAC;QACX,CAAC,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formBody.d.ts","sourceRoot":"","sources":["../../src/body/formBody.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAcvC,wBAA8B,QAAQ,CAAE,GAAG,EAAE,OAAO,iBAInD"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Context } from "../context.js";
|
|
2
|
+
import TextBody from "./textBody.js";
|
|
3
|
+
function urlParamsToObject(params) {
|
|
4
|
+
const obj = {};
|
|
5
|
+
for (const key of params.keys()) {
|
|
6
|
+
const values = params.getAll(key);
|
|
7
|
+
obj[key] = values.length === 1 ? values[0] : values;
|
|
8
|
+
}
|
|
9
|
+
return obj;
|
|
10
|
+
}
|
|
11
|
+
export default async function FormBody(ctx) {
|
|
12
|
+
await TextBody(ctx);
|
|
13
|
+
ctx.body = urlParamsToObject(new URLSearchParams(ctx.body));
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=formBody.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formBody.js","sourceRoot":"","sources":["../../src/body/formBody.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACvC,OAAO,QAAQ,MAAM,eAAe,CAAA;AAEpC,SAAS,iBAAiB,CAAC,MAAuB;IAChD,MAAM,GAAG,GAAsC,EAAE,CAAC;IAElD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;QAChC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAClC,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IACtD,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,CAAC,OAAO,CAAC,KAAK,UAAU,QAAQ,CAAE,GAAY;IACnD,MAAM,QAAQ,CAAC,GAAG,CAAC,CAAC;IAEpB,GAAG,CAAC,IAAI,GAAG,iBAAiB,CAAC,IAAI,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;AAC7D,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonBody.d.ts","sourceRoot":"","sources":["../../src/body/jsonBody.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAGvC,wBAA8B,QAAQ,CAAE,GAAG,EAAE,OAAO,iBAInD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonBody.js","sourceRoot":"","sources":["../../src/body/jsonBody.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACvC,OAAO,QAAQ,MAAM,eAAe,CAAA;AAEpC,MAAM,CAAC,OAAO,CAAC,KAAK,UAAU,QAAQ,CAAE,GAAY;IACnD,MAAM,QAAQ,CAAC,GAAG,CAAC,CAAC;IAEpB,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AACjC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"multipartBody.d.ts","sourceRoot":"","sources":["../../src/body/multipartBody.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAcvC,wBAA8B,aAAa,CAAE,GAAG,EAAE,OAAO,iBAexD"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { Context } from "../context.js";
|
|
2
|
+
import BinaryBody from "./binaryBody.js";
|
|
3
|
+
const RN_MARK_2 = Buffer.from("\r\n\r\n", "utf8");
|
|
4
|
+
const RN_MARK = '\r\n';
|
|
5
|
+
export default async function MultipartBody(ctx) {
|
|
6
|
+
await BinaryBody(ctx);
|
|
7
|
+
let ct = ctx.headers["content-type"].split(';');
|
|
8
|
+
let boundary = "";
|
|
9
|
+
for (let i = 0; i < ct.length; i++) {
|
|
10
|
+
let str = ct[i].trim();
|
|
11
|
+
if (str.match(/^boundary\=.*$/)) {
|
|
12
|
+
boundary = str.slice(str.indexOf('=') + 1);
|
|
13
|
+
break;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
if (!boundary)
|
|
17
|
+
throw new Error("MultipartBody error: No boundary");
|
|
18
|
+
ctx.body = parseMultipart(ctx.body, Buffer.from(boundary, "utf8"));
|
|
19
|
+
}
|
|
20
|
+
function deQuote(str) {
|
|
21
|
+
if (str[0] === '"' && str[str.length - 1] === '"') {
|
|
22
|
+
return str.slice(1, str.length - 1);
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
return str;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function parseMultipart(body, boundary) {
|
|
29
|
+
const result = {};
|
|
30
|
+
let index = body.indexOf(boundary);
|
|
31
|
+
while (index != -1) {
|
|
32
|
+
// extracting the part. If there is next boundary, read until it, else read to end of buffer.
|
|
33
|
+
let part;
|
|
34
|
+
let nIndex = body.indexOf(boundary, index + boundary.length);
|
|
35
|
+
if (nIndex == -1) {
|
|
36
|
+
part = body.subarray(index + boundary.length + RN_MARK.length);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
part = body.subarray(index + boundary.length + RN_MARK.length, nIndex);
|
|
40
|
+
}
|
|
41
|
+
// extract headers from part
|
|
42
|
+
const headersIndex = part.indexOf(RN_MARK_2);
|
|
43
|
+
if (headersIndex === -1) {
|
|
44
|
+
if (nIndex === -1) {
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
index = nIndex;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const headers = part.subarray(0, headersIndex).toString("utf8").split(RN_MARK);
|
|
53
|
+
let isFile, fileName, partName, mimeType;
|
|
54
|
+
for (let i = 0; i < headers.length; i++) {
|
|
55
|
+
const header = headers[i];
|
|
56
|
+
if (header.startsWith("Content-Disposition")) {
|
|
57
|
+
const arr = header.split("; ").slice(1);
|
|
58
|
+
for (let j = 0; j < arr.length; j++) {
|
|
59
|
+
const pair = arr[j].split("=");
|
|
60
|
+
switch (pair[0]) {
|
|
61
|
+
case "name":
|
|
62
|
+
partName = pair[1];
|
|
63
|
+
break;
|
|
64
|
+
case "filename":
|
|
65
|
+
isFile = true;
|
|
66
|
+
fileName = pair[1];
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else if (header.startsWith("Content-Type")) {
|
|
72
|
+
mimeType = header.split(": ")[1];
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (!partName) {
|
|
76
|
+
if (nIndex === -1) {
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
index = nIndex;
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// extract value
|
|
85
|
+
const fieldName = deQuote(partName);
|
|
86
|
+
let content;
|
|
87
|
+
if (isFile) {
|
|
88
|
+
content = {
|
|
89
|
+
fileName: deQuote(fileName || ""),
|
|
90
|
+
mimeType: mimeType || "application/octet-stream",
|
|
91
|
+
content: part.subarray(headersIndex + RN_MARK_2.length, part.length - RN_MARK.length)
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
content = part.subarray(headersIndex + RN_MARK_2.length, part.length - RN_MARK.length).toString("utf8");
|
|
96
|
+
}
|
|
97
|
+
if (result[fieldName] === undefined) {
|
|
98
|
+
result[fieldName] = content;
|
|
99
|
+
}
|
|
100
|
+
else if (Array.isArray(result[fieldName])) {
|
|
101
|
+
result[fieldName].push(content);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
result[fieldName] = [result[fieldName], content];
|
|
105
|
+
}
|
|
106
|
+
if (nIndex === -1) {
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
index = nIndex;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return result;
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=multipartBody.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"multipartBody.js","sourceRoot":"","sources":["../../src/body/multipartBody.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"textBody.d.ts","sourceRoot":"","sources":["../../src/body/textBody.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAGvC,wBAA8B,QAAQ,CAAE,GAAG,EAAE,OAAO,iBAInD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"textBody.js","sourceRoot":"","sources":["../../src/body/textBody.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACvC,OAAO,UAAU,MAAM,iBAAiB,CAAA;AAExC,MAAM,CAAC,OAAO,CAAC,KAAK,UAAU,QAAQ,CAAE,GAAY;IACnD,MAAM,UAAU,CAAC,GAAG,CAAC,CAAC;IAEtB,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;AACvC,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Readable } from "stream";
|
|
2
|
+
import { IncomingMessage, ServerResponse } from "http";
|
|
3
|
+
import type { ServerHttp2Stream, IncomingHttpHeaders } from "http2";
|
|
4
|
+
export interface RawHttp1 {
|
|
5
|
+
httpVersion: 1;
|
|
6
|
+
req: IncomingMessage;
|
|
7
|
+
res: ServerResponse;
|
|
8
|
+
}
|
|
9
|
+
export interface RawHttp2 {
|
|
10
|
+
httpVersion: 2;
|
|
11
|
+
stream: ServerHttp2Stream;
|
|
12
|
+
headers: IncomingHttpHeaders;
|
|
13
|
+
}
|
|
14
|
+
export type RawHttp = RawHttp1 | RawHttp2;
|
|
15
|
+
export declare class Context {
|
|
16
|
+
raw: RawHttp;
|
|
17
|
+
private _finished;
|
|
18
|
+
private _notFound;
|
|
19
|
+
method: string;
|
|
20
|
+
path: string;
|
|
21
|
+
headers: Record<string, string>;
|
|
22
|
+
cookies: Record<string, string>;
|
|
23
|
+
params: Record<string, string>;
|
|
24
|
+
query: URLSearchParams;
|
|
25
|
+
body: any;
|
|
26
|
+
locals: Record<string, any>;
|
|
27
|
+
private _status;
|
|
28
|
+
private _responseHeaders;
|
|
29
|
+
constructor(raw: RawHttp);
|
|
30
|
+
parsePath(): void;
|
|
31
|
+
responseHeader(header: string, value: string): Context;
|
|
32
|
+
status(code: number): Context;
|
|
33
|
+
json(data: any): void;
|
|
34
|
+
text(data: string): void;
|
|
35
|
+
binary(data: Buffer | Readable): void;
|
|
36
|
+
send(data: Buffer | Readable, contentLength?: number): void;
|
|
37
|
+
end(): void;
|
|
38
|
+
redirect(to: string): void;
|
|
39
|
+
callNotFound(): void;
|
|
40
|
+
get finished(): boolean;
|
|
41
|
+
get notFound(): boolean;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AACjC,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,MAAM,CAAA;AACtD,OAAO,KAAK,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,OAAO,CAAA;AAEnE,MAAM,WAAW,QAAQ;IACxB,WAAW,EAAE,CAAC,CAAC;IACf,GAAG,EAAE,eAAe,CAAC;IACrB,GAAG,EAAE,cAAc,CAAC;CACpB;AAED,MAAM,WAAW,QAAQ;IACxB,WAAW,EAAE,CAAC,CAAC;IACf,MAAM,EAAE,iBAAiB,CAAC;IAC1B,OAAO,EAAE,mBAAmB,CAAC;CAC7B;AAED,MAAM,MAAM,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE1C,qBAAa,OAAO;IACZ,GAAG,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,SAAS,CAAkB;IAE5B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAM;IACrC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAM;IACpC,KAAK,EAAG,eAAe,CAAC;IACxB,IAAI,EAAE,GAAG,CAAC;IAEV,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAM;IAExC,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,gBAAgB,CAAyC;gBAEpD,GAAG,EAAE,OAAO;IAczB,SAAS;IAMT,cAAc,CAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO;IAevD,MAAM,CAAE,IAAI,EAAE,MAAM,GAAG,OAAO;IAK9B,IAAI,CAAE,IAAI,EAAE,GAAG;IAKf,IAAI,CAAE,IAAI,EAAE,MAAM;IAKlB,MAAM,CAAE,IAAI,EAAE,MAAM,GAAG,QAAQ;IAK/B,IAAI,CAAE,IAAI,EAAE,MAAM,GAAG,QAAQ,EAAE,aAAa,CAAC,EAAE,MAAM;IAmCrD,GAAG;IAiBH,QAAQ,CAAE,EAAE,EAAE,MAAM;IAIpB,YAAY;IAIZ,IAAI,QAAQ,IAAK,OAAO,CAEvB;IAED,IAAI,QAAQ,IAAK,OAAO,CAEvB;CACD"}
|