@homurajs/app-kit 26.10.13 → 26.10.14
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 +23 -10
- package/dist/adapter-CAQkfPMf.d.ts +28 -0
- package/dist/index.d.ts +10 -7
- package/dist/index.js +26 -8
- package/dist/qw-pages.d.ts +3 -10
- package/dist/qw-pages.js +26 -24
- package/dist/serve.d.ts +10 -1
- package/dist/serve.js +22 -14
- package/package.json +2 -2
- package/dist/adapter-BZXaYf3G.d.ts +0 -20
package/README.md
CHANGED
|
@@ -1,30 +1,43 @@
|
|
|
1
1
|
# @homurajs/app-kit
|
|
2
2
|
|
|
3
|
-
组装入口:`createApp` 渐进式装配(零注入也能跑)+ 双形态 RuntimeAdapter
|
|
3
|
+
组装入口:`createApp` 渐进式装配(零注入也能跑)+ 双形态 RuntimeAdapter(跨形态差异的单点收口)。
|
|
4
4
|
|
|
5
5
|
```ts
|
|
6
6
|
import { createApp } from '@homurajs/app-kit'
|
|
7
7
|
import { Hono } from 'hono'
|
|
8
8
|
|
|
9
9
|
// 最小 App:零能力注入
|
|
10
|
-
const { app, runtime } = createApp({ routes: (deps) => myRoutes(deps) })
|
|
10
|
+
const { app, runtime, healthzPath } = createApp({ routes: (deps) => myRoutes(deps) })
|
|
11
11
|
|
|
12
12
|
// 渐进解锁:注入即启用(实例由 App 构造,app-kit 不硬依赖能力包)
|
|
13
13
|
const composed = createApp({
|
|
14
|
-
auth: { sessions, sessionMiddleware, authRoutes },
|
|
15
|
-
license: { client: license, guard: licenseGuard },
|
|
16
|
-
thirdParty: { platform },
|
|
17
|
-
healthz: { checks: [{ name: 'db', check: () =>
|
|
14
|
+
auth: { sessions, sessionMiddleware, authRoutes }, // 登录链
|
|
15
|
+
license: { client: license, guard: licenseGuard }, // 激活守卫
|
|
16
|
+
thirdParty: { platform }, // deps.platform 暴露给路由
|
|
17
|
+
healthz: { checks: [{ name: 'db', check: () => pingDb() }] },
|
|
18
18
|
routes: myRoutes,
|
|
19
19
|
})
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
固定中间件链:`basePath → request-id/日志 →
|
|
22
|
+
固定中间件链:`basePath → request-id/日志 → healthz(豁免守卫,路径按形态取)→ licenseGuard → sessionMiddleware → authRoutes → App routes → 静态资源 → SPA fallback`
|
|
23
|
+
|
|
24
|
+
**健康检查路径按形态取**(`healthz.path` 可覆盖,缺省来自 `@homurajs/core` 的 `healthzPathFor`):
|
|
25
|
+
standalone 用 `/healthz`;qw-pages 用 `/_healthz`——因为 `/healthz` 被平台 Page Gateway 保留,
|
|
26
|
+
网关自答 `ok` 且请求**不下发到 App 进程**(2026-10-13 实测)。若你自定义了路径,
|
|
27
|
+
`@homurajs/license` 的守卫豁免清单要传同一个值:`licenseGuard(client, { healthzPath })`。
|
|
28
|
+
|
|
29
|
+
双形态启动(**两者都监听端口**,差异只在缺省值):
|
|
23
30
|
|
|
24
|
-
双形态启动:
|
|
25
31
|
```ts
|
|
26
|
-
import { createNodeAdapter } from '@homurajs/app-kit/serve'
|
|
27
|
-
import { createQwPagesAdapter
|
|
32
|
+
import { createNodeAdapter } from '@homurajs/app-kit/serve' // 独立:PORT ?? 3000,assetsDir=dist/client
|
|
33
|
+
import { createQwPagesAdapter } from '@homurajs/app-kit/qw-pages' // qw-pages:PORT ?? 8081 + HOST,assetsDir=client
|
|
28
34
|
```
|
|
29
35
|
|
|
36
|
+
> qw-pages 动态页 = 平台拉起 `node <entrypoint>` 的常驻进程,注入 `PORT`/`HOST` 并把网关流量反代到该 socket。
|
|
37
|
+
> **不存在** `default`/`fetch`/`app` 之类的 serverless 导出约定(实测:导出函数命中 0 次),
|
|
38
|
+
> 旧版的 `exportHandlers` 三重对冲已删除。详见仓库 `docs/specs/module-qw-pages.md`。
|
|
39
|
+
|
|
40
|
+
`trustProxy()` 的用途:qw-pages 恒为 true(网关后、公网 https),standalone 由 `TRUST_PROXY=1` 开启。
|
|
41
|
+
把它接到 `@homurajs/auth` 的 cookie `secure` 选项上,否则 https 站点会发非 Secure cookie。
|
|
42
|
+
|
|
30
43
|
使用示范见模板 `src/server/index.ts` 与 `src/server/qw-pages.ts`。
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Hono } from "hono";
|
|
2
|
+
import { RuntimeTarget } from "@homurajs/core";
|
|
3
|
+
//#region src/runtime/adapter.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* 双形态运行时适配器——跨形态差异的单点收口。
|
|
6
|
+
* 差异项(监听端口/HOST 缺省、trust proxy、静态资源目录)全走 adapter,业务代码零 if-else。
|
|
7
|
+
*
|
|
8
|
+
* 注:qw-pages 的约定已于 2026-10-13 全部实测确认(见 runtime/qw-pages.ts 头部与
|
|
9
|
+
* docs/specs/module-qw-pages.md),本接口不再是「对冲未知」,而是两形态真实差异的最小面。
|
|
10
|
+
* 两个适配器都监听端口——平台形态差异只剩默认值,不存在「平台调用导出函数」的形态。
|
|
11
|
+
*/
|
|
12
|
+
interface RuntimeAdapter {
|
|
13
|
+
target: RuntimeTarget;
|
|
14
|
+
start(app: Hono, opts: {
|
|
15
|
+
port?: number;
|
|
16
|
+
signal?: AbortSignal;
|
|
17
|
+
}): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* 是否在反向代理后(决定 cookie secure 与 X-Forwarded-* 可信度)。
|
|
20
|
+
* qw-pages 恒为 true;standalone 由 TRUST_PROXY=1 开启。
|
|
21
|
+
* 消费方:模板把它接到 @homurajs/auth 的 `secure` 选项上。
|
|
22
|
+
*/
|
|
23
|
+
trustProxy(): boolean;
|
|
24
|
+
/** 静态资源目录:standalone 默认 dist/client,qw-pages 默认 client;均可被 HOMURA_ASSETS_DIR 覆盖 */
|
|
25
|
+
assetsDir(env: NodeJS.ProcessEnv): string;
|
|
26
|
+
}
|
|
27
|
+
//#endregion
|
|
28
|
+
export { RuntimeAdapter as t };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as RuntimeAdapter } from "./adapter-
|
|
1
|
+
import { t as RuntimeAdapter } from "./adapter-CAQkfPMf.js";
|
|
2
2
|
import { Context, Hono, MiddlewareHandler } from "hono";
|
|
3
3
|
import { Logger, RuntimeInfo, RuntimeTarget, detectRuntime } from "@homurajs/core";
|
|
4
4
|
//#region src/create-app.d.ts
|
|
@@ -55,7 +55,13 @@ interface CreateAppOptions {
|
|
|
55
55
|
/** 注入 db/storage 汇总进 deps(供 routes 回调用) */
|
|
56
56
|
db?: Record<string, unknown>;
|
|
57
57
|
storage?: StorageLike;
|
|
58
|
+
/**
|
|
59
|
+
* 健康检查:路径缺省按形态取 core healthzPathFor(target)——
|
|
60
|
+
* qw-pages 上 '/healthz' 被 Page Gateway 保留(网关自答,请求不下发到 App),故用 '/_healthz'。
|
|
61
|
+
* 显式传 path 时,license guard 的豁免清单必须用同一个值(见 license createDefaultBypass)。
|
|
62
|
+
*/
|
|
58
63
|
healthz?: {
|
|
64
|
+
path?: string;
|
|
59
65
|
checks?: HealthCheck[];
|
|
60
66
|
};
|
|
61
67
|
/** 静态资源目录(adapter.assetsDir() 算出);提供则挂 static + SPA fallback */
|
|
@@ -68,10 +74,12 @@ interface CreateAppOptions {
|
|
|
68
74
|
interface HomuraApp {
|
|
69
75
|
app: Hono;
|
|
70
76
|
runtime: RuntimeInfo;
|
|
77
|
+
/** 实际注册的健康检查路径(供 license guard 豁免清单等同源消费) */
|
|
78
|
+
healthzPath: string;
|
|
71
79
|
}
|
|
72
80
|
/**
|
|
73
81
|
* 固定中间件链(模板文档逐环解释):
|
|
74
|
-
* basePath → requestId+访问日志 →
|
|
82
|
+
* basePath → requestId+访问日志 → healthz(注册于 guard 前,天然豁免;路径按形态取)→ licenseGuard
|
|
75
83
|
* → sessionMiddleware → authRoutes → App routes → 静态资源 → SPA fallback → onError
|
|
76
84
|
*/
|
|
77
85
|
export declare function createApp(options?: CreateAppOptions): HomuraApp;
|
|
@@ -84,11 +92,6 @@ declare module 'hono' {
|
|
|
84
92
|
//#region src/static.d.ts
|
|
85
93
|
/** 静态文件中间件:命中则回文件,未命中 next()(交给 SPA fallback) */
|
|
86
94
|
export declare function createStaticMiddleware(root: string): MiddlewareHandler;
|
|
87
|
-
/**
|
|
88
|
-
* SPA fallback(notFound 处理器):
|
|
89
|
-
* - API/鉴权/文件路径 → 404 JSON
|
|
90
|
-
* - 其余 GET(页面请求)→ index.html(前端路由接管)
|
|
91
|
-
*/
|
|
92
95
|
export declare function createSpaFallback(root: string): (c: Context) => Response | Promise<Response>;
|
|
93
96
|
//#endregion
|
|
94
97
|
export { type AppDeps, type AuthInject, type CreateAppOptions, type HealthCheck, type HomuraApp, type LicenseClientLike, type LicenseInject, type PlatformLike, type RuntimeAdapter, type SessionManagerLike, type StorageLike, detectRuntime };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
|
-
import { detectRuntime, detectRuntime as detectRuntime$1 } from "@homurajs/core";
|
|
3
|
+
import { HEALTHZ_PATH, detectRuntime, detectRuntime as detectRuntime$1, healthzPathFor } from "@homurajs/core";
|
|
4
4
|
import { createReadStream, existsSync, statSync } from "node:fs";
|
|
5
5
|
import { extname, join, normalize, resolve, sep } from "node:path";
|
|
6
6
|
import { Readable } from "node:stream";
|
|
@@ -30,9 +30,15 @@ const MIME = {
|
|
|
30
30
|
function mimeOf(file) {
|
|
31
31
|
return MIME[extname(file).toLowerCase()] ?? "application/octet-stream";
|
|
32
32
|
}
|
|
33
|
-
/** 路径穿越防护:解析后必须仍在 root
|
|
33
|
+
/** 路径穿越防护:解析后必须仍在 root 内;畸形百分号编码返回 null(404),不抛(避免 500) */
|
|
34
34
|
function safeResolve(root, reqPath) {
|
|
35
|
-
|
|
35
|
+
let decoded;
|
|
36
|
+
try {
|
|
37
|
+
decoded = decodeURIComponent(reqPath);
|
|
38
|
+
} catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
const rel = decoded.replace(/^\/+/, "");
|
|
36
42
|
const abs = resolve(root, normalize(rel));
|
|
37
43
|
const rootWithSep = root.endsWith(sep) ? root : root + sep;
|
|
38
44
|
if (abs !== root && !abs.startsWith(rootWithSep)) return null;
|
|
@@ -61,13 +67,23 @@ function createStaticMiddleware(root) {
|
|
|
61
67
|
}
|
|
62
68
|
/**
|
|
63
69
|
* SPA fallback(notFound 处理器):
|
|
64
|
-
* - API
|
|
70
|
+
* - API/鉴权/文件/健康检查路径 → 404 JSON
|
|
65
71
|
* - 其余 GET(页面请求)→ index.html(前端路由接管)
|
|
72
|
+
*
|
|
73
|
+
* 保留前缀含两形态的健康检查路径(core HEALTHZ_PATH 同源):qw-pages 用 '/_healthz',
|
|
74
|
+
* standalone 用 '/healthz',两个都列入,避免路径改名后 fallback 把健康检查当页面请求。
|
|
66
75
|
*/
|
|
76
|
+
const RESERVED_PREFIXES = [
|
|
77
|
+
"/api",
|
|
78
|
+
"/auth",
|
|
79
|
+
"/files",
|
|
80
|
+
HEALTHZ_PATH.standalone,
|
|
81
|
+
HEALTHZ_PATH["qw-pages"]
|
|
82
|
+
];
|
|
67
83
|
function createSpaFallback(root) {
|
|
68
84
|
return async (c) => {
|
|
69
85
|
const path = new URL(c.req.url).pathname;
|
|
70
|
-
if (
|
|
86
|
+
if (RESERVED_PREFIXES.some((prefix) => path.startsWith(prefix)) || c.req.method !== "GET") return c.json({
|
|
71
87
|
error: "not_found",
|
|
72
88
|
path
|
|
73
89
|
}, 404);
|
|
@@ -88,7 +104,7 @@ function createSpaFallback(root) {
|
|
|
88
104
|
*/
|
|
89
105
|
/**
|
|
90
106
|
* 固定中间件链(模板文档逐环解释):
|
|
91
|
-
* basePath → requestId+访问日志 →
|
|
107
|
+
* basePath → requestId+访问日志 → healthz(注册于 guard 前,天然豁免;路径按形态取)→ licenseGuard
|
|
92
108
|
* → sessionMiddleware → authRoutes → App routes → 静态资源 → SPA fallback → onError
|
|
93
109
|
*/
|
|
94
110
|
function createApp(options = {}) {
|
|
@@ -119,8 +135,9 @@ function createApp(options = {}) {
|
|
|
119
135
|
durationMs: Date.now() - start
|
|
120
136
|
});
|
|
121
137
|
});
|
|
138
|
+
const healthzPath = options.healthz?.path ?? healthzPathFor(runtime.target);
|
|
122
139
|
const checks = options.healthz?.checks ?? [];
|
|
123
|
-
app.get(
|
|
140
|
+
app.get(healthzPath, async (c) => {
|
|
124
141
|
if (checks.length === 0) return c.json({
|
|
125
142
|
status: "ok",
|
|
126
143
|
runtime: runtime.target
|
|
@@ -169,7 +186,8 @@ function createApp(options = {}) {
|
|
|
169
186
|
});
|
|
170
187
|
return {
|
|
171
188
|
app,
|
|
172
|
-
runtime
|
|
189
|
+
runtime,
|
|
190
|
+
healthzPath
|
|
173
191
|
};
|
|
174
192
|
}
|
|
175
193
|
//#endregion
|
package/dist/qw-pages.d.ts
CHANGED
|
@@ -1,13 +1,6 @@
|
|
|
1
|
-
import { t as RuntimeAdapter } from "./adapter-
|
|
2
|
-
import { Hono } from "hono";
|
|
1
|
+
import { t as RuntimeAdapter } from "./adapter-CAQkfPMf.js";
|
|
3
2
|
//#region src/runtime/qw-pages.d.ts
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
default: (req: Request) => Promise<Response>;
|
|
7
|
-
fetch: (req: Request) => Promise<Response>;
|
|
8
|
-
app: Hono;
|
|
9
|
-
}
|
|
3
|
+
/** qw-pages 推荐端口(实测平台注入 PORT=8081;9000 被平台禁用) */
|
|
4
|
+
export declare const QW_PAGES_DEFAULT_PORT = 8081;
|
|
10
5
|
export declare function createQwPagesAdapter(): RuntimeAdapter;
|
|
11
|
-
/** 三重导出对冲:default / fetch / app 同时给,覆盖 qw-pages 未知入口约定 */
|
|
12
|
-
export declare function exportHandlers(app: Hono): ExportHandlers;
|
|
13
6
|
//#endregion
|
package/dist/qw-pages.js
CHANGED
|
@@ -1,34 +1,36 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { listenWithNodeServer } from "./serve.js";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
3
|
//#region src/runtime/qw-pages.ts
|
|
4
4
|
/**
|
|
5
|
-
* qw-pages
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* qw-pages 适配器。
|
|
6
|
+
*
|
|
7
|
+
* 2026-10-13 在真实 qw-pages 动态页上实测(探针 + 双路计数归因,定论见 docs/specs/module-qw-pages.md):
|
|
8
|
+
*
|
|
9
|
+
* 1. **入口约定 = 常驻 Node 进程**:平台注入 PORT/HOST 并把网关流量反代到 App 自监听的 socket。
|
|
10
|
+
* 探针同时挂了 serve() 与 default/fetch/app 三重导出,全程 `serve` 命中 47 次、`handler` 命中 0 次
|
|
11
|
+
* → 平台从不调用导出函数。故原 exportHandlers 三重对冲已删除,本适配器改为与 standalone 共用监听实现。
|
|
12
|
+
* 2. **HOST 实测注入为 127.0.0.1**(网关与 App 同网络命名空间),故缺省绑 loopback 而非全网卡。
|
|
13
|
+
* 3. **发布根目录即产物根**(实测 cwd=/opt/qwenwork-page/app),静态目录默认 `client`,
|
|
14
|
+
* 不是 standalone 的 `dist/client`。不再用 import.meta.url 回溯定位:按 npm 依赖消费时
|
|
15
|
+
* 该算法会落到 `node_modules/@homurajs/app-kit/client`(实测证伪,目录不存在)。
|
|
16
|
+
* 4. **'/healthz' 被 Page Gateway 保留**(网关自答 `ok`,请求不下发到进程),
|
|
17
|
+
* 健康检查路径统一取 core 的 healthzPathFor('qw-pages') = '/_healthz'。
|
|
18
|
+
* 5. 平台对 HTML 响应做后处理(注入页面 chrome 脚本并覆盖 Cache-Control),非 HTML 资源原样透传,
|
|
19
|
+
* 故 static.ts 的缓存头策略在 Pages 上仍然有效,但不要依赖 HTML 的字节长度/ETag。
|
|
9
20
|
*/
|
|
21
|
+
/** qw-pages 推荐端口(实测平台注入 PORT=8081;9000 被平台禁用) */
|
|
22
|
+
const QW_PAGES_DEFAULT_PORT = 8081;
|
|
10
23
|
function createQwPagesAdapter() {
|
|
11
24
|
return {
|
|
12
25
|
target: "qw-pages",
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
26
|
+
start: (app, opts) => listenWithNodeServer(app, {
|
|
27
|
+
port: opts.port ?? Number(process.env.PORT ?? 8081),
|
|
28
|
+
hostname: process.env.HOST ?? "127.0.0.1",
|
|
29
|
+
signal: opts.signal
|
|
30
|
+
}),
|
|
16
31
|
trustProxy: () => true,
|
|
17
|
-
assetsDir: (env) =>
|
|
18
|
-
if (env.HOMURA_ASSETS_DIR) return resolve(env.HOMURA_ASSETS_DIR);
|
|
19
|
-
const here = dirname(fileURLToPath(import.meta.url));
|
|
20
|
-
return resolve(here, "../client");
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
/** 三重导出对冲:default / fetch / app 同时给,覆盖 qw-pages 未知入口约定 */
|
|
25
|
-
function exportHandlers(app) {
|
|
26
|
-
const handler = (req) => Promise.resolve(app.fetch(req));
|
|
27
|
-
return {
|
|
28
|
-
default: handler,
|
|
29
|
-
fetch: handler,
|
|
30
|
-
app
|
|
32
|
+
assetsDir: (env) => resolve(env.HOMURA_ASSETS_DIR ?? "client")
|
|
31
33
|
};
|
|
32
34
|
}
|
|
33
35
|
//#endregion
|
|
34
|
-
export {
|
|
36
|
+
export { QW_PAGES_DEFAULT_PORT, createQwPagesAdapter };
|
package/dist/serve.d.ts
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
import { t as RuntimeAdapter } from "./adapter-
|
|
1
|
+
import { t as RuntimeAdapter } from "./adapter-CAQkfPMf.js";
|
|
2
|
+
import { Hono } from "hono";
|
|
2
3
|
//#region src/runtime/node.d.ts
|
|
4
|
+
export interface ListenOptions {
|
|
5
|
+
port?: number;
|
|
6
|
+
/** 缺省读 process.env.HOST,再缺省 0.0.0.0 */
|
|
7
|
+
hostname?: string;
|
|
8
|
+
signal?: AbortSignal;
|
|
9
|
+
}
|
|
10
|
+
/** 共享监听实现:读 PORT/HOST,支持 AbortSignal 优雅关闭 */
|
|
11
|
+
export declare function listenWithNodeServer(app: Hono, opts?: ListenOptions): Promise<void>;
|
|
3
12
|
export declare function createNodeAdapter(): RuntimeAdapter;
|
|
4
13
|
//#endregion
|
package/dist/serve.js
CHANGED
|
@@ -4,26 +4,34 @@ import { resolve } from "node:path";
|
|
|
4
4
|
* standalone 适配器:@hono/node-server 端口监听。
|
|
5
5
|
* 可选 peer:@hono/node-server(动态 import,不装也能 typecheck 其余部分)。
|
|
6
6
|
* 静态资源由 createApp 的 staticDir 挂载(见 static.ts),adapter 只负责监听与 trust proxy。
|
|
7
|
+
*
|
|
8
|
+
* listenWithNodeServer 同时被 qw-pages 适配器复用:2026-10-13 线上实测确认,qw-pages 动态页
|
|
9
|
+
* 与 standalone 一样是「平台拉起 node <entrypoint>,网关反代到 App 自监听的 socket」,
|
|
10
|
+
* 并不存在 serverless fetch-handler 入口约定,故两形态共用同一套监听实现,只差端口/HOST 默认值。
|
|
7
11
|
*/
|
|
12
|
+
/** 共享监听实现:读 PORT/HOST,支持 AbortSignal 优雅关闭 */
|
|
13
|
+
async function listenWithNodeServer(app, opts = {}) {
|
|
14
|
+
const { serve } = await import("@hono/node-server");
|
|
15
|
+
const port = opts.port ?? Number(process.env.PORT ?? 3e3);
|
|
16
|
+
const hostname = opts.hostname ?? process.env.HOST ?? "0.0.0.0";
|
|
17
|
+
return new Promise((resolveStart) => {
|
|
18
|
+
const server = serve({
|
|
19
|
+
fetch: app.fetch,
|
|
20
|
+
port,
|
|
21
|
+
hostname
|
|
22
|
+
}, () => {
|
|
23
|
+
resolveStart();
|
|
24
|
+
});
|
|
25
|
+
opts.signal?.addEventListener("abort", () => server.close());
|
|
26
|
+
});
|
|
27
|
+
}
|
|
8
28
|
function createNodeAdapter() {
|
|
9
29
|
return {
|
|
10
30
|
target: "standalone",
|
|
11
|
-
|
|
12
|
-
const { serve } = await import("@hono/node-server");
|
|
13
|
-
const port = opts.port ?? Number(process.env.PORT ?? 3e3);
|
|
14
|
-
return new Promise((resolveStart) => {
|
|
15
|
-
const server = serve({
|
|
16
|
-
fetch: app.fetch,
|
|
17
|
-
port
|
|
18
|
-
}, () => {
|
|
19
|
-
resolveStart();
|
|
20
|
-
});
|
|
21
|
-
opts.signal?.addEventListener("abort", () => server.close());
|
|
22
|
-
});
|
|
23
|
-
},
|
|
31
|
+
start: (app, opts) => listenWithNodeServer(app, opts),
|
|
24
32
|
trustProxy: () => process.env.TRUST_PROXY === "1",
|
|
25
33
|
assetsDir: (env) => resolve(env.HOMURA_ASSETS_DIR ?? "dist/client")
|
|
26
34
|
};
|
|
27
35
|
}
|
|
28
36
|
//#endregion
|
|
29
|
-
export { createNodeAdapter };
|
|
37
|
+
export { createNodeAdapter, listenWithNodeServer };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@homurajs/app-kit",
|
|
3
|
-
"version": "26.10.
|
|
3
|
+
"version": "26.10.14",
|
|
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.
|
|
31
|
+
"@homurajs/core": "26.10.14",
|
|
32
32
|
"hono": "^4.10.0"
|
|
33
33
|
},
|
|
34
34
|
"peerDependencies": {
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { Hono } from "hono";
|
|
2
|
-
import { RuntimeTarget } from "@homurajs/core";
|
|
3
|
-
//#region src/runtime/adapter.d.ts
|
|
4
|
-
/**
|
|
5
|
-
* 双形态运行时适配器——qw-pages 全部未知约定的单点收口。
|
|
6
|
-
* 跨形态差异(trust proxy、静态服务、端口)全走 adapter,业务代码零 if-else。
|
|
7
|
-
*/
|
|
8
|
-
interface RuntimeAdapter {
|
|
9
|
-
target: RuntimeTarget;
|
|
10
|
-
start(app: Hono, opts: {
|
|
11
|
-
port?: number;
|
|
12
|
-
signal?: AbortSignal;
|
|
13
|
-
}): Promise<void>;
|
|
14
|
-
/** qw-pages 在网关后 → 信任 X-Forwarded-*(影响 cookie secure/redirect URL) */
|
|
15
|
-
trustProxy(): boolean;
|
|
16
|
-
/** 默认 dist/client,可被 HOMURA_ASSETS_DIR 覆盖 */
|
|
17
|
-
assetsDir(env: NodeJS.ProcessEnv): string;
|
|
18
|
-
}
|
|
19
|
-
//#endregion
|
|
20
|
-
export { RuntimeAdapter as t };
|