@tanstack/start-server-core 1.131.7 → 1.132.0-alpha.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.
Files changed (63) hide show
  1. package/dist/esm/createStartHandler.d.ts +1 -1
  2. package/dist/esm/createStartHandler.js +8 -9
  3. package/dist/esm/createStartHandler.js.map +1 -1
  4. package/dist/esm/index.d.ts +1 -1
  5. package/dist/esm/index.js +6 -90
  6. package/dist/esm/index.js.map +1 -1
  7. package/dist/esm/loadVirtualModule.js +2 -0
  8. package/dist/esm/loadVirtualModule.js.map +1 -1
  9. package/dist/esm/request-response.d.ts +124 -0
  10. package/dist/esm/request-response.js +177 -0
  11. package/dist/esm/request-response.js.map +1 -0
  12. package/dist/esm/router-manifest.js +6 -3
  13. package/dist/esm/router-manifest.js.map +1 -1
  14. package/dist/esm/server-functions-handler.js +25 -59
  15. package/dist/esm/server-functions-handler.js.map +1 -1
  16. package/dist/esm/serverRoute.js +1 -2
  17. package/dist/esm/serverRoute.js.map +1 -1
  18. package/dist/esm/session.d.ts +66 -0
  19. package/dist/esm/virtual-modules.d.ts +2 -0
  20. package/dist/esm/virtual-modules.js +2 -1
  21. package/dist/esm/virtual-modules.js.map +1 -1
  22. package/package.json +9 -19
  23. package/src/createStartHandler.ts +7 -7
  24. package/src/global.d.ts +1 -3
  25. package/src/index.tsx +1 -1
  26. package/src/loadVirtualModule.ts +2 -0
  27. package/src/request-response.ts +335 -0
  28. package/src/router-manifest.ts +6 -3
  29. package/src/server-functions-handler.ts +29 -67
  30. package/src/session.ts +75 -0
  31. package/src/tanstack-start.d.ts +4 -0
  32. package/src/virtual-modules.ts +2 -0
  33. package/dist/cjs/constants.cjs +0 -7
  34. package/dist/cjs/constants.cjs.map +0 -1
  35. package/dist/cjs/constants.d.cts +0 -3
  36. package/dist/cjs/createStartHandler.cjs +0 -342
  37. package/dist/cjs/createStartHandler.cjs.map +0 -1
  38. package/dist/cjs/createStartHandler.d.cts +0 -7
  39. package/dist/cjs/h3.cjs +0 -355
  40. package/dist/cjs/h3.cjs.map +0 -1
  41. package/dist/cjs/h3.d.cts +0 -109
  42. package/dist/cjs/index.cjs +0 -257
  43. package/dist/cjs/index.cjs.map +0 -1
  44. package/dist/cjs/index.d.cts +0 -10
  45. package/dist/cjs/loadVirtualModule.cjs +0 -39
  46. package/dist/cjs/loadVirtualModule.cjs.map +0 -1
  47. package/dist/cjs/loadVirtualModule.d.cts +0 -6
  48. package/dist/cjs/router-manifest.cjs +0 -46
  49. package/dist/cjs/router-manifest.cjs.map +0 -1
  50. package/dist/cjs/router-manifest.d.cts +0 -17
  51. package/dist/cjs/server-functions-handler.cjs +0 -181
  52. package/dist/cjs/server-functions-handler.cjs.map +0 -1
  53. package/dist/cjs/server-functions-handler.d.cts +0 -3
  54. package/dist/cjs/serverRoute.cjs +0 -104
  55. package/dist/cjs/serverRoute.cjs.map +0 -1
  56. package/dist/cjs/serverRoute.d.cts +0 -124
  57. package/dist/cjs/virtual-modules.cjs +0 -9
  58. package/dist/cjs/virtual-modules.cjs.map +0 -1
  59. package/dist/cjs/virtual-modules.d.cts +0 -10
  60. package/dist/esm/h3.d.ts +0 -109
  61. package/dist/esm/h3.js +0 -248
  62. package/dist/esm/h3.js.map +0 -1
  63. package/src/h3.ts +0 -492
