@ubean/app 0.1.13 → 0.2.0

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 (3) hide show
  1. package/dist/index.d.ts +202 -4
  2. package/dist/index.js +143 -8
  3. package/package.json +12 -15
package/dist/index.d.ts CHANGED
@@ -1,8 +1,168 @@
1
1
  import { Context, Hono, MiddlewareHandler } from "hono";
2
- import { RegisterOptions, RouteRegistrar } from "@ubean/api-routes";
2
+ import { RegisterOptions, RouteRegistrar } from "@ubean/routes";
3
+ import { ComposedHandler, ComposedHandler as ComposedHandler$1, RouteMeta, RouteMeta as RouteMeta$1, RouteRule, RouteRule as RouteRule$1, UbeanEnv, UbeanEnv as UbeanEnv$1, UbeanMiddleware, UbeanMiddleware as UbeanMiddleware$1 } from "@ubean/shared";
3
4
  import { Hookable } from "hookable";
4
- import { ScannedApiRoute, ScannedApiRoute as ScannedApiRoute$1, ScannedCronTask, ScannedLayout, ScannedLayout as ScannedLayout$1, ScannedMiddleware, ScannedMiddleware as ScannedMiddleware$1, ScannedPageRoute, ScannedPageRoute as ScannedPageRoute$1 } from "@ubean/routing";
5
- import { ComposedHandler, ComposedHandler as ComposedHandler$1, RouteMeta, RouteMeta as RouteMeta$1, RouteRule, RouteRule as RouteRule$1, UbeanEnv, UbeanEnv as UbeanEnv$1, UbeanMiddleware, UbeanMiddleware as UbeanMiddleware$1 } from "@ubean/types";
5
+ import { ScannedApiRoute, ScannedApiRoute as ScannedApiRoute$1, ScannedCronTask, ScannedLayout, ScannedLayout as ScannedLayout$1, ScannedMiddleware, ScannedMiddleware as ScannedMiddleware$1, ScannedPageRoute, ScannedPageRoute as ScannedPageRoute$1 } from "@ubean/scan";
6
+ //#region src/hooks.d.ts
7
+ /**
8
+ * 请求事件 —— 传递给 `handle` 和 `handleError` 的上下文对象。
9
+ */
10
+ interface HandleEvent {
11
+ /** 原始 Request 对象。 */
12
+ request: Request;
13
+ /** Hono Context(含 route params、variables、env 等)。 */
14
+ context: Context<UbeanEnv$1>;
15
+ /** 客户端 IP(从 `x-forwarded-for` 或 `x-real-ip` 提取)。 */
16
+ clientAddress: string;
17
+ /** 请求 ID(从 `x-request-id` header 或自动生成)。 */
18
+ requestId: string;
19
+ }
20
+ /**
21
+ * `handle` hook 的输入参数。
22
+ */
23
+ interface HandleInput {
24
+ /** 请求事件。 */
25
+ event: HandleEvent;
26
+ /** resolve 函数:执行正常的请求处理流程,返回 Response。 */
27
+ resolve: (event: HandleEvent) => Promise<Response>;
28
+ }
29
+ /**
30
+ * 全局请求处理 hook。
31
+ *
32
+ * 类似 Hono 中间件,但作用于**所有**请求(含 404、静态资源、错误响应)。
33
+ * 可用于:统一添加 header、请求日志、A/B 测试、认证预处理等。
34
+ *
35
+ * @example
36
+ * ```ts
37
+ * handle: async ({ event, resolve }) => {
38
+ * const start = Date.now();
39
+ * const response = await resolve(event);
40
+ * response.headers.set('X-Response-Time', `${Date.now() - start}ms`);
41
+ * return response;
42
+ * }
43
+ * ```
44
+ */
45
+ type Handle = (input: HandleInput) => Promise<Response>;
46
+ /**
47
+ * `handleFetch` hook 的输入参数。
48
+ */
49
+ interface HandleFetchInput {
50
+ /** 原始 fetch Request。 */
51
+ request: Request;
52
+ /** 内部 fetch 函数:执行实际的 HTTP 请求(进程内调度或网络请求)。 */
53
+ fetch: (request: Request) => Promise<Response>;
54
+ /** 调用 fetch 的服务端上下文(可能为 undefined)。 */
55
+ serverContext?: Context<UbeanEnv$1>;
56
+ }
57
+ /**
58
+ * 服务端 fetch 拦截 hook。
59
+ *
60
+ * 拦截服务端代码中的 `fetch()` 调用(含 ubean 的 `internalFetch`),
61
+ * 可修改请求头、URL、注入认证 token 等。
62
+ *
63
+ * @example
64
+ * ```ts
65
+ * handleFetch: async ({ request, fetch }) => {
66
+ * // 为所有服务端 fetch 添加内部 API token
67
+ * request.headers.set('X-Internal-Auth', process.env.INTERNAL_TOKEN);
68
+ * return fetch(request);
69
+ * }
70
+ * ```
71
+ */
72
+ type HandleFetch = (input: HandleFetchInput) => Promise<Response>;
73
+ /**
74
+ * `handleError` hook 的输入参数。
75
+ */
76
+ interface HandleErrorInput {
77
+ /** 请求事件。 */
78
+ event: HandleEvent;
79
+ /** 捕获的错误。 */
80
+ error: unknown;
81
+ /** HTTP 状态码(500/404 等)。 */
82
+ status: number;
83
+ /** 错误消息(已脱敏,适合展示给用户)。 */
84
+ message: string;
85
+ }
86
+ /**
87
+ * 全局错误处理 hook。
88
+ *
89
+ * 在未捕获错误发生时调用,可用于:日志记录、错误上报(Sentry 等)、
90
+ * 自定义错误响应(需在 `handle` 中实现)。
91
+ *
92
+ * 注意:此 hook 仅用于**副作用**(日志/上报),不影响返回给客户端的
93
+ * 错误响应。要自定义错误响应,请使用 `handle` 拦截 5xx 响应。
94
+ *
95
+ * @example
96
+ * ```ts
97
+ * handleError: async ({ event, error, status, message }) => {
98
+ * Sentry.captureException(error, {
99
+ * tags: { status, path: event.request.url },
100
+ * });
101
+ * }
102
+ * ```
103
+ */
104
+ type HandleError = (input: HandleErrorInput) => void | Promise<void>;
105
+ /**
106
+ * 全局 hooks 集合。
107
+ */
108
+ interface GlobalHooks {
109
+ /** 请求处理 hook。 */
110
+ handle?: Handle;
111
+ /** 服务端 fetch 拦截 hook。 */
112
+ handleFetch?: HandleFetch;
113
+ /** 错误处理 hook。 */
114
+ handleError?: HandleError;
115
+ }
116
+ /**
117
+ * 设置全局 hooks。
118
+ *
119
+ * 由 `applyServerConfig` 在应用启动时调用。
120
+ */
121
+ declare function setGlobalHooks(hooks: GlobalHooks): void;
122
+ /**
123
+ * 获取全局 hooks。
124
+ */
125
+ declare function getGlobalHooks(): GlobalHooks;
126
+ /**
127
+ * 清空全局 hooks(测试用)。
128
+ */
129
+ declare function clearGlobalHooks(): void;
130
+ /**
131
+ * 从 Hono Context 构建请求事件。
132
+ */
133
+ declare function createHandleEvent(c: Context<UbeanEnv$1>): HandleEvent;
134
+ /**
135
+ * 从错误中提取安全消息(移除敏感信息)。
136
+ *
137
+ * - 404 → "Not Found"
138
+ * - 5xx → "Internal Server Error"(生产环境不暴露内部错误详情,
139
+ * 也不区分 Error / 非 Error 类型,避免信息泄漏)
140
+ * - 4xx(非 404)→ 返回 `error.message`(仅当 error 是 Error 实例),
141
+ * 否则返回 "Bad Request"
142
+ */
143
+ declare function extractErrorMessage(error: unknown, status: number): string;
144
+ /**
145
+ * 包装 resolve 函数:将 Hono 的请求处理流程封装为 `resolve(event)` 调用。
146
+ *
147
+ * 返回一个函数,调用后会执行 `next()` 让 Hono 处理请求,然后返回 `c.res`。
148
+ */
149
+ declare function wrapResolve(c: Context<UbeanEnv$1>, next: () => Promise<void>): (event: HandleEvent) => Promise<Response>;
150
+ /**
151
+ * 应用 `handle` hook:如果已注册,则用 hook 包裹请求处理流程。
152
+ *
153
+ * 返回 `true` 表示 hook 已处理请求(调用方应跳过后续中间件),
154
+ * `false` 表示无 hook 或 hook 已调用 resolve。
155
+ */
156
+ declare function applyHandleHook(c: Context<UbeanEnv$1>, next: () => Promise<void>): Promise<boolean>;
157
+ /**
158
+ * 应用 `handleFetch` hook:如果已注册,则用 hook 包裹 fetch 调用。
159
+ */
160
+ declare function applyHandleFetchHook(request: Request, defaultFetch: (request: Request) => Promise<Response>, serverContext?: Context<UbeanEnv$1>): Promise<Response>;
161
+ /**
162
+ * 应用 `handleError` hook:如果已注册,则调用它。
163
+ */
164
+ declare function applyHandleErrorHook(c: Context<UbeanEnv$1>, error: unknown, status: number): Promise<void>;
165
+ //#endregion
6
166
  //#region src/define-server.d.ts
