@textjs/app 0.0.0-stage → 1.0.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.
- package/README.md +34 -2
- package/bin/text.js +29 -0
- package/bin/text.ts +13 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/cli/build.d.ts +39 -0
- package/dist/cli/build.d.ts.map +1 -0
- package/dist/cli/build.js +157 -0
- package/dist/cli/build.js.map +1 -0
- package/dist/cli/config.d.ts +22 -0
- package/dist/cli/config.d.ts.map +1 -0
- package/dist/cli/config.js +73 -0
- package/dist/cli/config.js.map +1 -0
- package/dist/cli/dev.d.ts +12 -0
- package/dist/cli/dev.d.ts.map +1 -0
- package/dist/cli/dev.js +49 -0
- package/dist/cli/dev.js.map +1 -0
- package/dist/cli/index.d.ts +26 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +192 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/run.d.ts +31 -0
- package/dist/cli/run.d.ts.map +1 -0
- package/dist/cli/run.js +111 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/cli/start.d.ts +14 -0
- package/dist/cli/start.d.ts.map +1 -0
- package/dist/cli/start.js +183 -0
- package/dist/cli/start.js.map +1 -0
- package/dist/cli/ui.d.ts +35 -0
- package/dist/cli/ui.d.ts.map +1 -0
- package/dist/cli/ui.js +73 -0
- package/dist/cli/ui.js.map +1 -0
- package/dist/client.d.ts +34 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +93 -0
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +143 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +28 -0
- package/dist/config.js.map +1 -0
- package/dist/context.d.ts +44 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +26 -0
- package/dist/context.js.map +1 -0
- package/dist/dev-errors.d.ts +68 -0
- package/dist/dev-errors.d.ts.map +1 -0
- package/dist/dev-errors.js +121 -0
- package/dist/dev-errors.js.map +1 -0
- package/dist/devtools.d.ts +64 -0
- package/dist/devtools.d.ts.map +1 -0
- package/dist/devtools.js +186 -0
- package/dist/devtools.js.map +1 -0
- package/dist/frame-mirror.d.ts +19 -0
- package/dist/frame-mirror.d.ts.map +1 -0
- package/dist/frame-mirror.js +141 -0
- package/dist/frame-mirror.js.map +1 -0
- package/dist/frame-protocol.d.ts +175 -0
- package/dist/frame-protocol.d.ts.map +1 -0
- package/dist/frame-protocol.js +369 -0
- package/dist/frame-protocol.js.map +1 -0
- package/dist/history.d.ts +54 -0
- package/dist/history.d.ts.map +1 -0
- package/dist/history.js +152 -0
- package/dist/history.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/link.d.ts +32 -0
- package/dist/link.d.ts.map +1 -0
- package/dist/link.js +54 -0
- package/dist/link.js.map +1 -0
- package/dist/live-player.d.ts +83 -0
- package/dist/live-player.d.ts.map +1 -0
- package/dist/live-player.js +413 -0
- package/dist/live-player.js.map +1 -0
- package/dist/live-protocol.d.ts +132 -0
- package/dist/live-protocol.d.ts.map +1 -0
- package/dist/live-protocol.js +278 -0
- package/dist/live-protocol.js.map +1 -0
- package/dist/live-stub.d.ts +10 -0
- package/dist/live-stub.d.ts.map +1 -0
- package/dist/live-stub.js +32 -0
- package/dist/live-stub.js.map +1 -0
- package/dist/mcp/cli.d.ts +22 -0
- package/dist/mcp/cli.d.ts.map +1 -0
- package/dist/mcp/cli.js +177 -0
- package/dist/mcp/cli.js.map +1 -0
- package/dist/mcp/frame.d.ts +58 -0
- package/dist/mcp/frame.d.ts.map +1 -0
- package/dist/mcp/frame.js +169 -0
- package/dist/mcp/frame.js.map +1 -0
- package/dist/mcp/index.d.ts +15 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +15 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/inspect.d.ts +95 -0
- package/dist/mcp/inspect.d.ts.map +1 -0
- package/dist/mcp/inspect.js +386 -0
- package/dist/mcp/inspect.js.map +1 -0
- package/dist/mcp/keys.d.ts +20 -0
- package/dist/mcp/keys.d.ts.map +1 -0
- package/dist/mcp/keys.js +87 -0
- package/dist/mcp/keys.js.map +1 -0
- package/dist/mcp/logs.d.ts +63 -0
- package/dist/mcp/logs.d.ts.map +1 -0
- package/dist/mcp/logs.js +217 -0
- package/dist/mcp/logs.js.map +1 -0
- package/dist/mcp/server.d.ts +64 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +485 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/session.d.ts +230 -0
- package/dist/mcp/session.d.ts.map +1 -0
- package/dist/mcp/session.js +742 -0
- package/dist/mcp/session.js.map +1 -0
- package/dist/navigation-errors.d.ts +28 -0
- package/dist/navigation-errors.d.ts.map +1 -0
- package/dist/navigation-errors.js +43 -0
- package/dist/navigation-errors.js.map +1 -0
- package/dist/navigation.d.ts +21 -0
- package/dist/navigation.d.ts.map +1 -0
- package/dist/navigation.js +33 -0
- package/dist/navigation.js.map +1 -0
- package/dist/router.d.ts +76 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +296 -0
- package/dist/router.js.map +1 -0
- package/dist/routing.d.ts +101 -0
- package/dist/routing.d.ts.map +1 -0
- package/dist/routing.js +286 -0
- package/dist/routing.js.map +1 -0
- package/dist/server/adapters/build-step.d.ts +34 -0
- package/dist/server/adapters/build-step.d.ts.map +1 -0
- package/dist/server/adapters/build-step.js +112 -0
- package/dist/server/adapters/build-step.js.map +1 -0
- package/dist/server/adapters/bun.d.ts +8 -0
- package/dist/server/adapters/bun.d.ts.map +1 -0
- package/dist/server/adapters/bun.js +89 -0
- package/dist/server/adapters/bun.js.map +1 -0
- package/dist/server/adapters/bundle.d.ts +21 -0
- package/dist/server/adapters/bundle.d.ts.map +1 -0
- package/dist/server/adapters/bundle.js +86 -0
- package/dist/server/adapters/bundle.js.map +1 -0
- package/dist/server/adapters/cloudflare.d.ts +29 -0
- package/dist/server/adapters/cloudflare.d.ts.map +1 -0
- package/dist/server/adapters/cloudflare.js +105 -0
- package/dist/server/adapters/cloudflare.js.map +1 -0
- package/dist/server/adapters/index.d.ts +10 -0
- package/dist/server/adapters/index.d.ts.map +1 -0
- package/dist/server/adapters/index.js +24 -0
- package/dist/server/adapters/index.js.map +1 -0
- package/dist/server/adapters/runtime-node.d.ts +16 -0
- package/dist/server/adapters/runtime-node.d.ts.map +1 -0
- package/dist/server/adapters/runtime-node.js +29 -0
- package/dist/server/adapters/runtime-node.js.map +1 -0
- package/dist/server/adapters/runtime.d.ts +42 -0
- package/dist/server/adapters/runtime.d.ts.map +1 -0
- package/dist/server/adapters/runtime.js +115 -0
- package/dist/server/adapters/runtime.js.map +1 -0
- package/dist/server/adapters/shared.d.ts +66 -0
- package/dist/server/adapters/shared.d.ts.map +1 -0
- package/dist/server/adapters/shared.js +124 -0
- package/dist/server/adapters/shared.js.map +1 -0
- package/dist/server/adapters/static.d.ts +9 -0
- package/dist/server/adapters/static.d.ts.map +1 -0
- package/dist/server/adapters/static.js +70 -0
- package/dist/server/adapters/static.js.map +1 -0
- package/dist/server/adapters/vercel.d.ts +43 -0
- package/dist/server/adapters/vercel.d.ts.map +1 -0
- package/dist/server/adapters/vercel.js +119 -0
- package/dist/server/adapters/vercel.js.map +1 -0
- package/dist/server/bun-jsx.d.ts +18 -0
- package/dist/server/bun-jsx.d.ts.map +1 -0
- package/dist/server/bun-jsx.js +56 -0
- package/dist/server/bun-jsx.js.map +1 -0
- package/dist/server/cache-route.d.ts +38 -0
- package/dist/server/cache-route.d.ts.map +1 -0
- package/dist/server/cache-route.js +109 -0
- package/dist/server/cache-route.js.map +1 -0
- package/dist/server/cache.d.ts +131 -0
- package/dist/server/cache.d.ts.map +1 -0
- package/dist/server/cache.js +409 -0
- package/dist/server/cache.js.map +1 -0
- package/dist/server/frames.d.ts +45 -0
- package/dist/server/frames.d.ts.map +1 -0
- package/dist/server/frames.js +294 -0
- package/dist/server/frames.js.map +1 -0
- package/dist/server/http.d.ts +80 -0
- package/dist/server/http.d.ts.map +1 -0
- package/dist/server/http.js +453 -0
- package/dist/server/http.js.map +1 -0
- package/dist/server/jsx-dev-runtime.d.ts +5 -0
- package/dist/server/jsx-dev-runtime.d.ts.map +1 -0
- package/dist/server/jsx-dev-runtime.js +6 -0
- package/dist/server/jsx-dev-runtime.js.map +1 -0
- package/dist/server/jsx-runtime.d.ts +10 -0
- package/dist/server/jsx-runtime.d.ts.map +1 -0
- package/dist/server/jsx-runtime.js +11 -0
- package/dist/server/jsx-runtime.js.map +1 -0
- package/dist/server/live-app.d.ts +87 -0
- package/dist/server/live-app.d.ts.map +1 -0
- package/dist/server/live-app.js +307 -0
- package/dist/server/live-app.js.map +1 -0
- package/dist/server/live-build.d.ts +70 -0
- package/dist/server/live-build.d.ts.map +1 -0
- package/dist/server/live-build.js +222 -0
- package/dist/server/live-build.js.map +1 -0
- package/dist/server/live-dev.d.ts +62 -0
- package/dist/server/live-dev.d.ts.map +1 -0
- package/dist/server/live-dev.js +313 -0
- package/dist/server/live-dev.js.map +1 -0
- package/dist/server/live-manifest.d.ts +23 -0
- package/dist/server/live-manifest.d.ts.map +1 -0
- package/dist/server/live-manifest.js +7 -0
- package/dist/server/live-manifest.js.map +1 -0
- package/dist/server/live-prod.d.ts +30 -0
- package/dist/server/live-prod.d.ts.map +1 -0
- package/dist/server/live-prod.js +97 -0
- package/dist/server/live-prod.js.map +1 -0
- package/dist/server/live-routes.d.ts +72 -0
- package/dist/server/live-routes.d.ts.map +1 -0
- package/dist/server/live-routes.js +274 -0
- package/dist/server/live-routes.js.map +1 -0
- package/dist/server/live-web.d.ts +16 -0
- package/dist/server/live-web.d.ts.map +1 -0
- package/dist/server/live-web.js +159 -0
- package/dist/server/live-web.js.map +1 -0
- package/dist/server/live.d.ts +138 -0
- package/dist/server/live.d.ts.map +1 -0
- package/dist/server/live.js +542 -0
- package/dist/server/live.js.map +1 -0
- package/dist/server/middleware-node.d.ts +48 -0
- package/dist/server/middleware-node.d.ts.map +1 -0
- package/dist/server/middleware-node.js +167 -0
- package/dist/server/middleware-node.js.map +1 -0
- package/dist/server/middleware-runtime.d.ts +97 -0
- package/dist/server/middleware-runtime.d.ts.map +1 -0
- package/dist/server/middleware-runtime.js +447 -0
- package/dist/server/middleware-runtime.js.map +1 -0
- package/dist/server/middleware.d.ts +145 -0
- package/dist/server/middleware.d.ts.map +1 -0
- package/dist/server/middleware.js +365 -0
- package/dist/server/middleware.js.map +1 -0
- package/dist/server/ppr.d.ts +22 -0
- package/dist/server/ppr.d.ts.map +1 -0
- package/dist/server/ppr.js +58 -0
- package/dist/server/ppr.js.map +1 -0
- package/dist/server/scan.d.ts +48 -0
- package/dist/server/scan.d.ts.map +1 -0
- package/dist/server/scan.js +161 -0
- package/dist/server/scan.js.map +1 -0
- package/dist/server/server-components.d.ts +65 -0
- package/dist/server/server-components.d.ts.map +1 -0
- package/dist/server/server-components.js +215 -0
- package/dist/server/server-components.js.map +1 -0
- package/dist/terminal.d.ts +5 -0
- package/dist/terminal.d.ts.map +1 -0
- package/dist/terminal.js +5 -0
- package/dist/terminal.js.map +1 -0
- package/dist/testing.d.ts +5 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +5 -0
- package/dist/testing.js.map +1 -0
- package/dist/vite-plugin.d.ts +51 -0
- package/dist/vite-plugin.d.ts.map +1 -0
- package/dist/vite-plugin.js +403 -0
- package/dist/vite-plugin.js.map +1 -0
- package/package.json +130 -4
- package/src/cli/build.ts +182 -0
- package/src/cli/config.ts +72 -0
- package/src/cli/dev.ts +54 -0
- package/src/cli/index.ts +185 -0
- package/src/cli/run.ts +124 -0
- package/src/cli/start.ts +194 -0
- package/src/cli/ui.ts +93 -0
- package/src/client.tsx +122 -0
- package/src/config.ts +155 -0
- package/src/context.ts +69 -0
- package/src/dev-errors.ts +167 -0
- package/src/devtools.tsx +419 -0
- package/src/frame-mirror.ts +145 -0
- package/src/frame-protocol.ts +435 -0
- package/src/history.ts +201 -0
- package/src/index.ts +9 -0
- package/src/link.tsx +108 -0
- package/src/live-player.ts +449 -0
- package/src/live-protocol.ts +317 -0
- package/src/live-stub.tsx +32 -0
- package/src/mcp/cli.ts +188 -0
- package/src/mcp/frame.ts +224 -0
- package/src/mcp/index.ts +15 -0
- package/src/mcp/inspect.ts +445 -0
- package/src/mcp/keys.ts +86 -0
- package/src/mcp/logs.ts +225 -0
- package/src/mcp/server.ts +595 -0
- package/src/mcp/session.ts +821 -0
- package/src/navigation-errors.ts +60 -0
- package/src/navigation.ts +44 -0
- package/src/router.tsx +497 -0
- package/src/routing.ts +344 -0
- package/src/server/adapters/build-step.ts +135 -0
- package/src/server/adapters/bun.ts +92 -0
- package/src/server/adapters/bundle.ts +97 -0
- package/src/server/adapters/cloudflare.ts +111 -0
- package/src/server/adapters/index.ts +29 -0
- package/src/server/adapters/runtime-node.ts +34 -0
- package/src/server/adapters/runtime.ts +145 -0
- package/src/server/adapters/shared.ts +159 -0
- package/src/server/adapters/static.ts +68 -0
- package/src/server/adapters/vercel.ts +149 -0
- package/src/server/bun-jsx.ts +65 -0
- package/src/server/cache-route.ts +139 -0
- package/src/server/cache.ts +498 -0
- package/src/server/frames.ts +326 -0
- package/src/server/http.ts +467 -0
- package/src/server/jsx-dev-runtime.ts +9 -0
- package/src/server/jsx-runtime.ts +13 -0
- package/src/server/live-app.tsx +374 -0
- package/src/server/live-build.ts +258 -0
- package/src/server/live-dev.ts +357 -0
- package/src/server/live-manifest.ts +25 -0
- package/src/server/live-prod.ts +120 -0
- package/src/server/live-routes.ts +298 -0
- package/src/server/live-web.ts +170 -0
- package/src/server/live.ts +551 -0
- package/src/server/middleware-node.ts +168 -0
- package/src/server/middleware-runtime.ts +478 -0
- package/src/server/middleware.ts +451 -0
- package/src/server/ppr.ts +62 -0
- package/src/server/scan.ts +190 -0
- package/src/server/server-components.ts +224 -0
- package/src/terminal.ts +5 -0
- package/src/testing.ts +5 -0
- package/src/vite-plugin.ts +413 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `text build --adapter vercel`: Vercel Build Output API v3 in `.vercel/output`.
|
|
3
|
+
*
|
|
4
|
+
* - `static/`: the client build (under `basePath`).
|
|
5
|
+
* - `functions/_textjs/server.func`: one Node.js function for every route handler.
|
|
6
|
+
* - `functions/_middleware.func`: `middleware.ts` as Edge Routing Middleware,
|
|
7
|
+
* speaking the `x-middleware-*` protocol Vercel's router understands.
|
|
8
|
+
* - `config.json`: asset headers, middleware, rewrites, handler routes and the
|
|
9
|
+
* SPA fallback.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
13
|
+
import { join } from "node:path";
|
|
14
|
+
import { VERSION } from "@textjs/core";
|
|
15
|
+
import type { MiddlewareModule } from "../middleware";
|
|
16
|
+
import { middlewareMatchers } from "../middleware-runtime";
|
|
17
|
+
import { bundleServerEntry, frameworkFile } from "./bundle";
|
|
18
|
+
import { assertNoServerRoutes, copyStatic, escapeRegex, matcherRegex, patternRegex, rewriteRoute, serverEntry, type AdapterContext, type AdapterResult } from "./shared";
|
|
19
|
+
|
|
20
|
+
export const VERCEL_FUNCTION = "_textjs/server";
|
|
21
|
+
export const VERCEL_MIDDLEWARE = "_middleware";
|
|
22
|
+
/** Query parameter carrying the routed path into the function. */
|
|
23
|
+
export const PATH_PARAM = "__textjs_path";
|
|
24
|
+
export const VERCEL_NODE_RUNTIME = "nodejs22.x";
|
|
25
|
+
|
|
26
|
+
/** A routed path the function accepts: one leading slash, no backslashes or control characters. */
|
|
27
|
+
export const ROUTED_PATH = /^\/(?![/\\])[^\\\u0000-\u001f\u007f]*$/;
|
|
28
|
+
|
|
29
|
+
const FUNCTION_BODY = `
|
|
30
|
+
import { normalizeRequestTarget, requestOrigin, sendWebResponse, toWebRequest } from ${JSON.stringify(frameworkFile("server/http.ts"))};
|
|
31
|
+
|
|
32
|
+
// Middleware already ran in Vercel's router. The function is only reachable
|
|
33
|
+
// through the handler routes in config.json, which reject client-supplied
|
|
34
|
+
// ${PATH_PARAM} parameters and direct requests to /_textjs/*.
|
|
35
|
+
const handler = createRequestHandler({ ...options, skipMiddleware: true });
|
|
36
|
+
const REQUEST_CONTEXT = Symbol.for("@vercel/request-context");
|
|
37
|
+
const ROUTED_PATH = ${ROUTED_PATH.toString()};
|
|
38
|
+
|
|
39
|
+
export default async function textjs(req, res) {
|
|
40
|
+
const url = new URL(normalizeRequestTarget(req.url), "http://vercel.internal");
|
|
41
|
+
const routed = url.searchParams.getAll(${JSON.stringify(PATH_PARAM)});
|
|
42
|
+
if (routed.length !== 1 || !ROUTED_PATH.test(routed[0])) {
|
|
43
|
+
res.statusCode = 400;
|
|
44
|
+
res.end();
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
url.searchParams.delete(${JSON.stringify(PATH_PARAM)});
|
|
48
|
+
req.url = routed[0] + url.search;
|
|
49
|
+
const request = toWebRequest(req, requestOrigin(req, { trustProxy: true, protocol: "https" }), res);
|
|
50
|
+
const vercel = globalThis[REQUEST_CONTEXT]?.get?.();
|
|
51
|
+
try {
|
|
52
|
+
const response = await handler(request, { waitUntil: (p) => vercel?.waitUntil?.(p) });
|
|
53
|
+
await sendWebResponse(res, response, request.method);
|
|
54
|
+
} catch (error) {
|
|
55
|
+
console.error(error);
|
|
56
|
+
if (!res.headersSent) res.statusCode = 500;
|
|
57
|
+
res.end();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
`;
|
|
61
|
+
|
|
62
|
+
const MIDDLEWARE_BODY = `
|
|
63
|
+
import { runMiddleware, toMiddlewareResponse } from ${JSON.stringify(frameworkFile("server/middleware-runtime.ts"))};
|
|
64
|
+
|
|
65
|
+
export default async function textjsMiddleware(request, context) {
|
|
66
|
+
const outcome = await runMiddleware(middleware, request, { basePath: options.basePath, waitUntil: (p) => context?.waitUntil?.(p) });
|
|
67
|
+
return toMiddlewareResponse(outcome, request);
|
|
68
|
+
}
|
|
69
|
+
`;
|
|
70
|
+
|
|
71
|
+
export interface VercelRoute {
|
|
72
|
+
src?: string;
|
|
73
|
+
dest?: string;
|
|
74
|
+
headers?: Record<string, string>;
|
|
75
|
+
continue?: boolean;
|
|
76
|
+
override?: boolean;
|
|
77
|
+
check?: boolean;
|
|
78
|
+
status?: number;
|
|
79
|
+
middlewarePath?: string;
|
|
80
|
+
middlewareRawSrc?: string[];
|
|
81
|
+
has?: unknown[];
|
|
82
|
+
missing?: unknown[];
|
|
83
|
+
handle?: "filesystem" | "rewrite" | "miss" | "hit" | "error" | "resource";
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface VercelConfig {
|
|
87
|
+
version: 3;
|
|
88
|
+
routes: VercelRoute[];
|
|
89
|
+
framework: { version: string };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function vercelConfig(ctx: AdapterContext, middleware: MiddlewareModule | null): VercelConfig {
|
|
93
|
+
const base = ctx.basePath;
|
|
94
|
+
const routes: VercelRoute[] = [
|
|
95
|
+
// The handler function is internal: only the handler routes below may reach it, with a path they set.
|
|
96
|
+
{ src: `^\\/${escapeRegex(VERCEL_FUNCTION.split("/")[0] as string)}(?:\\/.*)?$`, status: 404 },
|
|
97
|
+
{ src: "^.*$", has: [{ type: "query", key: PATH_PARAM }], status: 400 },
|
|
98
|
+
{ src: `^${escapeRegex(base)}\\/assets\\/(.*)$`, headers: { "cache-control": "public, max-age=31536000, immutable" }, continue: true },
|
|
99
|
+
];
|
|
100
|
+
if (middleware) {
|
|
101
|
+
const matchers = middlewareMatchers(middleware);
|
|
102
|
+
const list = matchers ?? [{ source: "/:path*", regex: "", has: undefined, missing: undefined }];
|
|
103
|
+
for (const m of list) {
|
|
104
|
+
routes.push({
|
|
105
|
+
src: matchers ? matcherRegex(m.source, base) : `^${escapeRegex(base)}(?:\\/.*)?$`,
|
|
106
|
+
middlewarePath: VERCEL_MIDDLEWARE,
|
|
107
|
+
middlewareRawSrc: [m.source],
|
|
108
|
+
continue: true,
|
|
109
|
+
override: true,
|
|
110
|
+
...(m.has ? { has: m.has } : {}),
|
|
111
|
+
...(m.missing ? { missing: m.missing } : {}),
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
for (const r of ctx.rewrites) {
|
|
116
|
+
const { src, dest } = rewriteRoute(r, base);
|
|
117
|
+
routes.push(/^[a-z]+:\/\//i.test(dest) ? { src, dest } : { src, dest, continue: true });
|
|
118
|
+
}
|
|
119
|
+
for (const a of ctx.api) routes.push({ src: patternRegex(a.pattern, base), dest: `/${VERCEL_FUNCTION}?${PATH_PARAM}=$1` });
|
|
120
|
+
routes.push({ handle: "filesystem" });
|
|
121
|
+
// Extensionless paths are console pages: serve the SPA document.
|
|
122
|
+
routes.push({ src: `^${escapeRegex(base)}(?:\\/(?:[^/]+\\/)*[^/.]*)?$`, dest: `${base}/index.html` });
|
|
123
|
+
return { version: 3, routes, framework: { version: VERSION } };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export async function adaptVercel(ctx: AdapterContext, middleware: MiddlewareModule | null): Promise<AdapterResult> {
|
|
127
|
+
assertNoServerRoutes(ctx, "vercel");
|
|
128
|
+
const out = join(ctx.root, ".vercel", "output");
|
|
129
|
+
if (existsSync(out)) rmSync(out, { recursive: true, force: true });
|
|
130
|
+
copyStatic(ctx.outDir, join(out, "static", ...ctx.basePath.split("/").filter(Boolean)));
|
|
131
|
+
|
|
132
|
+
if (ctx.api.length > 0) {
|
|
133
|
+
const dir = join(out, "functions", `${VERCEL_FUNCTION}.func`);
|
|
134
|
+
mkdirSync(dir, { recursive: true });
|
|
135
|
+
await bundleServerEntry({ root: ctx.root, config: ctx.config, code: serverEntry(ctx, { body: FUNCTION_BODY, middleware: false }), outFile: join(dir, "index.mjs"), target: "node" });
|
|
136
|
+
writeFileSync(
|
|
137
|
+
join(dir, ".vc-config.json"),
|
|
138
|
+
`${JSON.stringify({ runtime: VERCEL_NODE_RUNTIME, handler: "index.mjs", launcherType: "Nodejs", shouldAddHelpers: false, supportsResponseStreaming: true }, null, 2)}\n`,
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
if (middleware && ctx.middlewareFile) {
|
|
142
|
+
const dir = join(out, "functions", `${VERCEL_MIDDLEWARE}.func`);
|
|
143
|
+
mkdirSync(dir, { recursive: true });
|
|
144
|
+
await bundleServerEntry({ root: ctx.root, config: ctx.config, code: serverEntry(ctx, { body: MIDDLEWARE_BODY, handlers: false, runtime: false }), outFile: join(dir, "index.js"), target: "edge" });
|
|
145
|
+
writeFileSync(join(dir, ".vc-config.json"), `${JSON.stringify({ runtime: "edge", entrypoint: "index.js" }, null, 2)}\n`);
|
|
146
|
+
}
|
|
147
|
+
writeFileSync(join(out, "config.json"), `${JSON.stringify(vercelConfig(ctx, middleware), null, 2)}\n`);
|
|
148
|
+
return { adapter: "vercel", output: out, notes: ["Deploy: vercel deploy --prebuilt (or push: Vercel runs `text build` and picks up .vercel/output)"] };
|
|
149
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Async Server Components for app files Bun imports in-process (`text mcp`,
|
|
3
|
+
* `text run`). `text dev` / `text build` alias `react/jsx-runtime` for project
|
|
4
|
+
* files to `@textjs/app/server/jsx-runtime` in Vite; here a Bun runtime
|
|
5
|
+
* plugin does the same by adding a `@jsxImportSource` pragma to every
|
|
6
|
+
* `.tsx` / `.jsx` file under the project root (dependencies are untouched).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { readFileSync, realpathSync } from "node:fs";
|
|
10
|
+
import { resolve, sep } from "node:path";
|
|
11
|
+
|
|
12
|
+
/** Bun appends `/jsx-runtime` or `/jsx-dev-runtime` (see the package `exports`). */
|
|
13
|
+
export const SERVER_JSX_IMPORT_SOURCE = "@textjs/app/server";
|
|
14
|
+
|
|
15
|
+
interface BunPluginBuilder {
|
|
16
|
+
onLoad(options: { filter: RegExp }, callback: (args: { path: string }) => { contents: string; loader: "tsx" | "jsx" }): void;
|
|
17
|
+
}
|
|
18
|
+
interface BunRuntime {
|
|
19
|
+
plugin(options: { name: string; target?: "bun"; setup(build: BunPluginBuilder): void }): void;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const installed = new Set<string>();
|
|
23
|
+
|
|
24
|
+
function escapeRegExp(s: string): string {
|
|
25
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function realpath(p: string): string {
|
|
29
|
+
try {
|
|
30
|
+
return realpathSync(p);
|
|
31
|
+
} catch {
|
|
32
|
+
return p;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** The source as Bun should compile it: JSX through the server runtime, line numbers unchanged. */
|
|
37
|
+
export function withServerJsxPragma(source: string): string {
|
|
38
|
+
return `/** @jsxImportSource ${SERVER_JSX_IMPORT_SOURCE} */ ${source}`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Compile JSX in project files under `root` against the server JSX runtime,
|
|
43
|
+
* so `async function` components suspend instead of failing on the console
|
|
44
|
+
* reconciler. Idempotent per root; returns `false` outside Bun.
|
|
45
|
+
*/
|
|
46
|
+
export function installServerJsxRuntime(root: string): boolean {
|
|
47
|
+
const bun = (globalThis as { Bun?: BunRuntime }).Bun;
|
|
48
|
+
if (!bun?.plugin) return false;
|
|
49
|
+
const dirs = [...new Set([resolve(root), realpath(resolve(root))])].map((d) => (d.endsWith(sep) ? d : d + sep)).filter((d) => !installed.has(d));
|
|
50
|
+
if (dirs.length === 0) return true;
|
|
51
|
+
for (const d of dirs) installed.add(d);
|
|
52
|
+
const nm = escapeRegExp(`${sep}node_modules${sep}`);
|
|
53
|
+
const filter = new RegExp(`^(?:${dirs.map(escapeRegExp).join("|")})(?!.*${nm}).*\\.[jt]sx$`);
|
|
54
|
+
bun.plugin({
|
|
55
|
+
name: `textjs-server-jsx:${dirs[0]}`,
|
|
56
|
+
target: "bun",
|
|
57
|
+
setup(build) {
|
|
58
|
+
build.onLoad({ filter }, (args) => ({
|
|
59
|
+
contents: withServerJsxPragma(readFileSync(args.path, "utf8")),
|
|
60
|
+
loader: args.path.endsWith(".jsx") ? "jsx" : "tsx",
|
|
61
|
+
}));
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Full-route caching for `app/**\/route.ts` handlers: `export const dynamic`
|
|
3
|
+
* and `export const revalidate` (Next.js route segment config).
|
|
4
|
+
*
|
|
5
|
+
* - `dynamic = "force-static"` or `revalidate = N` (N > 0): `GET` / `HEAD` are
|
|
6
|
+
* cached per pathname with stale-while-revalidate. The handler receives a
|
|
7
|
+
* build-time-like request (no query string, headers, cookies or body).
|
|
8
|
+
* - Default, `"force-dynamic"` or `revalidate = 0`: run on every request.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { txError } from "@textjs/core";
|
|
12
|
+
import { markGeneratedCacheControl, PRIVATE_CACHE_CONTROL, runRouteHandler, type RouteHandlerModule } from "./http";
|
|
13
|
+
import type { RouteParams } from "../routing";
|
|
14
|
+
import { __validateRevalidate, EXPIRE_SECONDS, getOrCompute, implicitTags, normalizeCachePath, runInCacheScope, type CacheScope } from "./cache";
|
|
15
|
+
|
|
16
|
+
export const CACHE_HEADER = "x-textjs-cache";
|
|
17
|
+
|
|
18
|
+
export interface RouteCacheConfig {
|
|
19
|
+
/** Cached (static / ISR) for GET and HEAD. */
|
|
20
|
+
static: boolean;
|
|
21
|
+
revalidate: number | false;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Next.js's `"error"` (static, failing on request reads) is rejected: Text.js can't detect request reads.
|
|
25
|
+
const DYNAMIC_VALUES = new Set(["auto", "force-static", "force-dynamic"]);
|
|
26
|
+
|
|
27
|
+
/** Read and validate a route module's `dynamic` / `revalidate` exports. */
|
|
28
|
+
export function routeCacheConfig(mod: RouteHandlerModule, where = "route handler"): RouteCacheConfig {
|
|
29
|
+
const dynamic = mod.dynamic ?? "auto";
|
|
30
|
+
if (typeof dynamic !== "string" || !DYNAMIC_VALUES.has(dynamic)) throw txError("TX031", JSON.stringify(dynamic), where);
|
|
31
|
+
const revalidate = __validateRevalidate(mod.revalidate, where);
|
|
32
|
+
if (dynamic === "force-dynamic" || revalidate === 0) return { static: false, revalidate: 0 };
|
|
33
|
+
if (dynamic === "force-static") return { static: true, revalidate };
|
|
34
|
+
return { static: typeof revalidate === "number" && revalidate > 0, revalidate };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface RouteCacheContext {
|
|
38
|
+
params: RouteParams;
|
|
39
|
+
/** Route pattern, e.g. `/api/posts/[id]`. */
|
|
40
|
+
pattern: string;
|
|
41
|
+
/** Pathname without `basePath` (defaults to the request URL's). */
|
|
42
|
+
pathname?: string;
|
|
43
|
+
/** `false` in `text dev`: static semantics without storing responses (default `true`). */
|
|
44
|
+
cache?: boolean;
|
|
45
|
+
waitUntil?: (promise: Promise<unknown>) => void;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface StoredResponse {
|
|
49
|
+
status: number;
|
|
50
|
+
statusText: string;
|
|
51
|
+
headers: Array<[string, string]>;
|
|
52
|
+
/** Base64. */
|
|
53
|
+
body: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function toBase64(bytes: Uint8Array): string {
|
|
57
|
+
let s = "";
|
|
58
|
+
for (let i = 0; i < bytes.length; i += 0x8000) s += String.fromCharCode(...bytes.subarray(i, i + 0x8000));
|
|
59
|
+
return btoa(s);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function fromBase64(b64: string): Uint8Array<ArrayBuffer> {
|
|
63
|
+
const s = atob(b64);
|
|
64
|
+
const out = new Uint8Array(new ArrayBuffer(s.length));
|
|
65
|
+
for (let i = 0; i < s.length; i++) out[i] = s.charCodeAt(i);
|
|
66
|
+
return out;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** The request a static handler sees: same origin and path, nothing request-specific. */
|
|
70
|
+
export function staticRequest(request: Request): Request {
|
|
71
|
+
const url = new URL(request.url);
|
|
72
|
+
return new Request(new URL(url.pathname, url.origin), { method: "GET" });
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** `Cache-Control` for a cached route (Next.js: `s-maxage=N, stale-while-revalidate=<expire - N>`). */
|
|
76
|
+
export function cacheControlFor(revalidate: number | false): string {
|
|
77
|
+
if (revalidate === false || !Number.isFinite(revalidate)) return `s-maxage=${EXPIRE_SECONDS}`;
|
|
78
|
+
const seconds = Math.min(EXPIRE_SECONDS, Math.max(1, Math.ceil(revalidate)));
|
|
79
|
+
return `s-maxage=${seconds}, stale-while-revalidate=${EXPIRE_SECONDS - seconds}`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Responses a shared cache must never replay to other visitors. */
|
|
83
|
+
export function isUncacheableResponse(res: Response): boolean {
|
|
84
|
+
if (res.status >= 500) return true;
|
|
85
|
+
if (res.headers.has("set-cookie")) return true;
|
|
86
|
+
const cc = (res.headers.get("cache-control") ?? "").toLowerCase();
|
|
87
|
+
return /(?:^|,)\s*(?:private|no-store)\b/.test(cc);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Run a route handler with route segment config caching. */
|
|
91
|
+
export async function runRouteHandlerWithCache(mod: RouteHandlerModule, request: Request, ctx: RouteCacheContext): Promise<Response> {
|
|
92
|
+
const pathname = normalizeCachePath(ctx.pathname ?? new URL(request.url).pathname);
|
|
93
|
+
const config = routeCacheConfig(mod, `${ctx.pattern}/route`);
|
|
94
|
+
const method = request.method.toUpperCase();
|
|
95
|
+
const tags = implicitTags(pathname, ctx.pattern);
|
|
96
|
+
const scope = (): CacheScope => ({ tags: new Set(tags), revalidate: config.revalidate, implicitTags: tags, waitUntil: ctx.waitUntil });
|
|
97
|
+
|
|
98
|
+
if (!config.static || (method !== "GET" && method !== "HEAD") || typeof mod.GET !== "function") {
|
|
99
|
+
return runInCacheScope(scope(), () => runRouteHandler(mod, request, ctx.params));
|
|
100
|
+
}
|
|
101
|
+
const input = staticRequest(request);
|
|
102
|
+
if (ctx.cache === false) {
|
|
103
|
+
const res = await runInCacheScope(scope(), () => runRouteHandler(mod, input, ctx.params));
|
|
104
|
+
return method === "HEAD" ? new Response(null, { status: res.status, statusText: res.statusText, headers: res.headers }) : res;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const resolved = await getOrCompute(
|
|
108
|
+
`route:${pathname}`,
|
|
109
|
+
async () => {
|
|
110
|
+
const s = scope();
|
|
111
|
+
const res = await runInCacheScope(s, () => runRouteHandler(mod, input, ctx.params));
|
|
112
|
+
if ((res.headers.get("content-type") ?? "").includes("text/event-stream")) {
|
|
113
|
+
await res.body?.cancel().catch(() => undefined);
|
|
114
|
+
throw new Error(`Route ${ctx.pattern} is static (dynamic = "force-static" / revalidate) but returned a text/event-stream response. Streaming route handlers must be dynamic: export const dynamic = "force-dynamic".`);
|
|
115
|
+
}
|
|
116
|
+
const body = new Uint8Array(await res.arrayBuffer());
|
|
117
|
+
const value: StoredResponse = { status: res.status, statusText: res.statusText, headers: [...res.headers], body: toBase64(body) };
|
|
118
|
+
// Server errors, cookies and private responses are returned to this request but never stored.
|
|
119
|
+
return { value, tags: [...s.tags], revalidate: isUncacheableResponse(res) ? 0 : s.revalidate };
|
|
120
|
+
},
|
|
121
|
+
ctx.waitUntil,
|
|
122
|
+
);
|
|
123
|
+
const stored = resolved.entry.value as StoredResponse;
|
|
124
|
+
const headers = new Headers();
|
|
125
|
+
for (const [k, v] of stored.headers) headers.append(k, v);
|
|
126
|
+
let generated = false;
|
|
127
|
+
if (resolved.entry.revalidate === 0) {
|
|
128
|
+
if (stored.status >= 500 || !headers.has("cache-control")) headers.set("cache-control", PRIVATE_CACHE_CONTROL);
|
|
129
|
+
} else {
|
|
130
|
+
headers.set(CACHE_HEADER, resolved.status);
|
|
131
|
+
if (!headers.has("cache-control")) {
|
|
132
|
+
headers.set("cache-control", cacheControlFor(resolved.entry.revalidate));
|
|
133
|
+
generated = true;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
const body = method === "HEAD" || stored.status === 204 || stored.status === 304 ? null : fromBase64(stored.body);
|
|
137
|
+
const response = new Response(body, { status: stored.status, statusText: stored.statusText, headers });
|
|
138
|
+
return generated ? markGeneratedCacheControl(response) : response;
|
|
139
|
+
}
|