@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.
- package/dist/esm/createStartHandler.d.ts +1 -1
- package/dist/esm/createStartHandler.js +8 -9
- package/dist/esm/createStartHandler.js.map +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +6 -90
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/loadVirtualModule.js +2 -0
- package/dist/esm/loadVirtualModule.js.map +1 -1
- package/dist/esm/request-response.d.ts +124 -0
- package/dist/esm/request-response.js +177 -0
- package/dist/esm/request-response.js.map +1 -0
- package/dist/esm/router-manifest.js +6 -3
- package/dist/esm/router-manifest.js.map +1 -1
- package/dist/esm/server-functions-handler.js +25 -59
- package/dist/esm/server-functions-handler.js.map +1 -1
- package/dist/esm/serverRoute.js +1 -2
- package/dist/esm/serverRoute.js.map +1 -1
- package/dist/esm/session.d.ts +66 -0
- package/dist/esm/virtual-modules.d.ts +2 -0
- package/dist/esm/virtual-modules.js +2 -1
- package/dist/esm/virtual-modules.js.map +1 -1
- package/package.json +9 -19
- package/src/createStartHandler.ts +7 -7
- package/src/global.d.ts +1 -3
- package/src/index.tsx +1 -1
- package/src/loadVirtualModule.ts +2 -0
- package/src/request-response.ts +335 -0
- package/src/router-manifest.ts +6 -3
- package/src/server-functions-handler.ts +29 -67
- package/src/session.ts +75 -0
- package/src/tanstack-start.d.ts +4 -0
- package/src/virtual-modules.ts +2 -0
- package/dist/cjs/constants.cjs +0 -7
- package/dist/cjs/constants.cjs.map +0 -1
- package/dist/cjs/constants.d.cts +0 -3
- package/dist/cjs/createStartHandler.cjs +0 -342
- package/dist/cjs/createStartHandler.cjs.map +0 -1
- package/dist/cjs/createStartHandler.d.cts +0 -7
- package/dist/cjs/h3.cjs +0 -355
- package/dist/cjs/h3.cjs.map +0 -1
- package/dist/cjs/h3.d.cts +0 -109
- package/dist/cjs/index.cjs +0 -257
- package/dist/cjs/index.cjs.map +0 -1
- package/dist/cjs/index.d.cts +0 -10
- package/dist/cjs/loadVirtualModule.cjs +0 -39
- package/dist/cjs/loadVirtualModule.cjs.map +0 -1
- package/dist/cjs/loadVirtualModule.d.cts +0 -6
- package/dist/cjs/router-manifest.cjs +0 -46
- package/dist/cjs/router-manifest.cjs.map +0 -1
- package/dist/cjs/router-manifest.d.cts +0 -17
- package/dist/cjs/server-functions-handler.cjs +0 -181
- package/dist/cjs/server-functions-handler.cjs.map +0 -1
- package/dist/cjs/server-functions-handler.d.cts +0 -3
- package/dist/cjs/serverRoute.cjs +0 -104
- package/dist/cjs/serverRoute.cjs.map +0 -1
- package/dist/cjs/serverRoute.d.cts +0 -124
- package/dist/cjs/virtual-modules.cjs +0 -9
- package/dist/cjs/virtual-modules.cjs.map +0 -1
- package/dist/cjs/virtual-modules.d.cts +0 -10
- package/dist/esm/h3.d.ts +0 -109
- package/dist/esm/h3.js +0 -248
- package/dist/esm/h3.js.map +0 -1
- 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;
|
|
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.
|
|
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
|
-
"
|
|
51
|
-
"
|
|
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
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"@tanstack/
|
|
56
|
-
"@tanstack/start-
|
|
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 './
|
|
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 './
|
|
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=
|
|
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 (
|
|
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(
|
|
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
package/src/loadVirtualModule.ts
CHANGED
|
@@ -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
|
+
}
|
package/src/router-manifest.ts
CHANGED
|
@@ -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.
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
23
|
-
|
|
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
|
-
|
|
27
|
-
|
|
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
|
|
32
|
-
|
|
33
|
-
return holder['']
|
|
34
|
-
}
|
|
40
|
+
const isCreateServerFn = 'createServerFn' in search
|
|
41
|
+
const isRaw = 'raw' in search
|
|
35
42
|
|
|
36
|
-
|
|
37
|
-
|
|
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 ?
|
|
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 =
|
|
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:
|
|
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:
|
|
250
|
+
status: 404,
|
|
289
251
|
headers: {
|
|
290
252
|
'Content-Type': 'application/json',
|
|
291
253
|
...(headers || {}),
|