@tanstack/start-server-core 1.120.4-alpha.8 → 1.120.5

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 (62) hide show
  1. package/README.md +27 -6
  2. package/dist/cjs/createRequestHandler.cjs +3 -1
  3. package/dist/cjs/createRequestHandler.cjs.map +1 -1
  4. package/dist/cjs/createStartHandler.cjs +30 -235
  5. package/dist/cjs/createStartHandler.cjs.map +1 -1
  6. package/dist/cjs/createStartHandler.d.cts +6 -8
  7. package/dist/cjs/h3.cjs +73 -30
  8. package/dist/cjs/h3.cjs.map +1 -1
  9. package/dist/cjs/h3.d.cts +7 -11
  10. package/dist/cjs/handlerCallback.cjs.map +1 -1
  11. package/dist/cjs/handlerCallback.d.cts +4 -3
  12. package/dist/cjs/index.cjs +14 -20
  13. package/dist/cjs/index.cjs.map +1 -1
  14. package/dist/cjs/index.d.cts +1 -7
  15. package/dist/esm/createRequestHandler.js +3 -1
  16. package/dist/esm/createRequestHandler.js.map +1 -1
  17. package/dist/esm/createStartHandler.d.ts +6 -8
  18. package/dist/esm/createStartHandler.js +32 -215
  19. package/dist/esm/createStartHandler.js.map +1 -1
  20. package/dist/esm/h3.d.ts +7 -11
  21. package/dist/esm/h3.js +63 -26
  22. package/dist/esm/h3.js.map +1 -1
  23. package/dist/esm/handlerCallback.d.ts +4 -3
  24. package/dist/esm/handlerCallback.js.map +1 -1
  25. package/dist/esm/index.d.ts +1 -7
  26. package/dist/esm/index.js +5 -17
  27. package/dist/esm/index.js.map +1 -1
  28. package/package.json +6 -9
  29. package/src/createRequestHandler.ts +3 -1
  30. package/src/createStartHandler.ts +48 -325
  31. package/src/h3.ts +78 -71
  32. package/src/handlerCallback.ts +12 -5
  33. package/src/index.tsx +1 -13
  34. package/dist/cjs/router-manifest.cjs +0 -44
  35. package/dist/cjs/router-manifest.cjs.map +0 -1
  36. package/dist/cjs/router-manifest.d.cts +0 -17
  37. package/dist/cjs/server-functions-handler.cjs +0 -154
  38. package/dist/cjs/server-functions-handler.cjs.map +0 -1
  39. package/dist/cjs/server-functions-handler.d.cts +0 -4
  40. package/dist/cjs/serverRoute.cjs +0 -100
  41. package/dist/cjs/serverRoute.cjs.map +0 -1
  42. package/dist/cjs/serverRoute.d.cts +0 -115
  43. package/dist/cjs/undici.cjs +0 -14
  44. package/dist/cjs/undici.cjs.map +0 -1
  45. package/dist/cjs/undici.d.cts +0 -43
  46. package/dist/esm/router-manifest.d.ts +0 -17
  47. package/dist/esm/router-manifest.js +0 -44
  48. package/dist/esm/router-manifest.js.map +0 -1
  49. package/dist/esm/server-functions-handler.d.ts +0 -4
  50. package/dist/esm/server-functions-handler.js +0 -154
  51. package/dist/esm/server-functions-handler.js.map +0 -1
  52. package/dist/esm/serverRoute.d.ts +0 -115
  53. package/dist/esm/serverRoute.js +0 -100
  54. package/dist/esm/serverRoute.js.map +0 -1
  55. package/dist/esm/undici.d.ts +0 -43
  56. package/dist/esm/undici.js +0 -14
  57. package/dist/esm/undici.js.map +0 -1
  58. package/src/router-manifest.ts +0 -79
  59. package/src/server-functions-handler.ts +0 -273
  60. package/src/serverRoute.ts +0 -661
  61. package/src/tanstack-start.d.ts +0 -5
  62. package/src/undici.ts +0 -60