@@ -1,7 +1,8 @@
1
1
  const VIRTUAL_MODULES = {
2
2
  routeTree: "tanstack-start-route-tree:v",
3
3
  startManifest: "tanstack-start-manifest:v",
4
- serverFnManifest: "tanstack-start-server-fn-manifest:v"
4
+ serverFnManifest: "tanstack-start-server-fn-manifest:v",
5
+ injectedHeadScripts: "tanstack-start-injected-head-scripts:v"
5
6
  };
6
7
  export {
7
8
  VIRTUAL_MODULES
@@ -1 +1 @@
1
- {"version":3,"file":"virtual-modules.js","sources":["../../src/virtual-modules.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/consistent-type-imports */\n\nexport const VIRTUAL_MODULES = {\n routeTree: 'tanstack-start-route-tree:v',\n startManifest: 'tanstack-start-manifest:v',\n serverFnManifest: 'tanstack-start-server-fn-manifest:v',\n} as const\n\nexport type VirtualModules = {\n [VIRTUAL_MODULES.routeTree]: typeof import('tanstack-start-route-tree:v')\n [VIRTUAL_MODULES.startManifest]: typeof import('tanstack-start-manifest:v')\n [VIRTUAL_MODULES.serverFnManifest]: typeof import('tanstack-start-server-fn-manifest:v')\n}\n"],"names":[],"mappings":"AAEO,MAAM,kBAAkB;AAAA,EAC7B,WAAW;AAAA,EACX,eAAe;AAAA,EACf,kBAAkB;AACpB;"}
1
+ {"version":3,"file":"virtual-modules.js","sources":["../../src/virtual-modules.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/consistent-type-imports */\n\nexport const VIRTUAL_MODULES = {\n routeTree: 'tanstack-start-route-tree:v',\n startManifest: 'tanstack-start-manifest:v',\n serverFnManifest: 'tanstack-start-server-fn-manifest:v',\n injectedHeadScripts: 'tanstack-start-injected-head-scripts:v',\n} as const\n\nexport type VirtualModules = {\n [VIRTUAL_MODULES.routeTree]: typeof import('tanstack-start-route-tree:v')\n [VIRTUAL_MODULES.startManifest]: typeof import('tanstack-start-manifest:v')\n [VIRTUAL_MODULES.serverFnManifest]: typeof import('tanstack-start-server-fn-manifest:v')\n [VIRTUAL_MODULES.injectedHeadScripts]: typeof import('tanstack-start-injected-head-scripts:v')\n}\n"],"names":[],"mappings":"AAEO,MAAM,kBAAkB;AAAA,EAC7B,WAAW;AAAA,EACX,eAAe;AAAA,EACf,kBAAkB;AAAA,EAClB,qBAAqB;AACvB;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/start-server-core",
3
- "version": "1.131.7",
3
+ "version": "1.132.0-alpha.0",
4
4
  "description": "Modern and scalable routing for React applications",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -30,10 +30,6 @@
30
30
  "import": {
31
31
  "types": "./dist/esm/index.d.ts",
32
32
  "default": "./dist/esm/index.js"
33
- },
34
- "require": {
35
- "types": "./dist/cjs/index.d.cts",
36
- "default": "./dist/cjs/index.cjs"
37
33
  }
38
34
  },
39
35
  "./package.json": "./package.json"
@@ -47,21 +43,15 @@
47
43
  "node": ">=12"
48
44
  },
49
45
  "dependencies": {
50
- "h3": "1.13.0",
51
- "isbot": "^5.1.22",
46
+ "@standard-schema/spec": "^1.0.0",
47
+ "cookie-es": "^2.0.0",
48
+ "fetchdts": "^0.1.6",
49
+ "h3": "2.0.0-beta.3",
52
50
  "tiny-invariant": "^1.3.3",
53
- "tiny-warning": "^1.0.3",
54
- "unctx": "^2.4.1",
55
- "@tanstack/history": "1.131.2",
56
- "@tanstack/start-client-core": "1.131.7",
57
- "@tanstack/start-storage-context": "1.131.7",
58
- "@tanstack/router-core": "1.131.7"
59
- },
60
- "devDependencies": {
61
- "esbuild": "^0.25.0",
62
- "typescript": "^5.7.2",
63
- "vite": "^6.3.5",
64
- "@tanstack/directive-functions-plugin": "1.131.2"
51
+ "@tanstack/history": "1.132.0-alpha.0",
52
+ "@tanstack/router-core": "1.132.0-alpha.0",
53
+ "@tanstack/start-client-core": "1.132.0-alpha.0",
54
+ "@tanstack/start-storage-context": "1.132.0-alpha.0"
65
55
  },