7
167
  /**
8
168
  * Apply a resolved server config to a `UbeanApp` instance.
@@ -11,6 +171,7 @@ import { ComposedHandler, ComposedHandler as ComposedHandler$1, RouteMeta, Route
11
171
  * - `plugins` are available when `init()` calls `setup` / `ready`
12
172
  * - `hooks` are registered before `init()` fires `app:created` etc.
13
173
  * - `onAppCreate` runs before route registration
174
+ * - `globalHooks` (handle/handleFetch/handleError) are set before any request
14
175
  *
15
176
  * `onServerReady` is NOT called here — the caller must invoke it after
16
177
  * `app.init()` completes.
@@ -41,6 +202,14 @@ interface DefineServerOptions {
41
202
  * `middleware:register`, `error`.
42
203
  */
43
204
  hooks?: ServerHooks;
205
+ /**
206
+ * P9-09: Global hooks (SvelteKit-style `handle`/`handleFetch`/`handleError`).
207
+ *
208
+ * - `handle` wraps every request (including 404 and static assets)
209
+ * - `handleFetch` intercepts server-side `fetch()` calls
210
+ * - `handleError` is called on uncaught errors for logging/reporting
211
+ */
212
+ globalHooks?: GlobalHooks;
44
213
  /**
45
214
  * Called after `UbeanApp` is created but before `app.init()`.
46
215
  * Useful for initializing databases, external service connections, etc.
@@ -58,6 +227,7 @@ interface DefineServerOptions {
58
227
  interface ResolvedServerConfig {
59
228
  plugins: UbeanAppPlugin[];
60
229
  hooks: ServerHooks;
230
+ globalHooks?: GlobalHooks;
61
231
  onAppCreate?: (app: UbeanApp) => void | Promise<void>;
62
232
  onServerReady?: (app: UbeanApp) => void | Promise<void>;
63
233
  }
@@ -84,6 +254,16 @@ interface ResolvedServerConfig {
84
254
  * hooks: {
85
255
  * 'request:start': (c) => { console.log(c.req.method, c.req.path); }
86
256
  * },
257
+ * globalHooks: {
258
+ * handle: async ({ event, resolve }) => {
259
+ * const response = await resolve(event);
260
+ * response.headers.set('X-Custom', 'ubean');
261
+ * return response;
262
+ * },
263
+ * handleError: async ({ error, status }) => {
264
+ * console.error(`[${status}]`, error);
265
+ * }
266
+ * },
87
267
  * onAppCreate: async (app) => { /* init db *\/ },
88
268
  * onServerReady: async (app) => { /* start workers *\/ }
89
269
  * });
@@ -158,6 +338,18 @@ interface UbeanAppOptions {
158
338
  pageAssetTags?: PageAssetTags;
159
339
  /** 不进行 SSR 的路由模式列表(glob),匹配的页面走 CSR */
