@ubean/client 0.4.3 → 0.4.5

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.
@@ -48,19 +48,24 @@ function createUbeanRouter(options) {
48
48
  const router = createRouter({
49
49
  history,
50
50
  routes: options.routes,
51
- scrollBehavior(_to, _from, savedPosition) {
52
- if (savedPosition) return savedPosition;
53
- if (_to.hash) return {
54
- el: _to.hash,
55
- behavior: "smooth"
56
- };
57
- return { top: 0 };
58
- }
51
+ scrollBehavior: options.scrollBehavior ?? defaultScrollBehavior
59
52
  });
60
53
  if (options.setup) options.setup(router);
61
54
  if (options.ssr && options.initialUrl) router.push(options.initialUrl);
62
55
  return router;
63
56
  }
57
+ /**
58
+ * 默认滚动策略:返回顶部;带 hash 时仅在目标元素已存在时平滑滚动,
59
+ * 否则返回 `false`(跳过滚动,避免 VUE_ROUTER_R0042 告警)。
60
+ */
61
+ function defaultScrollBehavior(to, _from, savedPosition) {
62
+ if (savedPosition) return savedPosition;
63
+ if (to.hash) return typeof document !== "undefined" && document.querySelector(to.hash) ? {
64
+ el: to.hash,
65
+ behavior: "smooth"
66
+ } : false;
67
+ return { top: 0 };
68
+ }
64
69
  //#endregion
65
70
  //#region src/page-macro.ts
66
71
  function definePage(_meta) {}
@@ -185,7 +190,8 @@ function createUbeanClientApp(options) {
185
190
  const router = createUbeanRouter({
186
191
  routes: options.routes,
187
192
  ssr: false,
188
- setup: options.routerSetup
193
+ setup: options.router?.setup,
194
+ scrollBehavior: options.router?.scrollBehavior
189
195
  });
190
196
  const { LayoutView: ClientLayoutWrapper, loadLayoutsForRoute: loadClientLayouts } = createLayoutWrapper(options.resolveLayoutComponent, options.defaultLayout || null);
191
197
  initCachedViewsFromRoutes(options.routes);
@@ -223,7 +229,8 @@ function createUbeanSSRApp(initialPage, options) {
223
229
  routes: options.routes,
224
230
  ssr: true,
225
231
  initialUrl: initialPage.url,
226
- setup: options.routerSetup
232
+ setup: options.router?.setup,
233
+ scrollBehavior: options.router?.scrollBehavior
227
234
  });
228
235
  const { LayoutView: SsrLayoutWrapper, loadLayoutsForRoute: loadSsrLayouts } = createLayoutWrapper(options.resolveLayoutComponent, options.defaultLayout || null);
229
236
  initCachedViewsFromRoutes(options.routes);
@@ -1,5 +1,6 @@
1
+ import { i as RouterConfig } from "./define-app-RzHOZlYB.js";
1
2
  import { App, Component } from "vue";
2
- import { RouteRecordRaw, Router } from "vue-router";
3
+ import { RouteRecordRaw, Router, RouterOptions } from "vue-router";
3
4
  import { ERROR_KEY as ERROR_KEY$1, LAYOUT_CHAIN_KEY, LOADING_KEY as LOADING_KEY$1, LOCALIZE_PATH_KEY as LOCALIZE_PATH_KEY$1, LayoutChainContext, LayoutChainRenderer as LayoutChainRenderer$1, Link, PAGE_KEY as PAGE_KEY$1, PageMeta, PageView, SSR_KEY as SSR_KEY$1, SlotView, TRANSITION_KEY as TRANSITION_KEY$1, UbeanVueOptions, ViewTransitionOptions, ubeanVue, useViewTransition } from "@ubean/vue";
4
5
  import { VueHeadClient, useHead as useUnheadHead, useSeoMeta } from "@unhead/vue";
5
6
  import { PageObject } from "@ubean/pages";
@@ -21,6 +22,15 @@ interface CreateUbeanRouterOptions {
21
22
  * 该回调在 Client 和 SSR 环境都会执行。
22
23
  */
23
24
  setup?: (router: Router) => void;
25
+ /**
26
+ * vue-router `scrollBehavior`,覆盖默认滚动策略。
27
+ *
28
+ * 默认行为:返回顶部;带 hash 时仅当目标元素已存在才平滑滚动。
29
+ * 客户端延迟生成的锚点(如 SSR 后由客户端渲染的标题 id)在首次导航时
30
+ * 可能尚不存在,直接返回 `{ el }` 会触发 VUE_ROUTER_R0042,因此默认
31
+ * 先探测元素存在性。
32
+ */
33
+ scrollBehavior?: RouterOptions['scrollBehavior'];
24
34
  }
25
35
  declare function createUbeanRouter(options: CreateUbeanRouterOptions): Router;
26
36
  //#endregion
