@finesoft/front 0.1.31 → 0.1.33
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/app-OOLQDVXA.js +10 -0
- package/dist/browser.cjs +13 -9
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.d.cts +1 -1
- package/dist/browser.d.ts +1 -1
- package/dist/browser.js +2 -2
- package/dist/{chunk-ZMOE42LB.js → chunk-AYO3UUQC.js} +39 -37
- package/dist/chunk-AYO3UUQC.js.map +1 -0
- package/dist/{chunk-H3RNYNSD.js → chunk-OE5BU5MR.js} +2 -2
- package/dist/{chunk-BUYWNNNQ.js → chunk-OXKFPW4U.js} +14 -10
- package/dist/chunk-OXKFPW4U.js.map +1 -0
- package/dist/{chunk-FYP2ZYYV.js → chunk-PSPVIVC2.js} +2 -2
- package/dist/chunk-PSPVIVC2.js.map +1 -0
- package/dist/{chunk-M7VITIMR.js → chunk-UHQBKSHL.js} +3 -3
- package/dist/index.cjs +265 -49
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +75 -6
- package/dist/index.d.ts +75 -6
- package/dist/index.js +220 -12
- package/dist/index.js.map +1 -1
- package/dist/locale-CAZ4INCX.js +7 -0
- package/dist/{src-B5YAJXIM.js → src-MVACJWBF.js} +3 -3
- package/package.json +1 -1
- package/dist/app-IA6JQC7V.js +0 -10
- package/dist/chunk-BUYWNNNQ.js.map +0 -1
- package/dist/chunk-FYP2ZYYV.js.map +0 -1
- package/dist/chunk-ZMOE42LB.js.map +0 -1
- package/dist/locale-YK3THSI6.js +0 -7
- /package/dist/{app-IA6JQC7V.js.map → app-OOLQDVXA.js.map} +0 -0
- /package/dist/{chunk-H3RNYNSD.js.map → chunk-OE5BU5MR.js.map} +0 -0
- /package/dist/{chunk-M7VITIMR.js.map → chunk-UHQBKSHL.js.map} +0 -0
- /package/dist/{locale-YK3THSI6.js.map → locale-CAZ4INCX.js.map} +0 -0
- /package/dist/{src-B5YAJXIM.js.map → src-MVACJWBF.js.map} +0 -0
package/dist/index.d.cts
CHANGED
|
@@ -112,11 +112,45 @@ declare function injectCSRShell(template: string, locale: string): string;
|
|
|
112
112
|
declare function serializeServerData(data: PrefetchedIntent[]): string;
|
|
113
113
|
|
|
114
114
|
/**
|
|
115
|
-
*
|
|
115
|
+
* 框架级声明式代理路由
|
|
116
116
|
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
117
|
+
* 将代理路由从业务层手写 Hono 路由,改为框架级配置。
|
|
118
|
+
* 框架统一执行路径校验(SSRF 防护)、Host 限制、错误处理、响应头控制。
|
|
119
119
|
*/
|
|
120
|
+
|
|
121
|
+
/** 代理路由认证配置 */
|
|
122
|
+
interface ProxyAuthConfig {
|
|
123
|
+
/** 认证类型 */
|
|
124
|
+
type: "bearer" | "basic";
|
|
125
|
+
/** 环境变量名称(运行时从 process.env 读取) */
|
|
126
|
+
envKey: string;
|
|
127
|
+
}
|
|
128
|
+
/** 声明式代理路由配置 */
|
|
129
|
+
interface ProxyRouteConfig {
|
|
130
|
+
/** URL 前缀,如 "/api/apple"(必须以 "/" 开头) */
|
|
131
|
+
prefix: string;
|
|
132
|
+
/** 代理目标地址(必须以 "https://" 开头) */
|
|
133
|
+
target: string;
|
|
134
|
+
/** HTTP 方法(默认 ["all"]) */
|
|
135
|
+
methods?: ("all" | "get" | "post" | "put" | "delete" | "patch")[];
|
|
136
|
+
/** 附加到代理请求的头部 */
|
|
137
|
+
headers?: Record<string, string>;
|
|
138
|
+
/** 认证配置 */
|
|
139
|
+
auth?: ProxyAuthConfig;
|
|
140
|
+
/** Cache-Control 响应头 */
|
|
141
|
+
cache?: string;
|
|
142
|
+
/** 是否跟随重定向(默认 false) */
|
|
143
|
+
followRedirects?: boolean;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* 注册声明式代理路由到 Hono app(运行时使用:dev / preview / createServer)
|
|
147
|
+
*/
|
|
148
|
+
declare function registerProxyRoutes(app: Hono, configs: ProxyRouteConfig[]): void;
|
|
149
|
+
/**
|
|
150
|
+
* 生成代理路由的内联代码(用于 serverless/edge 入口,避免运行时依赖)
|
|
151
|
+
*/
|
|
152
|
+
declare function generateProxyCode(configs: ProxyRouteConfig[]): string;
|
|
153
|
+
|
|
120
154
|
/** 适配器上下文 — 由 vite-plugin 的 closeBundle 构造后传入 adapter.build() */
|
|
121
155
|
interface AdapterContext {
|
|
122
156
|
/** 项目根路径 */
|
|
@@ -142,6 +176,8 @@ interface AdapterContext {
|
|
|
142
176
|
* key: 精确路径或 glob 模式,value: "ssr" | "csr" | "prerender"
|
|
143
177
|
*/
|
|
144
178
|
renderModes?: Record<string, string>;
|
|
179
|
+
/** 声明式代理路由配置 */
|
|
180
|
+
proxies?: ProxyRouteConfig[];
|
|
145
181
|
vite: any;
|
|
146
182
|
fs: typeof node_fs;
|
|
147
183
|
path: {
|
|
@@ -369,8 +405,10 @@ interface ServerConfig {
|
|
|
369
405
|
defaultLocale?: string;
|
|
370
406
|
/** 端口号(默认 3000) */
|
|
371
407
|
port?: number;
|
|
372
|
-
/**
|
|
408
|
+
/** 注册自定义路由(在 SSR catch-all 之前调用,但在声明式代理之后) */
|
|
373
409
|
setup?: (app: Hono) => void | Promise<void>;
|
|
410
|
+
/** 声明式代理路由配置 */
|
|
411
|
+
proxies?: ProxyRouteConfig[];
|
|
374
412
|
/** SSR 相关选项(透传给 createSSRApp) */
|
|
375
413
|
ssr?: Pick<SSRAppOptions, "ssrEntryPath" | "ssrProductionModule">;
|
|
376
414
|
}
|
|
@@ -449,10 +487,17 @@ interface FinesoftFrontViteOptions {
|
|
|
449
487
|
entry?: string;
|
|
450
488
|
};
|
|
451
489
|
/**
|
|
452
|
-
*
|
|
490
|
+
* 声明式代理路由配置。
|
|
491
|
+
* 框架统一执行路径校验(SSRF 防护)、Host 限制、错误处理、响应头控制。
|
|
492
|
+
* 代理路由在 setup 之前注册,优先级高于自定义路由。
|
|
493
|
+
*/
|
|
494
|
+
proxies?: ProxyRouteConfig[];
|
|
495
|
+
/**
|
|
496
|
+
* 注册自定义路由(非代理类)。
|
|
453
497
|
* - 传入 Function:仅 dev/preview 时可用。
|
|
454
498
|
* - 传入 string(文件路径):dev/preview/adapter 均可用,
|
|
455
499
|
* 文件需 export default 一个 (app: Hono) => void 函数。
|
|
500
|
+
* 注意:代理路由请使用 proxies 选项,不要在 setup 中手写代理。
|
|
456
501
|
*/
|
|
457
502
|
setup?: ((app: Hono) => void | Promise<void>) | string;
|
|
458
503
|
/**
|
|
@@ -488,9 +533,33 @@ declare function finesoftFrontViteConfig(options?: FinesoftFrontViteOptions): {
|
|
|
488
533
|
command: string;
|
|
489
534
|
}): Record<string, any>;
|
|
490
535
|
configResolved(config: Record<string, any>): void;
|
|
536
|
+
/**
|
|
537
|
+
* Dev 模式 CSS 内联 — 消除 SSR 首屏布局抖动
|
|
538
|
+
*
|
|
539
|
+
* Vite dev 模式下,global.scss 等非组件 CSS 通过 JS 模块系统异步加载,
|
|
540
|
+
* 导致 SSR HTML 初次渲染缺少布局关键样式(box-sizing、flex 布局、padding-top 等)。
|
|
541
|
+
*
|
|
542
|
+
* 此 hook 在 HTML 模板变换阶段(SSR 渲染之前):
|
|
543
|
+
* 1. 找到浏览器入口脚本(排除 /@vite/client 等内部脚本)
|
|
544
|
+
* 2. 编译入口脚本,填充 Vite 模块图
|
|
545
|
+
* 3. 遍历模块图收集所有 CSS 依赖(排除 .svelte 组件 CSS,由 SSR 渲染自行处理)
|
|
546
|
+
* 4. 通过 ssrLoadModule 获取编译后 CSS(SCSS→CSS)
|
|
547
|
+
* 5. 注入 <style data-vite-dev-id> 标签到 <head>
|
|
548
|
+
*
|
|
549
|
+
* data-vite-dev-id 确保 Vite HMR 客户端复用已有标签,避免重复注入。
|
|
550
|
+
*/
|
|
551
|
+
transformIndexHtml: {
|
|
552
|
+
order: "pre";
|
|
553
|
+
handler(html: string, ctx: any): Promise<{
|
|
554
|
+
tag: string;
|
|
555
|
+
attrs: Record<string, string>;
|
|
556
|
+
children: string;
|
|
557
|
+
injectTo: string;
|
|
558
|
+
}[] | undefined>;
|
|
559
|
+
};
|
|
491
560
|
configureServer(server: any): () => Promise<void>;
|
|
492
561
|
configurePreviewServer(server: any): () => Promise<void>;
|
|
493
562
|
closeBundle(): Promise<void>;
|
|
494
563
|
};
|
|
495
564
|
|
|
496
|
-
export { type Adapter, type AdapterContext, BasePage, type FinesoftFrontViteOptions, Framework, FrameworkConfig, type InjectSSROptions, PrefetchedIntent, type RuntimeInfo, type SSRAppOptions, type SSRContext, type SSRModule, type SSRRenderConfig, type SSRRenderOptions, type SSRRenderResult, SSR_PLACEHOLDERS, type ServerConfig, type ServerInstance, type StartServerOptions, autoAdapter, cloudflareAdapter, createSSRApp, createSSRRender, createServer, detectRuntime, finesoftFrontViteConfig, injectCSRShell, injectSSRContent, netlifyAdapter, nodeAdapter, parseAcceptLanguage, resolveAdapter, resolveRoot, serializeServerData, ssrRender, startServer, staticAdapter, vercelAdapter };
|
|
565
|
+
export { type Adapter, type AdapterContext, BasePage, type FinesoftFrontViteOptions, Framework, FrameworkConfig, type InjectSSROptions, PrefetchedIntent, type ProxyAuthConfig, type ProxyRouteConfig, type RuntimeInfo, type SSRAppOptions, type SSRContext, type SSRModule, type SSRRenderConfig, type SSRRenderOptions, type SSRRenderResult, SSR_PLACEHOLDERS, type ServerConfig, type ServerInstance, type StartServerOptions, autoAdapter, cloudflareAdapter, createSSRApp, createSSRRender, createServer, detectRuntime, finesoftFrontViteConfig, generateProxyCode, injectCSRShell, injectSSRContent, netlifyAdapter, nodeAdapter, parseAcceptLanguage, registerProxyRoutes, resolveAdapter, resolveRoot, serializeServerData, ssrRender, startServer, staticAdapter, vercelAdapter };
|
package/dist/index.d.ts
CHANGED
|
@@ -112,11 +112,45 @@ declare function injectCSRShell(template: string, locale: string): string;
|
|
|
112
112
|
declare function serializeServerData(data: PrefetchedIntent[]): string;
|
|
113
113
|
|
|
114
114
|
/**
|
|
115
|
-
*
|
|
115
|
+
* 框架级声明式代理路由
|
|
116
116
|
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
117
|
+
* 将代理路由从业务层手写 Hono 路由,改为框架级配置。
|
|
118
|
+
* 框架统一执行路径校验(SSRF 防护)、Host 限制、错误处理、响应头控制。
|
|
119
119
|
*/
|
|
120
|
+
|
|
121
|
+
/** 代理路由认证配置 */
|
|
122
|
+
interface ProxyAuthConfig {
|
|
123
|
+
/** 认证类型 */
|
|
124
|
+
type: "bearer" | "basic";
|
|
125
|
+
/** 环境变量名称(运行时从 process.env 读取) */
|
|
126
|
+
envKey: string;
|
|
127
|
+
}
|
|
128
|
+
/** 声明式代理路由配置 */
|
|
129
|
+
interface ProxyRouteConfig {
|
|
130
|
+
/** URL 前缀,如 "/api/apple"(必须以 "/" 开头) */
|
|
131
|
+
prefix: string;
|
|
132
|
+
/** 代理目标地址(必须以 "https://" 开头) */
|
|
133
|
+
target: string;
|
|
134
|
+
/** HTTP 方法(默认 ["all"]) */
|
|
135
|
+
methods?: ("all" | "get" | "post" | "put" | "delete" | "patch")[];
|
|
136
|
+
/** 附加到代理请求的头部 */
|
|
137
|
+
headers?: Record<string, string>;
|
|
138
|
+
/** 认证配置 */
|
|
139
|
+
auth?: ProxyAuthConfig;
|
|
140
|
+
/** Cache-Control 响应头 */
|
|
141
|
+
cache?: string;
|
|
142
|
+
/** 是否跟随重定向(默认 false) */
|
|
143
|
+
followRedirects?: boolean;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* 注册声明式代理路由到 Hono app(运行时使用:dev / preview / createServer)
|
|
147
|
+
*/
|
|
148
|
+
declare function registerProxyRoutes(app: Hono, configs: ProxyRouteConfig[]): void;
|
|
149
|
+
/**
|
|
150
|
+
* 生成代理路由的内联代码(用于 serverless/edge 入口,避免运行时依赖)
|
|
151
|
+
*/
|
|
152
|
+
declare function generateProxyCode(configs: ProxyRouteConfig[]): string;
|
|
153
|
+
|
|
120
154
|
/** 适配器上下文 — 由 vite-plugin 的 closeBundle 构造后传入 adapter.build() */
|
|
121
155
|
interface AdapterContext {
|
|
122
156
|
/** 项目根路径 */
|
|
@@ -142,6 +176,8 @@ interface AdapterContext {
|
|
|
142
176
|
* key: 精确路径或 glob 模式,value: "ssr" | "csr" | "prerender"
|
|
143
177
|
*/
|
|
144
178
|
renderModes?: Record<string, string>;
|
|
179
|
+
/** 声明式代理路由配置 */
|
|
180
|
+
proxies?: ProxyRouteConfig[];
|
|
145
181
|
vite: any;
|
|
146
182
|
fs: typeof node_fs;
|
|
147
183
|
path: {
|
|
@@ -369,8 +405,10 @@ interface ServerConfig {
|
|
|
369
405
|
defaultLocale?: string;
|
|
370
406
|
/** 端口号(默认 3000) */
|
|
371
407
|
port?: number;
|
|
372
|
-
/**
|
|
408
|
+
/** 注册自定义路由(在 SSR catch-all 之前调用,但在声明式代理之后) */
|
|
373
409
|
setup?: (app: Hono) => void | Promise<void>;
|
|
410
|
+
/** 声明式代理路由配置 */
|
|
411
|
+
proxies?: ProxyRouteConfig[];
|
|
374
412
|
/** SSR 相关选项(透传给 createSSRApp) */
|
|
375
413
|
ssr?: Pick<SSRAppOptions, "ssrEntryPath" | "ssrProductionModule">;
|
|
376
414
|
}
|
|
@@ -449,10 +487,17 @@ interface FinesoftFrontViteOptions {
|
|
|
449
487
|
entry?: string;
|
|
450
488
|
};
|
|
451
489
|
/**
|
|
452
|
-
*
|
|
490
|
+
* 声明式代理路由配置。
|
|
491
|
+
* 框架统一执行路径校验(SSRF 防护)、Host 限制、错误处理、响应头控制。
|
|
492
|
+
* 代理路由在 setup 之前注册,优先级高于自定义路由。
|
|
493
|
+
*/
|
|
494
|
+
proxies?: ProxyRouteConfig[];
|
|
495
|
+
/**
|
|
496
|
+
* 注册自定义路由(非代理类)。
|
|
453
497
|
* - 传入 Function:仅 dev/preview 时可用。
|
|
454
498
|
* - 传入 string(文件路径):dev/preview/adapter 均可用,
|
|
455
499
|
* 文件需 export default 一个 (app: Hono) => void 函数。
|
|
500
|
+
* 注意:代理路由请使用 proxies 选项,不要在 setup 中手写代理。
|
|
456
501
|
*/
|
|
457
502
|
setup?: ((app: Hono) => void | Promise<void>) | string;
|
|
458
503
|
/**
|
|
@@ -488,9 +533,33 @@ declare function finesoftFrontViteConfig(options?: FinesoftFrontViteOptions): {
|
|
|
488
533
|
command: string;
|
|
489
534
|
}): Record<string, any>;
|
|
490
535
|
configResolved(config: Record<string, any>): void;
|
|
536
|
+
/**
|
|
537
|
+
* Dev 模式 CSS 内联 — 消除 SSR 首屏布局抖动
|
|
538
|
+
*
|
|
539
|
+
* Vite dev 模式下,global.scss 等非组件 CSS 通过 JS 模块系统异步加载,
|
|
540
|
+
* 导致 SSR HTML 初次渲染缺少布局关键样式(box-sizing、flex 布局、padding-top 等)。
|
|
541
|
+
*
|
|
542
|
+
* 此 hook 在 HTML 模板变换阶段(SSR 渲染之前):
|
|
543
|
+
* 1. 找到浏览器入口脚本(排除 /@vite/client 等内部脚本)
|
|
544
|
+
* 2. 编译入口脚本,填充 Vite 模块图
|
|
545
|
+
* 3. 遍历模块图收集所有 CSS 依赖(排除 .svelte 组件 CSS,由 SSR 渲染自行处理)
|
|
546
|
+
* 4. 通过 ssrLoadModule 获取编译后 CSS(SCSS→CSS)
|
|
547
|
+
* 5. 注入 <style data-vite-dev-id> 标签到 <head>
|
|
548
|
+
*
|
|
549
|
+
* data-vite-dev-id 确保 Vite HMR 客户端复用已有标签,避免重复注入。
|
|
550
|
+
*/
|
|
551
|
+
transformIndexHtml: {
|
|
552
|
+
order: "pre";
|
|
553
|
+
handler(html: string, ctx: any): Promise<{
|
|
554
|
+
tag: string;
|
|
555
|
+
attrs: Record<string, string>;
|
|
556
|
+
children: string;
|
|
557
|
+
injectTo: string;
|
|
558
|
+
}[] | undefined>;
|
|
559
|
+
};
|
|
491
560
|
configureServer(server: any): () => Promise<void>;
|
|
492
561
|
configurePreviewServer(server: any): () => Promise<void>;
|
|
493
562
|
closeBundle(): Promise<void>;
|
|
494
563
|
};
|
|
495
564
|
|
|
496
|
-
export { type Adapter, type AdapterContext, BasePage, type FinesoftFrontViteOptions, Framework, FrameworkConfig, type InjectSSROptions, PrefetchedIntent, type RuntimeInfo, type SSRAppOptions, type SSRContext, type SSRModule, type SSRRenderConfig, type SSRRenderOptions, type SSRRenderResult, SSR_PLACEHOLDERS, type ServerConfig, type ServerInstance, type StartServerOptions, autoAdapter, cloudflareAdapter, createSSRApp, createSSRRender, createServer, detectRuntime, finesoftFrontViteConfig, injectCSRShell, injectSSRContent, netlifyAdapter, nodeAdapter, parseAcceptLanguage, resolveAdapter, resolveRoot, serializeServerData, ssrRender, startServer, staticAdapter, vercelAdapter };
|
|
565
|
+
export { type Adapter, type AdapterContext, BasePage, type FinesoftFrontViteOptions, Framework, FrameworkConfig, type InjectSSROptions, PrefetchedIntent, type ProxyAuthConfig, type ProxyRouteConfig, type RuntimeInfo, type SSRAppOptions, type SSRContext, type SSRModule, type SSRRenderConfig, type SSRRenderOptions, type SSRRenderResult, SSR_PLACEHOLDERS, type ServerConfig, type ServerInstance, type StartServerOptions, autoAdapter, cloudflareAdapter, createSSRApp, createSSRRender, createServer, detectRuntime, finesoftFrontViteConfig, generateProxyCode, injectCSRShell, injectSSRContent, netlifyAdapter, nodeAdapter, parseAcceptLanguage, registerProxyRoutes, resolveAdapter, resolveRoot, serializeServerData, ssrRender, startServer, staticAdapter, vercelAdapter };
|
package/dist/index.js
CHANGED
|
@@ -7,13 +7,13 @@ import {
|
|
|
7
7
|
registerFlowActionHandler,
|
|
8
8
|
startBrowserApp,
|
|
9
9
|
tryScroll
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-OE5BU5MR.js";
|
|
11
11
|
import {
|
|
12
12
|
MAX_SSR_DEPTH,
|
|
13
13
|
SSR_DEPTH_HEADER,
|
|
14
14
|
createInternalFetch,
|
|
15
15
|
createSSRApp
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-UHQBKSHL.js";
|
|
17
17
|
import {
|
|
18
18
|
SSR_PLACEHOLDERS,
|
|
19
19
|
createSSRRender,
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
injectSSRContent,
|
|
22
22
|
serializeServerData,
|
|
23
23
|
ssrRender
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-AYO3UUQC.js";
|
|
25
25
|
import {
|
|
26
26
|
ACTION_KINDS,
|
|
27
27
|
ActionDispatcher,
|
|
@@ -61,10 +61,124 @@ import {
|
|
|
61
61
|
resetFilterCache,
|
|
62
62
|
shouldLog,
|
|
63
63
|
stableStringify
|
|
64
|
-
} from "./chunk-
|
|
64
|
+
} from "./chunk-OXKFPW4U.js";
|
|
65
65
|
import {
|
|
66
66
|
parseAcceptLanguage
|
|
67
|
-
} from "./chunk-
|
|
67
|
+
} from "./chunk-PSPVIVC2.js";
|
|
68
|
+
|
|
69
|
+
// ../server/src/proxy.ts
|
|
70
|
+
function sanitizeProxyPath(raw) {
|
|
71
|
+
if (raw.startsWith("//")) return null;
|
|
72
|
+
return raw.startsWith("/") ? raw : `/${raw}`;
|
|
73
|
+
}
|
|
74
|
+
function validateConfig(config) {
|
|
75
|
+
if (!config.prefix.startsWith("/")) {
|
|
76
|
+
throw new Error(
|
|
77
|
+
`[proxy] prefix must start with "/": "${config.prefix}"`
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
if (!config.target.startsWith("https://")) {
|
|
81
|
+
throw new Error(`[proxy] target must use HTTPS: "${config.target}"`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function registerProxyRoutes(app, configs) {
|
|
85
|
+
for (const config of configs) {
|
|
86
|
+
validateConfig(config);
|
|
87
|
+
const methods = config.methods ?? ["all"];
|
|
88
|
+
const pattern = `${config.prefix}/*`;
|
|
89
|
+
const handler = async (c) => {
|
|
90
|
+
const subPath = sanitizeProxyPath(
|
|
91
|
+
c.req.path.replace(config.prefix, "")
|
|
92
|
+
);
|
|
93
|
+
if (!subPath) return c.text("Invalid path", 400);
|
|
94
|
+
const targetUrl = new URL(subPath, config.target);
|
|
95
|
+
const reqUrl = new URL(c.req.url);
|
|
96
|
+
reqUrl.searchParams.forEach(
|
|
97
|
+
(v, k) => targetUrl.searchParams.set(k, v)
|
|
98
|
+
);
|
|
99
|
+
const headers = { ...config.headers };
|
|
100
|
+
if (config.auth) {
|
|
101
|
+
const token = process.env[config.auth.envKey] ?? "";
|
|
102
|
+
if (token) {
|
|
103
|
+
headers.Authorization = config.auth.type === "bearer" ? `Bearer ${token}` : `Basic ${token}`;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
const resp = await fetch(targetUrl.toString(), {
|
|
108
|
+
headers,
|
|
109
|
+
redirect: config.followRedirects ? "follow" : "manual"
|
|
110
|
+
});
|
|
111
|
+
const body = await resp.text();
|
|
112
|
+
const respHeaders = {
|
|
113
|
+
"Content-Type": resp.headers.get("Content-Type") ?? "application/json"
|
|
114
|
+
};
|
|
115
|
+
if (config.cache) {
|
|
116
|
+
respHeaders["Cache-Control"] = config.cache;
|
|
117
|
+
}
|
|
118
|
+
return c.newResponse(body, resp.status, respHeaders);
|
|
119
|
+
} catch (e) {
|
|
120
|
+
console.error(`[Proxy ${config.prefix}]`, e);
|
|
121
|
+
return c.json({ error: "Proxy request failed" }, 502);
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
for (const method of methods) {
|
|
125
|
+
app[method](pattern, handler);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function generateProxyCode(configs) {
|
|
130
|
+
if (!configs || configs.length === 0) return "";
|
|
131
|
+
for (const config of configs) {
|
|
132
|
+
validateConfig(config);
|
|
133
|
+
}
|
|
134
|
+
const blocks = [];
|
|
135
|
+
blocks.push(`
|
|
136
|
+
// \u2500\u2500\u2500 \u6846\u67B6\u58F0\u660E\u5F0F\u4EE3\u7406\u8DEF\u7531 \u2500\u2500\u2500
|
|
137
|
+
function _sanitizeProxyPath(raw) {
|
|
138
|
+
if (raw.startsWith("//")) return null;
|
|
139
|
+
return raw.startsWith("/") ? raw : "/" + raw;
|
|
140
|
+
}
|
|
141
|
+
`);
|
|
142
|
+
for (const config of configs) {
|
|
143
|
+
const methods = config.methods ?? ["all"];
|
|
144
|
+
const pattern = `"${config.prefix}/*"`;
|
|
145
|
+
const headersJson = JSON.stringify(config.headers ?? {});
|
|
146
|
+
const cacheStr = config.cache ? JSON.stringify(config.cache) : "null";
|
|
147
|
+
const redirect = config.followRedirects ? '"follow"' : '"manual"';
|
|
148
|
+
let authCode = "";
|
|
149
|
+
if (config.auth) {
|
|
150
|
+
const envKey = JSON.stringify(config.auth.envKey);
|
|
151
|
+
const prefix = config.auth.type === "bearer" ? "Bearer " : "Basic ";
|
|
152
|
+
authCode = `
|
|
153
|
+
const _token = (typeof process !== "undefined" && process.env && process.env[${envKey}]) || "";
|
|
154
|
+
if (_token) _headers.Authorization = "${prefix}" + _token;`;
|
|
155
|
+
}
|
|
156
|
+
const handlerCode = `async (c) => {
|
|
157
|
+
const _sub = _sanitizeProxyPath(c.req.path.replace(${JSON.stringify(
|
|
158
|
+
config.prefix
|
|
159
|
+
)}, ""));
|
|
160
|
+
if (!_sub) return c.text("Invalid path", 400);
|
|
161
|
+
const _target = new URL(_sub, ${JSON.stringify(config.target)});
|
|
162
|
+
const _reqUrl = new URL(c.req.url);
|
|
163
|
+
_reqUrl.searchParams.forEach((v, k) => _target.searchParams.set(k, v));
|
|
164
|
+
const _headers = ${headersJson};${authCode}
|
|
165
|
+
try {
|
|
166
|
+
const _resp = await fetch(_target.toString(), { headers: _headers, redirect: ${redirect} });
|
|
167
|
+
const _body = await _resp.text();
|
|
168
|
+
const _rh = { "Content-Type": _resp.headers.get("Content-Type") || "application/json" };
|
|
169
|
+
if (${cacheStr}) _rh["Cache-Control"] = ${cacheStr};
|
|
170
|
+
return c.newResponse(_body, _resp.status, _rh);
|
|
171
|
+
} catch (_e) {
|
|
172
|
+
console.error("[Proxy ${config.prefix}]", _e);
|
|
173
|
+
return c.json({ error: "Proxy request failed" }, 502);
|
|
174
|
+
}
|
|
175
|
+
}`;
|
|
176
|
+
for (const method of methods) {
|
|
177
|
+
blocks.push(`app.${method}(${pattern}, ${handlerCode});`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return blocks.join("\n");
|
|
181
|
+
}
|
|
68
182
|
|
|
69
183
|
// ../server/src/adapters/shared.ts
|
|
70
184
|
var BUILD_TOOL_EXTERNALS = [
|
|
@@ -109,7 +223,7 @@ function parseAcceptLanguage(header) {
|
|
|
109
223
|
if (!header) return DEFAULT_LOCALE;
|
|
110
224
|
const langs = header.split(",").map(p => {
|
|
111
225
|
const [l, q] = p.trim().split(";q=");
|
|
112
|
-
return { l: l.trim().toLowerCase(), q: q ? +q : 1 };
|
|
226
|
+
return { l: l.trim().toLowerCase(), q: q ? (+q || 0) : 1 };
|
|
113
227
|
}).sort((a, b) => b.q - a.q);
|
|
114
228
|
for (const { l } of langs) {
|
|
115
229
|
const prefix = l.split("-")[0];
|
|
@@ -139,7 +253,8 @@ function matchRenderMode(url) {
|
|
|
139
253
|
if (RENDER_MODES[path]) return RENDER_MODES[path];
|
|
140
254
|
for (const [pattern, mode] of Object.entries(RENDER_MODES)) {
|
|
141
255
|
if (pattern.includes("*")) {
|
|
142
|
-
const
|
|
256
|
+
const escaped = pattern.replace(/[.+?^\${}()|[\\]\\\\]/g, "\\\\$&");
|
|
257
|
+
const re = new RegExp("^" + escaped.replace(/\\*/g, ".*") + "$");
|
|
143
258
|
if (re.test(path)) return mode;
|
|
144
259
|
}
|
|
145
260
|
}
|
|
@@ -147,6 +262,7 @@ function matchRenderMode(url) {
|
|
|
147
262
|
}
|
|
148
263
|
|
|
149
264
|
const app = new Hono();
|
|
265
|
+
${generateProxyCode(ctx.proxies ?? [])}
|
|
150
266
|
${setupCall}
|
|
151
267
|
${opts.platformMiddleware ?? ""}
|
|
152
268
|
|
|
@@ -699,7 +815,8 @@ function resolveRenderMode(routePath, routeRenderMode, renderModes) {
|
|
|
699
815
|
if (renderModes[routePath]) return renderModes[routePath];
|
|
700
816
|
for (const [pattern, mode] of Object.entries(renderModes)) {
|
|
701
817
|
if (pattern.includes("*")) {
|
|
702
|
-
const
|
|
818
|
+
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
|
|
819
|
+
const re = new RegExp("^" + escaped.replace(/\*/g, ".*") + "$");
|
|
703
820
|
if (re.test(routePath)) return mode;
|
|
704
821
|
}
|
|
705
822
|
}
|
|
@@ -1001,6 +1118,7 @@ async function createServer(config = {}) {
|
|
|
1001
1118
|
defaultLocale,
|
|
1002
1119
|
port = Number(process.env.PORT) || 3e3,
|
|
1003
1120
|
setup,
|
|
1121
|
+
proxies,
|
|
1004
1122
|
ssr
|
|
1005
1123
|
} = config;
|
|
1006
1124
|
const root = rootOverride ?? process.cwd();
|
|
@@ -1037,6 +1155,9 @@ async function createServer(config = {}) {
|
|
|
1037
1155
|
});
|
|
1038
1156
|
}
|
|
1039
1157
|
const app = new Hono2();
|
|
1158
|
+
if (proxies?.length) {
|
|
1159
|
+
registerProxyRoutes(app, proxies);
|
|
1160
|
+
}
|
|
1040
1161
|
if (setup) {
|
|
1041
1162
|
await setup(app);
|
|
1042
1163
|
}
|
|
@@ -1076,7 +1197,8 @@ function matchRenderModeConfig(url, renderModes) {
|
|
|
1076
1197
|
if (renderModes[path]) return renderModes[path];
|
|
1077
1198
|
for (const [pattern, mode] of Object.entries(renderModes)) {
|
|
1078
1199
|
if (pattern.includes("*")) {
|
|
1079
|
-
const
|
|
1200
|
+
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
|
|
1201
|
+
const re = new RegExp("^" + escaped.replace(/\*/g, ".*") + "$");
|
|
1080
1202
|
if (re.test(path)) return mode;
|
|
1081
1203
|
}
|
|
1082
1204
|
}
|
|
@@ -1088,6 +1210,7 @@ function finesoftFrontViteConfig(options = {}) {
|
|
|
1088
1210
|
let resolvedCommand;
|
|
1089
1211
|
let resolvedResolve;
|
|
1090
1212
|
let resolvedCss;
|
|
1213
|
+
const CSS_EXTENSIONS = /\.(css|scss|less|sass|styl|stylus|pcss|postcss)($|\?)/;
|
|
1091
1214
|
return {
|
|
1092
1215
|
name: "finesoft-front",
|
|
1093
1216
|
config(userConfig, env) {
|
|
@@ -1107,6 +1230,82 @@ function finesoftFrontViteConfig(options = {}) {
|
|
|
1107
1230
|
resolvedCss = config.css;
|
|
1108
1231
|
root = config.root;
|
|
1109
1232
|
},
|
|
1233
|
+
/**
|
|
1234
|
+
* Dev 模式 CSS 内联 — 消除 SSR 首屏布局抖动
|
|
1235
|
+
*
|
|
1236
|
+
* Vite dev 模式下,global.scss 等非组件 CSS 通过 JS 模块系统异步加载,
|
|
1237
|
+
* 导致 SSR HTML 初次渲染缺少布局关键样式(box-sizing、flex 布局、padding-top 等)。
|
|
1238
|
+
*
|
|
1239
|
+
* 此 hook 在 HTML 模板变换阶段(SSR 渲染之前):
|
|
1240
|
+
* 1. 找到浏览器入口脚本(排除 /@vite/client 等内部脚本)
|
|
1241
|
+
* 2. 编译入口脚本,填充 Vite 模块图
|
|
1242
|
+
* 3. 遍历模块图收集所有 CSS 依赖(排除 .svelte 组件 CSS,由 SSR 渲染自行处理)
|
|
1243
|
+
* 4. 通过 ssrLoadModule 获取编译后 CSS(SCSS→CSS)
|
|
1244
|
+
* 5. 注入 <style data-vite-dev-id> 标签到 <head>
|
|
1245
|
+
*
|
|
1246
|
+
* data-vite-dev-id 确保 Vite HMR 客户端复用已有标签,避免重复注入。
|
|
1247
|
+
*/
|
|
1248
|
+
transformIndexHtml: {
|
|
1249
|
+
order: "pre",
|
|
1250
|
+
async handler(html, ctx) {
|
|
1251
|
+
const server = ctx.server;
|
|
1252
|
+
if (!server) return;
|
|
1253
|
+
const urlPath = (ctx.originalUrl || ctx.path || "").split(
|
|
1254
|
+
"?"
|
|
1255
|
+
)[0];
|
|
1256
|
+
if (/\.\w+$/.test(urlPath) && !urlPath.endsWith(".html")) {
|
|
1257
|
+
return;
|
|
1258
|
+
}
|
|
1259
|
+
const scripts = [
|
|
1260
|
+
...html.matchAll(
|
|
1261
|
+
/<script\b[^>]*\bsrc=["']([^"']+)["'][^>]*>/g
|
|
1262
|
+
)
|
|
1263
|
+
];
|
|
1264
|
+
const appEntry = scripts.find((m) => !m[1].startsWith("/@"));
|
|
1265
|
+
if (!appEntry) return;
|
|
1266
|
+
const browserEntry = appEntry[1];
|
|
1267
|
+
try {
|
|
1268
|
+
await server.transformRequest(browserEntry);
|
|
1269
|
+
} catch {
|
|
1270
|
+
return;
|
|
1271
|
+
}
|
|
1272
|
+
const cssUrls = [];
|
|
1273
|
+
const visited = /* @__PURE__ */ new Set();
|
|
1274
|
+
function walk(mod) {
|
|
1275
|
+
if (!mod?.url || visited.has(mod.url)) return;
|
|
1276
|
+
visited.add(mod.url);
|
|
1277
|
+
if (CSS_EXTENSIONS.test(mod.url) && !mod.url.includes(".svelte")) {
|
|
1278
|
+
cssUrls.push(mod.url);
|
|
1279
|
+
}
|
|
1280
|
+
if (mod.importedModules) {
|
|
1281
|
+
for (const imported of mod.importedModules) {
|
|
1282
|
+
walk(imported);
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
const mg = server.moduleGraph;
|
|
1287
|
+
const browserMod = await mg.getModuleByUrl(browserEntry);
|
|
1288
|
+
if (browserMod) walk(browserMod);
|
|
1289
|
+
if (cssUrls.length === 0) return;
|
|
1290
|
+
const tags = [];
|
|
1291
|
+
for (const url of cssUrls) {
|
|
1292
|
+
try {
|
|
1293
|
+
const mod = await server.ssrLoadModule(url);
|
|
1294
|
+
const css = mod?.default;
|
|
1295
|
+
if (typeof css === "string" && css.length > 0) {
|
|
1296
|
+
tags.push({
|
|
1297
|
+
tag: "style",
|
|
1298
|
+
attrs: { "data-vite-dev-id": url },
|
|
1299
|
+
children: css,
|
|
1300
|
+
injectTo: "head"
|
|
1301
|
+
});
|
|
1302
|
+
}
|
|
1303
|
+
} catch {
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
return tags;
|
|
1307
|
+
}
|
|
1308
|
+
},
|
|
1110
1309
|
// ─── Dev ───────────────────────────────────────────────
|
|
1111
1310
|
configureServer(server) {
|
|
1112
1311
|
return async () => {
|
|
@@ -1114,12 +1313,15 @@ function finesoftFrontViteConfig(options = {}) {
|
|
|
1114
1313
|
/* @vite-ignore */
|
|
1115
1314
|
"hono"
|
|
1116
1315
|
);
|
|
1117
|
-
const { createSSRApp: createSSRApp2 } = await import("./app-
|
|
1316
|
+
const { createSSRApp: createSSRApp2 } = await import("./app-OOLQDVXA.js");
|
|
1118
1317
|
const { getRequestListener } = await import(
|
|
1119
1318
|
/* @vite-ignore */
|
|
1120
1319
|
"@hono/node-server"
|
|
1121
1320
|
);
|
|
1122
1321
|
const app = new HonoClass();
|
|
1322
|
+
if (options.proxies?.length) {
|
|
1323
|
+
registerProxyRoutes(app, options.proxies);
|
|
1324
|
+
}
|
|
1123
1325
|
if (typeof options.setup === "function") {
|
|
1124
1326
|
await options.setup(app);
|
|
1125
1327
|
} else if (typeof options.setup === "string") {
|
|
@@ -1164,15 +1366,18 @@ function finesoftFrontViteConfig(options = {}) {
|
|
|
1164
1366
|
);
|
|
1165
1367
|
const { injectSSRContent: injectSSRContent2, injectCSRShell: injectCSRShell2 } = await import(
|
|
1166
1368
|
/* @vite-ignore */
|
|
1167
|
-
"./src-
|
|
1369
|
+
"./src-MVACJWBF.js"
|
|
1168
1370
|
);
|
|
1169
|
-
const { parseAcceptLanguage: parseAcceptLanguage2 } = await import("./locale-
|
|
1371
|
+
const { parseAcceptLanguage: parseAcceptLanguage2 } = await import("./locale-CAZ4INCX.js");
|
|
1170
1372
|
const { getRequestListener } = await import(
|
|
1171
1373
|
/* @vite-ignore */
|
|
1172
1374
|
"@hono/node-server"
|
|
1173
1375
|
);
|
|
1174
1376
|
const app = new HonoClass();
|
|
1175
1377
|
const isrCache = /* @__PURE__ */ new Map();
|
|
1378
|
+
if (options.proxies?.length) {
|
|
1379
|
+
registerProxyRoutes(app, options.proxies);
|
|
1380
|
+
}
|
|
1176
1381
|
if (typeof options.setup === "function") {
|
|
1177
1382
|
await options.setup(app);
|
|
1178
1383
|
} else if (typeof options.setup === "string") {
|
|
@@ -1327,6 +1532,7 @@ function finesoftFrontViteConfig(options = {}) {
|
|
|
1327
1532
|
defaultLocale,
|
|
1328
1533
|
templateHtml,
|
|
1329
1534
|
renderModes: options.renderModes,
|
|
1535
|
+
proxies: options.proxies,
|
|
1330
1536
|
resolvedResolve,
|
|
1331
1537
|
resolvedCss,
|
|
1332
1538
|
vite,
|
|
@@ -1383,6 +1589,7 @@ export {
|
|
|
1383
1589
|
deserializeServerData,
|
|
1384
1590
|
detectRuntime,
|
|
1385
1591
|
finesoftFrontViteConfig,
|
|
1592
|
+
generateProxyCode,
|
|
1386
1593
|
generateUuid,
|
|
1387
1594
|
getBaseUrl,
|
|
1388
1595
|
injectCSRShell,
|
|
@@ -1404,6 +1611,7 @@ export {
|
|
|
1404
1611
|
registerActionHandlers,
|
|
1405
1612
|
registerExternalUrlHandler,
|
|
1406
1613
|
registerFlowActionHandler,
|
|
1614
|
+
registerProxyRoutes,
|
|
1407
1615
|
removeHost,
|
|
1408
1616
|
removeQueryParams,
|
|
1409
1617
|
removeScheme,
|