spiceflow 1.1.1 → 1.1.3
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/dist/benchmark.benchmark.d.ts +2 -0
- package/dist/benchmark.benchmark.d.ts.map +1 -0
- package/dist/benchmark.benchmark.js +13 -0
- package/dist/benchmark.benchmark.js.map +1 -0
- package/dist/context.d.ts +0 -3
- package/dist/context.d.ts.map +1 -1
- package/dist/cors.d.ts +22 -0
- package/dist/cors.d.ts.map +1 -0
- package/dist/cors.js +76 -0
- package/dist/cors.js.map +1 -0
- package/dist/cors.test.d.ts +2 -0
- package/dist/cors.test.d.ts.map +1 -0
- package/dist/cors.test.js +39 -0
- package/dist/cors.test.js.map +1 -0
- package/dist/error.d.ts +0 -4
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js +0 -7
- package/dist/error.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/middleware.test.js +37 -0
- package/dist/middleware.test.js.map +1 -1
- package/dist/spiceflow.d.ts +0 -11
- package/dist/spiceflow.d.ts.map +1 -1
- package/dist/spiceflow.js +50 -31
- package/dist/spiceflow.js.map +1 -1
- package/dist/spiceflow.test.js +17 -0
- package/dist/spiceflow.test.js.map +1 -1
- package/dist/types.d.ts +3 -33
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +23 -1
- package/src/benchmark.benchmark.ts +16 -0
- package/src/context.ts +3 -3
- package/src/cors.test.ts +44 -0
- package/src/cors.ts +115 -0
- package/src/error.ts +0 -8
- package/src/index.ts +1 -0
- package/src/middleware.test.ts +39 -0
- package/src/spiceflow.test.ts +24 -0
- package/src/spiceflow.ts +55 -69
- package/src/types.test.ts +1 -0
- package/src/types.ts +10 -35
- package/dist/benchmark.test.d.ts +0 -2
- package/dist/benchmark.test.d.ts.map +0 -1
- package/dist/benchmark.test.js +0 -8
- package/dist/benchmark.test.js.map +0 -1
- package/src/benchmark.test.ts +0 -8
package/src/cors.ts
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { MiddlewareHandler } from './types.js'
|
|
2
|
+
/**
|
|
3
|
+
* Options for configuring CORS (Cross-Origin Resource Sharing) middleware.
|
|
4
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS MDN CORS documentation}
|
|
5
|
+
*/
|
|
6
|
+
type CORSOptions = {
|
|
7
|
+
/** Configures the Access-Control-Allow-Origin CORS header */
|
|
8
|
+
origin: string | string[]
|
|
9
|
+
/** Configures the Access-Control-Allow-Methods CORS header */
|
|
10
|
+
allowMethods?: string[]
|
|
11
|
+
/** Configures the Access-Control-Allow-Headers CORS header */
|
|
12
|
+
allowHeaders?: string[]
|
|
13
|
+
/** Configures the Access-Control-Max-Age CORS header */
|
|
14
|
+
maxAge?: number
|
|
15
|
+
/** Configures the Access-Control-Allow-Credentials CORS header */
|
|
16
|
+
credentials?: boolean
|
|
17
|
+
/** Configures the Access-Control-Expose-Headers CORS header */
|
|
18
|
+
exposeHeaders?: string[]
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const cors = (options?: CORSOptions): MiddlewareHandler => {
|
|
22
|
+
const defaults: CORSOptions = {
|
|
23
|
+
origin: '*',
|
|
24
|
+
allowMethods: ['GET', 'HEAD', 'PUT', 'POST', 'DELETE', 'PATCH'],
|
|
25
|
+
allowHeaders: [],
|
|
26
|
+
exposeHeaders: [],
|
|
27
|
+
}
|
|
28
|
+
const opts = {
|
|
29
|
+
...defaults,
|
|
30
|
+
...options,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const findAllowOrigin = ((optsOrigin) => {
|
|
34
|
+
if (typeof optsOrigin === 'string') {
|
|
35
|
+
return () => optsOrigin
|
|
36
|
+
} else if (typeof optsOrigin === 'function') {
|
|
37
|
+
return optsOrigin
|
|
38
|
+
} else {
|
|
39
|
+
return (origin: string) =>
|
|
40
|
+
optsOrigin.includes(origin) ? origin : optsOrigin[0]
|
|
41
|
+
}
|
|
42
|
+
})(opts.origin)
|
|
43
|
+
|
|
44
|
+
return async function cors(c, next) {
|
|
45
|
+
let response = await next()
|
|
46
|
+
|
|
47
|
+
function set(key: string, value: string) {
|
|
48
|
+
response.headers.set(key, value)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const allowOrigin = findAllowOrigin(
|
|
52
|
+
c.request.headers.get('origin') || '',
|
|
53
|
+
)
|
|
54
|
+
if (allowOrigin) {
|
|
55
|
+
set('Access-Control-Allow-Origin', allowOrigin)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Suppose the server sends a response with an Access-Control-Allow-Origin value with an explicit origin (rather than the "*" wildcard).
|
|
59
|
+
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
|
|
60
|
+
if (opts.origin !== '*') {
|
|
61
|
+
const existingVary = c.request.headers.get('Vary')
|
|
62
|
+
|
|
63
|
+
if (existingVary) {
|
|
64
|
+
set('Vary', existingVary)
|
|
65
|
+
} else {
|
|
66
|
+
set('Vary', 'Origin')
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (opts.credentials) {
|
|
71
|
+
set('Access-Control-Allow-Credentials', 'true')
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (opts.exposeHeaders?.length) {
|
|
75
|
+
set('Access-Control-Expose-Headers', opts.exposeHeaders.join(','))
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (c.request.method === 'OPTIONS') {
|
|
79
|
+
if (opts.maxAge != null) {
|
|
80
|
+
set('Access-Control-Max-Age', opts.maxAge.toString())
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (opts.allowMethods?.length) {
|
|
84
|
+
set('Access-Control-Allow-Methods', opts.allowMethods.join(','))
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
let headers = opts.allowHeaders
|
|
88
|
+
if (!headers?.length) {
|
|
89
|
+
const requestHeaders = c.request.headers.get(
|
|
90
|
+
'Access-Control-Request-Headers',
|
|
91
|
+
)
|
|
92
|
+
if (requestHeaders) {
|
|
93
|
+
headers = requestHeaders.split(/\s*,\s*/)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (headers?.length) {
|
|
97
|
+
set('Access-Control-Allow-Headers', headers.join(','))
|
|
98
|
+
c.request.headers.append(
|
|
99
|
+
'Vary',
|
|
100
|
+
'Access-Control-Request-Headers',
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
response.headers.delete('Content-Length')
|
|
105
|
+
response.headers.delete('Content-Type')
|
|
106
|
+
|
|
107
|
+
return new Response(null, {
|
|
108
|
+
headers: response.headers,
|
|
109
|
+
status: 204,
|
|
110
|
+
statusText: response.statusText,
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
return response
|
|
114
|
+
}
|
|
115
|
+
}
|
package/src/error.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
1
|
// ? Cloudflare worker support
|
|
4
2
|
const env =
|
|
5
3
|
// @ts-ignore
|
|
@@ -23,11 +21,6 @@ export class ValidationError extends Error {
|
|
|
23
21
|
status = 422
|
|
24
22
|
}
|
|
25
23
|
|
|
26
|
-
export class NotFoundError extends Error {
|
|
27
|
-
code = 'NOT_FOUND'
|
|
28
|
-
status = 404
|
|
29
|
-
}
|
|
30
|
-
|
|
31
24
|
export class ParseError extends Error {
|
|
32
25
|
code = 'PARSE'
|
|
33
26
|
status = 400
|
|
@@ -37,4 +30,3 @@ export class InternalServerError extends Error {
|
|
|
37
30
|
code = 'INTERNAL_SERVER_ERROR'
|
|
38
31
|
status = 500
|
|
39
32
|
}
|
|
40
|
-
|
package/src/index.ts
CHANGED
package/src/middleware.test.ts
CHANGED
|
@@ -23,6 +23,22 @@ test('middleware with next changes the response', async () => {
|
|
|
23
23
|
expect(await res.json()).toEqual('hi')
|
|
24
24
|
expect(res.headers.get('x-test')).toBe('ok')
|
|
25
25
|
})
|
|
26
|
+
test('middleware next returns a response even for 404, if there are no routes', async () => {
|
|
27
|
+
const res = await new Spiceflow()
|
|
28
|
+
.use(async ({ request }, next) => {
|
|
29
|
+
expect(request.method).toBe('GET')
|
|
30
|
+
const res = await next()
|
|
31
|
+
expect(res).toBeInstanceOf(Response)
|
|
32
|
+
if (res) {
|
|
33
|
+
res.headers.set('x-test', 'ok')
|
|
34
|
+
}
|
|
35
|
+
return res
|
|
36
|
+
})
|
|
37
|
+
.handle(new Request('http://localhost/non-existent', { method: 'GET' }))
|
|
38
|
+
expect(res.status).toBe(404)
|
|
39
|
+
expect(res.headers.get('x-test')).toBe('ok')
|
|
40
|
+
expect(await res.text()).toContain('Not Found')
|
|
41
|
+
})
|
|
26
42
|
|
|
27
43
|
test('middleware without next runs the next middleware and handler', async () => {
|
|
28
44
|
let middlewaresCalled = [] as string[]
|
|
@@ -82,6 +98,29 @@ test('middleware stops other middlewares', async () => {
|
|
|
82
98
|
expect(await res.text()).toEqual('ok')
|
|
83
99
|
})
|
|
84
100
|
|
|
101
|
+
test('calling next and then returning a new response works', async () => {
|
|
102
|
+
let middlewaresCalled = [] as string[]
|
|
103
|
+
const res = await new Spiceflow()
|
|
104
|
+
.use(async (ctx, next) => {
|
|
105
|
+
middlewaresCalled.push('first')
|
|
106
|
+
await next()
|
|
107
|
+
return new Response('middleware response')
|
|
108
|
+
})
|
|
109
|
+
.use(async (ctx, next) => {
|
|
110
|
+
middlewaresCalled.push('second')
|
|
111
|
+
return next()
|
|
112
|
+
})
|
|
113
|
+
.get('/ids/:id', () => {
|
|
114
|
+
middlewaresCalled.push('handler')
|
|
115
|
+
return 'handler response'
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
.handle(new Request('http://localhost/ids/xxx', { method: 'GET' }))
|
|
119
|
+
expect(res.status).toBe(200)
|
|
120
|
+
expect(middlewaresCalled).toEqual(['first', 'second', 'handler'])
|
|
121
|
+
expect(await res.text()).toEqual('middleware response')
|
|
122
|
+
})
|
|
123
|
+
|
|
85
124
|
test('middleware changes handler response body', async () => {
|
|
86
125
|
let middlewaresCalled = [] as string[]
|
|
87
126
|
const res = await new Spiceflow()
|
package/src/spiceflow.test.ts
CHANGED
|
@@ -28,6 +28,30 @@ test('GET dynamic route', async () => {
|
|
|
28
28
|
expect(await res.json()).toEqual('hi')
|
|
29
29
|
})
|
|
30
30
|
|
|
31
|
+
test.todo('HEAD uses GET route, does not add body', async () => {
|
|
32
|
+
const app = new Spiceflow().get('/ids/:id', () => {
|
|
33
|
+
console.trace('GET')
|
|
34
|
+
return {
|
|
35
|
+
message: 'hi',
|
|
36
|
+
length: 10,
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
const res = await app.handle(
|
|
41
|
+
new Request('http://localhost/ids/xxx', { method: 'HEAD' }),
|
|
42
|
+
)
|
|
43
|
+
expect(res.status).toBe(200)
|
|
44
|
+
// expect(res.headers.get('Content-Length')).toBe('10')
|
|
45
|
+
expect(await res.text()).toBe('')
|
|
46
|
+
|
|
47
|
+
// Compare with GET to ensure HEAD is using GET route
|
|
48
|
+
const getRes = await app.handle(
|
|
49
|
+
new Request('http://localhost/ids/xxx', { method: 'GET' }),
|
|
50
|
+
)
|
|
51
|
+
expect(getRes.status).toBe(200)
|
|
52
|
+
expect(await getRes.json()).toEqual({ message: 'hi', length: 10 })
|
|
53
|
+
})
|
|
54
|
+
|
|
31
55
|
test('GET with query, untyped', async () => {
|
|
32
56
|
const res = await new Spiceflow()
|
|
33
57
|
.get('/query', ({ query }) => {
|
package/src/spiceflow.ts
CHANGED
|
@@ -17,7 +17,6 @@ import {
|
|
|
17
17
|
JoinPath,
|
|
18
18
|
LocalHook,
|
|
19
19
|
MaybeArray,
|
|
20
|
-
MergeSchema,
|
|
21
20
|
MetadataBase,
|
|
22
21
|
MiddlewareHandler,
|
|
23
22
|
Reconcile,
|
|
@@ -30,14 +29,12 @@ import {
|
|
|
30
29
|
} from './types.js'
|
|
31
30
|
let globalIndex = 0
|
|
32
31
|
|
|
33
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
34
|
-
// @ts-ignore
|
|
35
32
|
import OriginalRouter from '@medley/router'
|
|
36
33
|
import Ajv, { ValidateFunction } from 'ajv'
|
|
37
34
|
import { z, ZodType } from 'zod'
|
|
38
35
|
import { zodToJsonSchema } from 'zod-to-json-schema'
|
|
39
36
|
import { Context, MiddlewareContext } from './context.js'
|
|
40
|
-
import { isProduction,
|
|
37
|
+
import { isProduction, ValidationError } from './error.js'
|
|
41
38
|
import { isAsyncIterable, redirect } from './utils.js'
|
|
42
39
|
|
|
43
40
|
const ajv = (addFormats.default || addFormats)(
|
|
@@ -74,7 +71,7 @@ export type InternalRoute = {
|
|
|
74
71
|
validateBody?: ValidateFunction
|
|
75
72
|
validateQuery?: ValidateFunction
|
|
76
73
|
validateParams?: ValidateFunction
|
|
77
|
-
prefix: string
|
|
74
|
+
// prefix: string
|
|
78
75
|
|
|
79
76
|
// store: Record<any, any>
|
|
80
77
|
}
|
|
@@ -88,9 +85,11 @@ type MedleyRouter = {
|
|
|
88
85
|
| undefined
|
|
89
86
|
register: (path: string | undefined) => Record<string, InternalRoute>
|
|
90
87
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
88
|
+
|
|
89
|
+
const notFoundHandler = (c) => {
|
|
90
|
+
return new Response('Not Found', { status: 404 })
|
|
91
|
+
}
|
|
92
|
+
|
|
94
93
|
export class Spiceflow<
|
|
95
94
|
const in out BasePath extends string = '',
|
|
96
95
|
const in out Scoped extends boolean = true,
|
|
@@ -151,7 +150,6 @@ export class Spiceflow<
|
|
|
151
150
|
const store = this.router.register(path)
|
|
152
151
|
let route: InternalRoute = {
|
|
153
152
|
...rest,
|
|
154
|
-
prefix: this.prefix || '',
|
|
155
153
|
method: (method || '') as any,
|
|
156
154
|
path: path || '',
|
|
157
155
|
handler: handler!,
|
|
@@ -166,6 +164,7 @@ export class Spiceflow<
|
|
|
166
164
|
|
|
167
165
|
private match(method: string, path: string) {
|
|
168
166
|
let root = this
|
|
167
|
+
let foundApp: AnySpiceflow | undefined
|
|
169
168
|
const result = bfsFind(this, (app) => {
|
|
170
169
|
app.topLevelApp = root
|
|
171
170
|
let prefix = this.getAppAndParents(app)
|
|
@@ -180,10 +179,12 @@ export class Spiceflow<
|
|
|
180
179
|
}
|
|
181
180
|
const medleyRoute = app.router.find(pathWithoutPrefix)
|
|
182
181
|
if (!medleyRoute) {
|
|
182
|
+
foundApp = app
|
|
183
183
|
return
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
let internalRoute: InternalRoute = medleyRoute.store[method]
|
|
187
|
+
|
|
187
188
|
if (internalRoute) {
|
|
188
189
|
const params = medleyRoute.params || {}
|
|
189
190
|
|
|
@@ -194,9 +195,46 @@ export class Spiceflow<
|
|
|
194
195
|
}
|
|
195
196
|
return res
|
|
196
197
|
}
|
|
198
|
+
if (method === 'HEAD') {
|
|
199
|
+
let internalRouteGet: InternalRoute = medleyRoute.store['GET']
|
|
200
|
+
if (!internalRouteGet?.handler) {
|
|
201
|
+
return
|
|
202
|
+
}
|
|
203
|
+
return {
|
|
204
|
+
app,
|
|
205
|
+
internalRoute: {
|
|
206
|
+
hooks: {},
|
|
207
|
+
handler: async (c) => {
|
|
208
|
+
const response = await internalRouteGet.handler(c)
|
|
209
|
+
if (response instanceof Response) {
|
|
210
|
+
return new Response('', {
|
|
211
|
+
status: response.status,
|
|
212
|
+
statusText: response.statusText,
|
|
213
|
+
headers: response.headers,
|
|
214
|
+
})
|
|
215
|
+
}
|
|
216
|
+
return new Response(null, { status: 200 })
|
|
217
|
+
},
|
|
218
|
+
method,
|
|
219
|
+
path,
|
|
220
|
+
} as InternalRoute,
|
|
221
|
+
params: medleyRoute.params,
|
|
222
|
+
}
|
|
223
|
+
}
|
|
197
224
|
})
|
|
198
225
|
|
|
199
|
-
return
|
|
226
|
+
return (
|
|
227
|
+
result || {
|
|
228
|
+
app: foundApp || root,
|
|
229
|
+
internalRoute: {
|
|
230
|
+
hooks: {},
|
|
231
|
+
handler: notFoundHandler,
|
|
232
|
+
method,
|
|
233
|
+
path,
|
|
234
|
+
} as InternalRoute,
|
|
235
|
+
params: {},
|
|
236
|
+
}
|
|
237
|
+
)
|
|
200
238
|
}
|
|
201
239
|
|
|
202
240
|
state<const Name extends string | number | symbol, Value>(
|
|
@@ -667,45 +705,6 @@ export class Spiceflow<
|
|
|
667
705
|
|
|
668
706
|
private scoped?: Scoped = true as Scoped
|
|
669
707
|
|
|
670
|
-
// group is not needed, you can add another prefixed app instead
|
|
671
|
-
// group<
|
|
672
|
-
// const Prefix extends string,
|
|
673
|
-
// const NewSpiceflow extends Spiceflow<any, any, any, any, any, any, any, any>
|
|
674
|
-
// >(
|
|
675
|
-
// prefix: Prefix,
|
|
676
|
-
// run: (
|
|
677
|
-
// group: Spiceflow<
|
|
678
|
-
// `${BasePath}${Prefix}`,
|
|
679
|
-
// Scoped,
|
|
680
|
-
// Singleton,
|
|
681
|
-
// Definitions,
|
|
682
|
-
// Metadata,
|
|
683
|
-
// {},
|
|
684
|
-
// Ephemeral,
|
|
685
|
-
// Volatile
|
|
686
|
-
// >
|
|
687
|
-
// ) => NewSpiceflow
|
|
688
|
-
// ): Spiceflow<
|
|
689
|
-
// BasePath,
|
|
690
|
-
// Scoped,
|
|
691
|
-
// Singleton,
|
|
692
|
-
// Definitions,
|
|
693
|
-
// Metadata,
|
|
694
|
-
// Prettify<Routes & NewSpiceflow['_routes']>,
|
|
695
|
-
// Ephemeral,
|
|
696
|
-
// Volatile
|
|
697
|
-
// > {
|
|
698
|
-
// let thisRouter = this.routers[0]
|
|
699
|
-
// this.routers.push(
|
|
700
|
-
// ...instance.routers.map((r) => ({
|
|
701
|
-
// ...r,
|
|
702
|
-
// prefix: (thisRouter.prefix || '') + r.prefix
|
|
703
|
-
// }))
|
|
704
|
-
// )
|
|
705
|
-
|
|
706
|
-
// return this
|
|
707
|
-
// }
|
|
708
|
-
|
|
709
708
|
use<const NewSpiceflow extends AnySpiceflow>(
|
|
710
709
|
instance: NewSpiceflow,
|
|
711
710
|
): IsAny<NewSpiceflow> extends true
|
|
@@ -752,12 +751,6 @@ export class Spiceflow<
|
|
|
752
751
|
return this
|
|
753
752
|
}
|
|
754
753
|
|
|
755
|
-
/**
|
|
756
|
-
* Pass a request through all matching route handles and return a response
|
|
757
|
-
* @param request The `Request`
|
|
758
|
-
* @param platform Platform specific context {@link Platform}
|
|
759
|
-
* @returns The final `Response`
|
|
760
|
-
*/
|
|
761
754
|
async handle(request: Request): Promise<Response> {
|
|
762
755
|
let u = new URL(request.url, 'http://localhost')
|
|
763
756
|
let path = u.pathname + u.search
|
|
@@ -773,18 +766,6 @@ export class Spiceflow<
|
|
|
773
766
|
|
|
774
767
|
const route = this.match(request.method, path)
|
|
775
768
|
|
|
776
|
-
if (!route) {
|
|
777
|
-
const error = new NotFoundError(`${path} not found`)
|
|
778
|
-
const res = await this.runErrorHandlers({
|
|
779
|
-
onErrorHandlers,
|
|
780
|
-
error,
|
|
781
|
-
request,
|
|
782
|
-
})
|
|
783
|
-
if (res) return res
|
|
784
|
-
return new Response(`Not Found`, {
|
|
785
|
-
status: 404,
|
|
786
|
-
})
|
|
787
|
-
}
|
|
788
769
|
onErrorHandlers = this.getAppsInScope(route.app).flatMap(
|
|
789
770
|
(x) => x.onErrorHandlers,
|
|
790
771
|
)
|
|
@@ -875,11 +856,12 @@ export class Spiceflow<
|
|
|
875
856
|
error: err,
|
|
876
857
|
request,
|
|
877
858
|
})
|
|
878
|
-
|
|
859
|
+
|
|
879
860
|
let status = err?.status ?? 500
|
|
880
|
-
|
|
861
|
+
res ||= new Response(err?.message || 'Internal Server Error', {
|
|
881
862
|
status,
|
|
882
863
|
})
|
|
864
|
+
return res
|
|
883
865
|
}
|
|
884
866
|
}
|
|
885
867
|
|
|
@@ -954,6 +936,7 @@ export class Spiceflow<
|
|
|
954
936
|
port,
|
|
955
937
|
development: !isProduction,
|
|
956
938
|
hostname,
|
|
939
|
+
reusePort: true,
|
|
957
940
|
error(error) {
|
|
958
941
|
console.error(error)
|
|
959
942
|
return new Response('Internal Server Error', {
|
|
@@ -966,6 +949,9 @@ export class Spiceflow<
|
|
|
966
949
|
return res
|
|
967
950
|
},
|
|
968
951
|
})
|
|
952
|
+
process.on('beforeExit', () => {
|
|
953
|
+
server.stop()
|
|
954
|
+
})
|
|
969
955
|
console.log(`Listening on http://localhost:${port}`)
|
|
970
956
|
return server
|
|
971
957
|
}
|
package/src/types.test.ts
CHANGED
package/src/types.ts
CHANGED
|
@@ -19,11 +19,10 @@ import type { Context, ErrorContext, MiddlewareContext } from './context.js'
|
|
|
19
19
|
import {
|
|
20
20
|
ELYSIA_RESPONSE,
|
|
21
21
|
InternalServerError,
|
|
22
|
-
NotFoundError,
|
|
23
22
|
ParseError,
|
|
24
23
|
ValidationError,
|
|
25
24
|
} from './error.js'
|
|
26
|
-
import {
|
|
25
|
+
import { Spiceflow } from './spiceflow.js'
|
|
27
26
|
|
|
28
27
|
export type MaybeArray<T> = T | T[]
|
|
29
28
|
export type MaybePromise<T> = T | Promise<T>
|
|
@@ -312,7 +311,6 @@ export interface MergeSchema<
|
|
|
312
311
|
export type Handler<
|
|
313
312
|
in out Route extends RouteSchema = {},
|
|
314
313
|
in out Singleton extends SingletonBase = {
|
|
315
|
-
decorator: {}
|
|
316
314
|
store: {}
|
|
317
315
|
},
|
|
318
316
|
Path extends string = '',
|
|
@@ -353,7 +351,6 @@ export type CoExist<Original, Target, With> = IsAny<Target> extends true
|
|
|
353
351
|
export type InlineHandler<
|
|
354
352
|
Route extends RouteSchema = {},
|
|
355
353
|
Singleton extends SingletonBase = {
|
|
356
|
-
decorator: {}
|
|
357
354
|
store: {}
|
|
358
355
|
},
|
|
359
356
|
Path extends string = '',
|
|
@@ -386,7 +383,6 @@ export type InlineHandler<
|
|
|
386
383
|
export type OptionalHandler<
|
|
387
384
|
in out Route extends RouteSchema = {},
|
|
388
385
|
in out Singleton extends SingletonBase = {
|
|
389
|
-
decorator: {}
|
|
390
386
|
store: {}
|
|
391
387
|
},
|
|
392
388
|
Path extends string = '',
|
|
@@ -399,7 +395,6 @@ export type OptionalHandler<
|
|
|
399
395
|
export type AfterHandler<
|
|
400
396
|
in out Route extends RouteSchema = {},
|
|
401
397
|
in out Singleton extends SingletonBase = {
|
|
402
|
-
decorator: {}
|
|
403
398
|
store: {}
|
|
404
399
|
},
|
|
405
400
|
Path extends string = '',
|
|
@@ -418,7 +413,6 @@ export type AfterHandler<
|
|
|
418
413
|
export type MapResponse<
|
|
419
414
|
in out Route extends RouteSchema = {},
|
|
420
415
|
in out Singleton extends SingletonBase = {
|
|
421
|
-
decorator: {}
|
|
422
416
|
store: {}
|
|
423
417
|
},
|
|
424
418
|
Path extends string = '',
|
|
@@ -437,7 +431,6 @@ export type MapResponse<
|
|
|
437
431
|
export type VoidHandler<
|
|
438
432
|
in out Route extends RouteSchema = {},
|
|
439
433
|
in out Singleton extends SingletonBase = {
|
|
440
|
-
decorator: {}
|
|
441
434
|
store: {}
|
|
442
435
|
},
|
|
443
436
|
> = (context: Context<Route, Singleton>) => MaybePromise<void>
|
|
@@ -445,7 +438,6 @@ export type VoidHandler<
|
|
|
445
438
|
export type TransformHandler<
|
|
446
439
|
in out Route extends RouteSchema = {},
|
|
447
440
|
in out Singleton extends SingletonBase = {
|
|
448
|
-
decorator: {}
|
|
449
441
|
store: {}
|
|
450
442
|
},
|
|
451
443
|
BasePath extends string = '',
|
|
@@ -466,7 +458,6 @@ export type TransformHandler<
|
|
|
466
458
|
export type BodyHandler<
|
|
467
459
|
in out Route extends RouteSchema = {},
|
|
468
460
|
in out Singleton extends SingletonBase = {
|
|
469
|
-
decorator: {}
|
|
470
461
|
store: {}
|
|
471
462
|
},
|
|
472
463
|
Path extends string = '',
|
|
@@ -476,38 +467,23 @@ export type BodyHandler<
|
|
|
476
467
|
contentType: string
|
|
477
468
|
} & Context<Route, Singleton, Path>
|
|
478
469
|
>,
|
|
479
|
-
|
|
480
|
-
* @deprecated
|
|
481
|
-
*
|
|
482
|
-
* use `context.contentType` instead
|
|
483
|
-
*
|
|
484
|
-
* @example
|
|
485
|
-
* ```ts
|
|
486
|
-
* new Spiceflow()
|
|
487
|
-
* .onParse(({ contentType, request }) => {
|
|
488
|
-
* if (contentType === 'application/json')
|
|
489
|
-
* return request.json()
|
|
490
|
-
* })
|
|
491
|
-
* ```
|
|
492
|
-
*/
|
|
470
|
+
|
|
493
471
|
contentType: string,
|
|
494
472
|
) => MaybePromise<any>
|
|
495
473
|
|
|
496
474
|
export type MiddlewareHandler<
|
|
497
475
|
in out Route extends RouteSchema = {},
|
|
498
476
|
in out Singleton extends SingletonBase = {
|
|
499
|
-
decorator: {}
|
|
500
477
|
store: {}
|
|
501
478
|
},
|
|
502
479
|
> = (
|
|
503
480
|
context: MiddlewareContext<Singleton>,
|
|
504
|
-
next: () => Promise<Response
|
|
481
|
+
next: () => Promise<Response>,
|
|
505
482
|
) => MaybePromise<Route['response'] | void>
|
|
506
483
|
|
|
507
484
|
export type AfterResponseHandler<
|
|
508
485
|
in out Route extends RouteSchema = {},
|
|
509
486
|
in out Singleton extends SingletonBase = {
|
|
510
|
-
decorator: {}
|
|
511
487
|
store: {}
|
|
512
488
|
},
|
|
513
489
|
> = (
|
|
@@ -550,13 +526,13 @@ export type ErrorHandler<
|
|
|
550
526
|
error: Readonly<ValidationError>
|
|
551
527
|
} & Singleton['store']
|
|
552
528
|
>
|
|
553
|
-
| Prettify<
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
529
|
+
// | Prettify<
|
|
530
|
+
// {
|
|
531
|
+
// request: Request
|
|
532
|
+
// code: 'NOT_FOUND'
|
|
533
|
+
// error: Readonly<NotFoundError>
|
|
534
|
+
// } & NeverKey<Singleton['store']>
|
|
535
|
+
// >
|
|
560
536
|
| Prettify<
|
|
561
537
|
{
|
|
562
538
|
request: Request
|
|
@@ -713,7 +689,6 @@ export type MergeSpiceflowInstances<
|
|
|
713
689
|
Prefix extends string = '',
|
|
714
690
|
Scoped extends boolean = false,
|
|
715
691
|
Singleton extends SingletonBase = {
|
|
716
|
-
decorator: {}
|
|
717
692
|
store: {}
|
|
718
693
|
},
|
|
719
694
|
Definitions extends DefinitionBase = {
|
package/dist/benchmark.test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"benchmark.test.d.ts","sourceRoot":"","sources":["../src/benchmark.test.ts"],"names":[],"mappings":""}
|
package/dist/benchmark.test.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"benchmark.test.js","sourceRoot":"","sources":["../src/benchmark.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAE1C,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IACjC,KAAK,CAAC,gBAAgB,EAAE,GAAG,EAAE;QAC5B,MAAM,GAAG,GAAG,IAAI,SAAS,EAAE,CAAA;IAC5B,CAAC,CAAC,CAAA;AACH,CAAC,CAAC,CAAA"}
|