@tanstack/start-server-core 1.120.5 → 1.121.0-alpha.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.
Files changed (60) hide show
  1. package/README.md +6 -27
  2. package/dist/cjs/createRequestHandler.cjs +1 -3
  3. package/dist/cjs/createRequestHandler.cjs.map +1 -1
  4. package/dist/cjs/createStartHandler.cjs +265 -31
  5. package/dist/cjs/createStartHandler.cjs.map +1 -1
  6. package/dist/cjs/createStartHandler.d.cts +8 -6
  7. package/dist/cjs/h3.cjs +30 -73
  8. package/dist/cjs/h3.cjs.map +1 -1
  9. package/dist/cjs/h3.d.cts +11 -7
  10. package/dist/cjs/handlerCallback.cjs.map +1 -1
  11. package/dist/cjs/handlerCallback.d.cts +3 -4
  12. package/dist/cjs/index.cjs +17 -14
  13. package/dist/cjs/index.cjs.map +1 -1
  14. package/dist/cjs/index.d.cts +6 -1
  15. package/dist/cjs/router-manifest.cjs +44 -0
  16. package/dist/cjs/router-manifest.cjs.map +1 -0
  17. package/dist/cjs/router-manifest.d.cts +17 -0
  18. package/dist/cjs/server-functions-handler.cjs +145 -0
  19. package/dist/cjs/server-functions-handler.cjs.map +1 -0
  20. package/dist/cjs/server-functions-handler.d.cts +3 -0
  21. package/dist/cjs/serverRoute.cjs +100 -0
  22. package/dist/cjs/serverRoute.cjs.map +1 -0
  23. package/dist/cjs/serverRoute.d.cts +115 -0
  24. package/dist/cjs/ssr-server.cjs +3 -2
  25. package/dist/cjs/ssr-server.cjs.map +1 -1
  26. package/dist/esm/createRequestHandler.js +1 -3
  27. package/dist/esm/createRequestHandler.js.map +1 -1
  28. package/dist/esm/createStartHandler.d.ts +8 -6
  29. package/dist/esm/createStartHandler.js +245 -33
  30. package/dist/esm/createStartHandler.js.map +1 -1
  31. package/dist/esm/h3.d.ts +11 -7
  32. package/dist/esm/h3.js +26 -63
  33. package/dist/esm/h3.js.map +1 -1
  34. package/dist/esm/handlerCallback.d.ts +3 -4
  35. package/dist/esm/handlerCallback.js.map +1 -1
  36. package/dist/esm/index.d.ts +6 -1
  37. package/dist/esm/index.js +14 -5
  38. package/dist/esm/index.js.map +1 -1
  39. package/dist/esm/router-manifest.d.ts +17 -0
  40. package/dist/esm/router-manifest.js +44 -0
  41. package/dist/esm/router-manifest.js.map +1 -0
  42. package/dist/esm/server-functions-handler.d.ts +3 -0
  43. package/dist/esm/server-functions-handler.js +145 -0
  44. package/dist/esm/server-functions-handler.js.map +1 -0
  45. package/dist/esm/serverRoute.d.ts +115 -0
  46. package/dist/esm/serverRoute.js +100 -0
  47. package/dist/esm/serverRoute.js.map +1 -0
  48. package/dist/esm/ssr-server.js +3 -2
  49. package/dist/esm/ssr-server.js.map +1 -1
  50. package/package.json +8 -6
  51. package/src/createRequestHandler.ts +1 -3
  52. package/src/createStartHandler.ts +373 -47
  53. package/src/h3.ts +71 -78
  54. package/src/handlerCallback.ts +5 -12
  55. package/src/index.tsx +11 -1
  56. package/src/router-manifest.ts +79 -0
  57. package/src/server-functions-handler.ts +260 -0
  58. package/src/serverRoute.ts +661 -0
  59. package/src/ssr-server.ts +2 -0
  60. package/src/tanstack-start.d.ts +5 -0
