@tanstack/start-server-core 1.120.7 → 1.121.0-alpha.11
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 +6 -27
- package/dist/cjs/createRequestHandler.cjs +1 -3
- package/dist/cjs/createRequestHandler.cjs.map +1 -1
- package/dist/cjs/createStartHandler.cjs +268 -31
- package/dist/cjs/createStartHandler.cjs.map +1 -1
- package/dist/cjs/createStartHandler.d.cts +8 -6
- package/dist/cjs/h3.cjs +30 -73
- package/dist/cjs/h3.cjs.map +1 -1
- package/dist/cjs/h3.d.cts +11 -7
- package/dist/cjs/handlerCallback.cjs.map +1 -1
- package/dist/cjs/handlerCallback.d.cts +3 -4
- package/dist/cjs/index.cjs +17 -14
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +6 -1
- package/dist/cjs/router-manifest.cjs +44 -0
- package/dist/cjs/router-manifest.cjs.map +1 -0
- package/dist/cjs/router-manifest.d.cts +17 -0
- package/dist/cjs/server-functions-handler.cjs +145 -0
- package/dist/cjs/server-functions-handler.cjs.map +1 -0
- package/dist/cjs/server-functions-handler.d.cts +3 -0
- package/dist/cjs/serverRoute.cjs +100 -0
- package/dist/cjs/serverRoute.cjs.map +1 -0
- package/dist/cjs/serverRoute.d.cts +115 -0
- package/dist/cjs/ssr-server.cjs +3 -2
- package/dist/cjs/ssr-server.cjs.map +1 -1
- package/dist/esm/createRequestHandler.js +1 -3
- package/dist/esm/createRequestHandler.js.map +1 -1
- package/dist/esm/createStartHandler.d.ts +8 -6
- package/dist/esm/createStartHandler.js +248 -33
- package/dist/esm/createStartHandler.js.map +1 -1
- package/dist/esm/h3.d.ts +11 -7
- package/dist/esm/h3.js +26 -63
- package/dist/esm/h3.js.map +1 -1
- package/dist/esm/handlerCallback.d.ts +3 -4
- package/dist/esm/handlerCallback.js.map +1 -1
- package/dist/esm/index.d.ts +6 -1
- package/dist/esm/index.js +14 -5
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/router-manifest.d.ts +17 -0
- package/dist/esm/router-manifest.js +44 -0
- package/dist/esm/router-manifest.js.map +1 -0
- package/dist/esm/server-functions-handler.d.ts +3 -0
- package/dist/esm/server-functions-handler.js +145 -0
- package/dist/esm/server-functions-handler.js.map +1 -0
- package/dist/esm/serverRoute.d.ts +115 -0
- package/dist/esm/serverRoute.js +100 -0
- package/dist/esm/serverRoute.js.map +1 -0
- package/dist/esm/ssr-server.js +3 -2
- package/dist/esm/ssr-server.js.map +1 -1
- package/package.json +8 -6
- package/src/createRequestHandler.ts +1 -3
- package/src/createStartHandler.ts +373 -47
- package/src/h3.ts +71 -78
- package/src/handlerCallback.ts +5 -12
- package/src/index.tsx +11 -1
- package/src/router-manifest.ts +79 -0
- package/src/server-functions-handler.ts +260 -0
- package/src/serverRoute.ts +661 -0
- package/src/ssr-server.ts +2 -0
- package/src/tanstack-start.d.ts +5 -0
|
@@ -1,82 +1,408 @@
|
|
|
1
1
|
import { createMemoryHistory } from '@tanstack/history'
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import {
|
|
3
|
+
flattenMiddlewares,
|
|
4
|
+
json,
|
|
5
|
+
mergeHeaders,
|
|
6
|
+
} from '@tanstack/start-client-core'
|
|
7
|
+
import {
|
|
8
|
+
getMatchedRoutes,
|
|
9
|
+
isRedirect,
|
|
10
|
+
isResolvedRedirect,
|
|
11
|
+
joinPaths,
|
|
12
|
+
processRouteTree,
|
|
13
|
+
rootRouteId,
|
|
14
|
+
trimPath,
|
|
15
|
+
} from '@tanstack/router-core'
|
|
16
|
+
import { getResponseHeaders, requestHandler } from './h3'
|
|
4
17
|
import { attachRouterServerSsrUtils, dehydrateRouter } from './ssr-server'
|
|
18
|
+
import { getStartManifest } from './router-manifest'
|
|
19
|
+
import { handleServerAction } from './server-functions-handler'
|
|
20
|
+
import type { AnyServerRoute, AnyServerRouteWithTypes } from './serverRoute'
|
|
21
|
+
import type { RequestHandler } from './h3'
|
|
22
|
+
import type { AnyRouter } from '@tanstack/router-core'
|
|
5
23
|
import type { HandlerCallback } from './handlerCallback'
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export type CustomizeStartHandler<
|
|
10
|
-
TRouter
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
24
|
+
|
|
25
|
+
type TODO = any
|
|
26
|
+
|
|
27
|
+
export type CustomizeStartHandler<TRouter extends AnyRouter> = (
|
|
28
|
+
cb: HandlerCallback<TRouter>,
|
|
29
|
+
) => RequestHandler
|
|
30
|
+
|
|
31
|
+
export function getStartResponseHeaders(opts: { router: AnyRouter }) {
|
|
32
|
+
let headers = mergeHeaders(
|
|
33
|
+
getResponseHeaders(),
|
|
34
|
+
{
|
|
35
|
+
'Content-Type': 'text/html; charset=UTF-8',
|
|
36
|
+
},
|
|
37
|
+
...opts.router.state.matches.map((match) => {
|
|
38
|
+
return match.headers
|
|
39
|
+
}),
|
|
40
|
+
)
|
|
41
|
+
// Handle Redirects
|
|
42
|
+
const { redirect } = opts.router.state
|
|
43
|
+
|
|
44
|
+
if (redirect) {
|
|
45
|
+
headers = mergeHeaders(headers, redirect.headers)
|
|
46
|
+
}
|
|
47
|
+
return headers
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function createStartHandler<TRouter extends AnyRouter>({
|
|
18
51
|
createRouter,
|
|
19
|
-
getRouterManifest,
|
|
20
52
|
}: {
|
|
21
53
|
createRouter: () => TRouter
|
|
22
|
-
|
|
23
|
-
}): CustomizeStartHandler<TRouter, TResponse> {
|
|
54
|
+
}): CustomizeStartHandler<TRouter> {
|
|
24
55
|
return (cb) => {
|
|
25
|
-
|
|
26
|
-
|
|
56
|
+
const originalFetch = globalThis.fetch
|
|
57
|
+
|
|
58
|
+
const startRequestResolver: RequestHandler = async ({ request }) => {
|
|
59
|
+
// Patching fetch function to use our request resolver
|
|
60
|
+
// if the input starts with `/` which is a common pattern for
|
|
61
|
+
// client-side routing.
|
|
62
|
+
// When we encounter similar requests, we can assume that the
|
|
63
|
+
// user wants to use the same origin as the current request.
|
|
64
|
+
globalThis.fetch = async function (input, init) {
|
|
65
|
+
function resolve(url: URL, requestOptions: RequestInit | undefined) {
|
|
66
|
+
const fetchRequest = new Request(url, requestOptions)
|
|
67
|
+
return startRequestResolver({ request: fetchRequest })
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function getOrigin() {
|
|
71
|
+
return (
|
|
72
|
+
request.headers.get('Origin') ||
|
|
73
|
+
request.headers.get('Referer') ||
|
|
74
|
+
'http://localhost'
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (typeof input === 'string' && input.startsWith('/')) {
|
|
79
|
+
// e.g: fetch('/api/data')
|
|
80
|
+
const url = new URL(input, getOrigin())
|
|
81
|
+
return resolve(url, init)
|
|
82
|
+
} else if (
|
|
83
|
+
typeof input === 'object' &&
|
|
84
|
+
'url' in input &&
|
|
85
|
+
typeof input.url === 'string' &&
|
|
86
|
+
input.url.startsWith('/')
|
|
87
|
+
) {
|
|
88
|
+
// e.g: fetch(new Request('/api/data'))
|
|
89
|
+
const url = new URL(input.url, getOrigin())
|
|
90
|
+
return resolve(url, init)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// If not, it should just use the original fetch
|
|
94
|
+
return originalFetch(input, init)
|
|
95
|
+
}
|
|
27
96
|
|
|
28
97
|
const url = new URL(request.url)
|
|
29
98
|
const href = url.href.replace(url.origin, '')
|
|
30
99
|
|
|
31
|
-
// Create a history for the router
|
|
100
|
+
// Create a history for the client-side router
|
|
32
101
|
const history = createMemoryHistory({
|
|
33
102
|
initialEntries: [href],
|
|
34
103
|
})
|
|
35
104
|
|
|
105
|
+
// Create the client-side router
|
|
36
106
|
const router = createRouter()
|
|
37
107
|
|
|
38
|
-
|
|
108
|
+
// Attach the server-side SSR utils to the client-side router
|
|
109
|
+
attachRouterServerSsrUtils(router, getStartManifest())
|
|
39
110
|
|
|
40
|
-
// Update the router with the history and context
|
|
111
|
+
// Update the client-side router with the history and context
|
|
41
112
|
router.update({
|
|
42
113
|
history,
|
|
43
114
|
})
|
|
44
115
|
|
|
45
|
-
await
|
|
116
|
+
const response = await (async () => {
|
|
117
|
+
try {
|
|
118
|
+
if (!process.env.TSS_SERVER_FN_BASE) {
|
|
119
|
+
throw new Error(
|
|
120
|
+
'tanstack/start-server-core: TSS_SERVER_FN_BASE must be defined in your environment for createStartHandler()',
|
|
121
|
+
)
|
|
122
|
+
}
|
|
46
123
|
|
|
47
|
-
|
|
124
|
+
// First, let's attempt to handle server functions
|
|
125
|
+
// Add trailing slash to sanitise user defined TSS_SERVER_FN_BASE
|
|
126
|
+
const serverFnBase = joinPaths([
|
|
127
|
+
'/',
|
|
128
|
+
trimPath(process.env.TSS_SERVER_FN_BASE),
|
|
129
|
+
'/',
|
|
130
|
+
])
|
|
131
|
+
if (href.startsWith(serverFnBase)) {
|
|
132
|
+
return await handleServerAction({ request })
|
|
133
|
+
}
|
|
48
134
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
135
|
+
// Then move on to attempting to load server routes
|
|
136
|
+
const serverRouteTreeModule = await (async () => {
|
|
137
|
+
try {
|
|
138
|
+
return (await import(
|
|
139
|
+
// @ts-expect-error
|
|
140
|
+
'tanstack-start-server-routes-manifest:v'
|
|
141
|
+
)) as { routeTree: AnyServerRoute }
|
|
142
|
+
} catch (e) {
|
|
143
|
+
console.log(e)
|
|
144
|
+
return undefined
|
|
145
|
+
}
|
|
146
|
+
})()
|
|
147
|
+
|
|
148
|
+
// If we have a server route tree, then we try matching to see if we have a
|
|
149
|
+
// server route that matches the request.
|
|
150
|
+
if (serverRouteTreeModule) {
|
|
151
|
+
const [_matchedRoutes, response] = await handleServerRoutes({
|
|
152
|
+
routeTree: serverRouteTreeModule.routeTree,
|
|
153
|
+
request,
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
if (response) return response
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const requestAcceptHeader = request.headers.get('Accept') || '*/*'
|
|
160
|
+
const splitRequestAcceptHeader = requestAcceptHeader.split(',')
|
|
161
|
+
|
|
162
|
+
const supportedMimeTypes = ['*/*', 'text/html']
|
|
163
|
+
const isRouterAcceptSupported = supportedMimeTypes.some((mimeType) =>
|
|
164
|
+
splitRequestAcceptHeader.some((acceptedMimeType) =>
|
|
165
|
+
acceptedMimeType.trim().startsWith(mimeType),
|
|
166
|
+
),
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
if (!isRouterAcceptSupported) {
|
|
170
|
+
return json(
|
|
171
|
+
{
|
|
172
|
+
error: 'Only HTML requests are supported here',
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
status: 500,
|
|
176
|
+
},
|
|
177
|
+
)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// If no Server Routes were found, so fallback to normal SSR matching using
|
|
181
|
+
// the router
|
|
182
|
+
|
|
183
|
+
await router.load()
|
|
184
|
+
|
|
185
|
+
// If there was a redirect, skip rendering the page at all
|
|
186
|
+
if (router.state.redirect) return router.state.redirect
|
|
187
|
+
|
|
188
|
+
dehydrateRouter(router)
|
|
189
|
+
|
|
190
|
+
const responseHeaders = getStartResponseHeaders({ router })
|
|
191
|
+
const response = await cb({
|
|
192
|
+
request,
|
|
193
|
+
router,
|
|
194
|
+
responseHeaders,
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
return response
|
|
198
|
+
} catch (err) {
|
|
199
|
+
if (err instanceof Response) {
|
|
200
|
+
return err
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
throw err
|
|
204
|
+
}
|
|
205
|
+
})()
|
|
206
|
+
|
|
207
|
+
if (isRedirect(response)) {
|
|
208
|
+
if (isResolvedRedirect(response)) {
|
|
209
|
+
if (request.headers.get('x-tsr-redirect') === 'manual') {
|
|
210
|
+
return json(
|
|
211
|
+
{
|
|
212
|
+
...response.options,
|
|
213
|
+
isSerializedRedirect: true,
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
headers: response.headers,
|
|
217
|
+
},
|
|
218
|
+
)
|
|
219
|
+
}
|
|
220
|
+
return response
|
|
221
|
+
}
|
|
222
|
+
if (
|
|
223
|
+
response.options.to &&
|
|
224
|
+
typeof response.options.to === 'string' &&
|
|
225
|
+
!response.options.to.startsWith('/')
|
|
226
|
+
) {
|
|
227
|
+
throw new Error(
|
|
228
|
+
`Server side redirects must use absolute paths via the 'href' or 'to' options. Received: ${JSON.stringify(response.options)}`,
|
|
229
|
+
)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (
|
|
233
|
+
['params', 'search', 'hash'].some(
|
|
234
|
+
(d) => typeof (response.options as any)[d] === 'function',
|
|
235
|
+
)
|
|
236
|
+
) {
|
|
237
|
+
throw new Error(
|
|
238
|
+
`Server side redirects must use static search, params, and hash values and do not support functional values. Received functional values for: ${Object.keys(
|
|
239
|
+
response.options,
|
|
240
|
+
)
|
|
241
|
+
.filter((d) => typeof (response.options as any)[d] === 'function')
|
|
242
|
+
.map((d) => `"${d}"`)
|
|
243
|
+
.join(', ')}`,
|
|
244
|
+
)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const redirect = router.resolveRedirect(response)
|
|
248
|
+
|
|
249
|
+
if (request.headers.get('x-tsr-redirect') === 'manual') {
|
|
250
|
+
return json(
|
|
251
|
+
{
|
|
252
|
+
...response.options,
|
|
253
|
+
isSerializedRedirect: true,
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
headers: response.headers,
|
|
257
|
+
},
|
|
258
|
+
)
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return redirect
|
|
262
|
+
}
|
|
55
263
|
|
|
56
264
|
return response
|
|
57
|
-
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return requestHandler(startRequestResolver)
|
|
58
268
|
}
|
|
59
269
|
}
|
|
60
270
|
|
|
61
|
-
function
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
271
|
+
async function handleServerRoutes({
|
|
272
|
+
routeTree,
|
|
273
|
+
request,
|
|
274
|
+
}: {
|
|
275
|
+
routeTree: AnyServerRouteWithTypes
|
|
276
|
+
request: Request
|
|
277
|
+
}) {
|
|
278
|
+
const { flatRoutes, routesById, routesByPath } = processRouteTree({
|
|
279
|
+
routeTree,
|
|
280
|
+
initRoute: (route, i) => {
|
|
281
|
+
route.init({
|
|
282
|
+
originalIndex: i,
|
|
283
|
+
})
|
|
67
284
|
},
|
|
68
|
-
|
|
69
|
-
return match.headers
|
|
70
|
-
}),
|
|
71
|
-
)
|
|
285
|
+
})
|
|
72
286
|
|
|
73
|
-
|
|
74
|
-
const
|
|
287
|
+
const url = new URL(request.url)
|
|
288
|
+
const pathname = url.pathname
|
|
75
289
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
290
|
+
const history = createMemoryHistory({
|
|
291
|
+
initialEntries: [pathname],
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
const { matchedRoutes, foundRoute, routeParams } =
|
|
295
|
+
getMatchedRoutes<AnyServerRouteWithTypes>({
|
|
296
|
+
pathname: history.location.pathname,
|
|
297
|
+
basepath: '/',
|
|
298
|
+
caseSensitive: true,
|
|
299
|
+
routesByPath,
|
|
300
|
+
routesById,
|
|
301
|
+
flatRoutes,
|
|
79
302
|
})
|
|
303
|
+
|
|
304
|
+
let response: Response | undefined
|
|
305
|
+
|
|
306
|
+
if (foundRoute && foundRoute.id !== rootRouteId) {
|
|
307
|
+
// We've found a server route that matches the request, so we can call it.
|
|
308
|
+
// TODO: Get the input type-signature correct
|
|
309
|
+
// TODO: Perform the middlewares?
|
|
310
|
+
// TODO: Error handling? What happens when its `throw redirect()` vs `throw new Error()`?
|
|
311
|
+
|
|
312
|
+
const method = Object.keys(foundRoute.options.methods).find(
|
|
313
|
+
(method) => method.toLowerCase() === request.method.toLowerCase(),
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
if (method) {
|
|
317
|
+
const handler = foundRoute.options.methods[method]
|
|
318
|
+
|
|
319
|
+
if (handler) {
|
|
320
|
+
const middlewares = flattenMiddlewares(
|
|
321
|
+
matchedRoutes.flatMap((r) => r.options.middleware).filter(Boolean),
|
|
322
|
+
).map((d) => d.options.server)
|
|
323
|
+
|
|
324
|
+
middlewares.push(handlerToMiddleware(handler) as TODO)
|
|
325
|
+
|
|
326
|
+
// TODO: This is starting to feel too much like a server function
|
|
327
|
+
// Do generalize the existing middleware execution? Or do we need to
|
|
328
|
+
// build a new middleware execution system for server routes?
|
|
329
|
+
const ctx = await executeMiddleware(middlewares, {
|
|
330
|
+
request,
|
|
331
|
+
context: {},
|
|
332
|
+
params: routeParams,
|
|
333
|
+
pathname: history.location.pathname,
|
|
334
|
+
})
|
|
335
|
+
|
|
336
|
+
response = ctx.response
|
|
337
|
+
}
|
|
338
|
+
}
|
|
80
339
|
}
|
|
81
|
-
|
|
340
|
+
|
|
341
|
+
// We return the matched routes too so if
|
|
342
|
+
// the app router happens to match the same path,
|
|
343
|
+
// it can use any request middleware from server routes
|
|
344
|
+
return [matchedRoutes, response] as const
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function handlerToMiddleware(
|
|
348
|
+
handler: AnyServerRouteWithTypes['options']['methods'][string],
|
|
349
|
+
) {
|
|
350
|
+
return async ({ next: _next, ...rest }: TODO) => ({
|
|
351
|
+
response: await handler(rest),
|
|
352
|
+
})
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function executeMiddleware(middlewares: TODO, ctx: TODO) {
|
|
356
|
+
let index = -1
|
|
357
|
+
|
|
358
|
+
const next = async (ctx: TODO) => {
|
|
359
|
+
index++
|
|
360
|
+
const middleware = middlewares[index]
|
|
361
|
+
if (!middleware) return ctx
|
|
362
|
+
|
|
363
|
+
const result = await middleware({
|
|
364
|
+
...ctx,
|
|
365
|
+
// Allow the middleware to call the next middleware in the chain
|
|
366
|
+
next: async (nextCtx: TODO) => {
|
|
367
|
+
// Allow the caller to extend the context for the next middleware
|
|
368
|
+
const nextResult = await next({ ...ctx, ...nextCtx })
|
|
369
|
+
|
|
370
|
+
// Merge the result into the context\
|
|
371
|
+
return Object.assign(ctx, handleCtxResult(nextResult))
|
|
372
|
+
},
|
|
373
|
+
// Allow the middleware result to extend the return context
|
|
374
|
+
}).catch((err: TODO) => {
|
|
375
|
+
if (isSpecialResponse(err)) {
|
|
376
|
+
return {
|
|
377
|
+
response: err,
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
throw err
|
|
382
|
+
})
|
|
383
|
+
|
|
384
|
+
// Merge the middleware result into the context, just in case it
|
|
385
|
+
// returns a partial context
|
|
386
|
+
return Object.assign(ctx, handleCtxResult(result))
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
return handleCtxResult(next(ctx))
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function handleCtxResult(result: TODO) {
|
|
393
|
+
if (isSpecialResponse(result)) {
|
|
394
|
+
return {
|
|
395
|
+
response: result,
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
return result
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
function isSpecialResponse(err: TODO) {
|
|
403
|
+
return isResponse(err) || isRedirect(err)
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
function isResponse(response: Response): response is Response {
|
|
407
|
+
return response instanceof Response
|
|
82
408
|
}
|
package/src/h3.ts
CHANGED
|
@@ -11,7 +11,9 @@ import {
|
|
|
11
11
|
clearResponseHeaders as _clearResponseHeaders,
|
|
12
12
|
clearSession as _clearSession,
|
|
13
13
|
defaultContentType as _defaultContentType,
|
|
14
|
+
defineEventHandler as _defineEventHandler,
|
|
14
15
|
deleteCookie as _deleteCookie,
|
|
16
|
+
eventHandler as _eventHandler,
|
|
15
17
|
fetchWithEvent as _fetchWithEvent,
|
|
16
18
|
getCookie as _getCookie,
|
|
17
19
|
getHeader as _getHeader,
|
|
@@ -61,20 +63,24 @@ import {
|
|
|
61
63
|
setResponseHeader as _setResponseHeader,
|
|
62
64
|
setResponseHeaders as _setResponseHeaders,
|
|
63
65
|
setResponseStatus as _setResponseStatus,
|
|
66
|
+
toWebRequest as _toWebRequest,
|
|
64
67
|
unsealSession as _unsealSession,
|
|
65
68
|
updateSession as _updateSession,
|
|
66
69
|
useSession as _useSession,
|
|
67
70
|
writeEarlyHints as _writeEarlyHints,
|
|
68
71
|
} from 'h3'
|
|
69
|
-
|
|
72
|
+
|
|
70
73
|
import type {
|
|
71
74
|
Encoding,
|
|
75
|
+
EventHandler,
|
|
72
76
|
HTTPHeaderName,
|
|
73
77
|
InferEventInput,
|
|
74
78
|
_RequestMiddleware,
|
|
75
79
|
_ResponseMiddleware,
|
|
76
80
|
} from 'h3'
|
|
77
81
|
|
|
82
|
+
const eventStorage = new AsyncLocalStorage()
|
|
83
|
+
|
|
78
84
|
function _setContext(event: H3Event, key: string, value: any) {
|
|
79
85
|
event.context[key] = value
|
|
80
86
|
}
|
|
@@ -90,46 +96,38 @@ export function defineMiddleware(options: {
|
|
|
90
96
|
return options
|
|
91
97
|
}
|
|
92
98
|
|
|
93
|
-
function toWebRequestH3(event: H3Event) {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
99
|
+
// function toWebRequestH3(event: H3Event) {
|
|
100
|
+
// /**
|
|
101
|
+
// * @type {ReadableStream | undefined}
|
|
102
|
+
// */
|
|
103
|
+
// let readableStream: ReadableStream | undefined
|
|
98
104
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
if ((event.node.req as any).body instanceof ArrayBuffer) {
|
|
108
|
-
return new Request(url, {
|
|
109
|
-
...base,
|
|
110
|
-
body: (event.node.req as any).body,
|
|
111
|
-
})
|
|
112
|
-
}
|
|
105
|
+
// const url = _getRequestURL(event)
|
|
106
|
+
// const base = {
|
|
107
|
+
// // @ts-ignore Undici option
|
|
108
|
+
// duplex: 'half',
|
|
109
|
+
// method: event.method,
|
|
110
|
+
// headers: event.headers,
|
|
111
|
+
// }
|
|
113
112
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
readableStream = getRequestWebStream(event)
|
|
121
|
-
return readableStream
|
|
122
|
-
},
|
|
123
|
-
})
|
|
124
|
-
}
|
|
113
|
+
// if ((event.node.req as any).body instanceof ArrayBuffer) {
|
|
114
|
+
// return new Request(url, {
|
|
115
|
+
// ...base,
|
|
116
|
+
// body: (event.node.req as any).body,
|
|
117
|
+
// })
|
|
118
|
+
// }
|
|
125
119
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
120
|
+
// return new Request(url, {
|
|
121
|
+
// ...base,
|
|
122
|
+
// get body() {
|
|
123
|
+
// if (readableStream) {
|
|
124
|
+
// return readableStream
|
|
125
|
+
// }
|
|
126
|
+
// readableStream = _getRequestWebStream(event)
|
|
127
|
+
// return readableStream
|
|
128
|
+
// },
|
|
129
|
+
// })
|
|
130
|
+
// }
|
|
133
131
|
|
|
134
132
|
export {
|
|
135
133
|
H3Error,
|
|
@@ -140,7 +138,6 @@ export {
|
|
|
140
138
|
createAppEventHandler,
|
|
141
139
|
createEvent,
|
|
142
140
|
createRouter,
|
|
143
|
-
defineEventHandler,
|
|
144
141
|
defineLazyEventHandler,
|
|
145
142
|
defineNodeListener,
|
|
146
143
|
defineNodeMiddleware,
|
|
@@ -148,7 +145,6 @@ export {
|
|
|
148
145
|
defineResponseMiddleware,
|
|
149
146
|
dynamicEventHandler,
|
|
150
147
|
defineWebSocket,
|
|
151
|
-
eventHandler,
|
|
152
148
|
splitCookiesString,
|
|
153
149
|
fromNodeMiddleware,
|
|
154
150
|
fromPlainHandler,
|
|
@@ -163,6 +159,7 @@ export {
|
|
|
163
159
|
toNodeListener,
|
|
164
160
|
toPlainHandler,
|
|
165
161
|
toWebHandler,
|
|
162
|
+
toWebRequest,
|
|
166
163
|
isCorsOriginAllowed,
|
|
167
164
|
isStream,
|
|
168
165
|
createError,
|
|
@@ -221,8 +218,33 @@ export {
|
|
|
221
218
|
type _ResponseMiddleware,
|
|
222
219
|
} from 'h3'
|
|
223
220
|
|
|
224
|
-
function
|
|
225
|
-
return
|
|
221
|
+
export function defineEventHandler(handler: EventHandler) {
|
|
222
|
+
return _defineEventHandler((event) => {
|
|
223
|
+
return runWithEvent(event, () => handler(event))
|
|
224
|
+
})
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export function eventHandler(handler: EventHandler) {
|
|
228
|
+
return _eventHandler((event) => {
|
|
229
|
+
return runWithEvent(event, () => handler(event))
|
|
230
|
+
})
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export async function runWithEvent<T>(
|
|
234
|
+
event: H3Event,
|
|
235
|
+
fn: () => T | Promise<T>,
|
|
236
|
+
): Promise<T> {
|
|
237
|
+
return eventStorage.run(event, fn)
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export function getEvent() {
|
|
241
|
+
const event = eventStorage.getStore() as H3Event | undefined
|
|
242
|
+
if (!event) {
|
|
243
|
+
throw new Error(
|
|
244
|
+
`No HTTPEvent found in AsyncLocalStorage. Make sure you are using the function within the server runtime.`,
|
|
245
|
+
)
|
|
246
|
+
}
|
|
247
|
+
return event
|
|
226
248
|
}
|
|
227
249
|
|
|
228
250
|
export const HTTPEventSymbol = Symbol('$HTTPEvent')
|
|
@@ -262,12 +284,7 @@ function createWrapperFunction<TFn extends (...args: Array<any>) => any>(
|
|
|
262
284
|
return function (...args: Array<any>) {
|
|
263
285
|
const event = args[0]
|
|
264
286
|
if (!isEvent(event)) {
|
|
265
|
-
|
|
266
|
-
throw new Error(
|
|
267
|
-
'AsyncLocalStorage was not enabled. Use the `server.experimental.asyncContext: true` option in your app configuration to enable it. Or, pass the instance of HTTPEvent that you have as the first argument to the function.',
|
|
268
|
-
)
|
|
269
|
-
}
|
|
270
|
-
args.unshift(getHTTPEvent())
|
|
287
|
+
args.unshift(getEvent())
|
|
271
288
|
} else {
|
|
272
289
|
args[0] =
|
|
273
290
|
event instanceof H3Event || (event as any).__is_event__
|
|
@@ -463,37 +480,13 @@ export const readValidatedBody: PrependOverload<
|
|
|
463
480
|
export const removeResponseHeader = createWrapperFunction(_removeResponseHeader)
|
|
464
481
|
export const getContext = createWrapperFunction(_getContext)
|
|
465
482
|
export const setContext = createWrapperFunction(_setContext)
|
|
466
|
-
|
|
467
483
|
export const clearResponseHeaders = createWrapperFunction(_clearResponseHeaders)
|
|
484
|
+
export const getWebRequest = createWrapperFunction(_toWebRequest)
|
|
468
485
|
|
|
469
|
-
export
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
function getNitroAsyncContext() {
|
|
474
|
-
const nitroAsyncContext = getUnctxContext('nitro-app', {
|
|
475
|
-
asyncContext: (globalThis as any).app.config.server.experimental
|
|
476
|
-
?.asyncContext
|
|
477
|
-
? true
|
|
478
|
-
: false,
|
|
479
|
-
AsyncLocalStorage,
|
|
480
|
-
})
|
|
481
|
-
|
|
482
|
-
return nitroAsyncContext
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
export function getEvent() {
|
|
486
|
-
const event = (getNitroAsyncContext().use() as any).event as
|
|
487
|
-
| H3Event
|
|
488
|
-
| undefined
|
|
489
|
-
if (!event) {
|
|
490
|
-
throw new Error(
|
|
491
|
-
`No HTTPEvent found in AsyncLocalStorage. Make sure you are using the function within the server runtime.`,
|
|
492
|
-
)
|
|
493
|
-
}
|
|
494
|
-
return event
|
|
495
|
-
}
|
|
486
|
+
export type RequestHandler = (ctx: {
|
|
487
|
+
request: Request
|
|
488
|
+
}) => Promise<Response> | Response
|
|
496
489
|
|
|
497
|
-
export
|
|
498
|
-
return
|
|
490
|
+
export function requestHandler(handler: RequestHandler) {
|
|
491
|
+
return handler
|
|
499
492
|
}
|