@ubean/app 0.1.2 → 0.1.4

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.
@@ -0,0 +1,238 @@
1
+ import { Context, Hono, MiddlewareHandler, Next } from "hono";
2
+ import { RegisterOptions, RouteRegistrar } from "@ubean/api-routes";
3
+ 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";
6
+ //#region src/define-server.d.ts
7
+ /**
8
+ * Apply a resolved server config to a `UbeanApp` instance.
9
+ *
10
+ * This must be called BEFORE `app.init()` so that:
11
+ * - `plugins` are available when `init()` calls `setup` / `ready`
12
+ * - `hooks` are registered before `init()` fires `app:created` etc.
13
+ * - `onAppCreate` runs before route registration
14
+ *
15
+ * `onServerReady` is NOT called here — the caller must invoke it after
16
+ * `app.init()` completes.
17
+ */
18
+ declare function applyServerConfig(app: UbeanApp, config: ResolvedServerConfig): Promise<void>;
19
+ /**
20
+ * Partial runtime hooks that users can register via `defineServer`.
21
+ * All hooks are optional — users only declare the ones they need.
22
+ */
23
+ type ServerHooks = Partial<UbeanRuntimeHooks>;
24
+ /**
25
+ * Options for `defineServer` — the server-side counterpart of `defineApp`.
26
+ *
27
+ * Users create `src/server.ts` (shared), `src/server.dev.ts` (dev-only),
28
+ * or `src/server.prod.ts` (prod-only) and export the result of
29
+ * `defineServer(...)` as the default export.
30
+ */
31
+ interface DefineServerOptions {
32
+ /**
33
+ * UbeanApp plugins to inject into `createUbeanApp({ plugins })`.
34
+ * Each plugin's `setup` runs before route registration; `ready` runs after.
35
+ */
36
+ plugins?: UbeanAppPlugin[];
37
+ /**
38
+ * Runtime hooks registered on `app.hooks`.
39
+ * Available hooks: `app:created`, `app:before:register`, `app:after:register`,
40
+ * `request:start`, `request:end`, `request:error`, `route:register`,
41
+ * `middleware:register`, `error`.
42
+ */
43
+ hooks?: ServerHooks;
44
+ /**
45
+ * Called after `UbeanApp` is created but before `app.init()`.
46
+ * Useful for initializing databases, external service connections, etc.
47
+ */
48
+ onAppCreate?: (app: UbeanApp) => void | Promise<void>;
49
+ /**
50
+ * Called after `app.init()` completes (all routes/middleware registered).
51
+ * Useful for starting queue workers, cron schedulers, etc.
52
+ */
53
+ onServerReady?: (app: UbeanApp) => void | Promise<void>;
54
+ }
55
+ /**
56
+ * Resolved server config — the normalized result of `defineServer`.
57
+ */
58
+ interface ResolvedServerConfig {
59
+ plugins: UbeanAppPlugin[];
60
+ hooks: ServerHooks;
61
+ onAppCreate?: (app: UbeanApp) => void | Promise<void>;
62
+ onServerReady?: (app: UbeanApp) => void | Promise<void>;
63
+ }
64
+ /**
65
+ * Define the backend server configuration.
66
+ *
67
+ * This is the server-side counterpart of `defineApp`. It allows users to
68
+ * inject plugins, register runtime hooks, and run startup/ready logic
69
+ * from a simple entry file (`src/server.ts`).
70
+ *
71
+ * @example
72
+ * ```ts
73
+ * // src/server.ts
74
+ * import { defineServer } from '@ubean/app';
75
+ *
76
+ * export default defineServer({
77
+ * plugins: [
78
+ * {
79
+ * name: 'my-plugin',
80
+ * setup(app) { app.use('/api/custom', handler); },
81
+ * ready(app) { /* routes registered *\/ }
82
+ * }
83
+ * ],
84
+ * hooks: {
85
+ * 'request:start': (c) => { console.log(c.req.method, c.req.path); }
86
+ * },
87
+ * onAppCreate: async (app) => { /* init db *\/ },
88
+ * onServerReady: async (app) => { /* start workers *\/ }
89
+ * });
90
+ * ```
91
+ */
92
+ declare function defineServer(options: DefineServerOptions): ResolvedServerConfig;
93
+ /**
94
+ * Create an empty server config — used as the fallback when no
95
+ * `src/server.ts` file exists.
96
+ */
97
+ declare function createDefaultServerConfig(): ResolvedServerConfig;
98
+ /**
99
+ * Merge multiple server configs (shared + mode-specific) into one.
100
+ * Plugins are concatenated; hooks are merged; mode-specific callbacks
101
+ * override shared ones.
102
+ */
103
+ declare function mergeServerConfigs(base: ResolvedServerConfig, ...configs: (ResolvedServerConfig | null | undefined)[]): ResolvedServerConfig;
104
+ //#endregion
105
+ //#region src/app.d.ts
106
+ interface UbeanRuntimeHooks {
107
+ 'app:created': (app: Hono<UbeanEnv$1>) => void | Promise<void>;
108
+ 'app:before:register': (app: Hono<UbeanEnv$1>) => void | Promise<void>;
109
+ 'app:after:register': (app: Hono<UbeanEnv$1>) => void | Promise<void>;
110
+ 'request:start': (c: Context<UbeanEnv$1>) => void | Promise<void>;
111
+ 'request:end': (c: Context<UbeanEnv$1>, res: Response) => void | Promise<void>;
112
+ 'request:error': (c: Context<UbeanEnv$1>, err: Error) => void | Promise<void>;
113
+ 'route:register': (route: {
114
+ method: string;
115
+ path: string;
116
+ handler: MiddlewareHandler[] | ComposedHandler$1 | Function;
117
+ meta?: RouteMeta$1;
118
+ }) => void | Promise<void>;
119
+ 'middleware:register': (mw: ScannedMiddleware$1) => void | Promise<void>;
120
+ error: (err: Error, c: Context<UbeanEnv$1>) => void | Promise<void>;
121
+ }
122
+ /**
123
+ * Page renderer shape (satisfied by `@ubean/ssr`'s `PageRenderer`).
124
+ * Declared as a type alias to the actual `@ubean/pages`'s `PageRenderer`
125
+ * interface so consumers don't need to install `@ubean/pages` separately
126
+ * to type-check against `UbeanAppOptions.pageRenderer`.
127
+ *
128
+ * Type-only import works even when `@ubean/pages` is an optional peerDep —
129
+ * only runtime imports would fail. Consumers without `@ubean/pages` will
130
+ * see this type fall back to `any` (TS' default for missing modules under
131
+ * `skipLibCheck`).
132
+ */
133
+ type PageRenderer = import('@ubean/pages').PageRenderer;
134
+ interface PageAssetTags {
135
+ head?: string;
136
+ bodyAttrs?: string;
137
+ htmlAttrs?: string;
138
+ }
139
+ interface UbeanAppOptions {
140
+ rootDir?: string;
141
+ routes?: ScannedApiRoute$1[];
142
+ middleware?: ScannedMiddleware$1[];
143
+ pages?: ScannedPageRoute$1[];
144
+ layouts?: ScannedLayout$1[];
145
+ crons?: ScannedCronTask[];
146
+ routeRules?: Record<string, RouteRule$1>;
147
+ plugins?: UbeanAppPlugin[];
148
+ routeLoaders?: Record<string, () => Promise<{
149
+ default?: ComposedHandler$1 | MiddlewareHandler[];
150
+ } | Record<string, ComposedHandler$1 | MiddlewareHandler[]>>>;
151
+ middlewareLoaders?: Record<string, () => Promise<{
152
+ default: UbeanMiddleware$1;
153
+ }>>;
154
+ pageLoaders?: Record<string, () => Promise<unknown>>;
155
+ pageRenderer?: PageRenderer | null;
156
+ pageAssetTags?: PageAssetTags;
157
+ publicDir?: string;
158
+ healthEndpoint?: boolean;
159
+ openAPI?: boolean | {
160
+ title?: string;
161
+ version?: string;
162
+ description?: string;
163
+ scalarPath?: string;
164
+ openAPIPath?: string;
165
+ };
166
+ i18nConfig?: {
167
+ strategy?: 'prefix' | 'prefix_except_default' | 'prefix_and_default' | 'no_prefix';
168
+ defaultLocale?: string;
169
+ locales?: string[];
170
+ };
171
+ }
172
+ interface UbeanAppPlugin {
173
+ name: string;
174
+ /** Called after app is created but before routes are registered */
175
+ setup?: (app: UbeanApp) => void | Promise<void>;
176
+ /** Called after all routes are registered */
177
+ ready?: (app: UbeanApp) => void | Promise<void>;
178
+ }
179
+ /**
180
+ * Structural view of the DevTools middleware returned by `createDevToolsMiddleware`
181
+ * in `@ubean/devtools`. Defined locally (rather than importing the real return
182
+ * type) so `ubean` has no static dependency on `@ubean/devtools` — the package is
183
+ * loaded via a dynamic `import()` only when DevTools is enabled.
184
+ *
185
+ * @deprecated DevTools is now a Vite plugin (`@ubean/devtools`'s
186
+ * `ubeanDevtoolsPlugin`) loaded by the dev runner. The Hono app no longer
187
+ * hosts DevTools middleware. This placeholder is kept only for type
188
+ * compatibility with external consumers that may still reference it.
189
+ */
190
+ interface DevToolsInstance {
191
+ rpc: {
192
+ setRoutes(routes: never[]): void;
193
+ setPages(pages: never[]): void;
194
+ setMiddlewares(middlewares: never[]): void;
195
+ setLayouts(layouts: never[]): void;
196
+ setCrons(crons: never[]): void;
197
+ setPresets(presets: string[]): void;
198
+ setOpenAPI(openAPI: {
199
+ enabled: boolean;
200
+ scalarPath?: string;
201
+ openAPIPath?: string;
202
+ }): void;
203
+ setCustomTabs(tabs: never[]): void;
204
+ updateInfo(partial: Record<string, unknown>): void;
205
+ };
206
+ middleware: (c: Context<UbeanEnv$1>, next: Next) => Promise<Response | void>;
207
+ }
208
+ declare class UbeanApp {
209
+ readonly hono: Hono<UbeanEnv$1>;
210
+ readonly hooks: Hookable<UbeanRuntimeHooks>;
211
+ readonly plugins: UbeanAppPlugin[];
212
+ readonly options: UbeanAppOptions;
213
+ private _ready;
214
+ constructor(options?: UbeanAppOptions);
215
+ private _setupBaseMiddleware;
216
+ init(): Promise<this>;
217
+ private _lazyInitPromise;
218
+ lazyInit(): Promise<this>;
219
+ resetInit(): void;
220
+ private _setupFallback;
221
+ use(path: string, ...handlers: MiddlewareHandler<UbeanEnv$1>[]): this;
222
+ use(...handlers: MiddlewareHandler<UbeanEnv$1>[]): this;
223
+ on(method: string | string[], path: string, ...handlers: MiddlewareHandler<UbeanEnv$1>[]): this;
224
+ get(path: string, ...handlers: MiddlewareHandler<UbeanEnv$1>[]): this;
225
+ post(path: string, ...handlers: MiddlewareHandler<UbeanEnv$1>[]): this;
226
+ put(path: string, ...handlers: MiddlewareHandler<UbeanEnv$1>[]): this;
227
+ patch(path: string, ...handlers: MiddlewareHandler<UbeanEnv$1>[]): this;
228
+ delete(path: string, ...handlers: MiddlewareHandler<UbeanEnv$1>[]): this;
229
+ fetch: Hono<UbeanEnv$1>['fetch'];
230
+ request: Hono<UbeanEnv$1>['request'];
231
+ }
232
+ interface AppPlugin {
233
+ name: string;
234
+ setup?: (app: Hono<UbeanEnv$1>) => void | Promise<void>;
235
+ }
236
+ declare function createUbeanApp(options?: UbeanAppOptions): UbeanApp;
237
+ //#endregion
238
+ export { type AppPlugin, type ComposedHandler, type DefineServerOptions, type DevToolsInstance, 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 };
package/dist/index.js ADDED
@@ -0,0 +1,240 @@
1
+ import { existsSync } from "node:fs";
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";
6
+ import { requestId } from "hono/request-id";
7
+ import { createHooks } from "hookable";
8
+ import { isAbsolute, join } from "pathe";
9
+ //#region src/define-server.ts
10
+ /**
11
+ * Apply a resolved server config to a `UbeanApp` instance.
12
+ *
13
+ * This must be called BEFORE `app.init()` so that:
14
+ * - `plugins` are available when `init()` calls `setup` / `ready`
15
+ * - `hooks` are registered before `init()` fires `app:created` etc.
16
+ * - `onAppCreate` runs before route registration
17
+ *
18
+ * `onServerReady` is NOT called here — the caller must invoke it after
19
+ * `app.init()` completes.
20
+ */
21
+ async function applyServerConfig(app, config) {
22
+ if (config.plugins.length > 0) app.plugins.push(...config.plugins);
23
+ for (const [name, handler] of Object.entries(config.hooks)) if (handler) app.hooks.hook(name, handler);
24
+ if (config.onAppCreate) await config.onAppCreate(app);
25
+ }
26
+ /**
27
+ * Define the backend server configuration.
28
+ *
29
+ * This is the server-side counterpart of `defineApp`. It allows users to
30
+ * inject plugins, register runtime hooks, and run startup/ready logic
31
+ * from a simple entry file (`src/server.ts`).
32
+ *
33
+ * @example
34
+ * ```ts
35
+ * // src/server.ts
36
+ * import { defineServer } from '@ubean/app';
37
+ *
38
+ * export default defineServer({
39
+ * plugins: [
40
+ * {
41
+ * name: 'my-plugin',
42
+ * setup(app) { app.use('/api/custom', handler); },
43
+ * ready(app) { /* routes registered *\/ }
44
+ * }
45
+ * ],
46
+ * hooks: {
47
+ * 'request:start': (c) => { console.log(c.req.method, c.req.path); }
48
+ * },
49
+ * onAppCreate: async (app) => { /* init db *\/ },
50
+ * onServerReady: async (app) => { /* start workers *\/ }
51
+ * });
52
+ * ```
53
+ */
54
+ function defineServer(options) {
55
+ return {
56
+ plugins: options.plugins || [],
57
+ hooks: options.hooks || {},
58
+ onAppCreate: options.onAppCreate,
59
+ onServerReady: options.onServerReady
60
+ };
61
+ }
62
+ /**
63
+ * Create an empty server config — used as the fallback when no
64
+ * `src/server.ts` file exists.
65
+ */
66
+ function createDefaultServerConfig() {
67
+ return {
68
+ plugins: [],
69
+ hooks: {}
70
+ };
71
+ }
72
+ /**
73
+ * Merge multiple server configs (shared + mode-specific) into one.
74
+ * Plugins are concatenated; hooks are merged; mode-specific callbacks
75
+ * override shared ones.
76
+ */
77
+ function mergeServerConfigs(base, ...configs) {
78
+ const result = {
79
+ plugins: [...base.plugins],
80
+ hooks: { ...base.hooks },
81
+ onAppCreate: base.onAppCreate,
82
+ onServerReady: base.onServerReady
83
+ };
84
+ for (const cfg of configs) {
85
+ if (!cfg) continue;
86
+ if (cfg.plugins) result.plugins.push(...cfg.plugins);
87
+ if (cfg.hooks) Object.assign(result.hooks, cfg.hooks);
88
+ if (cfg.onAppCreate) result.onAppCreate = cfg.onAppCreate;
89
+ if (cfg.onServerReady) result.onServerReady = cfg.onServerReady;
90
+ }
91
+ return result;
92
+ }
93
+ //#endregion
94
+ //#region src/app.ts
95
+ var UbeanApp = class {
96
+ hono;
97
+ hooks;
98
+ plugins;
99
+ options;
100
+ _ready = false;
101
+ constructor(options = {}) {
102
+ this.options = options;
103
+ this.hono = new Hono({ strict: false });
104
+ this.hooks = createHooks();
105
+ this.plugins = options.plugins || [];
106
+ this._setupBaseMiddleware();
107
+ this._setupFallback();
108
+ }
109
+ _setupBaseMiddleware() {
110
+ this.hono.use("*", requestId());
111
+ if (this.options.routeRules && Object.keys(this.options.routeRules).length > 0) {
112
+ this.hono.use("*", createRouteRulesMiddleware(this.options.routeRules));
113
+ const cacheRules = resolveRouteCacheRules(this.options.routeRules);
114
+ if (Object.keys(cacheRules).length > 0) {
115
+ useCacheStore(createMemoryStore());
116
+ this.hono.use("*", createCacheMiddleware({ rules: cacheRules }));
117
+ }
118
+ }
119
+ this.hono.use("*", createWebSocketMiddleware());
120
+ this.hono.use("*", async (c, next) => {
121
+ c.set("route", {
122
+ meta: { requiresAuth: true },
123
+ path: c.req.path,
124
+ method: c.req.method
125
+ });
126
+ await this.hooks.callHook("request:start", c);
127
+ try {
128
+ await next();
129
+ await this.hooks.callHook("request:end", c, c.res);
130
+ } catch (err) {
131
+ await this.hooks.callHook("request:error", c, err);
132
+ throw err;
133
+ }
134
+ });
135
+ if (this.options.healthEndpoint !== false) this.hono.get("/_health", (c) => {
136
+ return c.json({
137
+ status: "ok",
138
+ timestamp: Date.now()
139
+ });
140
+ });
141
+ }
142
+ async init() {
143
+ if (this._ready) return this;
144
+ await this.hooks.callHook("app:created", this.hono);
145
+ for (const plugin of this.plugins) if (plugin.setup) await plugin.setup(this);
146
+ await this.hooks.callHook("app:before:register", this.hono);
147
+ if (this.options.publicDir) {
148
+ const publicDir = isAbsolute(this.options.publicDir) ? this.options.publicDir : this.options.rootDir ? join(this.options.rootDir, this.options.publicDir) : this.options.publicDir;
149
+ if (existsSync(publicDir)) this.hono.use("/*", serveStatic({ publicDir }));
150
+ }
151
+ const registerOpts = {
152
+ routes: this.options.routes || [],
153
+ middleware: this.options.middleware || [],
154
+ pages: this.options.pages || [],
155
+ layouts: this.options.layouts || [],
156
+ routeLoaders: this.options.routeLoaders || {},
157
+ middlewareLoaders: this.options.middlewareLoaders || {},
158
+ pageLoaders: this.options.pageLoaders || {},
159
+ pageRenderer: this.options.pageRenderer ?? null,
160
+ pageAssetTags: this.options.pageAssetTags ?? {},
161
+ i18nConfig: this.options.i18nConfig
162
+ };
163
+ await registerRoutes(this, registerOpts);
164
+ if (this.options.openAPI) {
165
+ const openAPIOpts = typeof this.options.openAPI === "object" ? this.options.openAPI : {};
166
+ registerOpenAPIRoutes(this.hono, openAPIOpts);
167
+ }
168
+ await this.hooks.callHook("app:after:register", this.hono);
169
+ setInternalFetcher((req) => this.hono.fetch(req));
170
+ for (const plugin of this.plugins) if (plugin.ready) await plugin.ready(this);
171
+ this._ready = true;
172
+ return this;
173
+ }
174
+ _lazyInitPromise = null;
175
+ lazyInit() {
176
+ if (this._ready) return Promise.resolve(this);
177
+ if (!this._lazyInitPromise) this._lazyInitPromise = this.init();
178
+ return this._lazyInitPromise;
179
+ }
180
+ resetInit() {
181
+ this._ready = false;
182
+ this._lazyInitPromise = null;
183
+ }
184
+ _setupFallback() {
185
+ this.hono.notFound((c) => {
186
+ return c.json({
187
+ error: "Not Found",
188
+ path: c.req.path,
189
+ method: c.req.method
190
+ }, 404);
191
+ });
192
+ this.hono.onError((err, c) => {
193
+ this.hooks.callHook("error", err, c);
194
+ if (isUbeanError(err)) return errorToResponse(c, err);
195
+ return errorToResponse(c, new UbeanError(500, err.message || "Internal Server Error"));
196
+ });
197
+ }
198
+ use(pathOrHandler, ...handlers) {
199
+ this.hono.use(pathOrHandler, ...handlers);
200
+ return this;
201
+ }
202
+ on(method, path, ...handlers) {
203
+ const methods = Array.isArray(method) ? method : [method];
204
+ for (const m of methods) this.hono.on(m, path, ...handlers);
205
+ return this;
206
+ }
207
+ get(path, ...handlers) {
208
+ this.hono.get(path, ...handlers);
209
+ return this;
210
+ }
211
+ post(path, ...handlers) {
212
+ this.hono.post(path, ...handlers);
213
+ return this;
214
+ }
215
+ put(path, ...handlers) {
216
+ this.hono.put(path, ...handlers);
217
+ return this;
218
+ }
219
+ patch(path, ...handlers) {
220
+ this.hono.patch(path, ...handlers);
221
+ return this;
222
+ }
223
+ delete(path, ...handlers) {
224
+ this.hono.delete(path, ...handlers);
225
+ return this;
226
+ }
227
+ fetch = async (...args) => {
228
+ await this.lazyInit();
229
+ return this.hono.fetch(...args);
230
+ };
231
+ request = async (...args) => {
232
+ await this.lazyInit();
233
+ return this.hono.request(...args);
234
+ };
235
+ };
236
+ function createUbeanApp(options = {}) {
237
+ return new UbeanApp(options);
238
+ }
239
+ //#endregion
240
+ export { UbeanApp, applyServerConfig, createDefaultServerConfig, createUbeanApp, defineServer, mergeServerConfigs };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ubean/app",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Hono app factory and server config for ubean (createUbeanApp, defineServer)",
5
5
  "files": [
6
6
  "dist"
@@ -19,21 +19,21 @@
19
19
  "hono": "4.12.32",
20
20
  "hookable": "^6.1.1",
21
21
  "pathe": "^2.0.3",
22
- "@ubean/server": "0.1.2",
23
- "@ubean/error": "0.1.2",
24
- "@ubean/types": "0.1.2",
25
- "@ubean/api-routes": "0.1.2"
22
+ "@ubean/api-routes": "0.1.4",
23
+ "@ubean/error": "0.1.4",
24
+ "@ubean/types": "0.1.4",
25
+ "@ubean/server": "0.1.4"
26
26
  },
27
27
  "devDependencies": {
28
28
  "@types/node": "^26.1.1",
29
29
  "typescript": "6.0.3",
30
30
  "vite-plus": "0.2.6",
31
- "@ubean/pages": "0.1.2",
32
- "@ubean/routing": "0.1.2"
31
+ "@ubean/pages": "0.1.4",
32
+ "@ubean/routing": "0.1.4"
33
33
  },
34
34
  "peerDependencies": {
35
- "@ubean/pages": "0.1.2",
36
- "@ubean/routing": "0.1.2"
35
+ "@ubean/pages": "0.1.4",
36
+ "@ubean/routing": "0.1.4"
37
37
  },
38
38
  "peerDependenciesMeta": {
39
39
  "@ubean/routing": {