@@ -1,82 +1,408 @@
1
1
  import { createMemoryHistory } from '@tanstack/history'
2
- import { mergeHeaders } from '@tanstack/start-client-core'
3
- import { eventHandler, getResponseHeaders, toWebRequest } from 'h3'
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
- import type { EventHandlerResponse, H3Event } from 'h3'
7
- import type { AnyRouter, Manifest } from '@tanstack/router-core'
8
-
9
- export type CustomizeStartHandler<
10
- TRouter extends AnyRouter,
11
- TResponse extends EventHandlerResponse = EventHandlerResponse,
12
- > = (cb: HandlerCallback<TRouter, TResponse>) => ReturnType<typeof eventHandler>
13
-
14
- export function createStartHandler<
15
- TRouter extends AnyRouter,
16
- TResponse extends EventHandlerResponse = EventHandlerResponse,
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
- getRouterManifest?: () => Manifest | Promise<Manifest>
23
- }): CustomizeStartHandler<TRouter, TResponse> {
54
+ }): CustomizeStartHandler<TRouter> {
24
55
  return (cb) => {
25
- return eventHandler(async (event) => {
26
- const request = toWebRequest(event)
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
- attachRouterServerSsrUtils(router, await getRouterManifest?.())
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 router.load()
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
- dehydrateRouter(router)
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
- const responseHeaders = getStartResponseHeaders({ event, router })
50
- const response = await cb({
51
- request,
52
- router,
53
- responseHeaders,
54
- })
135
+ // Then move on to attempting to load server routes
136
+ const serverRouteTreeModule = await (async () => {
137
+ try {
138
+ // @ts-expect-error
139
+ return (await import('tanstack:server-routes')) as {
140
+ routeTree: AnyServerRoute
141
+ }
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 getStartResponseHeaders(opts: { event: H3Event; router: AnyRouter }) {
62
- let headers = mergeHeaders(
63
- getResponseHeaders(opts.event),
64
- (opts.event as any).___ssrRpcResponseHeaders,
65
- {
66
- 'Content-Type': 'text/html; charset=UTF-8',
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
- ...opts.router.state.matches.map((match) => {
69
- return match.headers
70
- }),
71
- )
285
+ })
72
286
 
73
- // Handle Redirects
74
- const { redirect } = opts.router.state
287
+ const url = new URL(request.url)
288
+ const pathname = url.pathname
75
289
 
76
- if (redirect) {
77
- headers = mergeHeaders(headers, redirect.headers, {
78
- Location: redirect.href,
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
- return headers
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, ...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
- import { getContext as getUnctxContext } from 'unctx'
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
- * @type {ReadableStream | undefined}
96
- */
97
- let readableStream: ReadableStream | undefined
99
+ // function toWebRequestH3(event: H3Event) {
100
+ // /**
101
+ // * @type {ReadableStream | undefined}
102
+ // */
103
+ // let readableStream: ReadableStream | undefined
98
104
 
99
- const url = getRequestURL(event)
100
- const base = {
101
- // @ts-ignore Undici option
102
- duplex: 'half',
103
- method: event.method,
104
- headers: event.headers,
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
- return new Request(url, {
115
- ...base,
116
- get body() {
117
- if (readableStream) {
118
- return readableStream
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
- export function toWebRequest(event: H3Event) {
127
- event.web ??= {
128
- request: toWebRequestH3(event),
129
- url: getRequestURL(event),
130
- }
131
- return event.web.request
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 getHTTPEvent() {
225
- return getEvent()
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
- if (!(globalThis as any).app.config.server.experimental?.asyncContext) {
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 const getWebRequest = createWrapperFunction(toWebRequest)
470
-
471
- export { createApp as createServer } from 'h3'
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 async function handleHTTPEvent(event: H3Event) {
498
- return await (globalThis as any).$handle(event)
490
+ export function requestHandler(handler: RequestHandler) {
491
+ return handler
499
492
  }