dx-server 0.8.3 → 0.9.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 +481 -164
- package/cjs/router.js +1 -2
- package/esm/router.js +1 -2
- package/package.json +5 -3
- package/cjs/router.d.ts +0 -43
- package/cjs/static.d.ts +0 -5
- package/esm/router.d.ts +0 -43
- package/esm/static.d.ts +0 -5
package/README.md
CHANGED
|
@@ -1,43 +1,126 @@
|
|
|
1
|
-
# dx-server
|
|
1
|
+
# dx-server
|
|
2
|
+
|
|
3
|
+
A modern, unopinionated, and performant Node.js server framework built on AsyncLocalStorage for elegant request/response handling without prop drilling.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/dx-server)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- 🚀 **Context-based architecture** - Access request/response from anywhere using AsyncLocalStorage
|
|
11
|
+
- 🔗 **Chainable middleware** - Elegant middleware composition with [jchain](https://www.npmjs.com/package/jchain)
|
|
12
|
+
- 🎯 **Type-safe** - Written in TypeScript with comprehensive type definitions
|
|
13
|
+
- 🔄 **Express compatible** - Use existing Express middleware and applications
|
|
14
|
+
- 📦 **Minimal dependencies** - Only one runtime dependency (`send` for static file serving, planned for removal)
|
|
15
|
+
- 🛡️ **Built-in body parsing** - JSON, text, URL-encoded, and raw body parsing with size limits
|
|
16
|
+
- 🗂️ **Static file serving** - Efficient static file handling with ETag support
|
|
17
|
+
- 🔀 **Modern routing** - URLPattern-based routing (not Express patterns)
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
2
20
|
|
|
3
|
-
## Install
|
|
4
21
|
```bash
|
|
22
|
+
# npm
|
|
23
|
+
npm install dx-server jchain
|
|
24
|
+
|
|
25
|
+
# yarn
|
|
5
26
|
yarn add dx-server jchain
|
|
27
|
+
|
|
28
|
+
# pnpm
|
|
29
|
+
pnpm add dx-server jchain
|
|
6
30
|
```
|
|
7
31
|
|
|
8
|
-
|
|
32
|
+
### URLPattern Support
|
|
33
|
+
|
|
34
|
+
dx-server uses the [URLPattern API](https://developer.mozilla.org/en-US/docs/Web/API/URLPattern) for routing, which is natively supported in Node.js v19.0.0 and later.
|
|
9
35
|
|
|
10
|
-
|
|
36
|
+
**For Node.js < 19.0.0**, you need to install a polyfill:
|
|
11
37
|
|
|
12
|
-
|
|
38
|
+
```bash
|
|
39
|
+
npm install urlpattern-polyfill
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Then import it before using dx-server:
|
|
43
|
+
|
|
44
|
+
```javascript
|
|
45
|
+
// Add this at the top of your entry file
|
|
46
|
+
import 'urlpattern-polyfill'
|
|
47
|
+
|
|
48
|
+
// Then import dx-server
|
|
49
|
+
import dxServer from 'dx-server'
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
To check if your runtime supports URLPattern natively:
|
|
53
|
+
|
|
54
|
+
```javascript
|
|
55
|
+
if (typeof URLPattern === 'undefined') {
|
|
56
|
+
console.log('URLPattern not supported, polyfill required')
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Future Roadmap
|
|
61
|
+
|
|
62
|
+
**Zero Dependencies**: The `send` package (currently used for static file serving) is planned for removal in a future version. This will make dx-server a true zero-dependency framework. Until then, if you don't need static file serving, the `send` dependency won't be loaded or affect your application.
|
|
63
|
+
|
|
64
|
+
## Quick Start
|
|
65
|
+
|
|
66
|
+
### Basic Server
|
|
13
67
|
|
|
14
68
|
```javascript
|
|
15
69
|
import {Server} from 'node:http'
|
|
16
70
|
import chain from 'jchain'
|
|
17
|
-
import dxServer, {getReq, getRes, router, setHtml, setText
|
|
71
|
+
import dxServer, {getReq, getRes, router, setHtml, setText} from 'dx-server'
|
|
18
72
|
|
|
19
73
|
new Server().on('request', (req, res) => chain(
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
).listen(3000, () => console.log('server is listening at 3000'))
|
|
74
|
+
dxServer(req, res),
|
|
75
|
+
async next => {
|
|
76
|
+
try {
|
|
77
|
+
// Access req/res from anywhere - no prop drilling!
|
|
78
|
+
getRes().setHeader('Cache-Control', 'no-cache')
|
|
79
|
+
console.log(getReq().method, getReq().url)
|
|
80
|
+
await next()
|
|
81
|
+
} catch (e) {
|
|
82
|
+
console.error(e)
|
|
83
|
+
setHtml('internal server error', {status: 500})
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
router.get({
|
|
87
|
+
'/'() {setHtml('hello world')},
|
|
88
|
+
'/health'() {setText('ok')}
|
|
89
|
+
}),
|
|
90
|
+
() => setHtml('not found', {status: 404}),
|
|
91
|
+
)()).listen(3000, () => console.log('server is listening at 3000'))
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### TypeScript Example
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
import {Server} from 'node:http'
|
|
98
|
+
import chain from 'jchain'
|
|
99
|
+
import dxServer, {router, setJson, getJson} from 'dx-server'
|
|
100
|
+
|
|
101
|
+
interface User {
|
|
102
|
+
id: number
|
|
103
|
+
name: string
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
new Server().on('request', (req, res) => chain(
|
|
107
|
+
dxServer(req, res),
|
|
108
|
+
router.post({
|
|
109
|
+
async '/api/users'() {
|
|
110
|
+
const body = await getJson<{name: string}>()
|
|
111
|
+
if (!body?.name) {
|
|
112
|
+
setJson({error: 'Name required'}, {status: 400})
|
|
113
|
+
return
|
|
114
|
+
}
|
|
115
|
+
const user: User = {id: 1, name: body.name}
|
|
116
|
+
setJson(user, {status: 201})
|
|
117
|
+
}
|
|
118
|
+
}),
|
|
119
|
+
() => setJson({error: 'Not found'}, {status: 404})
|
|
120
|
+
)()).listen(3000)
|
|
38
121
|
```
|
|
39
122
|
|
|
40
|
-
File
|
|
123
|
+
### Static File Server
|
|
41
124
|
|
|
42
125
|
```javascript
|
|
43
126
|
import {Server} from 'node:http'
|
|
@@ -47,15 +130,19 @@ import {resolve, dirname} from 'node:path'
|
|
|
47
130
|
import {fileURLToPath} from 'node:url'
|
|
48
131
|
|
|
49
132
|
new Server().on('request', (req, res) => chain(
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
133
|
+
dxServer(req, res),
|
|
134
|
+
chainStatic('/*', {
|
|
135
|
+
root: resolve(dirname(fileURLToPath(import.meta.url)), 'public'),
|
|
136
|
+
index: ['index.html'],
|
|
137
|
+
dotfiles: 'deny'
|
|
138
|
+
}),
|
|
139
|
+
() => setHtml('not found', {status: 404}),
|
|
140
|
+
)()).listen(3000)
|
|
55
141
|
```
|
|
56
142
|
|
|
57
|
-
|
|
58
|
-
|
|
143
|
+
### Production-Ready Server with Express Integration
|
|
144
|
+
|
|
145
|
+
This example requires: `npm install express morgan helmet cors`
|
|
59
146
|
|
|
60
147
|
|
|
61
148
|
```javascript
|
|
@@ -88,7 +175,7 @@ const authContext = makeDxContext(async () => {
|
|
|
88
175
|
})
|
|
89
176
|
|
|
90
177
|
const requireAuth = () => {
|
|
91
|
-
if (!authContext.value) throw new ServerError('
|
|
178
|
+
if (!authContext.value) throw new ServerError('Unauthorized', 401, 'UNAUTHORIZED')
|
|
92
179
|
}
|
|
93
180
|
|
|
94
181
|
const serverChain = chain(
|
|
@@ -148,7 +235,7 @@ const serverChain = chain(
|
|
|
148
235
|
router.get('/', () => setHtml('ok')), // router.method() accepts 2 formats
|
|
149
236
|
router.get('/health', () => setText('ok')),
|
|
150
237
|
() => { // not found router
|
|
151
|
-
throw new ServerError('
|
|
238
|
+
throw new ServerError('Not found', 404, 'NOT_FOUND')
|
|
152
239
|
},
|
|
153
240
|
)
|
|
154
241
|
|
|
@@ -169,196 +256,426 @@ await promisify(tcpServer.listen.bind(tcpServer))(3000)
|
|
|
169
256
|
console.log('server is listening at 3000')
|
|
170
257
|
```
|
|
171
258
|
|
|
172
|
-
##
|
|
259
|
+
## Core Concepts
|
|
260
|
+
|
|
261
|
+
### Context-Based Architecture
|
|
262
|
+
|
|
263
|
+
dx-server uses Node.js AsyncLocalStorage to provide request/response context globally, eliminating prop drilling:
|
|
264
|
+
|
|
265
|
+
```javascript
|
|
266
|
+
// Access request/response from anywhere
|
|
267
|
+
import {getReq, getRes} from 'dx-server'
|
|
268
|
+
|
|
269
|
+
function someDeepFunction() {
|
|
270
|
+
const req = getReq() // No need to pass req through multiple layers
|
|
271
|
+
const res = getRes()
|
|
272
|
+
res.setHeader('X-Custom', 'value')
|
|
273
|
+
}
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### Lazy Body Parsing
|
|
173
277
|
|
|
174
|
-
|
|
175
|
-
The associated results are calculated in the first time they are called and cached for subsequent calls.
|
|
278
|
+
Body parsing functions are asynchronous and cached per request:
|
|
176
279
|
|
|
177
|
-
If you want to get these values synchronously, chain it, like follows:
|
|
178
280
|
```javascript
|
|
179
|
-
import {getJson} from 'dx-server'
|
|
281
|
+
import {getJson, getText, getBuffer, getUrlEncoded} from 'dx-server'
|
|
282
|
+
|
|
283
|
+
// Async usage (lazy-loaded and cached)
|
|
284
|
+
const json = await getJson()
|
|
285
|
+
const text = await getText()
|
|
180
286
|
|
|
287
|
+
// Sync usage (requires chaining)
|
|
181
288
|
chain(
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
289
|
+
getJson.chain({bodyLimit: 1024 * 1024}), // 1MB limit
|
|
290
|
+
next => {
|
|
291
|
+
console.log(getJson.value) // Access synchronously
|
|
292
|
+
return next()
|
|
293
|
+
}
|
|
187
294
|
)
|
|
188
295
|
```
|
|
189
296
|
|
|
190
|
-
|
|
297
|
+
### Custom Contexts
|
|
298
|
+
|
|
299
|
+
Create reusable context objects with `makeDxContext`:
|
|
191
300
|
|
|
192
301
|
```javascript
|
|
193
|
-
import {makeDxContext} from 'dx-server'
|
|
302
|
+
import {makeDxContext, getReq} from 'dx-server'
|
|
194
303
|
|
|
195
|
-
|
|
196
|
-
|
|
304
|
+
// Create auth context
|
|
305
|
+
const authContext = makeDxContext(async () => {
|
|
306
|
+
const token = getReq().headers.authorization
|
|
307
|
+
if (!token) return null
|
|
308
|
+
return await validateToken(token) // Your validation logic
|
|
197
309
|
})
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
}
|
|
201
|
-
chain(
|
|
202
|
-
authContext.chain(),
|
|
203
|
-
next => {
|
|
204
|
-
requireAuth()
|
|
205
|
-
return next()
|
|
206
|
-
}
|
|
207
|
-
)
|
|
208
|
-
// or await authContext() to lazy load the context and don't require chaining authContext.chain()
|
|
310
|
+
|
|
311
|
+
// Use in middleware
|
|
209
312
|
chain(
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
313
|
+
authContext.chain(), // Initialize for all requests
|
|
314
|
+
next => {
|
|
315
|
+
if (!authContext.value) {
|
|
316
|
+
setJson({error: 'Unauthorized'}, {status: 401})
|
|
317
|
+
return
|
|
318
|
+
}
|
|
319
|
+
return next()
|
|
320
|
+
}
|
|
214
321
|
)
|
|
322
|
+
|
|
215
323
|
```
|
|
216
324
|
|
|
217
|
-
|
|
218
|
-
|
|
325
|
+
## API Reference
|
|
326
|
+
|
|
327
|
+
### Main Exports
|
|
328
|
+
|
|
219
329
|
```javascript
|
|
220
330
|
import dxServer, {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
331
|
+
// Request/Response access
|
|
332
|
+
getReq, getRes,
|
|
333
|
+
|
|
334
|
+
// Request body parsers
|
|
335
|
+
getBuffer, getJson, getRaw, getText, getUrlEncoded, getQuery,
|
|
336
|
+
|
|
337
|
+
// Response setters
|
|
338
|
+
setHtml, setJson, setText, setEmpty, setBuffer, setRedirect,
|
|
339
|
+
setNodeStream, setWebStream, setFile,
|
|
340
|
+
|
|
341
|
+
// Utilities
|
|
342
|
+
router, connectMiddlewares, chainStatic, makeDxContext
|
|
224
343
|
} from 'dx-server'
|
|
225
|
-
|
|
344
|
+
|
|
345
|
+
// Express integration (requires express installed)
|
|
346
|
+
import {expressApp, expressRouter} from 'dx-server/express'
|
|
347
|
+
|
|
348
|
+
// Low-level helpers
|
|
226
349
|
import {
|
|
227
|
-
|
|
228
|
-
|
|
350
|
+
setBufferBodyDefaultOptions,
|
|
351
|
+
bufferFromReq, jsonFromReq, rawFromReq, textFromReq,
|
|
352
|
+
urlEncodedFromReq, queryFromReq,
|
|
229
353
|
} from 'dx-server/helpers'
|
|
230
354
|
```
|
|
231
355
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
356
|
+
### Core Functions
|
|
357
|
+
|
|
358
|
+
#### Request/Response Access
|
|
359
|
+
- **`getReq()`** - Get the current request object
|
|
360
|
+
- **`getRes()`** - Get the current response object
|
|
361
|
+
|
|
362
|
+
#### Body Parsers
|
|
363
|
+
All body parsers are async, lazy-loaded, and cached per request:
|
|
364
|
+
|
|
365
|
+
- **`getJson(options?)`** - Parse JSON body (requires `Content-Type: application/json`)
|
|
366
|
+
- **`getText(options?)`** - Parse text body (requires `Content-Type: text/plain`)
|
|
367
|
+
- **`getBuffer(options?)`** - Get raw buffer
|
|
368
|
+
- **`getRaw(options?)`** - Get raw body (requires `Content-Type: application/octet-stream`)
|
|
369
|
+
- **`getUrlEncoded(options?)`** - Parse URL-encoded form (requires `Content-Type: application/x-www-form-urlencoded`)
|
|
370
|
+
- **`getQuery(options?)`** - Parse query string parameters
|
|
371
|
+
|
|
372
|
+
Options:
|
|
373
|
+
```typescript
|
|
374
|
+
{
|
|
375
|
+
bodyLimit?: number // Max body size in bytes (default: 100KB)
|
|
376
|
+
urlEncodedParser?: (search: string) => any
|
|
377
|
+
queryParser?: (search: string) => any
|
|
378
|
+
}
|
|
239
379
|
```
|
|
240
380
|
|
|
241
|
-
|
|
381
|
+
#### Response Setters
|
|
382
|
+
- **`setJson(data, {status?, headers?})`** - Send JSON response
|
|
383
|
+
- **`setHtml(html, {status?, headers?})`** - Send HTML response
|
|
384
|
+
- **`setText(text, {status?, headers?})`** - Send plain text
|
|
385
|
+
- **`setBuffer(buffer, {status?, headers?})`** - Send buffer
|
|
386
|
+
- **`setFile(path, options?)`** - Send file
|
|
387
|
+
- **`setNodeStream(stream, {status?, headers?})`** - Send Node.js stream
|
|
388
|
+
- **`setWebStream(stream, {status?, headers?})`** - Send Web stream
|
|
389
|
+
- **`setRedirect(url, {status?, headers?})`** - Redirect response
|
|
390
|
+
- **`setEmpty({status?, headers?})`** - Send empty response
|
|
391
|
+
|
|
392
|
+
#### Context Management
|
|
393
|
+
- **`makeDxContext(fn)`** - Create a custom context object
|
|
394
|
+
```javascript
|
|
395
|
+
const ctx = makeDxContext(() => computeValue())
|
|
396
|
+
|
|
397
|
+
// Access value
|
|
398
|
+
await ctx() // Lazy load
|
|
399
|
+
ctx.value // Sync access (after loading)
|
|
400
|
+
ctx.get(req) // Get for specific request
|
|
401
|
+
|
|
402
|
+
// Set value
|
|
403
|
+
ctx.value = newValue
|
|
404
|
+
ctx.set(req, newValue)
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
#### Middleware Utilities
|
|
408
|
+
- **`connectMiddlewares(...middlewares)`** - Use Connect/Express middleware
|
|
409
|
+
- **`chainStatic(pattern, options)`** - Serve static files
|
|
410
|
+
```javascript
|
|
411
|
+
chainStatic('/public/*', {
|
|
412
|
+
root: '/path/to/files',
|
|
413
|
+
index: ['index.html'],
|
|
414
|
+
dotfiles: 'deny',
|
|
415
|
+
etag: true,
|
|
416
|
+
lastModified: true
|
|
417
|
+
})
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
### Routing
|
|
421
|
+
|
|
422
|
+
dx-server uses [URLPattern API](https://developer.mozilla.org/en-US/docs/Web/API/URLPattern) for routing, which differs from Express patterns:
|
|
423
|
+
|
|
424
|
+
```javascript
|
|
425
|
+
import {router} from 'dx-server'
|
|
426
|
+
|
|
427
|
+
// Single route
|
|
428
|
+
router.get('/users/:id', ({matched}) => {
|
|
429
|
+
const {id} = matched.pathname.groups
|
|
430
|
+
setJson({userId: id})
|
|
431
|
+
})
|
|
242
432
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
433
|
+
// Multiple routes
|
|
434
|
+
router.post({
|
|
435
|
+
'/api/users': () => { /* create user */ },
|
|
436
|
+
'/api/users/:id': ({matched}) => { /* update user */ },
|
|
437
|
+
'/api/users/:id/posts': ({matched}) => { /* get user posts */ }
|
|
438
|
+
})
|
|
439
|
+
|
|
440
|
+
// All HTTP methods supported
|
|
441
|
+
router.get(pattern, handler)
|
|
442
|
+
router.post(pattern, handler)
|
|
443
|
+
router.put(pattern, handler)
|
|
444
|
+
router.delete(pattern, handler)
|
|
445
|
+
router.patch(pattern, handler)
|
|
446
|
+
router.head(pattern, handler)
|
|
447
|
+
router.options(pattern, handler)
|
|
448
|
+
router.all(pattern, handler) // Any method
|
|
449
|
+
|
|
450
|
+
// Custom method
|
|
451
|
+
router.method('CUSTOM', pattern, handler)
|
|
452
|
+
|
|
453
|
+
// With prefix option
|
|
454
|
+
router.get({
|
|
455
|
+
'/users': listUsers,
|
|
456
|
+
'/users/:id': getUser
|
|
457
|
+
}, {prefix: '/api'}) // Routes become /api/users, /api/users/:id
|
|
458
|
+
```
|
|
251
459
|
|
|
252
|
-
|
|
460
|
+
#### URLPattern vs Express Patterns
|
|
253
461
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
462
|
+
| Pattern | URLPattern | Express |
|
|
463
|
+
|---------|------------|---------|
|
|
464
|
+
| Wildcard | `/api/*` | `/api/*` or `/api/(.*)` |
|
|
465
|
+
| Optional trailing slash | `{/}?` | `/path/?` |
|
|
466
|
+
| Named params | `/:id` | `/:id` |
|
|
467
|
+
| Optional params | `/:id?` | `/:id?` |
|
|
257
468
|
|
|
258
|
-
|
|
469
|
+
**Important differences:**
|
|
470
|
+
- `'/foo'` matches `/foo` but NOT `/foo/`
|
|
471
|
+
- `'/foo/'` matches `/foo/` but NOT `/foo`
|
|
472
|
+
- Use `'/foo{/}?'` to match both
|
|
259
473
|
|
|
260
|
-
|
|
261
|
-
- `ctx.set(req, value)`.
|
|
474
|
+
### Express Integration
|
|
262
475
|
|
|
263
|
-
-
|
|
476
|
+
dx-server seamlessly integrates with Express applications and middleware:
|
|
264
477
|
|
|
265
|
-
- `connectMiddlewares(...middlewares)`: connect middlewares. For example:
|
|
266
478
|
```javascript
|
|
267
|
-
import {
|
|
268
|
-
import
|
|
479
|
+
import {expressApp, expressRouter} from 'dx-server/express'
|
|
480
|
+
import express from 'express'
|
|
269
481
|
import cors from 'cors'
|
|
482
|
+
import helmet from 'helmet'
|
|
270
483
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
484
|
+
chain(
|
|
485
|
+
// Use entire Express app
|
|
486
|
+
await expressApp(app => {
|
|
487
|
+
app.set('trust proxy', true)
|
|
488
|
+
app.set('json spaces', 2)
|
|
489
|
+
app.use(helmet())
|
|
490
|
+
app.use('/static', express.static('public'))
|
|
491
|
+
}),
|
|
492
|
+
|
|
493
|
+
// Or use Express router
|
|
494
|
+
expressRouter(router => {
|
|
495
|
+
router.use(cors())
|
|
496
|
+
router.get('/legacy', (req, res) => {
|
|
497
|
+
res.json({message: 'Express route'})
|
|
498
|
+
})
|
|
499
|
+
})
|
|
274
500
|
)
|
|
275
501
|
```
|
|
276
502
|
|
|
277
|
-
-
|
|
503
|
+
### Low-Level Helpers
|
|
504
|
+
|
|
505
|
+
Pure functions for custom implementations:
|
|
506
|
+
|
|
278
507
|
```javascript
|
|
279
|
-
import {
|
|
280
|
-
|
|
281
|
-
|
|
508
|
+
import {
|
|
509
|
+
setBufferBodyDefaultOptions,
|
|
510
|
+
bufferFromReq, jsonFromReq, rawFromReq,
|
|
511
|
+
textFromReq, urlEncodedFromReq, queryFromReq
|
|
512
|
+
} from 'dx-server/helpers'
|
|
282
513
|
|
|
514
|
+
// Set global defaults
|
|
515
|
+
setBufferBodyDefaultOptions({
|
|
516
|
+
bodyLimit: 10 * 1024 * 1024, // 10MB
|
|
517
|
+
queryParser: (search) => myCustomParser(search)
|
|
518
|
+
})
|
|
519
|
+
|
|
520
|
+
// Use directly with req/res (no context required)
|
|
521
|
+
const json = await jsonFromReq(req, {bodyLimit: 1024})
|
|
522
|
+
const query = queryFromReq(req)
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
## Security Considerations
|
|
526
|
+
|
|
527
|
+
### Body Size Limits
|
|
528
|
+
Always set appropriate body size limits to prevent DoS attacks:
|
|
529
|
+
|
|
530
|
+
```javascript
|
|
283
531
|
chain(
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
return new URL(getReq().url, 'http://localhost').pathname.slice('/assets'.length)
|
|
288
|
-
},
|
|
289
|
-
}),
|
|
532
|
+
getJson.chain({bodyLimit: 1024 * 1024}), // 1MB limit
|
|
533
|
+
// or globally:
|
|
534
|
+
dxServer(req, res, {bodyLimit: 5 * 1024 * 1024}) // 5MB
|
|
290
535
|
)
|
|
291
536
|
```
|
|
292
537
|
|
|
293
|
-
|
|
538
|
+
### Error Handling
|
|
539
|
+
Never expose internal errors to clients:
|
|
540
|
+
|
|
294
541
|
```javascript
|
|
295
|
-
|
|
296
|
-
|
|
542
|
+
class AppError extends Error {
|
|
543
|
+
constructor(message, status = 400, code = 'ERROR') {
|
|
544
|
+
super(message)
|
|
545
|
+
this.status = status
|
|
546
|
+
this.code = code
|
|
547
|
+
}
|
|
548
|
+
}
|
|
297
549
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
550
|
+
chain(
|
|
551
|
+
async next => {
|
|
552
|
+
try {
|
|
553
|
+
await next()
|
|
554
|
+
} catch (error) {
|
|
555
|
+
if (error instanceof AppError) {
|
|
556
|
+
setJson({error: error.message, code: error.code}, {status: error.status})
|
|
557
|
+
} else {
|
|
558
|
+
console.error(error) // Log for debugging
|
|
559
|
+
setJson({error: 'Internal server error'}, {status: 500})
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
)
|
|
564
|
+
```
|
|
302
565
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
- `router.method(method: string, routes: {[pattern: string]: Route}, options: RouterOptions)`: create multiple routes.
|
|
306
|
-
- `router.method(method: string, pattern: string, handler: Route, options: RouterOptions)`: create route for `method` method.
|
|
566
|
+
### Input Validation
|
|
567
|
+
Always validate input data:
|
|
307
568
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
569
|
+
```javascript
|
|
570
|
+
router.post('/api/users', async () => {
|
|
571
|
+
const data = await getJson()
|
|
572
|
+
|
|
573
|
+
// Validate
|
|
574
|
+
if (!data?.email || !isValidEmail(data.email)) {
|
|
575
|
+
throw new AppError('Invalid email', 400, 'INVALID_EMAIL')
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// Process...
|
|
579
|
+
})
|
|
313
580
|
```
|
|
314
581
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
For example, to match any path prefixed with `/api/`, use `/api/*`.
|
|
318
|
-
Note the following:
|
|
319
|
-
- `''` matches nothing.
|
|
320
|
-
- `'/'` matches both https://example.com and https://example.com/.
|
|
321
|
-
- `'/foo'` matches https://example.com/foo but not https://example.com/foo/.
|
|
322
|
-
- `'/foo/'` matches https://example.com/foo/ but not https://example.com/foo.
|
|
582
|
+
### Security Headers
|
|
583
|
+
Use security middleware:
|
|
323
584
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
585
|
+
```javascript
|
|
586
|
+
import helmet from 'helmet'
|
|
587
|
+
import cors from 'cors'
|
|
588
|
+
|
|
589
|
+
chain(
|
|
590
|
+
connectMiddlewares(
|
|
591
|
+
helmet(),
|
|
592
|
+
cors({
|
|
593
|
+
origin: process.env.ALLOWED_ORIGINS?.split(','),
|
|
594
|
+
credentials: true
|
|
595
|
+
})
|
|
596
|
+
)
|
|
597
|
+
)
|
|
331
598
|
```
|
|
332
599
|
|
|
333
|
-
##
|
|
600
|
+
## Advanced Examples
|
|
601
|
+
|
|
602
|
+
### File Upload with Busboy
|
|
334
603
|
```javascript
|
|
335
|
-
import
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
604
|
+
import busboy from 'busboy'
|
|
605
|
+
|
|
606
|
+
router.post('/upload', () => {
|
|
607
|
+
const req = getReq()
|
|
608
|
+
const bb = busboy({headers: req.headers, limits: {fileSize: 10 * 1024 * 1024}})
|
|
609
|
+
|
|
610
|
+
bb.on('file', (name, file, info) => {
|
|
611
|
+
// Handle file stream
|
|
612
|
+
})
|
|
613
|
+
|
|
614
|
+
req.pipe(bb)
|
|
615
|
+
})
|
|
339
616
|
```
|
|
340
617
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
618
|
+
### WebSocket Upgrade
|
|
619
|
+
```javascript
|
|
620
|
+
import {WebSocketServer} from 'ws'
|
|
621
|
+
|
|
622
|
+
const wss = new WebSocketServer({noServer: true})
|
|
623
|
+
|
|
624
|
+
server.on('upgrade', (request, socket, head) => {
|
|
625
|
+
if (request.url === '/ws') {
|
|
626
|
+
wss.handleUpgrade(request, socket, head, (ws) => {
|
|
627
|
+
wss.emit('connection', ws, request)
|
|
628
|
+
})
|
|
629
|
+
}
|
|
630
|
+
})
|
|
631
|
+
```
|
|
344
632
|
|
|
345
|
-
|
|
633
|
+
### Rate Limiting
|
|
346
634
|
```javascript
|
|
347
|
-
import
|
|
635
|
+
import rateLimit from 'express-rate-limit'
|
|
348
636
|
|
|
349
637
|
chain(
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
router.use(cors())
|
|
357
|
-
}),
|
|
638
|
+
connectMiddlewares(
|
|
639
|
+
rateLimit({
|
|
640
|
+
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
641
|
+
max: 100 // limit each IP to 100 requests per windowMs
|
|
642
|
+
})
|
|
643
|
+
)
|
|
358
644
|
)
|
|
359
645
|
```
|
|
360
646
|
|
|
361
|
-
##
|
|
362
|
-
|
|
363
|
-
-
|
|
364
|
-
|
|
647
|
+
## Performance Tips
|
|
648
|
+
|
|
649
|
+
1. **Use lazy body parsing** - Only parse bodies when needed
|
|
650
|
+
2. **Enable compression** at reverse proxy level (nginx, CDN)
|
|
651
|
+
3. **Use streaming** for large responses:
|
|
652
|
+
```javascript
|
|
653
|
+
import {createReadStream} from 'fs'
|
|
654
|
+
setNodeStream(createReadStream('large-file.pdf'))
|
|
655
|
+
```
|
|
656
|
+
4. **Cache contexts** that are expensive to compute
|
|
657
|
+
5. **Use `chainStatic` with proper cache headers** for static assets
|
|
658
|
+
|
|
659
|
+
## Migration from Express
|
|
660
|
+
|
|
661
|
+
```javascript
|
|
662
|
+
// Express
|
|
663
|
+
app.get('/users/:id', (req, res) => {
|
|
664
|
+
const {id} = req.params
|
|
665
|
+
res.json({userId: id})
|
|
666
|
+
})
|
|
667
|
+
|
|
668
|
+
// dx-server
|
|
669
|
+
router.get('/users/:id', ({matched}) => {
|
|
670
|
+
const {id} = matched.pathname.groups
|
|
671
|
+
setJson({userId: id})
|
|
672
|
+
})
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
## Contributing
|
|
676
|
+
|
|
677
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
678
|
+
|
|
679
|
+
## License
|
|
680
|
+
|
|
681
|
+
MIT © [Sang Tran](https://github.com/tranvansang)
|
package/cjs/router.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.router = void 0;
|
|
4
4
|
const dx_js_1 = require("./dx.js");
|
|
5
|
-
require("urlpattern-polyfill");
|
|
6
5
|
const bodyHelpers_js_1 = require("./bodyHelpers.js");
|
|
7
6
|
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods
|
|
8
7
|
const allMethods = [
|
|
@@ -44,4 +43,4 @@ exports.router = {
|
|
|
44
43
|
};
|
|
45
44
|
for (const method of allMethods)
|
|
46
45
|
exports.router[method] = exports.router.method.bind(exports.router, method);
|
|
47
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
46
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3JvdXRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSxtQ0FBeUM7QUFDekMscURBQTJDO0FBa0IzQyw0REFBNEQ7QUFDNUQsTUFBTSxVQUFVLEdBQUc7SUFDbEIsS0FBSyxFQUFFLE1BQU0sRUFBRSxNQUFNLEVBQUUsS0FBSyxFQUFFLFFBQVEsRUFBRSxTQUFTLEVBQUUsU0FBUyxFQUFFLE9BQU8sRUFBRSxPQUFPO0NBQ3JFLENBQUE7QUErQlYsU0FBUyxVQUFVLENBQ2xCLE1BQTBCLEVBQUUsNkJBQTZCO0FBQ3pELE1BQXlDLEVBQ3pDLEVBQUMsTUFBTSxHQUFHLEVBQUUsRUFBRSxHQUFHLE9BQU8sS0FBbUIsRUFBRTtJQUU3QyxPQUFPLElBQUksQ0FBQyxFQUFFO1FBQ2IsTUFBTSxHQUFHLEdBQUcsSUFBQSxjQUFNLEdBQUUsQ0FBQTtRQUNwQixJQUFJLE1BQU0sS0FBSyxTQUFTLElBQUksR0FBRyxDQUFDLE1BQU0sS0FBSyxNQUFNLENBQUMsV0FBVyxFQUFFO1lBQUUsT0FBTyxJQUFJLEVBQUUsQ0FBQTtRQUM5RSxLQUFLLE1BQU0sQ0FBQyxPQUFPLEVBQUUsT0FBTyxDQUFDLElBQUksTUFBTSxFQUFFLENBQUM7WUFDNUMscUJBQXFCO1lBQ3JCLGdFQUFnRTtZQUNoRSwwRUFBMEU7WUFDMUUsMkVBQTJFO1lBRXhFLHNDQUFzQztZQUN0QywyRUFBMkU7WUFDM0Usa0JBQWtCO1lBQ2xCLHdEQUF3RDtZQUN4RCxNQUFNLE9BQU8sR0FBRyxJQUFJLFVBQVUsQ0FBQyxFQUFDLFFBQVEsRUFBRSxHQUFHLE1BQU0sR0FBRyxPQUFPLEVBQUUsRUFBQyxDQUFDLENBQUMsSUFBSSxDQUFDLEVBQUMsUUFBUSxFQUFFLElBQUEsMkJBQVUsRUFBQyxHQUFHLENBQUMsQ0FBQyxRQUFRLEVBQUMsQ0FBQyxDQUFBO1lBQzVHLElBQUksT0FBTztnQkFBRSxPQUFPLE9BQU8sQ0FBQyxFQUFDLE9BQU8sRUFBRSxJQUFJLEVBQUMsQ0FBQyxDQUFBO1FBQzdDLENBQUM7UUFDRCxPQUFPLElBQUksRUFBRSxDQUFBO0lBQ2QsQ0FBQyxDQUFBO0FBQ0YsQ0FBQztBQUNZLFFBQUEsTUFBTSxHQUFXO0lBQzdCLE1BQU0sQ0FBQyxNQUFNLEVBQUUsR0FBRyxNQUFNO1FBQ3ZCLE9BQU8sT0FBTyxNQUFNLENBQUMsQ0FBQyxDQUFDLEtBQUssUUFBUTtZQUNuQyxDQUFDLENBQUMsVUFBVSxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQ3pELENBQUMsQ0FBQyxVQUFVLENBQUMsTUFBTSxFQUFFLE1BQU0sQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUE7SUFDNUQsQ0FBQztJQUNELEdBQUcsQ0FBQyxHQUFHLE1BQU07UUFDWixPQUFPLE9BQU8sTUFBTSxDQUFDLENBQUMsQ0FBQyxLQUFLLFFBQVE7WUFDbkMsQ0FBQyxDQUFDLFVBQVUsQ0FBQyxTQUFTLEVBQUUsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUM1RCxDQUFDLENBQUMsVUFBVSxDQUFDLFNBQVMsRUFBRSxNQUFNLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFBO0lBQy9ELENBQUM7Q0FDRCxDQUFBO0FBRUQsS0FBSyxNQUFNLE1BQU0sSUFBSSxVQUFVO0lBQUUsY0FBTSxDQUFDLE1BQU0sQ0FBQyxHQUFHLGNBQU0sQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLGNBQU0sRUFBRSxNQUFNLENBQUMsQ0FBQSJ9
|
package/esm/router.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { getReq } from './dx.js';
|
|
2
|
-
import 'urlpattern-polyfill';
|
|
3
2
|
import { urlFromReq } from './bodyHelpers.js';
|
|
4
3
|
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods
|
|
5
4
|
const allMethods = [
|
|
@@ -41,4 +40,4 @@ export const router = {
|
|
|
41
40
|
};
|
|
42
41
|
for (const method of allMethods)
|
|
43
42
|
router[method] = router.method.bind(router, method);
|
|
44
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
43
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3JvdXRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQVksTUFBTSxFQUFDLE1BQU0sU0FBUyxDQUFBO0FBQ3pDLE9BQU8sRUFBQyxVQUFVLEVBQUMsTUFBTSxrQkFBa0IsQ0FBQTtBQWtCM0MsNERBQTREO0FBQzVELE1BQU0sVUFBVSxHQUFHO0lBQ2xCLEtBQUssRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLEtBQUssRUFBRSxRQUFRLEVBQUUsU0FBUyxFQUFFLFNBQVMsRUFBRSxPQUFPLEVBQUUsT0FBTztDQUNyRSxDQUFBO0FBK0JWLFNBQVMsVUFBVSxDQUNsQixNQUEwQixFQUFFLDZCQUE2QjtBQUN6RCxNQUF5QyxFQUN6QyxFQUFDLE1BQU0sR0FBRyxFQUFFLEVBQUUsR0FBRyxPQUFPLEtBQW1CLEVBQUU7SUFFN0MsT0FBTyxJQUFJLENBQUMsRUFBRTtRQUNiLE1BQU0sR0FBRyxHQUFHLE1BQU0sRUFBRSxDQUFBO1FBQ3BCLElBQUksTUFBTSxLQUFLLFNBQVMsSUFBSSxHQUFHLENBQUMsTUFBTSxLQUFLLE1BQU0sQ0FBQyxXQUFXLEVBQUU7WUFBRSxPQUFPLElBQUksRUFBRSxDQUFBO1FBQzlFLEtBQUssTUFBTSxDQUFDLE9BQU8sRUFBRSxPQUFPLENBQUMsSUFBSSxNQUFNLEVBQUUsQ0FBQztZQUM1QyxxQkFBcUI7WUFDckIsZ0VBQWdFO1lBQ2hFLDBFQUEwRTtZQUMxRSwyRUFBMkU7WUFFeEUsc0NBQXNDO1lBQ3RDLDJFQUEyRTtZQUMzRSxrQkFBa0I7WUFDbEIsd0RBQXdEO1lBQ3hELE1BQU0sT0FBTyxHQUFHLElBQUksVUFBVSxDQUFDLEVBQUMsUUFBUSxFQUFFLEdBQUcsTUFBTSxHQUFHLE9BQU8sRUFBRSxFQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsRUFBQyxRQUFRLEVBQUUsVUFBVSxDQUFDLEdBQUcsQ0FBQyxDQUFDLFFBQVEsRUFBQyxDQUFDLENBQUE7WUFDNUcsSUFBSSxPQUFPO2dCQUFFLE9BQU8sT0FBTyxDQUFDLEVBQUMsT0FBTyxFQUFFLElBQUksRUFBQyxDQUFDLENBQUE7UUFDN0MsQ0FBQztRQUNELE9BQU8sSUFBSSxFQUFFLENBQUE7SUFDZCxDQUFDLENBQUE7QUFDRixDQUFDO0FBQ0QsTUFBTSxDQUFDLE1BQU0sTUFBTSxHQUFXO0lBQzdCLE1BQU0sQ0FBQyxNQUFNLEVBQUUsR0FBRyxNQUFNO1FBQ3ZCLE9BQU8sT0FBTyxNQUFNLENBQUMsQ0FBQyxDQUFDLEtBQUssUUFBUTtZQUNuQyxDQUFDLENBQUMsVUFBVSxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQ3pELENBQUMsQ0FBQyxVQUFVLENBQUMsTUFBTSxFQUFFLE1BQU0sQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUE7SUFDNUQsQ0FBQztJQUNELEdBQUcsQ0FBQyxHQUFHLE1BQU07UUFDWixPQUFPLE9BQU8sTUFBTSxDQUFDLENBQUMsQ0FBQyxLQUFLLFFBQVE7WUFDbkMsQ0FBQyxDQUFDLFVBQVUsQ0FBQyxTQUFTLEVBQUUsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUM1RCxDQUFDLENBQUMsVUFBVSxDQUFDLFNBQVMsRUFBRSxNQUFNLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFBO0lBQy9ELENBQUM7Q0FDRCxDQUFBO0FBRUQsS0FBSyxNQUFNLE1BQU0sSUFBSSxVQUFVO0lBQUUsTUFBTSxDQUFDLE1BQU0sQ0FBQyxHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxNQUFNLENBQUMsQ0FBQSJ9
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dx-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"main": "./cjs/index.js",
|
|
5
5
|
"homepage": "https://github.com/tranvansang/dx-server",
|
|
6
6
|
"repository": "https://github.com/tranvansang/dx-server",
|
|
@@ -24,6 +24,9 @@
|
|
|
24
24
|
}
|
|
25
25
|
},
|
|
26
26
|
"type": "module",
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=16.0.0"
|
|
29
|
+
},
|
|
27
30
|
"scripts": {
|
|
28
31
|
"prepublishOnly": "./compile.sh"
|
|
29
32
|
},
|
|
@@ -34,7 +37,6 @@
|
|
|
34
37
|
"typescript": "^5.4.5"
|
|
35
38
|
},
|
|
36
39
|
"dependencies": {
|
|
37
|
-
"send": "^0.18.0"
|
|
38
|
-
"urlpattern-polyfill": "^10.0.0"
|
|
40
|
+
"send": "^0.18.0"
|
|
39
41
|
}
|
|
40
42
|
}
|
package/cjs/router.d.ts
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { Chainable } from './dx.js';
|
|
2
|
-
import 'urlpattern-polyfill';
|
|
3
|
-
interface URLPatternOptions {
|
|
4
|
-
}
|
|
5
|
-
interface RouteContext {
|
|
6
|
-
matched: URLPatternResult;
|
|
7
|
-
next(): any;
|
|
8
|
-
}
|
|
9
|
-
interface Route {
|
|
10
|
-
(context: RouteContext): any;
|
|
11
|
-
}
|
|
12
|
-
interface Routes {
|
|
13
|
-
[k: string]: Route;
|
|
14
|
-
}
|
|
15
|
-
interface RouterOptions extends URLPatternOptions {
|
|
16
|
-
prefix?: string;
|
|
17
|
-
}
|
|
18
|
-
type Router = {
|
|
19
|
-
patch(routes: Routes, options?: RouterOptions): Chainable;
|
|
20
|
-
patch(pattern: string, route: Route, options?: RouterOptions): Chainable;
|
|
21
|
-
trace(routes: Routes, options?: RouterOptions): Chainable;
|
|
22
|
-
trace(pattern: string, route: Route, options?: RouterOptions): Chainable;
|
|
23
|
-
options(routes: Routes, options?: RouterOptions): Chainable;
|
|
24
|
-
options(pattern: string, route: Route, options?: RouterOptions): Chainable;
|
|
25
|
-
connect(routes: Routes, options?: RouterOptions): Chainable;
|
|
26
|
-
connect(pattern: string, route: Route, options?: RouterOptions): Chainable;
|
|
27
|
-
delete(routes: Routes, options?: RouterOptions): Chainable;
|
|
28
|
-
delete(pattern: string, route: Route, options?: RouterOptions): Chainable;
|
|
29
|
-
put(routes: Routes, options?: RouterOptions): Chainable;
|
|
30
|
-
put(pattern: string, route: Route, options?: RouterOptions): Chainable;
|
|
31
|
-
post(routes: Routes, options?: RouterOptions): Chainable;
|
|
32
|
-
post(pattern: string, route: Route, options?: RouterOptions): Chainable;
|
|
33
|
-
head(routes: Routes, options?: RouterOptions): Chainable;
|
|
34
|
-
head(pattern: string, route: Route, options?: RouterOptions): Chainable;
|
|
35
|
-
get(routes: Routes, options?: RouterOptions): Chainable;
|
|
36
|
-
get(pattern: string, route: Route, options?: RouterOptions): Chainable;
|
|
37
|
-
all(routes: Routes, options?: RouterOptions): Chainable;
|
|
38
|
-
all(pattern: string, route: Route, options?: RouterOptions): Chainable;
|
|
39
|
-
method(method: string, routes: Routes, options?: RouterOptions): Chainable;
|
|
40
|
-
method(method: string, pattern: string, route: Route, options?: RouterOptions): Chainable;
|
|
41
|
-
};
|
|
42
|
-
export declare const router: Router;
|
|
43
|
-
export {};
|
package/cjs/static.d.ts
DELETED
package/esm/router.d.ts
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { Chainable } from './dx.js';
|
|
2
|
-
import 'urlpattern-polyfill';
|
|
3
|
-
interface URLPatternOptions {
|
|
4
|
-
}
|
|
5
|
-
interface RouteContext {
|
|
6
|
-
matched: URLPatternResult;
|
|
7
|
-
next(): any;
|
|
8
|
-
}
|
|
9
|
-
interface Route {
|
|
10
|
-
(context: RouteContext): any;
|
|
11
|
-
}
|
|
12
|
-
interface Routes {
|
|
13
|
-
[k: string]: Route;
|
|
14
|
-
}
|
|
15
|
-
interface RouterOptions extends URLPatternOptions {
|
|
16
|
-
prefix?: string;
|
|
17
|
-
}
|
|
18
|
-
type Router = {
|
|
19
|
-
patch(routes: Routes, options?: RouterOptions): Chainable;
|
|
20
|
-
patch(pattern: string, route: Route, options?: RouterOptions): Chainable;
|
|
21
|
-
trace(routes: Routes, options?: RouterOptions): Chainable;
|
|
22
|
-
trace(pattern: string, route: Route, options?: RouterOptions): Chainable;
|
|
23
|
-
options(routes: Routes, options?: RouterOptions): Chainable;
|
|
24
|
-
options(pattern: string, route: Route, options?: RouterOptions): Chainable;
|
|
25
|
-
connect(routes: Routes, options?: RouterOptions): Chainable;
|
|
26
|
-
connect(pattern: string, route: Route, options?: RouterOptions): Chainable;
|
|
27
|
-
delete(routes: Routes, options?: RouterOptions): Chainable;
|
|
28
|
-
delete(pattern: string, route: Route, options?: RouterOptions): Chainable;
|
|
29
|
-
put(routes: Routes, options?: RouterOptions): Chainable;
|
|
30
|
-
put(pattern: string, route: Route, options?: RouterOptions): Chainable;
|
|
31
|
-
post(routes: Routes, options?: RouterOptions): Chainable;
|
|
32
|
-
post(pattern: string, route: Route, options?: RouterOptions): Chainable;
|
|
33
|
-
head(routes: Routes, options?: RouterOptions): Chainable;
|
|
34
|
-
head(pattern: string, route: Route, options?: RouterOptions): Chainable;
|
|
35
|
-
get(routes: Routes, options?: RouterOptions): Chainable;
|
|
36
|
-
get(pattern: string, route: Route, options?: RouterOptions): Chainable;
|
|
37
|
-
all(routes: Routes, options?: RouterOptions): Chainable;
|
|
38
|
-
all(pattern: string, route: Route, options?: RouterOptions): Chainable;
|
|
39
|
-
method(method: string, routes: Routes, options?: RouterOptions): Chainable;
|
|
40
|
-
method(method: string, pattern: string, route: Route, options?: RouterOptions): Chainable;
|
|
41
|
-
};
|
|
42
|
-
export declare const router: Router;
|
|
43
|
-
export {};
|
package/esm/static.d.ts
DELETED