66
56
  "scripts": {}
67
57
  }
@@ -14,7 +14,7 @@ import {
14
14
  } from '@tanstack/router-core'
15
15
  import { attachRouterServerSsrUtils } from '@tanstack/router-core/ssr/server'
16
16
  import { runWithStartContext } from '@tanstack/start-storage-context'
17
- import { getResponseHeaders, requestHandler } from './h3'
17
+ import { getResponseHeaders, requestHandler } from './request-response'
18
18
  import { getStartManifest } from './router-manifest'
19
19
  import { handleServerAction } from './server-functions-handler'
20
20
  import { VIRTUAL_MODULES } from './virtual-modules'
@@ -25,7 +25,7 @@ import type {
25
25
  AnyServerRouteWithTypes,
26
26
  ServerRouteMethodHandlerFn,
27
27
  } from './serverRoute'
28
- import type { RequestHandler } from './h3'
28
+ import type { RequestHandler } from './request-response'
29
29
  import type {
30
30
  AnyRoute,
31
31
  AnyRouter,
@@ -43,9 +43,9 @@ export type CustomizeStartHandler<TRouter extends AnyRouter> = (
43
43
 
44
44
  function getStartResponseHeaders(opts: { router: AnyRouter }) {
45
45
  const headers = mergeHeaders(
46
- getResponseHeaders(),
46
+ getResponseHeaders() as Headers,
47
47
  {
48
- 'Content-Type': 'text/html; charset=UTF-8',
48
+ 'Content-Type': 'text/html; charset=utf-8',
49
49
  },
50
50
  ...opts.router.state.matches.map((match) => {
51
51
  return match.headers
@@ -71,7 +71,7 @@ export function createStartHandler<TRouter extends AnyRouter>({
71
71
  return (cb) => {
72
72
  const originalFetch = globalThis.fetch
73
73
 
74
- const startRequestResolver: RequestHandler = async ({ request }) => {
74
+ const startRequestResolver: RequestHandler = async (request) => {
75
75
  // Patching fetch function to use our request resolver
76
76
  // if the input starts with `/` which is a common pattern for
77
77
  // client-side routing.
@@ -80,7 +80,7 @@ export function createStartHandler<TRouter extends AnyRouter>({
80
80
  globalThis.fetch = async function (input, init) {
81
81
  function resolve(url: URL, requestOptions: RequestInit | undefined) {
82
82
  const fetchRequest = new Request(url, requestOptions)
83
- return startRequestResolver({ request: fetchRequest })
83
+ return startRequestResolver(fetchRequest)
84
84
  }
85
85
 
86
86
  function getOrigin() {
@@ -111,7 +111,7 @@ export function createStartHandler<TRouter extends AnyRouter>({
111
111
  }
112
112
 
113
113
  const url = new URL(request.url)
114
- const href = url.href.replace(url.origin, '')
114
+ const href = decodeURIComponent(url.href.replace(url.origin, ''))
115
115
 
116
116
  const APP_BASE = process.env.TSS_APP_BASE || '/'
117
117
 
package/src/global.d.ts CHANGED
@@ -1,7 +1,4 @@
1
- /* eslint-disable no-var */
2
1
  declare global {
3
- var TSS_INJECTED_HEAD_SCRIPTS: string | undefined
4
-
5
2
  namespace NodeJS {
6
3
  interface ProcessEnv {
7
4
  TSS_APP_BASE?: string
@@ -9,6 +6,7 @@ declare global {
9
6
  TSS_OUTPUT_PUBLIC_DIR?: string
10
7
  TSS_SHELL?: 'true' | 'false'
11
8
  TSS_PRERENDERING?: 'true' | 'false'
9
+ TSS_DEV_SERVER?: 'true' | 'false'
12
10
  }
13
11
  }
14
12
  }
package/src/index.tsx CHANGED
@@ -12,7 +12,7 @@ export type { HandlerCallback } from '@tanstack/router-core/ssr/server'
12
12
 
13
13
  export { handleServerAction } from './server-functions-handler'
14
14
 
15
- export * from './h3'
15
+ export * from './request-response'
16
16
 
17
17
  export {
18
18
  createServerRoute,
@@ -15,6 +15,8 @@ export async function loadVirtualModule<TId extends keyof VirtualModules>(
15
15
  return (await import('tanstack-start-manifest:v')) as any
16
16
  case VIRTUAL_MODULES.serverFnManifest:
17
17
  return (await import('tanstack-start-server-fn-manifest:v')) as any
18
+ case VIRTUAL_MODULES.injectedHeadScripts:
19
+ return (await import('tanstack-start-injected-head-scripts:v')) as any
18
20
  default:
19
21
  throw new Error(`Unknown virtual module: ${id}`)
20
22
  }
@@ -0,0 +1,335 @@
1
+ import { AsyncLocalStorage } from 'node:async_hooks'
2
+
3
+ import {
4
+ H3Event,
5
+ clearSession as h3_clearSession,
6
+ deleteCookie as h3_deleteCookie,
7
+ getRequestHost as h3_getRequestHost,
8
+ getRequestIP as h3_getRequestIP,
9
+ getRequestProtocol as h3_getRequestProtocol,
10
+ getRequestURL as h3_getRequestURL,
11
+ getSession as h3_getSession,
12
+ getValidatedQuery as h3_getValidatedQuery,
13
+ parseCookies as h3_parseCookies,
14
+ sanitizeStatusCode as h3_sanitizeStatusCode,
15
+ sanitizeStatusMessage as h3_sanitizeStatusMessage,
16
+ sealSession as h3_sealSession,
17
+ setCookie as h3_setCookie,
18
+ toResponse as h3_toResponse,
19
+ unsealSession as h3_unsealSession,
20
+ updateSession as h3_updateSession,
21
+ useSession as h3_useSession,
22
+ } from 'h3'
23
+ import type {
24
+ RequestHeaderMap,
25
+ RequestHeaderName,
26
+ ResponseHeaderMap,
27
+ ResponseHeaderName,
28
+ TypedHeaders,
29
+ } from 'fetchdts'
30
+
31
+ import type { CookieSerializeOptions } from 'cookie-es'
32
+ import type {
33
+ Session,
34
+ SessionConfig,
35
+ SessionData,
36
+ SessionManager,
37
+ SessionUpdate,
38
+ } from './session'
39
+ import type { StandardSchemaV1 } from '@standard-schema/spec'
40
+
41
+ interface StartEvent {
42
+ h3Event: H3Event
43
+ }
44
+ const eventStorage = new AsyncLocalStorage<StartEvent>()
45
+
46
+ export type RequestHandler = (request: Request) => Promise<Response> | Response
47
+
48
+ export type { ResponseHeaderName, RequestHeaderName }
49
+
50
+ export function requestHandler(handler: RequestHandler) {
51
+ return (request: Request): Promise<Response> | Response => {
52
+ const h3Event = new H3Event(request)
53
+
54
+ const response = eventStorage.run({ h3Event }, () => handler(request))
55
+ return h3_toResponse(response, h3Event)
56
+ }
57
+ }
58
+
59
+ function getH3Event() {
60
+ const event = eventStorage.getStore()
61
+ if (!event) {
62
+ throw new Error(
63
+ `No StartEvent found in AsyncLocalStorage. Make sure you are using the function within the server runtime.`,
64
+ )
65
+ }
66
+ return event.h3Event
67
+ }
68
+
69
+ export function getRequest(): Request {
70
+ const event = getH3Event()
71
+ return event.req
72
+ }
73
+
74
+ export function getRequestHeaders(): TypedHeaders<RequestHeaderMap> {
75
+ // TODO `as any` not needed when fetchdts is updated
76
+ return getH3Event().req.headers as any
77
+ }
78
+
79
+ export function getRequestHeader(name: RequestHeaderName): string | undefined {
80
+ return getRequestHeaders().get(name) || undefined
81
+ }
82
+
83
+ export function getRequestIP(opts?: {
84
+ /**
85
+ * Use the X-Forwarded-For HTTP header set by proxies.
86
+ *
87
+ * Note: Make sure that this header can be trusted (your application running behind a CDN or reverse proxy) before enabling.
88
+ */
89
+ xForwardedFor?: boolean
90
+ }) {
91
+ return h3_getRequestIP(getH3Event(), opts)
92
+ }
93
+
94
+ /**
95
+ * Get the request hostname.
96
+ *
97
+ * If `xForwardedHost` is `true`, it will use the `x-forwarded-host` header if it exists.
98
+ *
99
+ * If no host header is found, it will default to "localhost".
100
+ */
101
+ export function getRequestHost(opts?: { xForwardedHost?: boolean }) {
102
+ return h3_getRequestHost(getH3Event(), opts)
103
+ }
104
+
105
+ /**
106
+ * Get the full incoming request URL.
107
+ *
108
+ * If `xForwardedHost` is `true`, it will use the `x-forwarded-host` header if it exists.
109
+ *
110
+ * If `xForwardedProto` is `false`, it will not use the `x-forwarded-proto` header.
111
+ */
112
+ export function getRequestUrl(opts?: {
113
+ xForwardedHost?: boolean
114
+ xForwardedProto?: boolean
115
+ }) {
116
+ return h3_getRequestURL(getH3Event(), opts)
117
+ }
118
+
119
+ /**
120
+ * Get the request protocol.
121
+ *
122
+ * If `x-forwarded-proto` header is set to "https", it will return "https". You can disable this behavior by setting `xForwardedProto` to `false`.
123
+ *
124
+ * If protocol cannot be determined, it will default to "http".
125
+ */
126
+ export function getRequestProtocol(opts?: {
127
+ xForwardedProto?: boolean
128
+ }): 'http' | 'https' | (string & {}) {
129
+ return h3_getRequestProtocol(getH3Event(), opts)
130
+ }
131
+
132
+ export function setResponseHeaders(
133
+ headers: TypedHeaders<ResponseHeaderMap>,
134
+ ): void {
135
+ const event = getH3Event()
136
+ for (const [name, value] of Object.entries(headers)) {
137
+ event.res.headers.set(name, value)
138
+ }
139
+ }
140
+
141
+ export function getResponseHeaders(): TypedHeaders<ResponseHeaderMap> {
142
+ const event = getH3Event()
143
+ return Object.fromEntries(event.res.headers.entries()) as any
144
+ }
145
+
146
+ export function getResponseHeader(
147
+ name: ResponseHeaderName,
148
+ ): string | undefined {
149
+ const event = getH3Event()
150
+ return event.res.headers.get(name) || undefined
151
+ }
152
+
153
+ export function setResponseHeader(
154
+ name: ResponseHeaderName,
155
+ value: string | Array<string>,
156
+ ): void {
157
+ const event = getH3Event()
158
+ if (Array.isArray(value)) {
159
+ event.res.headers.delete(name)
160
+ for (const valueItem of value) {
161
+ event.res.headers.append(name, valueItem)
162
+ }
163
+ } else {
164
+ event.res.headers.set(name, value)
165
+ }
166
+ }
167
+ export function removeResponseHeader(name: ResponseHeaderName): void {
168
+ const event = getH3Event()
169
+ event.res.headers.delete(name)
170
+ }
171
+
172
+ export function clearResponseHeaders(
173
+ headerNames?: Array<ResponseHeaderName>,
174
+ ): void {
175
+ const event = getH3Event()
176
+ // If headerNames is provided, clear only those headers
177
+ if (headerNames && headerNames.length > 0) {
178
+ for (const name of headerNames) {
179
+ event.res.headers.delete(name)
180
+ }
181
+ // Otherwise, clear all headers
182
+ } else {
183
+ for (const name of event.res.headers.keys()) {
184
+ event.res.headers.delete(name)
185
+ }
186
+ }
187
+ }
188
+
189
+ export function getResponseStatus(): number {
190
+ return getH3Event().res.status || 200
191
+ }
192
+
193
+ export function setResponseStatus(code?: number, text?: string): void {
194
+ const event = getH3Event()
195
+ if (code) {
196
+ event.res.status = h3_sanitizeStatusCode(code, event.res.status)
197
+ }
198
+ if (text) {
199
+ event.res.statusText = h3_sanitizeStatusMessage(text)
200
+ }
201
+ }
202
+
203
+ /**
204
+ * Parse the request to get HTTP Cookie header string and return an object of all cookie name-value pairs.
205
+ * @returns Object of cookie name-value pairs
206
+ * ```ts
207
+ * const cookies = getCookies()
208
+ * ```
209
+ */
210
+ export function getCookies(): Record<string, string> {
211
+ const event = getH3Event()
212
+ return h3_parseCookies(event)
213
+ }
214
+
215
+ /**
216
+ * Get a cookie value by name.
217
+ * @param name Name of the cookie to get
218
+ * @returns {*} Value of the cookie (String or undefined)
219
+ * ```ts
220
+ * const authorization = getCookie('Authorization')
221
+ * ```
222
+ */
223
+ export function getCookie(name: string): string | undefined {
224
+ return getCookies()[name] || undefined
225
+ }
226
+
227
+ /**
228
+ * Set a cookie value by name.
229
+ * @param name Name of the cookie to set
230
+ * @param value Value of the cookie to set
231
+ * @param options {CookieSerializeOptions} Options for serializing the cookie
232
+ * ```ts
233
+ * setCookie('Authorization', '1234567')
234
+ * ```
235
+ */
236
+ export function setCookie(
237
+ name: string,
238
+ value: string,
239
+ options?: CookieSerializeOptions,
240
+ ): void {
241
+ const event = getH3Event()
242
+ h3_setCookie(event, name, value, options)
243
+ }
244
+
245
+ /**
246
+ * Remove a cookie by name.
247
+ * @param name Name of the cookie to delete
248
+ * @param serializeOptions {CookieSerializeOptions} Cookie options
249
+ * ```ts
250
+ * deleteCookie('SessionId')
251
+ * ```
252
+ */
253
+ export function deleteCookie(
254
+ name: string,
255
+ options?: CookieSerializeOptions,
256
+ ): void {
257
+ const event = getH3Event()
258
+ h3_deleteCookie(event, name, options)
259
+ }
260
+
261
+ function getDefaultSessionConfig(config: SessionConfig): SessionConfig {
262
+ return {
263
+ name: 'start',
264
+ ...config,
265
+ }
266
+ }
267
+
268
+ /**
269
+ * Create a session manager for the current request.
270
+ */
271
+ export function useSession<TSessionData extends SessionData = SessionData>(
272
+ config: SessionConfig,
273
+ ): Promise<SessionManager<TSessionData>> {
274
+ const event = getH3Event()
275
+ return h3_useSession(event, getDefaultSessionConfig(config))
276
+ }
277
+ /**
278
+ * Get the session for the current request
279
+ */
280
+ export function getSession<TSessionData extends SessionData = SessionData>(
281
+ config: SessionConfig,
282
+ ): Promise<Session<TSessionData>> {
283
+ const event = getH3Event()
284
+ return h3_getSession(event, getDefaultSessionConfig(config))
285
+ }
286
+
287
+ /**
288
+ * Update the session data for the current request.
289
+ */
290
+ export function updateSession<TSessionData extends SessionData = SessionData>(
291
+ config: SessionConfig,
292
+ update?: SessionUpdate<TSessionData>,
293
+ ): Promise<Session<TSessionData>> {
294
+ const event = getH3Event()
295
+ return h3_updateSession(event, getDefaultSessionConfig(config), update)
296
+ }
297
+
298
+ /**
299
+ * Encrypt and sign the session data for the current request.
300
+ */
301
+ export function sealSession(config: SessionConfig): Promise<string> {
302
+ const event = getH3Event()
303
+ return h3_sealSession(event, getDefaultSessionConfig(config))
304
+ }
305
+ /**
306
+ * Decrypt and verify the session data for the current request.
307
+ */
308
+ export function unsealSession(
309
+ config: SessionConfig,
310
+ sealed: string,
311
+ ): Promise<Partial<Session>> {
312
+ const event = getH3Event()
313
+ return h3_unsealSession(event, getDefaultSessionConfig(config), sealed)
314
+ }
315
+
316
+ /**
317
+ * Clear the session data for the current request.
318
+ */
319
+ export function clearSession(config: Partial<SessionConfig>): Promise<void> {
320
+ const event = getH3Event()
321
+ return h3_clearSession(event, { name: 'start', ...config })
322
+ }
323
+
324
+ // not public API
325
+ export function getResponse() {
326
+ const event = getH3Event()
327
+ return event._res
328
+ }
329
+
330
+ // not public API (yet)
331
+ export function getValidatedQuery<TSchema extends StandardSchemaV1>(
332
+ schema: StandardSchemaV1,
333
+ ): Promise<StandardSchemaV1.InferOutput<TSchema>> {
334
+ return h3_getValidatedQuery(getH3Event(), schema)
335
+ }
@@ -20,9 +20,12 @@ export async function getStartManifest(opts: { basePath: string }) {
20
20
  rootRoute.assets = rootRoute.assets || []
21
21
 
22
22
  let script = `import('${startManifest.clientEntry}')`
23
- if (process.env.NODE_ENV === 'development') {
24
- if (globalThis.TSS_INJECTED_HEAD_SCRIPTS) {
25
- script = `${globalThis.TSS_INJECTED_HEAD_SCRIPTS + ';'}${script}`
23
+ if (process.env.TSS_DEV_SERVER === 'true') {
24
+ const { injectedHeadScripts } = await loadVirtualModule(
25
+ VIRTUAL_MODULES.injectedHeadScripts,
26
+ )
27
+ if (injectedHeadScripts) {
28
+ script = `${injectedHeadScripts + ';'}${script}`
26
29
  }
27
30
  }
28
31
  rootRoute.assets.push({
@@ -1,9 +1,9 @@
1
1
  import { isNotFound } from '@tanstack/router-core'
2
2
  import invariant from 'tiny-invariant'
3
3
  import { startSerializer } from '@tanstack/start-client-core'
4
- import { getEvent, getResponseStatus } from './h3'
5
4
  import { VIRTUAL_MODULES } from './virtual-modules'
6
5
  import { loadVirtualModule } from './loadVirtualModule'
6
+ import { getResponse } from './request-response'
7
7
 
8
8
  function sanitizeBase(base: string | undefined) {
9
9
  if (!base) {
@@ -15,36 +15,35 @@ function sanitizeBase(base: string | undefined) {
15
15
  return base.replace(/^\/|\/$/g, '')
16
16
  }
17
17
 
18
- async function revive(root: any, reviver?: (key: string, value: any) => any) {
19
- async function reviveNode(holder: any, key: string) {
20
- const value = holder[key]
18
+ export const handleServerAction = async ({ request }: { request: Request }) => {
19
+ const controller = new AbortController()
20
+ const signal = controller.signal
21
+ const abort = () => controller.abort()
22
+ request.signal.addEventListener('abort', abort)
21
23
 
22
- if (value && typeof value === 'object') {
23
- await Promise.all(Object.keys(value).map((k) => reviveNode(value, k)))
24
- }
24
+ const method = request.method
25
+ const url = new URL(request.url, 'http://localhost:3000')
26
+ // extract the serverFnId from the url as host/_serverFn/:serverFnId
27
+ // Define a regex to match the path and extract the :thing part
28
+ const regex = new RegExp(
29
+ `${sanitizeBase(process.env.TSS_SERVER_FN_BASE)}/([^/?#]+)`,
30
+ )
25
31
 
26
- if (reviver) {
27
- holder[key] = await reviver(key, holder[key])
28
- }
32
+ // Execute the regex
33
+ const match = url.pathname.match(regex)
34
+ const serverFnId = match ? match[1] : null
35
+ const search = Object.fromEntries(url.searchParams.entries()) as {
36
+ payload?: any
37
+ createServerFn?: boolean
29
38
  }
30
39
 
31
- const holder = { '': root }
32
- await reviveNode(holder, '')
33
- return holder['']
34
- }
40
+ const isCreateServerFn = 'createServerFn' in search
41
+ const isRaw = 'raw' in search
35
42
 
36
- async function reviveServerFns(key: string, value: any) {
37
- if (value && value.__serverFn === true && value.functionId) {
38
- const serverFn = await getServerFnById(value.functionId)
39
- return async (opts: any, signal: any): Promise<any> => {
40
- const result = await serverFn(opts ?? {}, signal)
41
- return result.result
42
- }
43
+ if (typeof serverFnId !== 'string') {
44
+ throw new Error('Invalid server action param for serverFnId: ' + serverFnId)
43
45
  }
44
- return value
45
- }
46
46
 
47
- async function getServerFnById(serverFnId: string) {
48
47
  const { default: serverFnManifest } = await loadVirtualModule(
49
48
  VIRTUAL_MODULES.serverFnManifest,
50
49
  )
@@ -72,45 +71,6 @@ async function getServerFnById(serverFnId: string) {
72
71
  `Server function module export not resolved for serverFn ID: ${serverFnId}`,
73
72
  )
74
73
  }
75
- return action
76
- }
77
-
78
- async function parsePayload(payload: any) {
79
- const parsedPayload = startSerializer.parse(payload)
80
- await revive(parsedPayload, reviveServerFns)
81
- return parsedPayload
82
- }
83
-
84
- export const handleServerAction = async ({ request }: { request: Request }) => {
85
- const controller = new AbortController()
86
- const signal = controller.signal
87
- const abort = () => controller.abort()
88
- request.signal.addEventListener('abort', abort)
89
-
90
- const method = request.method
91
- const url = new URL(request.url, 'http://localhost:3000')
92
- // extract the serverFnId from the url as host/_serverFn/:serverFnId
93
- // Define a regex to match the path and extract the :thing part
94
- const regex = new RegExp(
95
- `${sanitizeBase(process.env.TSS_SERVER_FN_BASE)}/([^/?#]+)`,
96
- )
97
-
98
- // Execute the regex
99
- const match = url.pathname.match(regex)
100
- const serverFnId = match ? match[1] : null
101
- const search = Object.fromEntries(url.searchParams.entries()) as {
102
- payload?: any
103
- createServerFn?: boolean
104
- }
105
-
106
- const isCreateServerFn = 'createServerFn' in search
107
- const isRaw = 'raw' in search
108
-
109
- if (typeof serverFnId !== 'string') {
110
- throw new Error('Invalid server action param for serverFnId: ' + serverFnId)
111
- }
112
-
113
- const action = await getServerFnById(serverFnId)
114
74
 
115
75
  // Known FormData 'Content-Type' header values
116
76
  const formDataContentTypes = [
@@ -149,7 +109,7 @@ export const handleServerAction = async ({ request }: { request: Request }) => {
149
109
  }
150
110
 
151
111
  // If there's a payload, we should try to parse it
152
- payload = payload ? await parsePayload(payload) : payload
112
+ payload = payload ? startSerializer.parse(payload) : payload
153
113
 
154
114
  // Send it through!
155
115
  return await action(payload, signal)
@@ -160,7 +120,7 @@ export const handleServerAction = async ({ request }: { request: Request }) => {
160
120
 
161
121
  // We should probably try to deserialize the payload
162
122
  // as JSON, but we'll just pass it through for now.
163
- const payload = await parsePayload(jsonPayloadAsString)
123
+ const payload = startSerializer.parse(jsonPayloadAsString)
164
124
 
165
125
  // If this POST request was created by createServerFn,
166
126
  // it's payload will be the only argument
@@ -227,10 +187,12 @@ export const handleServerAction = async ({ request }: { request: Request }) => {
227
187
  return isNotFoundResponse(result)
228
188
  }
229
189
 
190
+ const response = getResponse()
230
191
  return new Response(
231
192
  result !== undefined ? startSerializer.stringify(result) : undefined,
232
193
  {
233
- status: getResponseStatus(getEvent()),
194
+ status: response?.status,
195
+ statusText: response?.statusText,
234
196
  headers: {
235
197
  'Content-Type': 'application/json',
236
198
  },
@@ -285,7 +247,7 @@ function isNotFoundResponse(error: any) {
285
247
  const { headers, ...rest } = error
286
248
 
287
249
  return new Response(JSON.stringify(rest), {
288
- status: 200,
250
+ status: 404,
289
251
  headers: {
290
252
  'Content-Type': 'application/json',
291
253
  ...(headers || {}),