@@ -65,12 +75,12 @@ interface UbeanAppOptions {
65
75
  viewTransitions?: boolean | ViewTransitionOptions;
66
76
  hydrate?: boolean;
67
77
  /**
68
- * 在 router 实例创建后、`app.use(router)` 之前调用,
69
- * 用于注册 vue-router 的导航守卫(`beforeEach` / `beforeResolve` / `afterEach`)。
70
- *
71
- * Client 和 SSR 都会执行。守卫注册必须同步完成(守卫本身可返回 Promise)。
78
+ * router 配置(setup 守卫 + scrollBehavior 等初始化选项)。
79
+ * Client 和 SSR 都会执行;setup 注册守卫必须同步完成。
80
+ * 来自 `defineApp({ router })`,由 `virtual:ubean-app` / `virtual:ubean-server`
81
+ * 在调用本工厂前通过 `resolveAppConfig()` 合并注入。
72
82
  */
73
- routerSetup?: (router: ReturnType<typeof createUbeanRouter>) => void;
83
+ router?: RouterConfig;
74
84
  /**
75
85
  * 页面加载时的 fallback 组件,在 SPA 导航懒加载页面组件期间显示。
76
86
  * 来源:`pages/loading.vue` 自动检测 或 `defineApp({ loadingComponent })` 显式配置。
package/dist/app.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { C as defineMeta, D as usePage, E as UbeanVuePage, O as CreateUbeanRouterOptions, S as useViewTransition, T as definePage, _ as createUbeanClientApp, a as LOCALIZE_PATH_KEY, b as useSeoMeta, c as Link, d as SSR_KEY, f as SlotView, g as UbeanVueOptions, h as UbeanAppOptions, i as LOADING_KEY, k as createUbeanRouter, l as PAGE_KEY, m as UbeanAppInstance, n as Head, o as LayoutChainContext, p as TRANSITION_KEY, r as LAYOUT_CHAIN_KEY, s as LayoutChainRenderer, t as ERROR_KEY, u as PageView, v as createUbeanSSRApp, w as defineMiddleware, x as useUnheadHead, y as ubeanVue } from "./app-BQgi-8Qp.js";
1
+ import { C as defineMeta, D as usePage, E as UbeanVuePage, O as CreateUbeanRouterOptions, S as useViewTransition, T as definePage, _ as createUbeanClientApp, a as LOCALIZE_PATH_KEY, b as useSeoMeta, c as Link, d as SSR_KEY, f as SlotView, g as UbeanVueOptions, h as UbeanAppOptions, i as LOADING_KEY, k as createUbeanRouter, l as PAGE_KEY, m as UbeanAppInstance, n as Head, o as LayoutChainContext, p as TRANSITION_KEY, r as LAYOUT_CHAIN_KEY, s as LayoutChainRenderer, t as ERROR_KEY, u as PageView, v as createUbeanSSRApp, w as defineMiddleware, x as useUnheadHead, y as ubeanVue } from "./app-emLZM9XN.js";
2
2
  export { type CreateUbeanRouterOptions, ERROR_KEY, Head, LAYOUT_CHAIN_KEY, LOADING_KEY, LOCALIZE_PATH_KEY, type LayoutChainContext, LayoutChainRenderer, Link, PAGE_KEY, PageView, SSR_KEY, SlotView, TRANSITION_KEY, UbeanAppInstance, UbeanAppOptions, type UbeanVueOptions, type UbeanVuePage, createUbeanClientApp, createUbeanRouter, createUbeanSSRApp, defineMeta, defineMiddleware, definePage, ubeanVue, useUnheadHead as useHead, usePage, useSeoMeta, useViewTransition };
package/dist/app.js CHANGED
@@ -1,2 +1,2 @@
1
- import { C as usePage, S as createUbeanRouter, _ as useUnheadHead, a as LOCALIZE_PATH_KEY, b as defineMiddleware, c as PAGE_KEY, d as SlotView, f as TRANSITION_KEY, g as useSeoMeta, h as ubeanVue, i as LOADING_KEY, l as PageView, m as createUbeanSSRApp, n as Head, o as LayoutChainRenderer, p as createUbeanClientApp, r as LAYOUT_CHAIN_KEY, s as Link, t as ERROR_KEY, u as SSR_KEY, v as useViewTransition, x as definePage, y as defineMeta } from "./app-DiXW_14_.js";
1
+ import { C as usePage, S as createUbeanRouter, _ as useUnheadHead, a as LOCALIZE_PATH_KEY, b as defineMiddleware, c as PAGE_KEY, d as SlotView, f as TRANSITION_KEY, g as useSeoMeta, h as ubeanVue, i as LOADING_KEY, l as PageView, m as createUbeanSSRApp, n as Head, o as LayoutChainRenderer, p as createUbeanClientApp, r as LAYOUT_CHAIN_KEY, s as Link, t as ERROR_KEY, u as SSR_KEY, v as useViewTransition, x as definePage, y as defineMeta } from "./app-Bt-2meGm.js";
2
2
  export { ERROR_KEY, Head, LAYOUT_CHAIN_KEY, LOADING_KEY, LOCALIZE_PATH_KEY, LayoutChainRenderer, Link, PAGE_KEY, PageView, SSR_KEY, SlotView, TRANSITION_KEY, createUbeanClientApp, createUbeanRouter, createUbeanSSRApp, defineMeta, defineMiddleware, definePage, ubeanVue, useUnheadHead as useHead, usePage, useSeoMeta, useViewTransition };
@@ -0,0 +1,179 @@
1
+ import { App, Component, Plugin } from "vue";
2
+ import { Router, RouterOptions } from "vue-router";
3
+ import { ViewTransitionOptions } from "@ubean/vue";
4
+ import { PageHead } from "@ubean/shared";
5
+ //#region src/define-app.d.ts
6
+ interface AppPluginConfig {
7
+ plugin: Plugin | [Plugin, ...any[]];
8
+ mode?: 'all' | 'client' | 'server';
9
+ }
10
+ /**
11
+ * Router 钩子配置 — 暴露 vue-router 的导航守卫注册入口 + 初始化选项。
12
+ *
13
+ * `setup(router)` 在 router 实例创建后、`app.use(router)` 之前调用,
14
+ * 因此守卫能拦截首次导航(包括 SSR 的初始 URL push)。
15
+ *
16
+ * Client 和 SSR 都会执行,适合做鉴权、埋点、进度条等逻辑。
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * defineApp({
21
+ * router: {
22
+ * setup(router) {
23
+ * router.beforeEach((to, from) => {
24
+ * if (to.meta.requiresAuth && !isAuthenticated()) {
25
+ * return '/login';
26
+ * }
27
+ * });
28
+ * router.afterEach((to) => {
29
+ * analytics.track('page_view', { path: to.fullPath });
30
+ * });
31
+ * },
32
+ * scrollBehavior(to, from, savedPosition) {
33
+ * // 覆盖默认滚动策略(默认已守卫 hash 目标元素不存在时的 R0042)
34
+ * if (savedPosition) return savedPosition;
35
+ * if (to.hash) return { el: to.hash, behavior: 'smooth' };
36
+ * return { top: 0 };
37
+ * }
38
+ * }
39
+ * });
40
+ * ```
41
+ */
42
+ interface RouterConfig {
43
+ /**
44
+ * 在 router 创建后、初始导航前调用。
45
+ * 用于注册 `beforeEach` / `beforeResolve` / `afterEach` 等守卫。
46
+ *
47
+ * 注意:setup 必须同步注册守卫(虽然守卫本身可以返回 Promise)。
48
+ * 不要在 setup 中执行异步操作(如请求 API),因为这会延迟首次导航。
49
+ */
50
+ setup?: (router: Router) => void;
51
+ /**
52
+ * vue-router `scrollBehavior`,覆盖 `createUbeanRouter` 的默认滚动策略。
53
+ * 未配置时使用框架默认:回到顶部;带 hash 且目标元素存在时平滑滚动。
54
+ */
55
+ scrollBehavior?: RouterOptions['scrollBehavior'];
56
+ }
57
+ interface DefineAppOptions {
58
+ plugins?: Array<Plugin | [Plugin, ...any[]] | AppPluginConfig>;
59
+ globalComponents?: Record<string, Component>;
60
+ provides?: Record<string | symbol, unknown>;
61
+ head?: PageHead;
62
+ rootId?: string;
63
+ rootAttrs?: Record<string, string>;
64
+ /**
65
+ * 应用根组件(包装组件,可选)—— 经典 Vue `App.vue` 的编程式等价物。
66
+ *
67
+ * 组件包裹在框架根组件(`UbeanAppRoot`)之上,框架出口(布局链 + 页面)
68
+ * 通过**默认 slot** 注入,组件内用 `<slot />` 声明渲染位置:
69
+ *
70
+ * ```vue
71
+ * <!-- src/app.ts 对应的 AppRoot.vue -->
72
+ * <template>
73
+ * <SConfigProvider>
74
+ * <AppHeader />
75
+ * <slot /> <!-- 布局链 + 页面在这里渲染 -->
76
+ * </SConfigProvider>
77
+ * </template>
78
+ * ```
79
+ *
80
+ * 适合放置"必须在布局之上"的全局内容:全局 ConfigProvider/主题上下文、
81
+ * 覆盖布局自身的错误边界、全局过渡/动效容器等。不要用它渲染
82
+ * `<PageView />` / `<RouterView />` —— 那会绕过布局链;出口是 `<slot />`。
83
+ *
84
+ * 自动检测:`src/app.vue`(小写,Nuxt 风格,优先)/ `src/App.vue`
85
+ * (大写,经典 Vue 约定)文件约定(与 loading/error 特殊页同模式),
86
+ * 两者都存在时取小写;无该文件时忽略。优先级:
87
+ * `defineApp({ appRoot })` > `src/app.vue` > `src/App.vue`。
88
+ */
89
+ appRoot?: Component;
90
+ /**
91
+ * 路由钩子配置,用于注册 vue-router 的导航守卫。
92
+ * 在 Client 和 SSR 都会执行。
93
+ */
94
+ router?: RouterConfig;
95
+ onAppCreated?: (app: App) => void | Promise<void>;
96
+ onClientReady?: (app: App) => void | Promise<void>;
97
+ errorComponent?: Component;
98
+ loadingComponent?: Component;
99
+ viewTransitions?: boolean | ViewTransitionOptions;
100
+ /**
101
+ * SSR 专用:在 `renderToString(app)` 完成后调用,从 app 实例提取需要
102
+ * 序列化到客户端的状态(如 Pinia 的 `pinia.state.value`)。
103
+ *
104
+ * 返回的对象会被 `safeJsonStringify` 序列化并注入到 HTML 的
105
+ * `__UBEAN_STATE__` script 标签中。
106
+ *
107
+ * 仅在 server mode 下生效;client mode 下被忽略。
108
+ *
109
+ * @example
110
+ * ```ts
111
+ * import { createPinia } from 'pinia';
112
+ * defineApp({
113
+ * plugins: [createPinia()],
114
+ * serializeState: (app) => ({
115
+ * pinia: app.config.globalProperties.$pinia.state.value
116
+ * })
117
+ * });
118
+ * ```
119
+ */
120
+ serializeState?: (app: App) => Record<string, unknown> | Promise<Record<string, unknown>>;
121
+ /**
122
+ * Client 专用:在 `applyAppConfig`(注册插件/provide)之后、`app.mount()` 之前调用,
123
+ * 用于从 `__UBEAN_STATE__` 反序列化的状态回填到 app(如 Pinia 的水合)。
124
+ *
125
+ * `state` 即 `serializeState` 在服务端返回的对象;若服务端未配置 `serializeState`
126
+ * 或返回空,`state` 为 `null`。
127
+ *
128
+ * 仅在 client mode 下生效;server mode 下被忽略。
129
+ *
130
+ * @example
131
+ * ```ts
132
+ * import { createPinia } from 'pinia';
133
+ * defineApp({
134
+ * plugins: [createPinia()],
135
+ * hydrateState: (app, state) => {
136
+ * if (state?.pinia) {
137
+ * app.config.globalProperties.$pinia.state.value = state.pinia;
138
+ * }
139
+ * }
140
+ * });
141
+ * ```
142
+ */
143
+ hydrateState?: (app: App, state: Record<string, unknown> | null) => void;
144
+ }
145
+ interface ResolvedAppConfig {
146
+ plugins: AppPluginConfig[];
147
+ globalComponents: Record<string, Component>;
148
+ provides: Record<string | symbol, unknown>;
149
+ head?: PageHead;
150
+ rootId: string;
151
+ rootAttrs: Record<string, string>;
152
+ /**
153
+ * 应用根组件(包装组件,可选)。语义见 `DefineAppOptions.appRoot`。
154
+ */
155
+ appRoot?: Component;
156
+ router?: RouterConfig;
157
+ onAppCreated?: (app: App) => void | Promise<void>;
158
+ onClientReady?: (app: App) => void | Promise<void>;
159
+ errorComponent?: Component;
160
+ loadingComponent?: Component;
161
+ viewTransitions?: boolean | ViewTransitionOptions;
162
+ serializeState?: (app: App) => Record<string, unknown> | Promise<Record<string, unknown>>;
163
+ hydrateState?: (app: App, state: Record<string, unknown> | null) => void;
164
+ }
165
+ declare function defineApp(options: DefineAppOptions): ResolvedAppConfig;
166
+ declare function applyAppConfig(app: App, config: ResolvedAppConfig, mode: 'client' | 'server'): void;
167
+ declare function createDefaultAppConfig(): ResolvedAppConfig;
168
+ /**
169
+ * 从多个 `ResolvedAppConfig` 合并出一个最终配置(shared + client/server)。
170
+ *
171
+ * `serializeState` 与 `hydrateState` 的合并语义:server 配置的 `serializeState` 优先,
172
+ * client 配置的 `hydrateState` 优先;shared 配置作为默认值。
173
+ *
174
+ * 这个函数在 `virtual:ubean-app` 虚拟模块中以 JS 字符串形式重新实现
175
+ * (`_mergeAppConfig`),保持两者语义一致。
176
+ */
177
+ declare function mergeAppConfig(base: ResolvedAppConfig, ...configs: (ResolvedAppConfig | null | undefined)[]): ResolvedAppConfig;
178
+ //#endregion
179
+ export { applyAppConfig as a, mergeAppConfig as c, RouterConfig as i, DefineAppOptions as n, createDefaultAppConfig as o, ResolvedAppConfig as r, defineApp as s, AppPluginConfig as t };
@@ -1,168 +1,2 @@
1
- import { App, Component, Plugin } from "vue";
2
- import { Router } from "vue-router";
3
- import { ViewTransitionOptions } from "@ubean/vue";
4
- import { PageHead } from "@ubean/shared";
5
- //#region src/define-app.d.ts
6
- interface AppPluginConfig {
7
- plugin: Plugin | [Plugin, ...any[]];
8
- mode?: 'all' | 'client' | 'server';
9
- }
10
- /**
11
- * Router 钩子配置 — 暴露 vue-router 的导航守卫注册入口。
12
- *
13
- * `setup(router)` 在 router 实例创建后、`app.use(router)` 之前调用,
14
- * 因此守卫能拦截首次导航(包括 SSR 的初始 URL push)。
15
- *
16
- * Client 和 SSR 都会执行,适合做鉴权、埋点、进度条等逻辑。
17
- *
18
- * @example
19
- * ```ts
20
- * defineApp({
21
- * router: {
22
- * setup(router) {
23
- * router.beforeEach((to, from) => {
24
- * if (to.meta.requiresAuth && !isAuthenticated()) {
25
- * return '/login';
26
- * }
27
- * });
28
- * router.afterEach((to) => {
29
- * analytics.track('page_view', { path: to.fullPath });
30
- * });
31
- * }
32
- * }
33
- * });
34
- * ```
35
- */
36
- interface RouterConfig {
37
- /**
38
- * 在 router 创建后、初始导航前调用。
39
- * 用于注册 `beforeEach` / `beforeResolve` / `afterEach` 等守卫。
40
- *
41
- * 注意:setup 必须同步注册守卫(虽然守卫本身可以返回 Promise)。
42
- * 不要在 setup 中执行异步操作(如请求 API),因为这会延迟首次导航。
43
- */
44
- setup?: (router: Router) => void;
45
- }
46
- interface DefineAppOptions {
47
- plugins?: Array<Plugin | [Plugin, ...any[]] | AppPluginConfig>;
48
- globalComponents?: Record<string, Component>;
49
- provides?: Record<string | symbol, unknown>;
50
- head?: PageHead;
51
- rootId?: string;
52
- rootAttrs?: Record<string, string>;
53
- /**
54
- * 应用根组件(包装组件,可选)—— 经典 Vue `App.vue` 的编程式等价物。
55
- *
56
- * 组件包裹在框架根组件(`UbeanAppRoot`)之上,框架出口(布局链 + 页面)
57
- * 通过**默认 slot** 注入,组件内用 `<slot />` 声明渲染位置:
58
- *
59
- * ```vue
60
- * <!-- src/app.ts 对应的 AppRoot.vue -->
61
- * <template>
62
- * <SConfigProvider>
63
- * <AppHeader />
64
- * <slot /> <!-- 布局链 + 页面在这里渲染 -->
65
- * </SConfigProvider>
66
- * </template>
67
- * ```
68
- *
69
- * 适合放置"必须在布局之上"的全局内容:全局 ConfigProvider/主题上下文、
70
- * 覆盖布局自身的错误边界、全局过渡/动效容器等。不要用它渲染
71
- * `<PageView />` / `<RouterView />` —— 那会绕过布局链;出口是 `<slot />`。
72
- *
73
- * 自动检测:`src/app.vue`(小写,Nuxt 风格,优先)/ `src/App.vue`
74
- * (大写,经典 Vue 约定)文件约定(与 loading/error 特殊页同模式),
75
- * 两者都存在时取小写;无该文件时忽略。优先级:
76
- * `defineApp({ appRoot })` > `src/app.vue` > `src/App.vue`。
77
- */
78
- appRoot?: Component;
79
- /**
80
- * 路由钩子配置,用于注册 vue-router 的导航守卫。
81
- * 在 Client 和 SSR 都会执行。
82
- */
83
- router?: RouterConfig;
84
- onAppCreated?: (app: App) => void | Promise<void>;
85
- onClientReady?: (app: App) => void | Promise<void>;
86
- errorComponent?: Component;
87
- loadingComponent?: Component;
88
- viewTransitions?: boolean | ViewTransitionOptions;
89
- /**
90
- * SSR 专用:在 `renderToString(app)` 完成后调用,从 app 实例提取需要
91
- * 序列化到客户端的状态(如 Pinia 的 `pinia.state.value`)。
92
- *
93
- * 返回的对象会被 `safeJsonStringify` 序列化并注入到 HTML 的
94
- * `__UBEAN_STATE__` script 标签中。
95
- *
96
- * 仅在 server mode 下生效;client mode 下被忽略。
97
- *
98
- * @example
99
- * ```ts
100
- * import { createPinia } from 'pinia';
101
- * defineApp({
102
- * plugins: [createPinia()],
103
- * serializeState: (app) => ({
104
- * pinia: app.config.globalProperties.$pinia.state.value
105
- * })
106
- * });
107
- * ```
108
- */
109
- serializeState?: (app: App) => Record<string, unknown> | Promise<Record<string, unknown>>;
110
- /**
111
- * Client 专用:在 `applyAppConfig`(注册插件/provide)之后、`app.mount()` 之前调用,
112
- * 用于从 `__UBEAN_STATE__` 反序列化的状态回填到 app(如 Pinia 的水合)。
113
- *
114
- * `state` 即 `serializeState` 在服务端返回的对象;若服务端未配置 `serializeState`
115
- * 或返回空,`state` 为 `null`。
116
- *
117
- * 仅在 client mode 下生效;server mode 下被忽略。
118
- *
119
- * @example
120
- * ```ts
121
- * import { createPinia } from 'pinia';
122
- * defineApp({
123
- * plugins: [createPinia()],
124
- * hydrateState: (app, state) => {
125
- * if (state?.pinia) {
126
- * app.config.globalProperties.$pinia.state.value = state.pinia;
127
- * }
128
- * }
129
- * });
130
- * ```
131
- */
132
- hydrateState?: (app: App, state: Record<string, unknown> | null) => void;
133
- }
134
- interface ResolvedAppConfig {
135
- plugins: AppPluginConfig[];
136
- globalComponents: Record<string, Component>;
137
- provides: Record<string | symbol, unknown>;
138
- head?: PageHead;
139
- rootId: string;
140
- rootAttrs: Record<string, string>;
141
- /**
142
- * 应用根组件(包装组件,可选)。语义见 `DefineAppOptions.appRoot`。
143
- */
144
- appRoot?: Component;
145
- router?: RouterConfig;
146
- onAppCreated?: (app: App) => void | Promise<void>;
147
- onClientReady?: (app: App) => void | Promise<void>;
148
- errorComponent?: Component;
149
- loadingComponent?: Component;
150
- viewTransitions?: boolean | ViewTransitionOptions;
151
- serializeState?: (app: App) => Record<string, unknown> | Promise<Record<string, unknown>>;
152
- hydrateState?: (app: App, state: Record<string, unknown> | null) => void;
153
- }
154
- declare function defineApp(options: DefineAppOptions): ResolvedAppConfig;
155
- declare function applyAppConfig(app: App, config: ResolvedAppConfig, mode: 'client' | 'server'): void;
156
- declare function createDefaultAppConfig(): ResolvedAppConfig;
157
- /**
158
- * 从多个 `ResolvedAppConfig` 合并出一个最终配置(shared + client/server)。
159
- *
160
- * `serializeState` 与 `hydrateState` 的合并语义:server 配置的 `serializeState` 优先,
161
- * client 配置的 `hydrateState` 优先;shared 配置作为默认值。
162
- *
163
- * 这个函数在 `virtual:ubean-app` 虚拟模块中以 JS 字符串形式重新实现
164
- * (`_mergeAppConfig`),保持两者语义一致。
165
- */
166
- declare function mergeAppConfig(base: ResolvedAppConfig, ...configs: (ResolvedAppConfig | null | undefined)[]): ResolvedAppConfig;
167
- //#endregion
1
+ import { a as applyAppConfig, c as mergeAppConfig, i as RouterConfig, n as DefineAppOptions, o as createDefaultAppConfig, r as ResolvedAppConfig, s as defineApp, t as AppPluginConfig } from "./define-app-RzHOZlYB.js";
168
2
  export { AppPluginConfig, DefineAppOptions, ResolvedAppConfig, RouterConfig, applyAppConfig, createDefaultAppConfig, defineApp, mergeAppConfig };
@@ -84,13 +84,17 @@ function mergeAppConfig(base, ...configs) {
84
84
  if (cfg.viewTransitions !== void 0) result.viewTransitions = cfg.viewTransitions;
85
85
  if (cfg.serializeState) result.serializeState = cfg.serializeState;
86
86
  if (cfg.hydrateState) result.hydrateState = cfg.hydrateState;
87
- if (cfg.router?.setup) {
88
- const prevSetup = result.router?.setup;
89
- if (prevSetup) result.router = { setup: (router) => {
90
- prevSetup(router);
87
+ if (cfg.router) {
88
+ const prev = result.router;
89
+ const setup = cfg.router.setup ? prev?.setup ? (router) => {
90
+ prev.setup(router);
91
91
  cfg.router.setup(router);
92
- } };
93
- else result.router = { setup: cfg.router.setup };
92
+ } : cfg.router.setup : prev?.setup;
93
+ const scrollBehavior = cfg.router.scrollBehavior !== void 0 ? cfg.router.scrollBehavior : prev?.scrollBehavior;
94
+ result.router = {
95
+ setup,
96
+ ...scrollBehavior !== void 0 ? { scrollBehavior } : {}
97
+ };
94
98
  }
95
99
  }
96
100
  return result;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { C as defineMeta, D as usePage, E as UbeanVuePage, O as CreateUbeanRouterOptions, T as definePage, _ as createUbeanClientApp, b as useSeoMeta, h as UbeanAppOptions, k as createUbeanRouter, m as UbeanAppInstance, n as Head, v as createUbeanSSRApp, w as defineMiddleware, x as useUnheadHead } from "./app-BQgi-8Qp.js";
2
- import { AppPluginConfig, DefineAppOptions, ResolvedAppConfig, applyAppConfig, createDefaultAppConfig, defineApp, mergeAppConfig } from "./define-app.js";
1
+ import { C as defineMeta, D as usePage, E as UbeanVuePage, O as CreateUbeanRouterOptions, T as definePage, _ as createUbeanClientApp, b as useSeoMeta, h as UbeanAppOptions, k as createUbeanRouter, m as UbeanAppInstance, n as Head, v as createUbeanSSRApp, w as defineMiddleware, x as useUnheadHead } from "./app-emLZM9XN.js";
2
+ import { a as applyAppConfig, c as mergeAppConfig, n as DefineAppOptions, o as createDefaultAppConfig, r as ResolvedAppConfig, s as defineApp, t as AppPluginConfig } from "./define-app-RzHOZlYB.js";
3
3
  import { App, ComputedRef, Ref, ShallowRef } from "vue";
4
4
  import "vue-router";
5
5
  import { ERROR_KEY, ErrorBoundary, LAYOUT_CHAIN_KEY, LOADING_KEY, LOCALIZE_PATH_KEY, LayoutChainContext, LayoutChainRenderer, Link, PAGE_KEY, PageView, RouteLocation, RouteLocationRaw, SSR_KEY, SlotView, TRANSITION_KEY, TypedLinkProps, UbeanVueOptions, UbeanVuePageData, UseCacheViewsReturn, UsePageTransitionReturn, UseReloadSignalReturn, ViewTransitionOptions, clearPageTransition, disablePageCache, enablePageCache, excludePageCache, getCacheEnabled, getCachedViewNames, getExcludedViewNames, getNamedPageWrapper, getNavigationType, getPageTransitionName, getReloadCounter, includePageCache, initCachedViewsFromRoutes, invalidatePageCache, isActiveRoute, isCacheEnabled, isPageCached, isPageExcluded, isReloading, reloadPage, resetNamedPageWrappers, resetRouteCache, resolveRoute, setPageTransition, supportsViewTransitions, ubeanVue, useCacheViews, usePageTransition, useReloadSignal, useViewTransition, useViewTransitionState, withViewTransition } from "@ubean/vue";
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { C as usePage, S as createUbeanRouter, _ as useUnheadHead, b as defineMiddleware, g as useSeoMeta, m as createUbeanSSRApp, n as Head, p as createUbeanClientApp, x as definePage, y as defineMeta } from "./app-DiXW_14_.js";
1
+ import { C as usePage, S as createUbeanRouter, _ as useUnheadHead, b as defineMiddleware, g as useSeoMeta, m as createUbeanSSRApp, n as Head, p as createUbeanClientApp, x as definePage, y as defineMeta } from "./app-Bt-2meGm.js";
2
2
  import { a as getI18nRuntimeConfig, c as installUbeanI18n, d as switchLocalePath, f as useLocaleHead, h as useSwitchLocalePath, i as extractLocaleFromPath, l as localizePath, m as useLocaleRoute, n as configureI18nRuntime, o as getLocale, p as useLocalePath, r as createUbeanI18n, s as initClientI18n, u as setLocale } from "./i18n-CAeXKTpy.js";
3
3
  import { applyAppConfig, createDefaultAppConfig, defineApp, mergeAppConfig } from "./define-app.js";
4
4
  import { computed, getCurrentInstance, markRaw, onMounted, onScopeDispose, onUnmounted, ref, shallowRef, watch } from "vue";
package/dist/server.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- import { C as defineMeta, D as usePage, E as UbeanVuePage, O as CreateUbeanRouterOptions, S as useViewTransition, T as definePage, _ as createUbeanClientApp, a as LOCALIZE_PATH_KEY, b as useSeoMeta, c as Link, d as SSR_KEY, f as SlotView, g as UbeanVueOptions, h as UbeanAppOptions, i as LOADING_KEY, k as createUbeanRouter, l as PAGE_KEY, m as UbeanAppInstance, n as Head, o as LayoutChainContext, p as TRANSITION_KEY, r as LAYOUT_CHAIN_KEY, s as LayoutChainRenderer, t as ERROR_KEY, u as PageView, v as createUbeanSSRApp, w as defineMiddleware, x as useUnheadHead, y as ubeanVue } from "./app-BQgi-8Qp.js";
1
+ import { C as defineMeta, D as usePage, E as UbeanVuePage, O as CreateUbeanRouterOptions, S as useViewTransition, T as definePage, _ as createUbeanClientApp, a as LOCALIZE_PATH_KEY, b as useSeoMeta, c as Link, d as SSR_KEY, f as SlotView, g as UbeanVueOptions, h as UbeanAppOptions, i as LOADING_KEY, k as createUbeanRouter, l as PAGE_KEY, m as UbeanAppInstance, n as Head, o as LayoutChainContext, p as TRANSITION_KEY, r as LAYOUT_CHAIN_KEY, s as LayoutChainRenderer, t as ERROR_KEY, u as PageView, v as createUbeanSSRApp, w as defineMiddleware, x as useUnheadHead, y as ubeanVue } from "./app-emLZM9XN.js";
2
2
  import { createHead as createServerHead } from "@unhead/vue/server";
3
3
  export { type CreateUbeanRouterOptions, ERROR_KEY, Head, LAYOUT_CHAIN_KEY, LOADING_KEY, LOCALIZE_PATH_KEY, type LayoutChainContext, LayoutChainRenderer, Link, PAGE_KEY, PageView, SSR_KEY, SlotView, TRANSITION_KEY, UbeanAppInstance, UbeanAppOptions, type UbeanVueOptions, type UbeanVuePage, createServerHead, createUbeanClientApp, createUbeanRouter, createUbeanSSRApp, defineMeta, defineMiddleware, definePage, ubeanVue, useUnheadHead as useHead, usePage, useSeoMeta, useViewTransition };
package/dist/server.js CHANGED
@@ -1,3 +1,3 @@
1
- import { C as usePage, S as createUbeanRouter, _ as useUnheadHead, a as LOCALIZE_PATH_KEY, b as defineMiddleware, c as PAGE_KEY, d as SlotView, f as TRANSITION_KEY, g as useSeoMeta, h as ubeanVue, i as LOADING_KEY, l as PageView, m as createUbeanSSRApp, n as Head, o as LayoutChainRenderer, p as createUbeanClientApp, r as LAYOUT_CHAIN_KEY, s as Link, t as ERROR_KEY, u as SSR_KEY, v as useViewTransition, x as definePage, y as defineMeta } from "./app-DiXW_14_.js";
1
+ import { C as usePage, S as createUbeanRouter, _ as useUnheadHead, a as LOCALIZE_PATH_KEY, b as defineMiddleware, c as PAGE_KEY, d as SlotView, f as TRANSITION_KEY, g as useSeoMeta, h as ubeanVue, i as LOADING_KEY, l as PageView, m as createUbeanSSRApp, n as Head, o as LayoutChainRenderer, p as createUbeanClientApp, r as LAYOUT_CHAIN_KEY, s as Link, t as ERROR_KEY, u as SSR_KEY, v as useViewTransition, x as definePage, y as defineMeta } from "./app-Bt-2meGm.js";
2
2
  import { createHead as createServerHead } from "@unhead/vue/server";
3
3
  export { ERROR_KEY, Head, LAYOUT_CHAIN_KEY, LOADING_KEY, LOCALIZE_PATH_KEY, LayoutChainRenderer, Link, PAGE_KEY, PageView, SSR_KEY, SlotView, TRANSITION_KEY, createServerHead, createUbeanClientApp, createUbeanRouter, createUbeanSSRApp, defineMeta, defineMiddleware, definePage, ubeanVue, useUnheadHead as useHead, usePage, useSeoMeta, useViewTransition };
package/dist/ssr.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ResolvedAppConfig } from "./define-app.js";
1
+ import { r as ResolvedAppConfig } from "./define-app-RzHOZlYB.js";
2
2
  import { Component } from "vue";
3
3
  import { RouteRecordRaw } from "vue-router";
4
4
  import { PageRenderer } from "@ubean/pages";
package/dist/ssr.js CHANGED
@@ -124,6 +124,7 @@ async function prepareRender(options, pageObj, renderContext) {
124
124
  defaultLayout: options.defaultLayout,
125
125
  head,
126
126
  i18n,
127
+ router: appConfig?.router,
127
128
  appRoot: appConfig?.appRoot
128
129
  });