@@ -1,79 +0,0 @@
1
- import { tsrStartManifest } from 'tanstack:start-manifest'
2
- import { rootRouteId } from '@tanstack/router-core'
3
-
4
- declare global {
5
- // eslint-disable-next-line no-var
6
- var TSS_INJECTED_HEAD_SCRIPTS: string | undefined
7
- }
8
-
9
- /**
10
- * @description Returns the router manifest that should be sent to the client.
11
- * This includes only the assets and preloads for the current route and any
12
- * special assets that are needed for the client. It does not include relationships
13
- * between routes or any other data that is not needed for the client.
14
- */
15
- export function getStartManifest() {
16
- const startManifest = tsrStartManifest()
17
-
18
- const rootRoute = (startManifest.routes[rootRouteId] =
19
- startManifest.routes[rootRouteId] || {})
20
-
21
- rootRoute.assets = rootRoute.assets || []
22
-
23
- // Get the entry for the client
24
- // const ClientManifest = getManifest('client')
25
-
26
- // const importPath =
27
- // ClientManifest.inputs[ClientManifest.handler]?.output.path
28
- // if (!importPath) {
29
- // invariant(importPath, 'Could not find client entry in manifest')
30
- // }
31
-
32
- if (process.env.NODE_ENV === 'development' && !process.env.TSS_CLIENT_ENTRY) {
33
- throw new Error(
34
- 'tanstack/start-server-core: TSS_CLIENT_ENTRY must be defined in your environment for getStartManifest()',
35
- )
36
- }
37
-
38
- if (process.env.NODE_ENV === 'development') {
39
- // Always fake that HMR is ready
40
- // const CLIENT_BASE = sanitizeBase(process.env.TSS_CLIENT_BASE || '')
41
-
42
- // if (!CLIENT_BASE) {
43
- // throw new Error(
44
- // 'tanstack/start-router-manifest: TSS_CLIENT_BASE must be defined in your environment for getFullRouterManifest()',
45
- // )
46
- // }
47
-
48
- const script = `${globalThis.TSS_INJECTED_HEAD_SCRIPTS ? globalThis.TSS_INJECTED_HEAD_SCRIPTS + '; ' : ''}import(${JSON.stringify(process.env.TSS_CLIENT_ENTRY)})`
49
-
50
- rootRoute.assets.push({
51
- tag: 'script',
52
- attrs: {
53
- type: 'module',
54
- suppressHydrationWarning: true,
55
- async: true,
56
- },
57
- children: script,
58
- })
59
- }
60
-
61
- const manifest = {
62
- ...startManifest,
63
- routes: Object.fromEntries(
64
- Object.entries(startManifest.routes).map(([k, v]) => {
65
- const { preloads, assets } = v
66
- return [
67
- k,
68
- {
69
- preloads,
70
- assets,
71
- },
72
- ]
73
- }),
74
- ),
75
- }
76
-
77
- // Strip out anything that isn't needed for the client
78
- return manifest
79
- }
@@ -1,273 +0,0 @@
1
- import { isNotFound } from '@tanstack/router-core'
2
- import invariant from 'tiny-invariant'
3
- import { startSerializer } from '@tanstack/start-client-core'
4
- // @ts-expect-error
5
- import _serverFnManifest from 'tanstack:server-fn-manifest'
6
- import { getEvent, getResponseStatus } from './h3'
7
-
8
- // NOTE: This is a dummy export to silence warnings about
9
- // only having a default export.
10
- export const dummy = 2
11
-
12
- const serverFnManifest = _serverFnManifest as Record<
13
- string,
14
- {
15
- functionName: string
16
- extractedFilename: string
17
- importer: () => Promise<any>
18
- }
19
- >
20
-
21
- function sanitizeBase(base: string | undefined) {
22
- if (!base) {
23
- throw new Error(
24
- '🚨 process.env.TSS_SERVER_FN_BASE is required in start/server-handler/index',
25
- )
26
- }
27
-
28
- return base.replace(/^\/|\/$/g, '')
29
- }
30
-
31
- export const handleServerAction = async ({ request }: { request: Request }) => {
32
- const controller = new AbortController()
33
- const signal = controller.signal
34
- const abort = () => controller.abort()
35
- request.signal.addEventListener('abort', abort)
36
-
37
- const method = request.method
38
- const url = new URL(request.url, 'http://localhost:3000')
39
- // extract the serverFnId from the url as host/_server/:serverFnId
40
- // Define a regex to match the path and extract the :thing part
41
- const regex = new RegExp(
42
- `${sanitizeBase(process.env.TSS_SERVER_FN_BASE)}/([^/?#]+)`,
43
- )
44
-
45
- // Execute the regex
46
- const match = url.pathname.match(regex)
47
- const serverFnId = match ? match[1] : null
48
- const search = Object.fromEntries(url.searchParams.entries()) as {
49
- payload?: any
50
- createServerFn?: boolean
51
- }
52
-
53
- const isCreateServerFn = 'createServerFn' in search
54
- const isRaw = 'raw' in search
55
-
56
- if (typeof serverFnId !== 'string') {
57
- throw new Error('Invalid server action param for serverFnId: ' + serverFnId)
58
- }
59
-
60
- const serverFnInfo = serverFnManifest[serverFnId]
61
-
62
- if (!serverFnInfo) {
63
- console.info('serverFnManifest', serverFnManifest)
64
- throw new Error('Server function info not found for ' + serverFnId)
65
- }
66
-
67
- let fnModule: undefined | { [key: string]: any }
68
-
69
- if (process.env.NODE_ENV === 'development') {
70
- const serverEnv = (globalThis as any).viteDevServer.environments['server']
71
- if (!serverEnv) {
72
- throw new Error(`'server' vite dev environment not found`)
73
- }
74
- fnModule = await serverEnv.runner.import(serverFnInfo.extractedFilename)
75
- } else {
76
- fnModule = await serverFnInfo.importer()
77
- }
78
-
79
- if (!fnModule) {
80
- console.info('serverFnInfo', serverFnInfo)
81
- throw new Error('Server function module not resolved for ' + serverFnId)
82
- }
83
-
84
- const action = fnModule[serverFnInfo.functionName]
85
-
86
- if (!action) {
87
- console.info('serverFnInfo', serverFnInfo)
88
- console.info('fnModule', fnModule)
89
- throw new Error(
90
- `Server function module export not resolved for serverFn ID: ${serverFnId}`,
91
- )
92
- }
93
-
94
- // Known FormData 'Content-Type' header values
95
- const formDataContentTypes = [
96
- 'multipart/form-data',
97
- 'application/x-www-form-urlencoded',
98
- ]
99
-
100
- const response = await (async () => {
101
- try {
102
- let result = await (async () => {
103
- // FormData
104
- if (
105
- request.headers.get('Content-Type') &&
106
- formDataContentTypes.some((type) =>
107
- request.headers.get('Content-Type')?.includes(type),
108
- )
109
- ) {
110
- // We don't support GET requests with FormData payloads... that seems impossible
111
- invariant(
112
- method.toLowerCase() !== 'get',
113
- 'GET requests with FormData payloads are not supported',
114
- )
115
-
116
- return await action(await request.formData(), signal)
117
- }
118
-
119
- // Get requests use the query string
120
- if (method.toLowerCase() === 'get') {
121
- // By default the payload is the search params
122
- let payload: any = search
123
-
124
- // If this GET request was created by createServerFn,
125
- // then the payload will be on the payload param
126
- if (isCreateServerFn) {
127
- payload = search.payload
128
- }
129
-
130
- // If there's a payload, we should try to parse it
131
- payload = payload ? startSerializer.parse(payload) : payload
132
-
133
- // Send it through!
134
- return await action(payload, signal)
135
- }
136
-
137
- // This must be a POST request, likely JSON???
138
- const jsonPayloadAsString = await request.text()
139
-
140
- // We should probably try to deserialize the payload
141
- // as JSON, but we'll just pass it through for now.
142
- const payload = startSerializer.parse(jsonPayloadAsString)
143
-
144
- // If this POST request was created by createServerFn,
145
- // it's payload will be the only argument
146
- if (isCreateServerFn) {
147
- return await action(payload, signal)
148
- }
149
-
150
- // Otherwise, we'll spread the payload. Need to
151
- // support `use server` functions that take multiple
152
- // arguments.
153
- return await action(...(payload as any), signal)
154
- })()
155
-
156
- // Any time we get a Response back, we should just
157
- // return it immediately.
158
- if (result.result instanceof Response) {
159
- return result.result
160
- }
161
-
162
- // If this is a non createServerFn request, we need to
163
- // pull out the result from the result object
164
- if (!isCreateServerFn) {
165
- result = result.result
166
-
167
- // The result might again be a response,
168
- // and if it is, return it.
169
- if (result instanceof Response) {
170
- return result
171
- }
172
- }
173
-
174
- // if (!search.createServerFn) {
175
- // result = result.result
176
- // }
177
-
178
- // else if (
179
- // isPlainObject(result) &&
180
- // 'result' in result &&
181
- // result.result instanceof Response
182
- // ) {
183
- // return result.result
184
- // }
185
-
186
- // TODO: RSCs Where are we getting this package?
187
- // if (isValidElement(result)) {
188
- // const { renderToPipeableStream } = await import(
189
- // // @ts-expect-error
190
- // 'react-server-dom/server'
191
- // )
192
-
193
- // const pipeableStream = renderToPipeableStream(result)
194
-
195
- // setHeaders(event, {
196
- // 'Content-Type': 'text/x-component',
197
- // } as any)
198
-
199
- // sendStream(event, response)
200
- // event._handled = true
201
-
202
- // return new Response(null, { status: 200 })
203
- // }
204
-
205
- if (isNotFound(result)) {
206
- return isNotFoundResponse(result)
207
- }
208
-
209
- return new Response(
210
- result !== undefined ? startSerializer.stringify(result) : undefined,
211
- {
212
- status: getResponseStatus(getEvent()),
213
- headers: {
214
- 'Content-Type': 'application/json',
215
- },
216
- },
217
- )
218
- } catch (error: any) {
219
- if (error instanceof Response) {
220
- return error
221
- }
222
- // else if (
223
- // isPlainObject(error) &&
224
- // 'result' in error &&
225
- // error.result instanceof Response
226
- // ) {
227
- // return error.result
228
- // }
229
-
230
- // Currently this server-side context has no idea how to
231
- // build final URLs, so we need to defer that to the client.
232
- // The client will check for __redirect and __notFound keys,
233
- // and if they exist, it will handle them appropriately.
234
-
235
- if (isNotFound(error)) {
236
- return isNotFoundResponse(error)
237
- }
238
-
239
- console.info()
240
- console.info('Server Fn Error!')
241
- console.info()
242
- console.error(error)
243
- console.info()
244
-
245
- return new Response(startSerializer.stringify(error), {
246
- status: 500,
247
- headers: {
248
- 'Content-Type': 'application/json',
249
- },
250
- })
251
- }
252
- })()
253
-
254
- request.signal.removeEventListener('abort', abort)
255
-
256
- if (isRaw) {
257
- return response
258
- }
259
-
260
- return response
261
- }
262
-
263
- function isNotFoundResponse(error: any) {
264
- const { headers, ...rest } = error
265
-
266
- return new Response(JSON.stringify(rest), {
267
- status: 200,
268
- headers: {
269
- 'Content-Type': 'application/json',
270
- ...(headers || {}),
271
- },
272
- })
273
- }