| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 66f596e commit 6b09d62
14 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,4 +1,4 @@ | |||
| 1 | - import { defineMiddleware, getContext, setContext } from 'vinxi/server' | ||
| 1 | + import { defineMiddleware, getContext, setContext } from 'vinxi/http' | ||
| 2 | 2 | ||
| 3 | 3 | export default defineMiddleware({ | |
| 4 | 4 | onRequest: (event) => { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2,7 +2,7 @@ | |||
| 2 | 2 | import { renderAsset } from '@vinxi/react' | |
| 3 | 3 | import { Suspense } from 'react' | |
| 4 | 4 | import { PipeableStream, renderToPipeableStream } from 'react-dom/server' | |
| 5 | - import { eventHandler, setHeader, toWebRequest } from 'vinxi/server' | ||
| 5 | + import { eventHandler, setHeader, toWebRequest } from 'vinxi/http' | ||
| 6 | 6 | import { | |
| 7 | 7 | StartServer, | |
| 8 | 8 | transformStreamWithRouter, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,4 +1,4 @@ | |||
| 1 | - import { defineMiddleware, getContext, setContext } from 'vinxi/server' | ||
| 1 | + import { defineMiddleware, getContext, setContext } from 'vinxi/http' | ||
| 2 | 2 | ||
| 3 | 3 | export default defineMiddleware({ | |
| 4 | 4 | onRequest: (event) => { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2,7 +2,7 @@ | |||
| 2 | 2 | import { renderAsset } from '@vinxi/react' | |
| 3 | 3 | import React, { Suspense } from 'react' | |
| 4 | 4 | import { PipeableStream, renderToPipeableStream } from 'react-dom/server' | |
| 5 | - import { eventHandler, setHeader, toWebRequest } from 'vinxi/server' | ||
| 5 | + import { eventHandler, setHeader, toWebRequest } from 'vinxi/http' | ||
| 6 | 6 | import { | |
| 7 | 7 | StartServer, | |
| 8 | 8 | transformStreamWithRouter, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,4 +1,4 @@ | |||
| 1 | - import { defineMiddleware, getContext, setContext } from 'vinxi/server' | ||
| 1 | + import { defineMiddleware, getContext, setContext } from 'vinxi/http' | ||
| 2 | 2 | ||
| 3 | 3 | export default defineMiddleware({ | |
| 4 | 4 | onRequest: (event) => { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -36,6 +36,36 @@ | |||
| 36 | 36 | "default": "./dist/cjs/server.cjs" | |
| 37 | 37 | } | |
| 38 | 38 | }, | |
| 39 | + "./client-runtime": { | ||
| 40 | + "import": { | ||
| 41 | + "types": "./dist/esm/client-runtime.d.ts", | ||
| 42 | + "default": "./dist/esm/client-runtime.js" | ||
| 43 | + }, | ||
| 44 | + "require": { | ||
| 45 | + "types": "./dist/cjs/client-runtime.d.cts", | ||
| 46 | + "default": "./dist/cjs/client-runtime.cjs" | ||
| 47 | + } | ||
| 48 | + }, | ||
| 49 | + "./server-runtime": { | ||
| 50 | + "import": { | ||
| 51 | + "types": "./dist/esm/server-runtime.d.ts", | ||
| 52 | + "default": "./dist/esm/server-runtime.js" | ||
| 53 | + }, | ||
| 54 | + "require": { | ||
| 55 | + "types": "./dist/cjs/server-runtime.d.cts", | ||
| 56 | + "default": "./dist/cjs/server-runtime.cjs" | ||
| 57 | + } | ||
| 58 | + }, | ||
| 59 | + "./server-handler": { | ||
| 60 | + "import": { | ||
| 61 | + "types": "./dist/esm/server-handler.d.ts", | ||
| 62 | + "default": "./dist/esm/server-handler.js" | ||
| 63 | + }, | ||
| 64 | + "require": { | ||
| 65 | + "types": "./dist/cjs/server-handler.d.cts", | ||
| 66 | + "default": "./dist/cjs/server-handler.cjs" | ||
| 67 | + } | ||
| 68 | + }, | ||
| 39 | 69 | "./package.json": "./package.json" | |
| 40 | 70 | }, | |
| 41 | 71 | "sideEffects": false, | |
@@ -63,7 +93,8 @@ | |||
| 63 | 93 | ], | |
| 64 | 94 | "dependencies": { | |
| 65 | 95 | "@tanstack/react-cross-context": "workspace:^", | |
| 66 | - "@tanstack/react-router": "workspace:*" | ||
| 96 | + "@tanstack/react-router": "workspace:*", | ||
| 97 | + "vinxi": "0.2.1" | ||
| 67 | 98 | }, | |
| 68 | 99 | "devDependencies": { | |
| 69 | 100 | "@vitejs/plugin-react": "^4.2.1", | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,104 @@ | |||
| 1 | + import { | ||
| 2 | + CompiledFetcherFnOptions, | ||
| 3 | + FetchFn, | ||
| 4 | + isPlainObject, | ||
| 5 | + } from '@tanstack/react-router' | ||
| 6 | + | ||
| 7 | + export function getBaseUrl(base: string | undefined, id: string, name: string) { | ||
| 8 | + return `${base}/_server?_serverId=${encodeURI(id)}&_serverName=${encodeURI(name)}` | ||
| 9 | + } | ||
| 10 | + | ||
| 11 | + export function createServerReference<TPayload, TResponse>( | ||
| 12 | + _fn: FetchFn<TPayload, TResponse>, | ||
| 13 | + id: string, | ||
| 14 | + name: string, | ||
| 15 | + ) { | ||
| 16 | + let base = getBaseUrl(import.meta.env.SERVER_BASE_URL, id, name) | ||
| 17 | + | ||
| 18 | + const proxyFn = (...args: any[]) => handleFetcherArgs(base, args, fetch) | ||
| 19 | + | ||
| 20 | + return Object.assign(proxyFn, { | ||
| 21 | + url: base, | ||
| 22 | + }) | ||
| 23 | + } | ||
| 24 | + | ||
| 25 | + export async function handleFetcherArgs<TPayload>( | ||
| 26 | + base: string, | ||
| 27 | + args: any[], | ||
| 28 | + handler: (request: Request) => Promise<Response>, | ||
| 29 | + ) { | ||
| 30 | + const first = args[0] | ||
| 31 | + | ||
| 32 | + // If a custom fetcher request is passed, use it | ||
| 33 | + if (isPlainObject(first) && first.method && first.type) { | ||
| 34 | + const opts = first as CompiledFetcherFnOptions<TPayload> | ||
| 35 | + | ||
| 36 | + // Arrange the headers | ||
| 37 | + const headers = new Headers({ | ||
| 38 | + 'server-action-type': opts.type, | ||
| 39 | + ...(opts.type === 'payload' | ||
| 40 | + ? { | ||
| 41 | + 'content-type': 'application/json', | ||
| 42 | + accept: 'application/json', | ||
| 43 | + } | ||
| 44 | + : {}), | ||
| 45 | + ...opts.requestInit?.headers, | ||
| 46 | + }) | ||
| 47 | + | ||
| 48 | + // If the method is GET, we need to move the payload to the query string | ||
| 49 | + if (opts.method === 'GET') { | ||
| 50 | + // If the method is GET, we need to move the payload to the query string | ||
| 51 | + const query = new URLSearchParams(opts.payload as any) | ||
| 52 | + base += `&${query.toString()}` | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + // Create the request | ||
| 56 | + const request = new Request(base, { | ||
| 57 | + ...opts.requestInit, | ||
| 58 | + headers, | ||
| 59 | + ...(opts.method === 'POST' && opts.payload | ||
| 60 | + ? { body: JSON.stringify(opts.payload) } | ||
| 61 | + : {}), | ||
| 62 | + }) | ||
| 63 | + | ||
| 64 | + // Fetch it | ||
| 65 | + const response = await handler(request) | ||
| 66 | + | ||
| 67 | + // After handling, return the response itself | ||
| 68 | + return handleResponse(response) | ||
| 69 | + } | ||
| 70 | + | ||
| 71 | + // If not a custom fetcher, just proxy the arguments | ||
| 72 | + // through as a POST request | ||
| 73 | + const request = new Request(base, { | ||
| 74 | + method: 'POST', | ||
| 75 | + headers: { | ||
| 76 | + Accept: 'application/json', | ||
| 77 | + 'Content-Type': 'application/json', | ||
| 78 | + 'server-action-type': 'args', | ||
| 79 | + }, | ||
| 80 | + body: JSON.stringify(args), | ||
| 81 | + }) | ||
| 82 | + | ||
| 83 | + const result = handleResponse(await handler(request)) | ||
| 84 | + | ||
| 85 | + // If the response is JSON, return it parsed | ||
| 86 | + const contentType = result.headers.get('content-type') | ||
| 87 | + if (contentType && contentType.includes('application/json')) { | ||
| 88 | + return result.json() | ||
| 89 | + } else { | ||
| 90 | + // Otherwise, return the text as a fallback | ||
| 91 | + // If the user wants more than this, they can pass a | ||
| 92 | + // request instead | ||
| 93 | + return result.text() | ||
| 94 | + } | ||
| 95 | + } | ||
| 96 | + | ||
| 97 | + function handleResponse(response: Response) { | ||
| 98 | + if (!response.ok) { | ||
| 99 | + const err = new Error(`HTTP error! status: ${response.status}`) | ||
| 100 | + throw Object.assign(err, { response }) | ||
| 101 | + } | ||
| 102 | + | ||
| 103 | + return response | ||
| 104 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,87 @@ | |||
| 1 | + /// <reference types="vinxi/types/server" /> | ||
| 2 | + import { isNotFound, isRedirect } from '@tanstack/react-router' | ||
| 3 | + import { eventHandler, toWebRequest } from 'vinxi/http' | ||
| 4 | + import invariant from 'vinxi/lib/invariant' | ||
| 5 | + import { getManifest } from 'vinxi/manifest' | ||
| 6 | + | ||
| 7 | + export default eventHandler(handleServerAction) | ||
| 8 | + | ||
| 9 | + export async function handleServerAction(event: any) { | ||
| 10 | + const request = toWebRequest(event) as Request | ||
| 11 | + return await handleServerRequest(request) | ||
| 12 | + } | ||
| 13 | + | ||
| 14 | + export async function handleServerRequest(request: Request) { | ||
| 15 | + const method = request.method | ||
| 16 | + const url = new URL(request.url, 'http://localhost') | ||
| 17 | + const search = new URLSearchParams(url.search) | ||
| 18 | + | ||
| 19 | + const serverId = search.get('_serverId') | ||
| 20 | + | ||
| 21 | + if (serverId) { | ||
| 22 | + invariant(typeof serverId === 'string', 'Invalid server action') | ||
| 23 | + | ||
| 24 | + // This is the client-side case | ||
| 25 | + const [filepath, name] = serverId.split('#') | ||
| 26 | + | ||
| 27 | + const action = ( | ||
| 28 | + await getManifest(import.meta.env.ROUTER_NAME).chunks[filepath].import() | ||
| 29 | + )[name] as Function | ||
| 30 | + | ||
| 31 | + // If the request is a | ||
| 32 | + | ||
| 33 | + try { | ||
| 34 | + const args = await (async () => { | ||
| 35 | + if (request.headers.get('server-action-type') === 'payload') { | ||
| 36 | + return [await request.json(), { method, request }] as const | ||
| 37 | + } | ||
| 38 | + | ||
| 39 | + if (request.headers.get('server-action-type') === 'request') { | ||
| 40 | + return [request, { method, request }] as const | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + // if (request.headers.get('server-action-type') === 'args') { | ||
| 44 | + return (await request.json()) as any[] | ||
| 45 | + // } | ||
| 46 | + })() | ||
| 47 | + | ||
| 48 | + const response = await action.call(null, args) | ||
| 49 | + | ||
| 50 | + if (response instanceof Response) { | ||
| 51 | + return response | ||
| 52 | + } | ||
| 53 | + | ||
| 54 | + return new Response(JSON.stringify(response ?? null), { | ||
| 55 | + status: 200, | ||
| 56 | + headers: { | ||
| 57 | + 'Content-Type': 'application/json', | ||
| 58 | + }, | ||
| 59 | + }) | ||
| 60 | + } catch (error: any) { | ||
| 61 | + // Currently this server-side context has no idea how to | ||
| 62 | + // build final URLs, so we need to defer that to the client. | ||
| 63 | + // The client will check for __redirect and __notFound keys, | ||
| 64 | + // and if they exist, it will handle them appropriately. | ||
| 65 | + | ||
| 66 | + if (isRedirect(error)) { | ||
| 67 | + // TODO: Use a common variable for the __redirect key | ||
| 68 | + return new Response(JSON.stringify({ __redirect: error })) | ||
| 69 | + } | ||
| 70 | + if (isNotFound(error)) { | ||
| 71 | + // TODO: Use a common variable for the __notFound key | ||
| 72 | + return new Response(JSON.stringify({ __notFound: error })) | ||
| 73 | + } | ||
| 74 | + | ||
| 75 | + console.error(error) | ||
| 76 | + return new Response(JSON.stringify({ error: error.message }), { | ||
| 77 | + status: 500, | ||
| 78 | + headers: { | ||
| 79 | + 'Content-Type': 'application/json', | ||
| 80 | + 'server-function': 'error', | ||
| 81 | + }, | ||
| 82 | + }) | ||
| 83 | + } | ||
| 84 | + } else { | ||
| 85 | + throw new Error('Invalid request') | ||
| 86 | + } | ||
| 87 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,18 @@ | |||
| 1 | + import { FetchFn } from '@tanstack/react-router' | ||
| 2 | + import { getBaseUrl, handleFetcherArgs } from './client-runtime' | ||
| 3 | + import { handleServerRequest } from './server-handler' | ||
| 4 | + | ||
| 5 | + export function createServerReference<TPayload, TResponse>( | ||
| 6 | + _fn: FetchFn<TPayload, TResponse>, | ||
| 7 | + id: string, | ||
| 8 | + name: string, | ||
| 9 | + ) { | ||
| 10 | + let base = getBaseUrl(import.meta.env.SERVER_BASE_URL, id, name) | ||
| 11 | + | ||
| 12 | + const proxyFn = (...args: any[]) => | ||
| 13 | + handleFetcherArgs(base, args, handleServerRequest) | ||
| 14 | + | ||
| 15 | + return Object.assign(proxyFn, { | ||
| 16 | + url: base, | ||
| 17 | + }) | ||
| 18 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -9,7 +9,13 @@ const config = defineConfig({ | |||
| 9 | 9 | export default mergeConfig( | |
| 10 | 10 | config, | |
| 11 | 11 | tanstackBuildConfig({ | |
| 12 | - entry: ['./src/client.tsx', './src/server.tsx'], | ||
| 12 | + entry: [ | ||
| 13 | + './src/client.tsx', | ||
| 14 | + './src/server.tsx', | ||
| 15 | + './src/client-runtime.tsx', | ||
| 16 | + './src/server-runtime.tsx', | ||
| 17 | + './src/server-handler.tsx', | ||
| 18 | + ], | ||
| 13 | 19 | srcDir: './src', | |
| 14 | 20 | }), | |
| 15 | 21 | ) | |
| Back | FazBrowse Home | New Git URL |
0 commit comments