160
340
  ssrExclude?: string[];
341
+ /**
342
+ * 启用流式 SSR。`true` 时页面响应以 `ReadableStream` 分块输出
343
+ * (头部先发送,app HTML 边渲染边输出),改善 TTFB/LCP。
344
+ * renderer 不支持流式时自动降级为缓冲渲染。
345
+ */
346
+ streaming?: boolean;
347
+ /**
348
+ * 爬虫降级(P9-24):当 `streaming` 启用且检测到爬虫/社交预览 UA 时,
349
+ * 自动降级为缓冲渲染以保证 metadata 出现在初始 `<head>`。
350
+ * 默认 `true`。设为 `false` 可禁用爬虫检测(不推荐)。
351
+ */
352
+ botFallback?: boolean;
161
353
  publicDir?: string;
162
354
  healthEndpoint?: boolean;
163
355
  openAPI?: boolean | {
@@ -174,6 +366,12 @@ interface UbeanAppOptions {
174
366
  };
175
367
  /** `pages/404.vue` 自动检测的 404 页面,注册为 Hono 兜底处理器 */
176
368
  notFoundPage?: ScannedPageRoute$1;
369
+ /**
370
+ * Pre-rendered no-FOUC color-mode script (from `getColorModeScript`).
371
+ * Injected into `<head>` of every SSR/prerendered HTML response. Covers
372
+ * the SSG/prerender path that bypasses Vite's `transformIndexHtml`.
373
+ */
374
+ colorModeScript?: string;
177
375
  }