129
130
  if (appConfig) await applyServerAppConfig(createdApp, appConfig);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ubean/client",
3
- "version": "0.4.3",
3
+ "version": "0.4.5",
4
4
  "description": "Framework client runtime for ubean — layered over the lean @ubean/vue kernel, adding app factories, unhead, i18n, data layer, islands hydration and SSR state helpers",
5
5
  "files": [
6
6
  "dist"
@@ -32,11 +32,11 @@
32
32
  }
33
33
  },
34
34
  "dependencies": {
35
- "@ubean/i18n": "0.4.3",
36
- "@ubean/islands": "0.4.3",
37
- "@ubean/pages": "0.4.3",
38
- "@ubean/shared": "0.4.3",
39
- "@ubean/vue": "0.4.3",
35
+ "@ubean/i18n": "0.4.5",
36
+ "@ubean/islands": "0.4.5",
37
+ "@ubean/pages": "0.4.5",
38
+ "@ubean/shared": "0.4.5",
39
+ "@ubean/vue": "0.4.5",
40
40
  "@unhead/vue": "^3.4.0",
41
41
  "@vue/server-renderer": "^3.5.42",
42
42
  "vue": "^3.5.42",
@@ -49,7 +49,7 @@
49
49
  "vite-plus": "0.3.0"
50
50
  },
51
51
  "peerDependencies": {
52
- "@ubean/seo": "0.4.3"
52
+ "@ubean/seo": "0.4.5"
53
53
  },
54
54
  "peerDependenciesMeta": {
55
55
  "@ubean/seo": {