@homurajs/app-kit 26.10.14 → 26.10.15

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.
Files changed (2) hide show
  1. package/dist/index.js +46 -1
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -44,6 +44,42 @@ function safeResolve(root, reqPath) {
44
44
  if (abs !== root && !abs.startsWith(rootWithSep)) return null;
45
45
  return abs;
46
46
  }
47
+ /** 支持的压缩编码,按优先级排列(br 压缩率优于 gzip) */
48
+ const COMPRESSED_EXTS = [{
49
+ encoding: "br",
50
+ ext: ".br"
51
+ }, {
52
+ encoding: "gzip",
53
+ ext: ".gz"
54
+ }];
55
+ /**
56
+ * 解析 Accept-Encoding 头,返回客户端支持的编码集合。
57
+ * 不解析 q 值——只要声明了该编码就视为支持(实际场景足够)。
58
+ */
59
+ function parseAcceptEncoding(header) {
60
+ if (!header) return /* @__PURE__ */ new Set();
61
+ const encodings = /* @__PURE__ */ new Set();
62
+ for (const part of header.split(",")) {
63
+ const name = part.trim().split(";")[0]?.trim().toLowerCase();
64
+ if (name) encodings.add(name);
65
+ }
66
+ return encodings;
67
+ }
68
+ /**
69
+ * 尝试找到预压缩侧车文件(.br / .gz)。
70
+ * 返回命中的压缩文件绝对路径与编码名;未命中返回 null。
71
+ */
72
+ function findCompressedSidecar(abs, accepted) {
73
+ for (const { encoding, ext } of COMPRESSED_EXTS) {
74
+ if (!accepted.has(encoding)) continue;
75
+ const compressed = abs + ext;
76
+ if (existsSync(compressed) && statSync(compressed).isFile()) return {
77
+ path: compressed,
78
+ encoding
79
+ };
80
+ }
81
+ return null;
82
+ }
47
83
  /** 静态文件中间件:命中则回文件,未命中 next()(交给 SPA fallback) */
48
84
  function createStaticMiddleware(root) {
49
85
  return async (c, next) => {
@@ -58,8 +94,17 @@ function createStaticMiddleware(root) {
58
94
  return;
59
95
  }
60
96
  const isAsset = urlPath.startsWith("/assets/") || urlPath.includes(".");
61
- c.header("content-type", mimeOf(abs));
97
+ const mime = mimeOf(abs);
98
+ c.header("content-type", mime);
62
99
  c.header("cache-control", isAsset ? "public, max-age=31536000, immutable" : "no-cache");
100
+ const sidecar = findCompressedSidecar(abs, parseAcceptEncoding(c.req.header("accept-encoding")));
101
+ if (sidecar) {
102
+ c.header("content-encoding", sidecar.encoding);
103
+ c.header("vary", "Accept-Encoding");
104
+ if (c.req.method === "HEAD") return c.body(null);
105
+ const stream = Readable.toWeb(createReadStream(sidecar.path));
106
+ return c.body(stream);
107
+ }
63
108
  if (c.req.method === "HEAD") return c.body(null);
64
109
  const stream = Readable.toWeb(createReadStream(abs));
65
110
  return c.body(stream);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@homurajs/app-kit",
3
- "version": "26.10.14",
3
+ "version": "26.10.15",
4
4
  "type": "module",
5
5
  "description": "homura 组装入口:createApp 中间件链装配 + 双形态 RuntimeAdapter(standalone / qw-pages)",
6
6
  "engines": {
@@ -28,7 +28,7 @@
28
28
  "registry": "https://registry.npmjs.org/"
29
29
  },
30
30
  "dependencies": {
31
- "@homurajs/core": "26.10.14",
31
+ "@homurajs/core": "26.10.15",
32
32
  "hono": "^4.10.0"
33
33
  },
34
34
  "peerDependencies": {