178
376
  interface UbeanAppPlugin {
179
377
  name: string;
@@ -212,4 +410,4 @@ interface AppPlugin {
212
410
  }
213
411
  declare function createUbeanApp(options?: UbeanAppOptions): UbeanApp;
214
412
  //#endregion
215
- export { type AppPlugin, type ComposedHandler, type DefineServerOptions, type PageAssetTags, type PageRenderer, type RegisterOptions, type ResolvedServerConfig, type RouteMeta, type RouteRegistrar, type RouteRule, type ScannedApiRoute, type ScannedLayout, type ScannedMiddleware, type ScannedPageRoute, type ServerHooks, UbeanApp, type UbeanAppOptions, type UbeanAppPlugin, type UbeanEnv, type UbeanMiddleware, type UbeanRuntimeHooks, applyServerConfig, createDefaultServerConfig, createUbeanApp, defineServer, mergeServerConfigs };
413
+ export { type AppPlugin, type ComposedHandler, type DefineServerOptions, type GlobalHooks, type Handle, type HandleError, type HandleErrorInput, type HandleEvent, type HandleFetch, type HandleFetchInput, type HandleInput, type PageAssetTags, type PageRenderer, type RegisterOptions, type ResolvedServerConfig, type RouteMeta, type RouteRegistrar, type RouteRule, type ScannedApiRoute, type ScannedLayout, type ScannedMiddleware, type ScannedPageRoute, type ServerHooks, UbeanApp, type UbeanAppOptions, type UbeanAppPlugin, type UbeanEnv, type UbeanMiddleware, type UbeanRuntimeHooks, applyHandleErrorHook, applyHandleFetchHook, applyHandleHook, applyServerConfig, clearGlobalHooks, createDefaultServerConfig, createHandleEvent, createUbeanApp, defineServer, extractErrorMessage, getGlobalHooks, mergeServerConfigs, setGlobalHooks, wrapResolve };
package/dist/index.js CHANGED
@@ -1,11 +1,117 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { Hono } from "hono";
3
- import { createRouteRulesMiddleware, registerOpenAPIRoutes, registerRoutes, setInternalFetcher } from "@ubean/api-routes";
4
- import { UbeanError, errorToResponse, isUbeanError } from "@ubean/error";
5
- import { createCacheMiddleware, createMemoryStore, createWebSocketMiddleware, resolveRouteCacheRules, serveStatic, useCacheStore } from "@ubean/server";
3
+ import { ACTIONS_ENDPOINT, createActionsMiddleware } from "@ubean/actions";
4
+ import { createRouteRulesMiddleware, registerOpenAPIRoutes, registerRoutes, setInternalFetcher } from "@ubean/routes";
5
+ import { UbeanError, errorToResponse, isUbeanError } from "@ubean/shared";
6
+ import { SERVER_COMPONENT_ENDPOINT, createServerComponentMiddleware } from "@ubean/islands/server";
7
+ import { createCacheMiddleware, createMemoryStore, resolveRouteCacheRules, useCacheStore } from "@ubean/server/cache";
8
+ import { serveStatic } from "@ubean/server/static";
9
+ import { createWebSocketMiddleware } from "@ubean/server/realtime";
6
10
  import { requestId } from "hono/request-id";
7
11
  import { createHooks } from "hookable";
8
12
  import { isAbsolute, join } from "pathe";
13
+ //#region src/hooks.ts
14
+ let globalHooks = {};
15
+ /**
16
+ * 设置全局 hooks。
17
+ *
18
+ * 由 `applyServerConfig` 在应用启动时调用。
19
+ */
20
+ function setGlobalHooks(hooks) {
21
+ globalHooks = { ...hooks };
22
+ }
23
+ /**
24
+ * 获取全局 hooks。
25
+ */
26
+ function getGlobalHooks() {
27
+ return globalHooks;
28
+ }
29
+ /**
30
+ * 清空全局 hooks(测试用)。
31
+ */
32
+ function clearGlobalHooks() {
33
+ globalHooks = {};
34
+ }
35
+ /**
36
+ * 从 Hono Context 构建请求事件。
37
+ */
38
+ function createHandleEvent(c) {
39
+ const requestId = c.get("requestId") || c.req.header("x-request-id") || "";
40
+ const clientAddress = c.req.header("x-forwarded-for")?.split(",")[0]?.trim() || c.req.header("x-real-ip") || "unknown";
41
+ return {
42
+ request: c.req.raw,
43
+ context: c,
44
+ clientAddress,
45
+ requestId
46
+ };
47
+ }
48
+ /**
49
+ * 从错误中提取安全消息(移除敏感信息)。
50
+ *
51
+ * - 404 → "Not Found"
52
+ * - 5xx → "Internal Server Error"(生产环境不暴露内部错误详情,
53
+ * 也不区分 Error / 非 Error 类型,避免信息泄漏)
54
+ * - 4xx(非 404)→ 返回 `error.message`(仅当 error 是 Error 实例),
55
+ * 否则返回 "Bad Request"
56
+ */
57
+ function extractErrorMessage(error, status) {
58
+ if (status === 404) return "Not Found";
59
+ if (status >= 500) return "Internal Server Error";
60
+ if (error instanceof Error) return error.message;
61
+ return "Bad Request";
62
+ }
63
+ /**
64
+ * 包装 resolve 函数:将 Hono 的请求处理流程封装为 `resolve(event)` 调用。
65
+ *
66
+ * 返回一个函数,调用后会执行 `next()` 让 Hono 处理请求,然后返回 `c.res`。
67
+ */
68
+ function wrapResolve(c, next) {
69
+ return async (_event) => {
70
+ await next();
71
+ return c.res;
72
+ };
73
+ }
74
+ /**
75
+ * 应用 `handle` hook:如果已注册,则用 hook 包裹请求处理流程。
76
+ *
77
+ * 返回 `true` 表示 hook 已处理请求(调用方应跳过后续中间件),
78
+ * `false` 表示无 hook 或 hook 已调用 resolve。
79
+ */
80
+ async function applyHandleHook(c, next) {
81
+ const hook = globalHooks.handle;
82
+ if (!hook) return false;
83
+ c.res = await hook({
84
+ event: createHandleEvent(c),
85
+ resolve: wrapResolve(c, next)
86
+ });
87
+ return true;
88
+ }
89
+ /**
90
+ * 应用 `handleFetch` hook:如果已注册,则用 hook 包裹 fetch 调用。
91
+ */
92
+ async function applyHandleFetchHook(request, defaultFetch, serverContext) {
93
+ const hook = globalHooks.handleFetch;
94
+ if (!hook) return defaultFetch(request);
95
+ return hook({
96
+ request,
97
+ fetch: defaultFetch,
98
+ serverContext
99
+ });
100
+ }
101
+ /**
102
+ * 应用 `handleError` hook:如果已注册,则调用它。
103
+ */
104
+ async function applyHandleErrorHook(c, error, status) {
105
+ const hook = globalHooks.handleError;
106
+ if (!hook) return;
107
+ await hook({
108
+ event: createHandleEvent(c),
109
+ error,
110
+ status,
111
+ message: extractErrorMessage(error, status)
112
+ });
113
+ }
114
+ //#endregion
9
115
  //#region src/define-server.ts
10
116
  /**
11
117
  * Apply a resolved server config to a `UbeanApp` instance.
@@ -14,6 +120,7 @@ import { isAbsolute, join } from "pathe";
14
120
  * - `plugins` are available when `init()` calls `setup` / `ready`
15
121
  * - `hooks` are registered before `init()` fires `app:created` etc.
16
122
  * - `onAppCreate` runs before route registration
123
+ * - `globalHooks` (handle/handleFetch/handleError) are set before any request
17
124
  *
18
125
  * `onServerReady` is NOT called here — the caller must invoke it after
19
126
  * `app.init()` completes.
@@ -21,6 +128,7 @@ import { isAbsolute, join } from "pathe";
21
128
  async function applyServerConfig(app, config) {
22
129
  if (config.plugins.length > 0) app.plugins.push(...config.plugins);
23
130
  for (const [name, handler] of Object.entries(config.hooks)) if (handler) app.hooks.hook(name, handler);
131
+ if (config.globalHooks) setGlobalHooks(config.globalHooks);
24
132
  if (config.onAppCreate) await config.onAppCreate(app);
25
133
  }
26
134
  /**
@@ -46,6 +154,16 @@ async function applyServerConfig(app, config) {
46
154
  * hooks: {
47
155
  * 'request:start': (c) => { console.log(c.req.method, c.req.path); }
48
156
  * },
157
+ * globalHooks: {
158
+ * handle: async ({ event, resolve }) => {
159
+ * const response = await resolve(event);
160
+ * response.headers.set('X-Custom', 'ubean');
161
+ * return response;
162
+ * },
163
+ * handleError: async ({ error, status }) => {
164
+ * console.error(`[${status}]`, error);
165
+ * }
166
+ * },
49
167
  * onAppCreate: async (app) => { /* init db *\/ },
50
168
  * onServerReady: async (app) => { /* start workers *\/ }
51
169
  * });
@@ -55,6 +173,7 @@ function defineServer(options) {
55
173
  return {
56
174
  plugins: options.plugins || [],
57
175
  hooks: options.hooks || {},
176
+ globalHooks: options.globalHooks,
58
177
  onAppCreate: options.onAppCreate,
59
178
  onServerReady: options.onServerReady
60
179
  };
@@ -78,6 +197,7 @@ function mergeServerConfigs(base, ...configs) {
78
197
  const result = {
79
198
  plugins: [...base.plugins],
80
199
  hooks: { ...base.hooks },
200
+ globalHooks: base.globalHooks ? { ...base.globalHooks } : void 0,
81
201
  onAppCreate: base.onAppCreate,
82
202
  onServerReady: base.onServerReady
83
203
  };
@@ -85,6 +205,10 @@ function mergeServerConfigs(base, ...configs) {
85
205
  if (!cfg) continue;
86
206
  if (cfg.plugins) result.plugins.push(...cfg.plugins);
87
207
  if (cfg.hooks) Object.assign(result.hooks, cfg.hooks);
208
+ if (cfg.globalHooks) result.globalHooks = {
209
+ ...result.globalHooks,
210
+ ...cfg.globalHooks
211
+ };
88
212
  if (cfg.onAppCreate) result.onAppCreate = cfg.onAppCreate;
89
213
  if (cfg.onServerReady) result.onServerReady = cfg.onServerReady;
90
214
  }
@@ -107,13 +231,17 @@ var UbeanApp = class {
107
231
  this._setupFallback();
108
232
  }
109
233
  _setupBaseMiddleware() {
234
+ this.hono.use("*", async (c, next) => {
235
+ if (!await applyHandleHook(c, next)) await next();
236
+ });
110
237
  this.hono.use("*", requestId());
111
238
  if (this.options.routeRules && Object.keys(this.options.routeRules).length > 0) {
112
239
  this.hono.use("*", createRouteRulesMiddleware(this.options.routeRules));
113
240
  const cacheRules = resolveRouteCacheRules(this.options.routeRules);
114
- if (Object.keys(cacheRules).length > 0) {
241
+ const hasIsrRules = Object.values(this.options.routeRules).some((r) => r?.isr !== void 0);
242
+ if (Object.keys(cacheRules).length > 0 || hasIsrRules) {
115
243
  useCacheStore(createMemoryStore());
116
- this.hono.use("*", createCacheMiddleware({ rules: cacheRules }));
244
+ if (Object.keys(cacheRules).length > 0) this.hono.use("*", createCacheMiddleware({ rules: cacheRules }));
117
245
  }
118
246
  }
119
247
  this.hono.use("*", createWebSocketMiddleware());
@@ -159,16 +287,22 @@ var UbeanApp = class {
159
287
  pageRenderer: this.options.pageRenderer ?? null,
160
288
  pageAssetTags: this.options.pageAssetTags ?? {},
161
289
  ssrExclude: this.options.ssrExclude,
290
+ streaming: this.options.streaming,
291
+ botFallback: this.options.botFallback,
162
292
  i18nConfig: this.options.i18nConfig,
163
- notFoundPage: this.options.notFoundPage
293
+ notFoundPage: this.options.notFoundPage,
294
+ colorModeScript: this.options.colorModeScript,
295
+ cacheStore: this.options.routeRules && Object.keys(this.options.routeRules).length > 0 ? useCacheStore() : void 0
164
296
  };
165
297
  await registerRoutes(this, registerOpts);
298
+ this.hono.on("POST", ACTIONS_ENDPOINT, createActionsMiddleware());
299
+ this.hono.on("POST", SERVER_COMPONENT_ENDPOINT, createServerComponentMiddleware());
166
300
  if (this.options.openAPI) {
167
301
  const openAPIOpts = typeof this.options.openAPI === "object" ? this.options.openAPI : {};
168
302
  registerOpenAPIRoutes(this.hono, openAPIOpts);
169
303
  }
170
304
  await this.hooks.callHook("app:after:register", this.hono);
171
- setInternalFetcher((req) => this.hono.fetch(req));
305
+ setInternalFetcher((req) => applyHandleFetchHook(req, (r) => Promise.resolve(this.hono.fetch(r))));
172
306
  for (const plugin of this.plugins) if (plugin.ready) await plugin.ready(this);
173
307
  this._ready = true;
174
308
  return this;
@@ -193,6 +327,7 @@ var UbeanApp = class {
193
327
  });
194
328
  this.hono.onError((err, c) => {
195
329
  this.hooks.callHook("error", err, c);
330
+ applyHandleErrorHook(c, err, isUbeanError(err) ? err.statusCode : 500);
196
331
  if (isUbeanError(err)) return errorToResponse(c, err);
197
332
  return errorToResponse(c, new UbeanError(500, err.message || "Internal Server Error"));
198
333
  });
@@ -239,4 +374,4 @@ function createUbeanApp(options = {}) {
239
374
  return new UbeanApp(options);
240
375
  }
241
376
  //#endregion
242
- export { UbeanApp, applyServerConfig, createDefaultServerConfig, createUbeanApp, defineServer, mergeServerConfigs };
377
+ export { UbeanApp, applyHandleErrorHook, applyHandleFetchHook, applyHandleHook, applyServerConfig, clearGlobalHooks, createDefaultServerConfig, createHandleEvent, createUbeanApp, defineServer, extractErrorMessage, getGlobalHooks, mergeServerConfigs, setGlobalHooks, wrapResolve };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ubean/app",
3
- "version": "0.1.13",
3
+ "version": "0.2.0",
4
4
  "description": "Hono app factory and server config for ubean (createUbeanApp, defineServer)",
5
5
  "files": [
6
6
  "dist"
@@ -16,29 +16,26 @@
16
16
  }
17
17
  },
18
18
  "dependencies": {
19
- "hono": "4.12.32",
19
+ "hono": "4.13.2",
20
20
  "hookable": "^6.1.1",
21
21
  "pathe": "^2.0.3",
22
- "@ubean/error": "0.1.13",
23
- "@ubean/api-routes": "0.1.13",
24
- "@ubean/types": "0.1.13",
25
- "@ubean/server": "0.1.13"
22
+ "@ubean/actions": "0.2.0",
23
+ "@ubean/islands": "0.2.0",
24
+ "@ubean/scan": "0.2.0",
25
+ "@ubean/routes": "0.2.0",
26
+ "@ubean/server": "0.2.0",
27
+ "@ubean/shared": "0.2.0"
26
28
  },
27
29
  "devDependencies": {
28
- "@types/node": "^26.1.2",
30
+ "@types/node": "^26.2.0",
29
31
  "typescript": "7.0.2",
30
- "vite-plus": "0.2.6",
31
- "@ubean/routing": "0.1.13",
32
- "@ubean/pages": "0.1.13"
32
+ "vite-plus": "0.2.9",
33
+ "@ubean/pages": "0.2.0"
33
34
  },
34
35
  "peerDependencies": {
35
- "@ubean/pages": "0.1.13",
36
- "@ubean/routing": "0.1.13"
36
+ "@ubean/pages": "0.2.0"
37
37
  },
38
38
  "peerDependenciesMeta": {
39
- "@ubean/routing": {
40
- "optional": true
41
- },
42
39
  "@ubean/pages": {
43
40
  "optional": true
44
41
  }