@finesoft/front 0.1.32 → 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/index.cjs CHANGED
@@ -1358,6 +1358,7 @@ __export(index_exports, {
1358
1358
  deserializeServerData: () => deserializeServerData,
1359
1359
  detectRuntime: () => detectRuntime,
1360
1360
  finesoftFrontViteConfig: () => finesoftFrontViteConfig,
1361
+ generateProxyCode: () => generateProxyCode,
1361
1362
  generateUuid: () => generateUuid,
1362
1363
  getBaseUrl: () => getBaseUrl,
1363
1364
  injectCSRShell: () => injectCSRShell,
@@ -1379,6 +1380,7 @@ __export(index_exports, {
1379
1380
  registerActionHandlers: () => registerActionHandlers,
1380
1381
  registerExternalUrlHandler: () => registerExternalUrlHandler,
1381
1382
  registerFlowActionHandler: () => registerFlowActionHandler,
1383
+ registerProxyRoutes: () => registerProxyRoutes,
1382
1384
  removeHost: () => removeHost,
1383
1385
  removeQueryParams: () => removeQueryParams,
1384
1386
  removeScheme: () => removeScheme,
@@ -1732,6 +1734,120 @@ init_src();
1732
1734
  // src/index.ts
1733
1735
  init_src2();
1734
1736
 
1737
+ // ../server/src/proxy.ts
1738
+ function sanitizeProxyPath(raw) {
1739
+ if (raw.startsWith("//")) return null;
1740
+ return raw.startsWith("/") ? raw : `/${raw}`;
1741
+ }
1742
+ function validateConfig(config) {
1743
+ if (!config.prefix.startsWith("/")) {
1744
+ throw new Error(
1745
+ `[proxy] prefix must start with "/": "${config.prefix}"`
1746
+ );
1747
+ }
1748
+ if (!config.target.startsWith("https://")) {
1749
+ throw new Error(`[proxy] target must use HTTPS: "${config.target}"`);
1750
+ }
1751
+ }
1752
+ function registerProxyRoutes(app, configs) {
1753
+ for (const config of configs) {
1754
+ validateConfig(config);
1755
+ const methods = config.methods ?? ["all"];
1756
+ const pattern = `${config.prefix}/*`;
1757
+ const handler = async (c) => {
1758
+ const subPath = sanitizeProxyPath(
1759
+ c.req.path.replace(config.prefix, "")
1760
+ );
1761
+ if (!subPath) return c.text("Invalid path", 400);
1762
+ const targetUrl = new URL(subPath, config.target);
1763
+ const reqUrl = new URL(c.req.url);
1764
+ reqUrl.searchParams.forEach(
1765
+ (v, k) => targetUrl.searchParams.set(k, v)
1766
+ );
1767
+ const headers = { ...config.headers };
1768
+ if (config.auth) {
1769
+ const token = process.env[config.auth.envKey] ?? "";
1770
+ if (token) {
1771
+ headers.Authorization = config.auth.type === "bearer" ? `Bearer ${token}` : `Basic ${token}`;
1772
+ }
1773
+ }
1774
+ try {
1775
+ const resp = await fetch(targetUrl.toString(), {
1776
+ headers,
1777
+ redirect: config.followRedirects ? "follow" : "manual"
1778
+ });
1779
+ const body = await resp.text();
1780
+ const respHeaders = {
1781
+ "Content-Type": resp.headers.get("Content-Type") ?? "application/json"
1782
+ };
1783
+ if (config.cache) {
1784
+ respHeaders["Cache-Control"] = config.cache;
1785
+ }
1786
+ return c.newResponse(body, resp.status, respHeaders);
1787
+ } catch (e) {
1788
+ console.error(`[Proxy ${config.prefix}]`, e);
1789
+ return c.json({ error: "Proxy request failed" }, 502);
1790
+ }
1791
+ };
1792
+ for (const method of methods) {
1793
+ app[method](pattern, handler);
1794
+ }
1795
+ }
1796
+ }
1797
+ function generateProxyCode(configs) {
1798
+ if (!configs || configs.length === 0) return "";
1799
+ for (const config of configs) {
1800
+ validateConfig(config);
1801
+ }
1802
+ const blocks = [];
1803
+ blocks.push(`
1804
+ // \u2500\u2500\u2500 \u6846\u67B6\u58F0\u660E\u5F0F\u4EE3\u7406\u8DEF\u7531 \u2500\u2500\u2500
1805
+ function _sanitizeProxyPath(raw) {
1806
+ if (raw.startsWith("//")) return null;
1807
+ return raw.startsWith("/") ? raw : "/" + raw;
1808
+ }
1809
+ `);
1810
+ for (const config of configs) {
1811
+ const methods = config.methods ?? ["all"];
1812
+ const pattern = `"${config.prefix}/*"`;
1813
+ const headersJson = JSON.stringify(config.headers ?? {});
1814
+ const cacheStr = config.cache ? JSON.stringify(config.cache) : "null";
1815
+ const redirect = config.followRedirects ? '"follow"' : '"manual"';
1816
+ let authCode = "";
1817
+ if (config.auth) {
1818
+ const envKey = JSON.stringify(config.auth.envKey);
1819
+ const prefix = config.auth.type === "bearer" ? "Bearer " : "Basic ";
1820
+ authCode = `
1821
+ const _token = (typeof process !== "undefined" && process.env && process.env[${envKey}]) || "";
1822
+ if (_token) _headers.Authorization = "${prefix}" + _token;`;
1823
+ }
1824
+ const handlerCode = `async (c) => {
1825
+ const _sub = _sanitizeProxyPath(c.req.path.replace(${JSON.stringify(
1826
+ config.prefix
1827
+ )}, ""));
1828
+ if (!_sub) return c.text("Invalid path", 400);
1829
+ const _target = new URL(_sub, ${JSON.stringify(config.target)});
1830
+ const _reqUrl = new URL(c.req.url);
1831
+ _reqUrl.searchParams.forEach((v, k) => _target.searchParams.set(k, v));
1832
+ const _headers = ${headersJson};${authCode}
1833
+ try {
1834
+ const _resp = await fetch(_target.toString(), { headers: _headers, redirect: ${redirect} });
1835
+ const _body = await _resp.text();
1836
+ const _rh = { "Content-Type": _resp.headers.get("Content-Type") || "application/json" };
1837
+ if (${cacheStr}) _rh["Cache-Control"] = ${cacheStr};
1838
+ return c.newResponse(_body, _resp.status, _rh);
1839
+ } catch (_e) {
1840
+ console.error("[Proxy ${config.prefix}]", _e);
1841
+ return c.json({ error: "Proxy request failed" }, 502);
1842
+ }
1843
+ }`;
1844
+ for (const method of methods) {
1845
+ blocks.push(`app.${method}(${pattern}, ${handlerCode});`);
1846
+ }
1847
+ }
1848
+ return blocks.join("\n");
1849
+ }
1850
+
1735
1851
  // ../server/src/adapters/shared.ts
1736
1852
  var BUILD_TOOL_EXTERNALS = [
1737
1853
  "vite",
@@ -1814,6 +1930,7 @@ function matchRenderMode(url) {
1814
1930
  }
1815
1931
 
1816
1932
  const app = new Hono();
1933
+ ${generateProxyCode(ctx.proxies ?? [])}
1817
1934
  ${setupCall}
1818
1935
  ${opts.platformMiddleware ?? ""}
1819
1936
 
@@ -2673,6 +2790,7 @@ async function createServer(config = {}) {
2673
2790
  defaultLocale,
2674
2791
  port = Number(process.env.PORT) || 3e3,
2675
2792
  setup,
2793
+ proxies,
2676
2794
  ssr
2677
2795
  } = config;
2678
2796
  const root = rootOverride ?? process.cwd();
@@ -2709,6 +2827,9 @@ async function createServer(config = {}) {
2709
2827
  });
2710
2828
  }
2711
2829
  const app = new import_hono3.Hono();
2830
+ if (proxies?.length) {
2831
+ registerProxyRoutes(app, proxies);
2832
+ }
2712
2833
  if (setup) {
2713
2834
  await setup(app);
2714
2835
  }
@@ -2765,6 +2886,7 @@ function finesoftFrontViteConfig(options = {}) {
2765
2886
  let resolvedCommand;
2766
2887
  let resolvedResolve;
2767
2888
  let resolvedCss;
2889
+ const CSS_EXTENSIONS = /\.(css|scss|less|sass|styl|stylus|pcss|postcss)($|\?)/;
2768
2890
  return {
2769
2891
  name: "finesoft-front",
2770
2892
  config(userConfig, env) {
@@ -2784,6 +2906,82 @@ function finesoftFrontViteConfig(options = {}) {
2784
2906
  resolvedCss = config.css;
2785
2907
  root = config.root;
2786
2908
  },
2909
+ /**
2910
+ * Dev 模式 CSS 内联 — 消除 SSR 首屏布局抖动
2911
+ *
2912
+ * Vite dev 模式下,global.scss 等非组件 CSS 通过 JS 模块系统异步加载,
2913
+ * 导致 SSR HTML 初次渲染缺少布局关键样式(box-sizing、flex 布局、padding-top 等)。
2914
+ *
2915
+ * 此 hook 在 HTML 模板变换阶段(SSR 渲染之前):
2916
+ * 1. 找到浏览器入口脚本(排除 /@vite/client 等内部脚本)
2917
+ * 2. 编译入口脚本,填充 Vite 模块图
2918
+ * 3. 遍历模块图收集所有 CSS 依赖(排除 .svelte 组件 CSS,由 SSR 渲染自行处理)
2919
+ * 4. 通过 ssrLoadModule 获取编译后 CSS(SCSS→CSS)
2920
+ * 5. 注入 <style data-vite-dev-id> 标签到 <head>
2921
+ *
2922
+ * data-vite-dev-id 确保 Vite HMR 客户端复用已有标签,避免重复注入。
2923
+ */
2924
+ transformIndexHtml: {
2925
+ order: "pre",
2926
+ async handler(html, ctx) {
2927
+ const server = ctx.server;
2928
+ if (!server) return;
2929
+ const urlPath = (ctx.originalUrl || ctx.path || "").split(
2930
+ "?"
2931
+ )[0];
2932
+ if (/\.\w+$/.test(urlPath) && !urlPath.endsWith(".html")) {
2933
+ return;
2934
+ }
2935
+ const scripts = [
2936
+ ...html.matchAll(
2937
+ /<script\b[^>]*\bsrc=["']([^"']+)["'][^>]*>/g
2938
+ )
2939
+ ];
2940
+ const appEntry = scripts.find((m) => !m[1].startsWith("/@"));
2941
+ if (!appEntry) return;
2942
+ const browserEntry = appEntry[1];
2943
+ try {
2944
+ await server.transformRequest(browserEntry);
2945
+ } catch {
2946
+ return;
2947
+ }
2948
+ const cssUrls = [];
2949
+ const visited = /* @__PURE__ */ new Set();
2950
+ function walk(mod) {
2951
+ if (!mod?.url || visited.has(mod.url)) return;
2952
+ visited.add(mod.url);
2953
+ if (CSS_EXTENSIONS.test(mod.url) && !mod.url.includes(".svelte")) {
2954
+ cssUrls.push(mod.url);
2955
+ }
2956
+ if (mod.importedModules) {
2957
+ for (const imported of mod.importedModules) {
2958
+ walk(imported);
2959
+ }
2960
+ }
2961
+ }
2962
+ const mg = server.moduleGraph;
2963
+ const browserMod = await mg.getModuleByUrl(browserEntry);
2964
+ if (browserMod) walk(browserMod);
2965
+ if (cssUrls.length === 0) return;
2966
+ const tags = [];
2967
+ for (const url of cssUrls) {
2968
+ try {
2969
+ const mod = await server.ssrLoadModule(url);
2970
+ const css = mod?.default;
2971
+ if (typeof css === "string" && css.length > 0) {
2972
+ tags.push({
2973
+ tag: "style",
2974
+ attrs: { "data-vite-dev-id": url },
2975
+ children: css,
2976
+ injectTo: "head"
2977
+ });
2978
+ }
2979
+ } catch {
2980
+ }
2981
+ }
2982
+ return tags;
2983
+ }
2984
+ },
2787
2985
  // ─── Dev ───────────────────────────────────────────────
2788
2986
  configureServer(server) {
2789
2987
  return async () => {
@@ -2797,6 +2995,9 @@ function finesoftFrontViteConfig(options = {}) {
2797
2995
  "@hono/node-server"
2798
2996
  );
2799
2997
  const app = new HonoClass();
2998
+ if (options.proxies?.length) {
2999
+ registerProxyRoutes(app, options.proxies);
3000
+ }
2800
3001
  if (typeof options.setup === "function") {
2801
3002
  await options.setup(app);
2802
3003
  } else if (typeof options.setup === "string") {
@@ -2847,6 +3048,9 @@ function finesoftFrontViteConfig(options = {}) {
2847
3048
  );
2848
3049
  const app = new HonoClass();
2849
3050
  const isrCache = /* @__PURE__ */ new Map();
3051
+ if (options.proxies?.length) {
3052
+ registerProxyRoutes(app, options.proxies);
3053
+ }
2850
3054
  if (typeof options.setup === "function") {
2851
3055
  await options.setup(app);
2852
3056
  } else if (typeof options.setup === "string") {
@@ -3001,6 +3205,7 @@ function finesoftFrontViteConfig(options = {}) {
3001
3205
  defaultLocale,
3002
3206
  templateHtml,
3003
3207
  renderModes: options.renderModes,
3208
+ proxies: options.proxies,
3004
3209
  resolvedResolve,
3005
3210
  resolvedCss,
3006
3211
  vite,
@@ -3058,6 +3263,7 @@ function finesoftFrontViteConfig(options = {}) {
3058
3263
  deserializeServerData,
3059
3264
  detectRuntime,
3060
3265
  finesoftFrontViteConfig,
3266
+ generateProxyCode,
3061
3267
  generateUuid,
3062
3268
  getBaseUrl,
3063
3269
  injectCSRShell,
@@ -3079,6 +3285,7 @@ function finesoftFrontViteConfig(options = {}) {
3079
3285
  registerActionHandlers,
3080
3286
  registerExternalUrlHandler,
3081
3287
  registerFlowActionHandler,
3288
+ registerProxyRoutes,
3082
3289
  removeHost,
3083
3290
  removeQueryParams,
3084
3291
  removeScheme,