@objectstack/plugin-hono-server 3.2.8 → 3.3.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.
- package/.turbo/turbo-build.log +10 -10
- package/CHANGELOG.md +15 -0
- package/dist/index.d.mts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +31 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +31 -6
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/adapter.ts +31 -7
- package/src/hono-plugin.ts +7 -4
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @objectstack/plugin-hono-server@3.
|
|
2
|
+
> @objectstack/plugin-hono-server@3.3.0 build /home/runner/work/spec/spec/packages/plugins/plugin-hono-server
|
|
3
3
|
> tsup --config ../../../tsup.config.ts
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/index.ts
|
|
@@ -10,13 +10,13 @@
|
|
|
10
10
|
[34mCLI[39m Cleaning output folder
|
|
11
11
|
[34mESM[39m Build start
|
|
12
12
|
[34mCJS[39m Build start
|
|
13
|
-
[
|
|
14
|
-
[
|
|
15
|
-
[
|
|
16
|
-
[
|
|
17
|
-
[
|
|
18
|
-
[
|
|
13
|
+
[32mESM[39m [1mdist/index.mjs [22m[32m13.25 KB[39m
|
|
14
|
+
[32mESM[39m [1mdist/index.mjs.map [22m[32m26.13 KB[39m
|
|
15
|
+
[32mESM[39m ⚡️ Build success in 80ms
|
|
16
|
+
[32mCJS[39m [1mdist/index.js [22m[32m14.19 KB[39m
|
|
17
|
+
[32mCJS[39m [1mdist/index.js.map [22m[32m26.12 KB[39m
|
|
18
|
+
[32mCJS[39m ⚡️ Build success in 85ms
|
|
19
19
|
[34mDTS[39m Build start
|
|
20
|
-
[32mDTS[39m ⚡️ Build success in
|
|
21
|
-
[32mDTS[39m [1mdist/index.d.mts [22m[32m3.
|
|
22
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[32m3.
|
|
20
|
+
[32mDTS[39m ⚡️ Build success in 19222ms
|
|
21
|
+
[32mDTS[39m [1mdist/index.d.mts [22m[32m3.29 KB[39m
|
|
22
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m3.29 KB[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# @objectstack/plugin-hono-server
|
|
2
2
|
|
|
3
|
+
## 3.3.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- @objectstack/spec@3.3.0
|
|
8
|
+
- @objectstack/core@3.3.0
|
|
9
|
+
|
|
10
|
+
## 3.2.9
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- 0bc7b0c: fix port confict
|
|
15
|
+
- @objectstack/spec@3.2.9
|
|
16
|
+
- @objectstack/core@3.2.9
|
|
17
|
+
|
|
3
18
|
## 3.2.8
|
|
4
19
|
|
|
5
20
|
### Patch Changes
|
package/dist/index.d.mts
CHANGED
|
@@ -100,6 +100,7 @@ declare class HonoHttpServer implements IHttpServer {
|
|
|
100
100
|
*/
|
|
101
101
|
mount(path: string, subApp: Hono): void;
|
|
102
102
|
listen(port: number): Promise<void>;
|
|
103
|
+
private tryListen;
|
|
103
104
|
getPort(): number;
|
|
104
105
|
getRawApp(): Hono<hono_types.BlankEnv, hono_types.BlankSchema, "/">;
|
|
105
106
|
close(): Promise<void>;
|
package/dist/index.d.ts
CHANGED
|
@@ -100,6 +100,7 @@ declare class HonoHttpServer implements IHttpServer {
|
|
|
100
100
|
*/
|
|
101
101
|
mount(path: string, subApp: Hono): void;
|
|
102
102
|
listen(port: number): Promise<void>;
|
|
103
|
+
private tryListen;
|
|
103
104
|
getPort(): number;
|
|
104
105
|
getRawApp(): Hono<hono_types.BlankEnv, hono_types.BlankSchema, "/">;
|
|
105
106
|
close(): Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -137,18 +137,40 @@ var HonoHttpServer = class {
|
|
|
137
137
|
this.app.route(path2, subApp);
|
|
138
138
|
}
|
|
139
139
|
async listen(port) {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
140
|
+
if (this.staticRoot) {
|
|
141
|
+
this.app.get("/*", (0, import_serve_static.serveStatic)({ root: this.staticRoot }));
|
|
142
|
+
}
|
|
143
|
+
const targetPort = port || this.port;
|
|
144
|
+
const maxRetries = 20;
|
|
145
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
146
|
+
const tryPort = targetPort + attempt;
|
|
147
|
+
try {
|
|
148
|
+
await this.tryListen(tryPort);
|
|
149
|
+
return;
|
|
150
|
+
} catch (err) {
|
|
151
|
+
if (err.code === "EADDRINUSE" && attempt < maxRetries - 1) {
|
|
152
|
+
if (this.server && typeof this.server.close === "function") {
|
|
153
|
+
this.server.close();
|
|
154
|
+
}
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
throw err;
|
|
143
158
|
}
|
|
144
|
-
|
|
145
|
-
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
tryListen(port) {
|
|
162
|
+
return new Promise((resolve2, reject) => {
|
|
163
|
+
const server = (0, import_node_server.serve)({
|
|
146
164
|
fetch: this.app.fetch,
|
|
147
|
-
port
|
|
165
|
+
port
|
|
148
166
|
}, (info) => {
|
|
149
167
|
this.listeningPort = info.port;
|
|
150
168
|
resolve2();
|
|
151
169
|
});
|
|
170
|
+
this.server = server;
|
|
171
|
+
server.on("error", (err) => {
|
|
172
|
+
reject(err);
|
|
173
|
+
});
|
|
152
174
|
});
|
|
153
175
|
}
|
|
154
176
|
getPort() {
|
|
@@ -289,6 +311,9 @@ var HonoServerPlugin = class {
|
|
|
289
311
|
ctx.logger.debug("Starting HTTP server", { port });
|
|
290
312
|
await this.server.listen(port);
|
|
291
313
|
const actualPort = this.server.getPort();
|
|
314
|
+
if (actualPort !== port) {
|
|
315
|
+
ctx.logger.warn(`Port ${port} is in use, using port ${actualPort} instead`);
|
|
316
|
+
}
|
|
292
317
|
ctx.logger.info("HTTP server started successfully", {
|
|
293
318
|
port: actualPort,
|
|
294
319
|
url: `http://localhost:${actualPort}`
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/adapter.ts","../src/hono-plugin.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nexport * from './hono-plugin';\nexport * from './adapter';\n\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n// Export IHttpServer from core\nexport * from '@objectstack/core';\n\nimport { \n IHttpServer, \n RouteHandler, \n Middleware \n} from '@objectstack/core';\nimport { Hono } from 'hono';\nimport { serve } from '@hono/node-server';\nimport { serveStatic } from '@hono/node-server/serve-static';\n\n/**\n * Hono Implementation of IHttpServer\n */\nexport class HonoHttpServer implements IHttpServer {\n private app: Hono;\n private server: any;\n private listeningPort: number | undefined;\n\n constructor(\n private port: number = 3000,\n private staticRoot?: string\n ) {\n this.app = new Hono();\n }\n\n // internal helper to convert standard handler to Hono handler\n private wrap(handler: RouteHandler) {\n return async (c: any) => {\n let body: any = {};\n \n // Try to parse JSON body first if content-type is JSON\n if (c.req.header('content-type')?.includes('application/json')) {\n try { \n body = await c.req.json(); \n } catch(e) {\n // If JSON parsing fails, try parseBody\n try { \n body = await c.req.parseBody(); \n } catch(e2) {}\n }\n } else {\n // For non-JSON content types, use parseBody\n try { \n body = await c.req.parseBody(); \n } catch(e) {}\n }\n \n const req = {\n params: c.req.param(),\n query: c.req.query(),\n body,\n headers: c.req.header(),\n method: c.req.method,\n path: c.req.path\n };\n\n let capturedResponse: any;\n\n const res = {\n json: (data: any) => { capturedResponse = c.json(data); },\n send: (data: string) => { capturedResponse = c.html(data); },\n status: (code: number) => { c.status(code); return res; },\n header: (name: string, value: string) => { c.header(name, value); return res; }\n };\n\n await handler(req as any, res as any);\n return capturedResponse;\n };\n }\n\n get(path: string, handler: RouteHandler) {\n this.app.get(path, this.wrap(handler));\n }\n post(path: string, handler: RouteHandler) {\n this.app.post(path, this.wrap(handler));\n }\n put(path: string, handler: RouteHandler) {\n this.app.put(path, this.wrap(handler));\n }\n delete(path: string, handler: RouteHandler) {\n this.app.delete(path, this.wrap(handler));\n }\n patch(path: string, handler: RouteHandler) {\n this.app.patch(path, this.wrap(handler));\n }\n \n use(pathOrHandler: string | Middleware, handler?: Middleware) {\n if (typeof pathOrHandler === 'string' && handler) {\n // Path based middleware\n // Hono middleware signature is different (c, next) => ...\n this.app.use(pathOrHandler, async (c, next) => {\n // Simplistic conversion\n await handler({} as any, {} as any, next);\n });\n } else if (typeof pathOrHandler === 'function') {\n // Global middleware\n this.app.use('*', async (c, next) => {\n await pathOrHandler({} as any, {} as any, next);\n });\n }\n }\n\n /**\n * Mount a sub-application or router\n */\n mount(path: string, subApp: Hono) {\n this.app.route(path, subApp);\n }\n\n\n async listen(port: number) {\n return new Promise<void>((resolve) => {\n if (this.staticRoot) {\n this.app.get('/*', serveStatic({ root: this.staticRoot }));\n }\n \n const targetPort = port || this.port;\n this.server = serve({\n fetch: this.app.fetch,\n port: targetPort\n }, (info) => {\n this.listeningPort = info.port;\n resolve();\n });\n });\n }\n\n getPort() {\n return this.listeningPort || this.port;\n }\n\n // Expose raw app for scenarios where standard interface is not enough\n getRawApp() {\n return this.app;\n }\n\n async close() {\n if (this.server && typeof this.server.close === 'function') {\n this.server.close();\n }\n }\n\n\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { Plugin, PluginContext, IHttpServer } from '@objectstack/core';\nimport {\n RestServerConfig,\n} from '@objectstack/spec/api';\nimport { HonoHttpServer } from './adapter';\nimport { serveStatic } from '@hono/node-server/serve-static';\nimport * as fs from 'fs';\nimport * as path from 'path';\n\nexport interface StaticMount {\n root: string;\n path?: string;\n rewrite?: boolean;\n spa?: boolean;\n}\n\nexport interface HonoPluginOptions {\n port?: number;\n staticRoot?: string;\n /**\n * Multiple static resource mounts\n */\n staticMounts?: StaticMount[];\n /**\n * REST server configuration\n * Controls automatic endpoint generation and API behavior\n */\n restConfig?: RestServerConfig;\n /**\n * Whether to register standard ObjectStack CRUD endpoints\n * @default true\n */\n registerStandardEndpoints?: boolean;\n /**\n * Whether to load endpoints from API Registry\n * @default true\n */\n useApiRegistry?: boolean;\n\n /**\n * Whether to enable SPA fallback\n * If true, returns index.html for non-API 404s\n * @default false\n */\n spaFallback?: boolean;\n}\n\n/**\n * Hono Server Plugin\n * \n * Provides HTTP server capabilities using Hono framework.\n * Registers the IHttpServer service so other plugins can register routes.\n * \n * Route registration is handled by plugins:\n * - `@objectstack/rest` → CRUD, metadata, discovery, UI, batch\n * - `createDispatcherPlugin()` → auth, graphql, analytics, packages, etc.\n */\nexport class HonoServerPlugin implements Plugin {\n name = 'com.objectstack.server.hono';\n type = 'server';\n version = '0.9.0';\n \n // Constants\n private static readonly DEFAULT_ENDPOINT_PRIORITY = 100;\n private static readonly CORE_ENDPOINT_PRIORITY = 950;\n private static readonly DISCOVERY_ENDPOINT_PRIORITY = 900;\n \n private options: HonoPluginOptions;\n private server: HonoHttpServer;\n\n constructor(options: HonoPluginOptions = {}) {\n this.options = { \n port: 3000,\n registerStandardEndpoints: true,\n useApiRegistry: true,\n spaFallback: false,\n ...options\n };\n // We handle static root manually in start() to support SPA fallback\n this.server = new HonoHttpServer(this.options.port);\n }\n\n /**\n * Init phase - Setup HTTP server and register as service\n */\n init = async (ctx: PluginContext) => {\n ctx.logger.debug('Initializing Hono server plugin', { \n port: this.options.port,\n staticRoot: this.options.staticRoot \n });\n \n // Register HTTP server service as IHttpServer\n // Register as 'http.server' to match core requirements\n ctx.registerService('http.server', this.server);\n // Alias 'http-server' for backward compatibility\n ctx.registerService('http-server', this.server);\n ctx.logger.debug('HTTP server service registered', { serviceName: 'http.server' });\n }\n\n /**\n * Start phase - Configure static files and start listening\n */\n start = async (ctx: PluginContext) => {\n ctx.logger.debug('Starting Hono server plugin');\n\n // Configure Static Files & SPA Fallback\n const mounts: StaticMount[] = this.options.staticMounts || [];\n\n // Auto-discover UI Plugins\n try {\n const rawKernel = ctx.getKernel() as any;\n if (rawKernel.plugins) {\n const loadedPlugins = rawKernel.plugins instanceof Map \n ? Array.from(rawKernel.plugins.values()) \n : Array.isArray(rawKernel.plugins) ? rawKernel.plugins : Object.values(rawKernel.plugins);\n\n for (const plugin of (loadedPlugins as any[])) {\n // Check for UI Plugin signature\n // Support legacy 'ui-plugin' and new 'ui' type\n if ((plugin.type === 'ui' || plugin.type === 'ui-plugin') && plugin.staticPath) {\n // Derive base route from name: @org/console -> console\n const slug = plugin.slug || plugin.name.split('/').pop();\n const baseRoute = `/${slug}`;\n \n ctx.logger.debug(`Auto-mounting UI Plugin: ${plugin.name}`, { \n path: baseRoute, \n root: plugin.staticPath \n });\n\n mounts.push({\n root: plugin.staticPath,\n path: baseRoute,\n rewrite: true, // Strip prefix: /console/assets/x -> /assets/x\n spa: true\n });\n\n // Handle Default Plugin Redirect\n if (plugin.default || plugin.isDefault) {\n const rawApp = this.server.getRawApp();\n rawApp.get('/', (c) => c.redirect(baseRoute));\n ctx.logger.debug(`Set default UI redirect: / -> ${baseRoute}`);\n }\n }\n }\n }\n } catch (err: any) {\n ctx.logger.warn('Failed to auto-discover UI plugins', { error: err.message || err });\n }\n\n // Backward compatibility for staticRoot\n if (this.options.staticRoot) {\n mounts.push({\n root: this.options.staticRoot,\n path: '/',\n rewrite: false,\n spa: this.options.spaFallback\n });\n }\n\n if (mounts.length > 0) {\n const rawApp = this.server.getRawApp();\n \n for (const mount of mounts) {\n const mountRoot = path.resolve(process.cwd(), mount.root);\n\n if (!fs.existsSync(mountRoot)) {\n ctx.logger.warn(`Static mount root not found: ${mountRoot}. Skipping.`);\n continue;\n }\n\n const mountPath = mount.path || '/';\n const normalizedPath = mountPath.startsWith('/') ? mountPath : `/${mountPath}`;\n const routePattern = normalizedPath === '/' ? '/*' : `${normalizedPath.replace(/\\/$/, '')}/*`;\n \n // Routes to register: both /mount and /mount/*\n const routes = normalizedPath === '/' ? [routePattern] : [normalizedPath, routePattern];\n\n ctx.logger.debug('Mounting static files', { \n to: routes, \n from: mountRoot, \n rewrite: mount.rewrite, \n spa: mount.spa \n });\n\n routes.forEach(route => {\n // 1. Serve Static Files\n rawApp.get(\n route, \n serveStatic({ \n root: mount.root,\n rewriteRequestPath: (reqPath) => {\n if (mount.rewrite && normalizedPath !== '/') {\n // /console/assets/style.css -> /assets/style.css\n if (reqPath.startsWith(normalizedPath)) {\n return reqPath.substring(normalizedPath.length) || '/';\n }\n }\n return reqPath;\n }\n })\n );\n\n // 2. SPA Fallback (Scoped)\n if (mount.spa) {\n rawApp.get(route, async (c, next) => {\n // Skip if API path check\n const config = this.options.restConfig || {};\n const basePath = config.api?.basePath || '/api';\n \n if (c.req.path.startsWith(basePath)) {\n return next();\n }\n\n return serveStatic({ \n root: mount.root,\n rewriteRequestPath: () => 'index.html'\n })(c, next);\n });\n }\n });\n }\n }\n\n // Start server on kernel:ready hook\n ctx.hook('kernel:ready', async () => {\n // Register standard endpoints before starting to listen\n if (this.options.registerStandardEndpoints) {\n this.registerDiscoveryAndCrudEndpoints(ctx);\n }\n\n const port = this.options.port ?? 3000;\n ctx.logger.debug('Starting HTTP server', { port });\n \n await this.server.listen(port);\n \n const actualPort = this.server.getPort();\n ctx.logger.info('HTTP server started successfully', { \n port: actualPort, \n url: `http://localhost:${actualPort}` \n });\n });\n }\n\n /**\n * Register discovery and basic CRUD endpoints.\n * Called when `registerStandardEndpoints` is true, before the server starts listening.\n */\n private registerDiscoveryAndCrudEndpoints(ctx: PluginContext) {\n const rawApp = this.server.getRawApp();\n const prefix = '/api/v1';\n\n // Build the standard discovery response\n const discovery = {\n version: 'v1',\n apiName: 'ObjectStack API',\n routes: {\n data: `${prefix}/data`,\n metadata: `${prefix}/meta`,\n auth: `${prefix}/auth`,\n packages: `${prefix}/packages`,\n analytics: `${prefix}/analytics`,\n realtime: `${prefix}/realtime`,\n workflow: `${prefix}/workflow`,\n automation: `${prefix}/automation`,\n ai: `${prefix}/ai`,\n notifications: `${prefix}/notifications`,\n i18n: `${prefix}/i18n`,\n storage: `${prefix}/storage`,\n ui: `${prefix}/ui`,\n },\n };\n\n // Discovery endpoints\n rawApp.get('/.well-known/objectstack', (c: any) => c.json({ data: discovery }));\n rawApp.get(prefix, (c: any) => c.json({ data: discovery }));\n\n ctx.logger.info('Registered discovery endpoints', { prefix });\n\n // Basic CRUD data endpoints — delegate to kernel.broker when available\n const getBroker = () => (ctx.getKernel() as any).broker;\n\n // Create\n rawApp.post(`${prefix}/data/:object`, async (c: any) => {\n const broker = getBroker();\n if (!broker) return c.json({ error: 'Broker not available' }, 500);\n const object = c.req.param('object');\n const data = await c.req.json().catch(() => ({}));\n const result = await broker.call('data.create', { object, data }, {});\n return c.json(result);\n });\n\n // Get by ID\n rawApp.get(`${prefix}/data/:object/:id`, async (c: any) => {\n const broker = getBroker();\n if (!broker) return c.json({ error: 'Broker not available' }, 500);\n const object = c.req.param('object');\n const id = c.req.param('id');\n const result = await broker.call('data.get', { object, id }, {});\n return result ? c.json(result) : c.json({ error: 'Not found' }, 404);\n });\n\n // Find / List\n rawApp.get(`${prefix}/data/:object`, async (c: any) => {\n const broker = getBroker();\n if (!broker) return c.json({ error: 'Broker not available' }, 500);\n const object = c.req.param('object');\n const filters = c.req.query();\n const result = await broker.call('data.find', { object, filters }, {});\n return c.json(result);\n });\n\n ctx.logger.debug('Registered standard CRUD data endpoints', { prefix });\n }\n\n /**\n * Destroy phase - Stop server\n */\n async destroy() {\n this.server.close();\n // Note: Can't use ctx.logger here since we're in destroy\n console.log('[HonoServerPlugin] Server stopped');\n }\n}\n"],"mappings":"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","names":["path","resolve","import_serve_static"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/adapter.ts","../src/hono-plugin.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nexport * from './hono-plugin';\nexport * from './adapter';\n\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n// Export IHttpServer from core\nexport * from '@objectstack/core';\n\nimport { \n IHttpServer, \n RouteHandler, \n Middleware \n} from '@objectstack/core';\nimport { Hono } from 'hono';\nimport { serve } from '@hono/node-server';\nimport { serveStatic } from '@hono/node-server/serve-static';\n\n/**\n * Hono Implementation of IHttpServer\n */\nexport class HonoHttpServer implements IHttpServer {\n private app: Hono;\n private server: any;\n private listeningPort: number | undefined;\n\n constructor(\n private port: number = 3000,\n private staticRoot?: string\n ) {\n this.app = new Hono();\n }\n\n // internal helper to convert standard handler to Hono handler\n private wrap(handler: RouteHandler) {\n return async (c: any) => {\n let body: any = {};\n \n // Try to parse JSON body first if content-type is JSON\n if (c.req.header('content-type')?.includes('application/json')) {\n try { \n body = await c.req.json(); \n } catch(e) {\n // If JSON parsing fails, try parseBody\n try { \n body = await c.req.parseBody(); \n } catch(e2) {}\n }\n } else {\n // For non-JSON content types, use parseBody\n try { \n body = await c.req.parseBody(); \n } catch(e) {}\n }\n \n const req = {\n params: c.req.param(),\n query: c.req.query(),\n body,\n headers: c.req.header(),\n method: c.req.method,\n path: c.req.path\n };\n\n let capturedResponse: any;\n\n const res = {\n json: (data: any) => { capturedResponse = c.json(data); },\n send: (data: string) => { capturedResponse = c.html(data); },\n status: (code: number) => { c.status(code); return res; },\n header: (name: string, value: string) => { c.header(name, value); return res; }\n };\n\n await handler(req as any, res as any);\n return capturedResponse;\n };\n }\n\n get(path: string, handler: RouteHandler) {\n this.app.get(path, this.wrap(handler));\n }\n post(path: string, handler: RouteHandler) {\n this.app.post(path, this.wrap(handler));\n }\n put(path: string, handler: RouteHandler) {\n this.app.put(path, this.wrap(handler));\n }\n delete(path: string, handler: RouteHandler) {\n this.app.delete(path, this.wrap(handler));\n }\n patch(path: string, handler: RouteHandler) {\n this.app.patch(path, this.wrap(handler));\n }\n \n use(pathOrHandler: string | Middleware, handler?: Middleware) {\n if (typeof pathOrHandler === 'string' && handler) {\n // Path based middleware\n // Hono middleware signature is different (c, next) => ...\n this.app.use(pathOrHandler, async (c, next) => {\n // Simplistic conversion\n await handler({} as any, {} as any, next);\n });\n } else if (typeof pathOrHandler === 'function') {\n // Global middleware\n this.app.use('*', async (c, next) => {\n await pathOrHandler({} as any, {} as any, next);\n });\n }\n }\n\n /**\n * Mount a sub-application or router\n */\n mount(path: string, subApp: Hono) {\n this.app.route(path, subApp);\n }\n\n\n async listen(port: number) {\n if (this.staticRoot) {\n this.app.get('/*', serveStatic({ root: this.staticRoot }));\n }\n\n const targetPort = port || this.port;\n const maxRetries = 20;\n\n for (let attempt = 0; attempt < maxRetries; attempt++) {\n const tryPort = targetPort + attempt;\n try {\n await this.tryListen(tryPort);\n return;\n } catch (err: any) {\n if (err.code === 'EADDRINUSE' && attempt < maxRetries - 1) {\n if (this.server && typeof this.server.close === 'function') {\n this.server.close();\n }\n continue;\n }\n throw err;\n }\n }\n }\n\n private tryListen(port: number): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const server = serve({\n fetch: this.app.fetch,\n port\n }, (info) => {\n this.listeningPort = info.port;\n resolve();\n });\n this.server = server;\n server.on('error', (err: any) => {\n reject(err);\n });\n });\n }\n\n getPort() {\n return this.listeningPort || this.port;\n }\n\n // Expose raw app for scenarios where standard interface is not enough\n getRawApp() {\n return this.app;\n }\n\n async close() {\n if (this.server && typeof this.server.close === 'function') {\n this.server.close();\n }\n }\n\n\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { Plugin, PluginContext, IHttpServer } from '@objectstack/core';\nimport {\n RestServerConfig,\n} from '@objectstack/spec/api';\nimport { HonoHttpServer } from './adapter';\nimport { serveStatic } from '@hono/node-server/serve-static';\nimport * as fs from 'fs';\nimport * as path from 'path';\n\nexport interface StaticMount {\n root: string;\n path?: string;\n rewrite?: boolean;\n spa?: boolean;\n}\n\nexport interface HonoPluginOptions {\n port?: number;\n staticRoot?: string;\n /**\n * Multiple static resource mounts\n */\n staticMounts?: StaticMount[];\n /**\n * REST server configuration\n * Controls automatic endpoint generation and API behavior\n */\n restConfig?: RestServerConfig;\n /**\n * Whether to register standard ObjectStack CRUD endpoints\n * @default true\n */\n registerStandardEndpoints?: boolean;\n /**\n * Whether to load endpoints from API Registry\n * @default true\n */\n useApiRegistry?: boolean;\n\n /**\n * Whether to enable SPA fallback\n * If true, returns index.html for non-API 404s\n * @default false\n */\n spaFallback?: boolean;\n}\n\n/**\n * Hono Server Plugin\n * \n * Provides HTTP server capabilities using Hono framework.\n * Registers the IHttpServer service so other plugins can register routes.\n * \n * Route registration is handled by plugins:\n * - `@objectstack/rest` → CRUD, metadata, discovery, UI, batch\n * - `createDispatcherPlugin()` → auth, graphql, analytics, packages, etc.\n */\nexport class HonoServerPlugin implements Plugin {\n name = 'com.objectstack.server.hono';\n type = 'server';\n version = '0.9.0';\n \n // Constants\n private static readonly DEFAULT_ENDPOINT_PRIORITY = 100;\n private static readonly CORE_ENDPOINT_PRIORITY = 950;\n private static readonly DISCOVERY_ENDPOINT_PRIORITY = 900;\n \n private options: HonoPluginOptions;\n private server: HonoHttpServer;\n\n constructor(options: HonoPluginOptions = {}) {\n this.options = { \n port: 3000,\n registerStandardEndpoints: true,\n useApiRegistry: true,\n spaFallback: false,\n ...options\n };\n // We handle static root manually in start() to support SPA fallback\n this.server = new HonoHttpServer(this.options.port);\n }\n\n /**\n * Init phase - Setup HTTP server and register as service\n */\n init = async (ctx: PluginContext) => {\n ctx.logger.debug('Initializing Hono server plugin', { \n port: this.options.port,\n staticRoot: this.options.staticRoot \n });\n \n // Register HTTP server service as IHttpServer\n // Register as 'http.server' to match core requirements\n ctx.registerService('http.server', this.server);\n // Alias 'http-server' for backward compatibility\n ctx.registerService('http-server', this.server);\n ctx.logger.debug('HTTP server service registered', { serviceName: 'http.server' });\n }\n\n /**\n * Start phase - Configure static files and start listening\n */\n start = async (ctx: PluginContext) => {\n ctx.logger.debug('Starting Hono server plugin');\n\n // Configure Static Files & SPA Fallback\n const mounts: StaticMount[] = this.options.staticMounts || [];\n\n // Auto-discover UI Plugins\n try {\n const rawKernel = ctx.getKernel() as any;\n if (rawKernel.plugins) {\n const loadedPlugins = rawKernel.plugins instanceof Map \n ? Array.from(rawKernel.plugins.values()) \n : Array.isArray(rawKernel.plugins) ? rawKernel.plugins : Object.values(rawKernel.plugins);\n\n for (const plugin of (loadedPlugins as any[])) {\n // Check for UI Plugin signature\n // Support legacy 'ui-plugin' and new 'ui' type\n if ((plugin.type === 'ui' || plugin.type === 'ui-plugin') && plugin.staticPath) {\n // Derive base route from name: @org/console -> console\n const slug = plugin.slug || plugin.name.split('/').pop();\n const baseRoute = `/${slug}`;\n \n ctx.logger.debug(`Auto-mounting UI Plugin: ${plugin.name}`, { \n path: baseRoute, \n root: plugin.staticPath \n });\n\n mounts.push({\n root: plugin.staticPath,\n path: baseRoute,\n rewrite: true, // Strip prefix: /console/assets/x -> /assets/x\n spa: true\n });\n\n // Handle Default Plugin Redirect\n if (plugin.default || plugin.isDefault) {\n const rawApp = this.server.getRawApp();\n rawApp.get('/', (c) => c.redirect(baseRoute));\n ctx.logger.debug(`Set default UI redirect: / -> ${baseRoute}`);\n }\n }\n }\n }\n } catch (err: any) {\n ctx.logger.warn('Failed to auto-discover UI plugins', { error: err.message || err });\n }\n\n // Backward compatibility for staticRoot\n if (this.options.staticRoot) {\n mounts.push({\n root: this.options.staticRoot,\n path: '/',\n rewrite: false,\n spa: this.options.spaFallback\n });\n }\n\n if (mounts.length > 0) {\n const rawApp = this.server.getRawApp();\n \n for (const mount of mounts) {\n const mountRoot = path.resolve(process.cwd(), mount.root);\n\n if (!fs.existsSync(mountRoot)) {\n ctx.logger.warn(`Static mount root not found: ${mountRoot}. Skipping.`);\n continue;\n }\n\n const mountPath = mount.path || '/';\n const normalizedPath = mountPath.startsWith('/') ? mountPath : `/${mountPath}`;\n const routePattern = normalizedPath === '/' ? '/*' : `${normalizedPath.replace(/\\/$/, '')}/*`;\n \n // Routes to register: both /mount and /mount/*\n const routes = normalizedPath === '/' ? [routePattern] : [normalizedPath, routePattern];\n\n ctx.logger.debug('Mounting static files', { \n to: routes, \n from: mountRoot, \n rewrite: mount.rewrite, \n spa: mount.spa \n });\n\n routes.forEach(route => {\n // 1. Serve Static Files\n rawApp.get(\n route, \n serveStatic({ \n root: mount.root,\n rewriteRequestPath: (reqPath) => {\n if (mount.rewrite && normalizedPath !== '/') {\n // /console/assets/style.css -> /assets/style.css\n if (reqPath.startsWith(normalizedPath)) {\n return reqPath.substring(normalizedPath.length) || '/';\n }\n }\n return reqPath;\n }\n })\n );\n\n // 2. SPA Fallback (Scoped)\n if (mount.spa) {\n rawApp.get(route, async (c, next) => {\n // Skip if API path check\n const config = this.options.restConfig || {};\n const basePath = config.api?.basePath || '/api';\n \n if (c.req.path.startsWith(basePath)) {\n return next();\n }\n\n return serveStatic({ \n root: mount.root,\n rewriteRequestPath: () => 'index.html'\n })(c, next);\n });\n }\n });\n }\n }\n\n // Start server on kernel:ready hook\n ctx.hook('kernel:ready', async () => {\n // Register standard endpoints before starting to listen\n if (this.options.registerStandardEndpoints) {\n this.registerDiscoveryAndCrudEndpoints(ctx);\n }\n\n const port = this.options.port ?? 3000;\n ctx.logger.debug('Starting HTTP server', { port });\n \n await this.server.listen(port);\n\n const actualPort = this.server.getPort();\n if (actualPort !== port) {\n ctx.logger.warn(`Port ${port} is in use, using port ${actualPort} instead`);\n }\n ctx.logger.info('HTTP server started successfully', {\n port: actualPort,\n url: `http://localhost:${actualPort}`\n });\n });\n }\n\n /**\n * Register discovery and basic CRUD endpoints.\n * Called when `registerStandardEndpoints` is true, before the server starts listening.\n */\n private registerDiscoveryAndCrudEndpoints(ctx: PluginContext) {\n const rawApp = this.server.getRawApp();\n const prefix = '/api/v1';\n\n // Build the standard discovery response\n const discovery = {\n version: 'v1',\n apiName: 'ObjectStack API',\n routes: {\n data: `${prefix}/data`,\n metadata: `${prefix}/meta`,\n auth: `${prefix}/auth`,\n packages: `${prefix}/packages`,\n analytics: `${prefix}/analytics`,\n realtime: `${prefix}/realtime`,\n workflow: `${prefix}/workflow`,\n automation: `${prefix}/automation`,\n ai: `${prefix}/ai`,\n notifications: `${prefix}/notifications`,\n i18n: `${prefix}/i18n`,\n storage: `${prefix}/storage`,\n ui: `${prefix}/ui`,\n },\n };\n\n // Discovery endpoints\n rawApp.get('/.well-known/objectstack', (c: any) => c.json({ data: discovery }));\n rawApp.get(prefix, (c: any) => c.json({ data: discovery }));\n\n ctx.logger.info('Registered discovery endpoints', { prefix });\n\n // Basic CRUD data endpoints — delegate to kernel.broker when available\n const getBroker = () => (ctx.getKernel() as any).broker;\n\n // Create\n rawApp.post(`${prefix}/data/:object`, async (c: any) => {\n const broker = getBroker();\n if (!broker) return c.json({ error: 'Broker not available' }, 500);\n const object = c.req.param('object');\n const data = await c.req.json().catch(() => ({}));\n const result = await broker.call('data.create', { object, data }, {});\n return c.json(result);\n });\n\n // Get by ID\n rawApp.get(`${prefix}/data/:object/:id`, async (c: any) => {\n const broker = getBroker();\n if (!broker) return c.json({ error: 'Broker not available' }, 500);\n const object = c.req.param('object');\n const id = c.req.param('id');\n const result = await broker.call('data.get', { object, id }, {});\n return result ? c.json(result) : c.json({ error: 'Not found' }, 404);\n });\n\n // Find / List\n rawApp.get(`${prefix}/data/:object`, async (c: any) => {\n const broker = getBroker();\n if (!broker) return c.json({ error: 'Broker not available' }, 500);\n const object = c.req.param('object');\n const filters = c.req.query();\n const result = await broker.call('data.find', { object, filters }, {});\n return c.json(result);\n });\n\n ctx.logger.debug('Registered standard CRUD data endpoints', { prefix });\n }\n\n /**\n * Destroy phase - Stop server\n */\n async destroy() {\n this.server.close();\n // Note: Can't use ctx.logger here since we're in destroy\n console.log('[HonoServerPlugin] Server stopped');\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA;AAAA;AAAA;AAAA;AAGA,4BAAc;AAOd,kBAAqB;AACrB,yBAAsB;AACtB,0BAA4B;AAKrB,IAAM,iBAAN,MAA4C;AAAA,EAK/C,YACY,OAAe,KACf,YACV;AAFU;AACA;AANZ,wBAAQ;AACR,wBAAQ;AACR,wBAAQ;AAMJ,SAAK,MAAM,IAAI,iBAAK;AAAA,EACxB;AAAA;AAAA,EAGQ,KAAK,SAAuB;AAChC,WAAO,OAAO,MAAW;AACrB,UAAI,OAAY,CAAC;AAGjB,UAAI,EAAE,IAAI,OAAO,cAAc,GAAG,SAAS,kBAAkB,GAAG;AAC5D,YAAI;AACA,iBAAO,MAAM,EAAE,IAAI,KAAK;AAAA,QAC5B,SAAQ,GAAG;AAEP,cAAI;AACA,mBAAO,MAAM,EAAE,IAAI,UAAU;AAAA,UACjC,SAAQ,IAAI;AAAA,UAAC;AAAA,QACjB;AAAA,MACJ,OAAO;AAEH,YAAI;AACA,iBAAO,MAAM,EAAE,IAAI,UAAU;AAAA,QACjC,SAAQ,GAAG;AAAA,QAAC;AAAA,MAChB;AAEA,YAAM,MAAM;AAAA,QACR,QAAQ,EAAE,IAAI,MAAM;AAAA,QACpB,OAAO,EAAE,IAAI,MAAM;AAAA,QACnB;AAAA,QACA,SAAS,EAAE,IAAI,OAAO;AAAA,QACtB,QAAQ,EAAE,IAAI;AAAA,QACd,MAAM,EAAE,IAAI;AAAA,MAChB;AAEA,UAAI;AAEJ,YAAM,MAAM;AAAA,QACR,MAAM,CAAC,SAAc;AAAE,6BAAmB,EAAE,KAAK,IAAI;AAAA,QAAG;AAAA,QACxD,MAAM,CAAC,SAAiB;AAAE,6BAAmB,EAAE,KAAK,IAAI;AAAA,QAAG;AAAA,QAC3D,QAAQ,CAAC,SAAiB;AAAE,YAAE,OAAO,IAAI;AAAG,iBAAO;AAAA,QAAK;AAAA,QACxD,QAAQ,CAAC,MAAc,UAAkB;AAAE,YAAE,OAAO,MAAM,KAAK;AAAG,iBAAO;AAAA,QAAK;AAAA,MAClF;AAEA,YAAM,QAAQ,KAAY,GAAU;AACpC,aAAO;AAAA,IACX;AAAA,EACJ;AAAA,EAEA,IAAIA,OAAc,SAAuB;AACrC,SAAK,IAAI,IAAIA,OAAM,KAAK,KAAK,OAAO,CAAC;AAAA,EACzC;AAAA,EACA,KAAKA,OAAc,SAAuB;AACtC,SAAK,IAAI,KAAKA,OAAM,KAAK,KAAK,OAAO,CAAC;AAAA,EAC1C;AAAA,EACA,IAAIA,OAAc,SAAuB;AACrC,SAAK,IAAI,IAAIA,OAAM,KAAK,KAAK,OAAO,CAAC;AAAA,EACzC;AAAA,EACA,OAAOA,OAAc,SAAuB;AACxC,SAAK,IAAI,OAAOA,OAAM,KAAK,KAAK,OAAO,CAAC;AAAA,EAC5C;AAAA,EACA,MAAMA,OAAc,SAAuB;AACvC,SAAK,IAAI,MAAMA,OAAM,KAAK,KAAK,OAAO,CAAC;AAAA,EAC3C;AAAA,EAEA,IAAI,eAAoC,SAAsB;AAC1D,QAAI,OAAO,kBAAkB,YAAY,SAAS;AAG7C,WAAK,IAAI,IAAI,eAAe,OAAO,GAAG,SAAS;AAE3C,cAAM,QAAQ,CAAC,GAAU,CAAC,GAAU,IAAI;AAAA,MAC5C,CAAC;AAAA,IACN,WAAW,OAAO,kBAAkB,YAAY;AAE3C,WAAK,IAAI,IAAI,KAAK,OAAO,GAAG,SAAS;AACjC,cAAM,cAAc,CAAC,GAAU,CAAC,GAAU,IAAI;AAAA,MAClD,CAAC;AAAA,IACN;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAKA,MAAMA,OAAc,QAAc;AAC9B,SAAK,IAAI,MAAMA,OAAM,MAAM;AAAA,EAC/B;AAAA,EAGA,MAAM,OAAO,MAAc;AACvB,QAAI,KAAK,YAAY;AACjB,WAAK,IAAI,IAAI,UAAM,iCAAY,EAAE,MAAM,KAAK,WAAW,CAAC,CAAC;AAAA,IAC7D;AAEA,UAAM,aAAa,QAAQ,KAAK;AAChC,UAAM,aAAa;AAEnB,aAAS,UAAU,GAAG,UAAU,YAAY,WAAW;AACnD,YAAM,UAAU,aAAa;AAC7B,UAAI;AACA,cAAM,KAAK,UAAU,OAAO;AAC5B;AAAA,MACJ,SAAS,KAAU;AACf,YAAI,IAAI,SAAS,gBAAgB,UAAU,aAAa,GAAG;AACvD,cAAI,KAAK,UAAU,OAAO,KAAK,OAAO,UAAU,YAAY;AACxD,iBAAK,OAAO,MAAM;AAAA,UACtB;AACA;AAAA,QACJ;AACA,cAAM;AAAA,MACV;AAAA,IACJ;AAAA,EACJ;AAAA,EAEQ,UAAU,MAA6B;AAC3C,WAAO,IAAI,QAAc,CAACC,UAAS,WAAW;AAC1C,YAAM,aAAS,0BAAM;AAAA,QACjB,OAAO,KAAK,IAAI;AAAA,QAChB;AAAA,MACJ,GAAG,CAAC,SAAS;AACT,aAAK,gBAAgB,KAAK;AAC1B,QAAAA,SAAQ;AAAA,MACZ,CAAC;AACD,WAAK,SAAS;AACd,aAAO,GAAG,SAAS,CAAC,QAAa;AAC7B,eAAO,GAAG;AAAA,MACd,CAAC;AAAA,IACL,CAAC;AAAA,EACL;AAAA,EAEA,UAAU;AACN,WAAO,KAAK,iBAAiB,KAAK;AAAA,EACtC;AAAA;AAAA,EAGA,YAAY;AACR,WAAO,KAAK;AAAA,EAChB;AAAA,EAEA,MAAM,QAAQ;AACV,QAAI,KAAK,UAAU,OAAO,KAAK,OAAO,UAAU,YAAY;AACxD,WAAK,OAAO,MAAM;AAAA,IACtB;AAAA,EACJ;AAGJ;;;ACpKA,IAAAC,uBAA4B;AAC5B,SAAoB;AACpB,WAAsB;AAkDf,IAAM,mBAAN,MAAyC;AAAA,EAa5C,YAAY,UAA6B,CAAC,GAAG;AAZ7C,gCAAO;AACP,gCAAO;AACP,mCAAU;AAOV,wBAAQ;AACR,wBAAQ;AAiBR;AAAA;AAAA;AAAA,gCAAO,OAAO,QAAuB;AACjC,UAAI,OAAO,MAAM,mCAAmC;AAAA,QAChD,MAAM,KAAK,QAAQ;AAAA,QACnB,YAAY,KAAK,QAAQ;AAAA,MAC7B,CAAC;AAID,UAAI,gBAAgB,eAAe,KAAK,MAAM;AAE9C,UAAI,gBAAgB,eAAe,KAAK,MAAM;AAC9C,UAAI,OAAO,MAAM,kCAAkC,EAAE,aAAa,cAAc,CAAC;AAAA,IACrF;AAKA;AAAA;AAAA;AAAA,iCAAQ,OAAO,QAAuB;AAClC,UAAI,OAAO,MAAM,6BAA6B;AAG9C,YAAM,SAAwB,KAAK,QAAQ,gBAAgB,CAAC;AAG5D,UAAI;AACA,cAAM,YAAY,IAAI,UAAU;AAChC,YAAI,UAAU,SAAS;AACnB,gBAAM,gBAAgB,UAAU,mBAAmB,MAC7C,MAAM,KAAK,UAAU,QAAQ,OAAO,CAAC,IACrC,MAAM,QAAQ,UAAU,OAAO,IAAI,UAAU,UAAU,OAAO,OAAO,UAAU,OAAO;AAE5F,qBAAW,UAAW,eAAyB;AAG3C,iBAAK,OAAO,SAAS,QAAQ,OAAO,SAAS,gBAAgB,OAAO,YAAY;AAE5E,oBAAM,OAAO,OAAO,QAAQ,OAAO,KAAK,MAAM,GAAG,EAAE,IAAI;AACvD,oBAAM,YAAY,IAAI,IAAI;AAE1B,kBAAI,OAAO,MAAM,4BAA4B,OAAO,IAAI,IAAI;AAAA,gBACxD,MAAM;AAAA,gBACN,MAAM,OAAO;AAAA,cACjB,CAAC;AAED,qBAAO,KAAK;AAAA,gBACR,MAAM,OAAO;AAAA,gBACb,MAAM;AAAA,gBACN,SAAS;AAAA;AAAA,gBACT,KAAK;AAAA,cACT,CAAC;AAGD,kBAAI,OAAO,WAAW,OAAO,WAAW;AACnC,sBAAM,SAAS,KAAK,OAAO,UAAU;AACrC,uBAAO,IAAI,KAAK,CAAC,MAAM,EAAE,SAAS,SAAS,CAAC;AAC5C,oBAAI,OAAO,MAAM,iCAAiC,SAAS,EAAE;AAAA,cAClE;AAAA,YACJ;AAAA,UACJ;AAAA,QACJ;AAAA,MACJ,SAAS,KAAU;AACf,YAAI,OAAO,KAAK,sCAAsC,EAAE,OAAO,IAAI,WAAW,IAAI,CAAC;AAAA,MACvF;AAGA,UAAI,KAAK,QAAQ,YAAY;AACzB,eAAO,KAAK;AAAA,UACR,MAAM,KAAK,QAAQ;AAAA,UACnB,MAAM;AAAA,UACN,SAAS;AAAA,UACT,KAAK,KAAK,QAAQ;AAAA,QACtB,CAAC;AAAA,MACL;AAEA,UAAI,OAAO,SAAS,GAAG;AACnB,cAAM,SAAS,KAAK,OAAO,UAAU;AAErC,mBAAW,SAAS,QAAQ;AACxB,gBAAM,YAAiB,aAAQ,QAAQ,IAAI,GAAG,MAAM,IAAI;AAExD,cAAI,CAAI,cAAW,SAAS,GAAG;AAC3B,gBAAI,OAAO,KAAK,gCAAgC,SAAS,aAAa;AACtE;AAAA,UACJ;AAEA,gBAAM,YAAY,MAAM,QAAQ;AAChC,gBAAM,iBAAiB,UAAU,WAAW,GAAG,IAAI,YAAY,IAAI,SAAS;AAC5E,gBAAM,eAAe,mBAAmB,MAAM,OAAO,GAAG,eAAe,QAAQ,OAAO,EAAE,CAAC;AAGzF,gBAAM,SAAS,mBAAmB,MAAM,CAAC,YAAY,IAAI,CAAC,gBAAgB,YAAY;AAEtF,cAAI,OAAO,MAAM,yBAAyB;AAAA,YACtC,IAAI;AAAA,YACJ,MAAM;AAAA,YACN,SAAS,MAAM;AAAA,YACf,KAAK,MAAM;AAAA,UACf,CAAC;AAED,iBAAO,QAAQ,WAAS;AAEpB,mBAAO;AAAA,cACH;AAAA,kBACA,kCAAY;AAAA,gBACR,MAAM,MAAM;AAAA,gBACZ,oBAAoB,CAAC,YAAY;AAC7B,sBAAI,MAAM,WAAW,mBAAmB,KAAK;AAEzC,wBAAI,QAAQ,WAAW,cAAc,GAAG;AACpC,6BAAO,QAAQ,UAAU,eAAe,MAAM,KAAK;AAAA,oBACvD;AAAA,kBACJ;AACA,yBAAO;AAAA,gBACX;AAAA,cACJ,CAAC;AAAA,YACL;AAGA,gBAAI,MAAM,KAAK;AACX,qBAAO,IAAI,OAAO,OAAO,GAAG,SAAS;AAEjC,sBAAM,SAAS,KAAK,QAAQ,cAAc,CAAC;AAC3C,sBAAM,WAAW,OAAO,KAAK,YAAY;AAEzC,oBAAI,EAAE,IAAI,KAAK,WAAW,QAAQ,GAAG;AACjC,yBAAO,KAAK;AAAA,gBAChB;AAEA,2BAAO,kCAAY;AAAA,kBACf,MAAM,MAAM;AAAA,kBACZ,oBAAoB,MAAM;AAAA,gBAC9B,CAAC,EAAE,GAAG,IAAI;AAAA,cACd,CAAC;AAAA,YACL;AAAA,UACJ,CAAC;AAAA,QACL;AAAA,MACJ;AAGA,UAAI,KAAK,gBAAgB,YAAY;AAEjC,YAAI,KAAK,QAAQ,2BAA2B;AACxC,eAAK,kCAAkC,GAAG;AAAA,QAC9C;AAEA,cAAM,OAAO,KAAK,QAAQ,QAAQ;AAClC,YAAI,OAAO,MAAM,wBAAwB,EAAE,KAAK,CAAC;AAEjD,cAAM,KAAK,OAAO,OAAO,IAAI;AAE7B,cAAM,aAAa,KAAK,OAAO,QAAQ;AACvC,YAAI,eAAe,MAAM;AACrB,cAAI,OAAO,KAAK,QAAQ,IAAI,0BAA0B,UAAU,UAAU;AAAA,QAC9E;AACA,YAAI,OAAO,KAAK,oCAAoC;AAAA,UAChD,MAAM;AAAA,UACN,KAAK,oBAAoB,UAAU;AAAA,QACvC,CAAC;AAAA,MACL,CAAC;AAAA,IACL;AA7KI,SAAK,UAAU;AAAA,MACX,MAAM;AAAA,MACN,2BAA2B;AAAA,MAC3B,gBAAgB;AAAA,MAChB,aAAa;AAAA,MACb,GAAG;AAAA,IACP;AAEA,SAAK,SAAS,IAAI,eAAe,KAAK,QAAQ,IAAI;AAAA,EACtD;AAAA;AAAA;AAAA;AAAA;AAAA,EA0KQ,kCAAkC,KAAoB;AAC1D,UAAM,SAAS,KAAK,OAAO,UAAU;AACrC,UAAM,SAAS;AAGf,UAAM,YAAY;AAAA,MACd,SAAS;AAAA,MACT,SAAS;AAAA,MACT,QAAQ;AAAA,QACJ,MAAe,GAAG,MAAM;AAAA,QACxB,UAAe,GAAG,MAAM;AAAA,QACxB,MAAe,GAAG,MAAM;AAAA,QACxB,UAAe,GAAG,MAAM;AAAA,QACxB,WAAe,GAAG,MAAM;AAAA,QACxB,UAAe,GAAG,MAAM;AAAA,QACxB,UAAe,GAAG,MAAM;AAAA,QACxB,YAAe,GAAG,MAAM;AAAA,QACxB,IAAe,GAAG,MAAM;AAAA,QACxB,eAAe,GAAG,MAAM;AAAA,QACxB,MAAe,GAAG,MAAM;AAAA,QACxB,SAAe,GAAG,MAAM;AAAA,QACxB,IAAe,GAAG,MAAM;AAAA,MAC5B;AAAA,IACJ;AAGA,WAAO,IAAI,4BAA4B,CAAC,MAAW,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC,CAAC;AAC9E,WAAO,IAAI,QAAQ,CAAC,MAAW,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC,CAAC;AAE1D,QAAI,OAAO,KAAK,kCAAkC,EAAE,OAAO,CAAC;AAG5D,UAAM,YAAY,MAAO,IAAI,UAAU,EAAU;AAGjD,WAAO,KAAK,GAAG,MAAM,iBAAiB,OAAO,MAAW;AACpD,YAAM,SAAS,UAAU;AACzB,UAAI,CAAC,OAAQ,QAAO,EAAE,KAAK,EAAE,OAAO,uBAAuB,GAAG,GAAG;AACjE,YAAM,SAAS,EAAE,IAAI,MAAM,QAAQ;AACnC,YAAM,OAAO,MAAM,EAAE,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAChD,YAAM,SAAS,MAAM,OAAO,KAAK,eAAe,EAAE,QAAQ,KAAK,GAAG,CAAC,CAAC;AACpE,aAAO,EAAE,KAAK,MAAM;AAAA,IACxB,CAAC;AAGD,WAAO,IAAI,GAAG,MAAM,qBAAqB,OAAO,MAAW;AACvD,YAAM,SAAS,UAAU;AACzB,UAAI,CAAC,OAAQ,QAAO,EAAE,KAAK,EAAE,OAAO,uBAAuB,GAAG,GAAG;AACjE,YAAM,SAAS,EAAE,IAAI,MAAM,QAAQ;AACnC,YAAM,KAAK,EAAE,IAAI,MAAM,IAAI;AAC3B,YAAM,SAAS,MAAM,OAAO,KAAK,YAAY,EAAE,QAAQ,GAAG,GAAG,CAAC,CAAC;AAC/D,aAAO,SAAS,EAAE,KAAK,MAAM,IAAI,EAAE,KAAK,EAAE,OAAO,YAAY,GAAG,GAAG;AAAA,IACvE,CAAC;AAGD,WAAO,IAAI,GAAG,MAAM,iBAAiB,OAAO,MAAW;AACnD,YAAM,SAAS,UAAU;AACzB,UAAI,CAAC,OAAQ,QAAO,EAAE,KAAK,EAAE,OAAO,uBAAuB,GAAG,GAAG;AACjE,YAAM,SAAS,EAAE,IAAI,MAAM,QAAQ;AACnC,YAAM,UAAU,EAAE,IAAI,MAAM;AAC5B,YAAM,SAAS,MAAM,OAAO,KAAK,aAAa,EAAE,QAAQ,QAAQ,GAAG,CAAC,CAAC;AACrE,aAAO,EAAE,KAAK,MAAM;AAAA,IACxB,CAAC;AAED,QAAI,OAAO,MAAM,2CAA2C,EAAE,OAAO,CAAC;AAAA,EAC1E;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UAAU;AACZ,SAAK,OAAO,MAAM;AAElB,YAAQ,IAAI,mCAAmC;AAAA,EACnD;AACJ;AAAA;AAtQI,cANS,kBAMe,6BAA4B;AACpD,cAPS,kBAOe,0BAAyB;AACjD,cARS,kBAQe,+BAA8B;;;AFhE1D,0BAAc,iBAHd;","names":["path","resolve","import_serve_static"]}
|
package/dist/index.mjs
CHANGED
|
@@ -125,18 +125,40 @@ var HonoHttpServer = class {
|
|
|
125
125
|
this.app.route(path2, subApp);
|
|
126
126
|
}
|
|
127
127
|
async listen(port) {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
128
|
+
if (this.staticRoot) {
|
|
129
|
+
this.app.get("/*", serveStatic({ root: this.staticRoot }));
|
|
130
|
+
}
|
|
131
|
+
const targetPort = port || this.port;
|
|
132
|
+
const maxRetries = 20;
|
|
133
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
134
|
+
const tryPort = targetPort + attempt;
|
|
135
|
+
try {
|
|
136
|
+
await this.tryListen(tryPort);
|
|
137
|
+
return;
|
|
138
|
+
} catch (err) {
|
|
139
|
+
if (err.code === "EADDRINUSE" && attempt < maxRetries - 1) {
|
|
140
|
+
if (this.server && typeof this.server.close === "function") {
|
|
141
|
+
this.server.close();
|
|
142
|
+
}
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
throw err;
|
|
131
146
|
}
|
|
132
|
-
|
|
133
|
-
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
tryListen(port) {
|
|
150
|
+
return new Promise((resolve2, reject) => {
|
|
151
|
+
const server = serve({
|
|
134
152
|
fetch: this.app.fetch,
|
|
135
|
-
port
|
|
153
|
+
port
|
|
136
154
|
}, (info) => {
|
|
137
155
|
this.listeningPort = info.port;
|
|
138
156
|
resolve2();
|
|
139
157
|
});
|
|
158
|
+
this.server = server;
|
|
159
|
+
server.on("error", (err) => {
|
|
160
|
+
reject(err);
|
|
161
|
+
});
|
|
140
162
|
});
|
|
141
163
|
}
|
|
142
164
|
getPort() {
|
|
@@ -277,6 +299,9 @@ var HonoServerPlugin = class {
|
|
|
277
299
|
ctx.logger.debug("Starting HTTP server", { port });
|
|
278
300
|
await this.server.listen(port);
|
|
279
301
|
const actualPort = this.server.getPort();
|
|
302
|
+
if (actualPort !== port) {
|
|
303
|
+
ctx.logger.warn(`Port ${port} is in use, using port ${actualPort} instead`);
|
|
304
|
+
}
|
|
280
305
|
ctx.logger.info("HTTP server started successfully", {
|
|
281
306
|
port: actualPort,
|
|
282
307
|
url: `http://localhost:${actualPort}`
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/adapter.ts","../src/hono-plugin.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nexport * from './hono-plugin';\nexport * from './adapter';\n\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n// Export IHttpServer from core\nexport * from '@objectstack/core';\n\nimport { \n IHttpServer, \n RouteHandler, \n Middleware \n} from '@objectstack/core';\nimport { Hono } from 'hono';\nimport { serve } from '@hono/node-server';\nimport { serveStatic } from '@hono/node-server/serve-static';\n\n/**\n * Hono Implementation of IHttpServer\n */\nexport class HonoHttpServer implements IHttpServer {\n private app: Hono;\n private server: any;\n private listeningPort: number | undefined;\n\n constructor(\n private port: number = 3000,\n private staticRoot?: string\n ) {\n this.app = new Hono();\n }\n\n // internal helper to convert standard handler to Hono handler\n private wrap(handler: RouteHandler) {\n return async (c: any) => {\n let body: any = {};\n \n // Try to parse JSON body first if content-type is JSON\n if (c.req.header('content-type')?.includes('application/json')) {\n try { \n body = await c.req.json(); \n } catch(e) {\n // If JSON parsing fails, try parseBody\n try { \n body = await c.req.parseBody(); \n } catch(e2) {}\n }\n } else {\n // For non-JSON content types, use parseBody\n try { \n body = await c.req.parseBody(); \n } catch(e) {}\n }\n \n const req = {\n params: c.req.param(),\n query: c.req.query(),\n body,\n headers: c.req.header(),\n method: c.req.method,\n path: c.req.path\n };\n\n let capturedResponse: any;\n\n const res = {\n json: (data: any) => { capturedResponse = c.json(data); },\n send: (data: string) => { capturedResponse = c.html(data); },\n status: (code: number) => { c.status(code); return res; },\n header: (name: string, value: string) => { c.header(name, value); return res; }\n };\n\n await handler(req as any, res as any);\n return capturedResponse;\n };\n }\n\n get(path: string, handler: RouteHandler) {\n this.app.get(path, this.wrap(handler));\n }\n post(path: string, handler: RouteHandler) {\n this.app.post(path, this.wrap(handler));\n }\n put(path: string, handler: RouteHandler) {\n this.app.put(path, this.wrap(handler));\n }\n delete(path: string, handler: RouteHandler) {\n this.app.delete(path, this.wrap(handler));\n }\n patch(path: string, handler: RouteHandler) {\n this.app.patch(path, this.wrap(handler));\n }\n \n use(pathOrHandler: string | Middleware, handler?: Middleware) {\n if (typeof pathOrHandler === 'string' && handler) {\n // Path based middleware\n // Hono middleware signature is different (c, next) => ...\n this.app.use(pathOrHandler, async (c, next) => {\n // Simplistic conversion\n await handler({} as any, {} as any, next);\n });\n } else if (typeof pathOrHandler === 'function') {\n // Global middleware\n this.app.use('*', async (c, next) => {\n await pathOrHandler({} as any, {} as any, next);\n });\n }\n }\n\n /**\n * Mount a sub-application or router\n */\n mount(path: string, subApp: Hono) {\n this.app.route(path, subApp);\n }\n\n\n async listen(port: number) {\n return new Promise<void>((resolve) => {\n if (this.staticRoot) {\n this.app.get('/*', serveStatic({ root: this.staticRoot }));\n }\n \n const targetPort = port || this.port;\n this.server = serve({\n fetch: this.app.fetch,\n port: targetPort\n }, (info) => {\n this.listeningPort = info.port;\n resolve();\n });\n });\n }\n\n getPort() {\n return this.listeningPort || this.port;\n }\n\n // Expose raw app for scenarios where standard interface is not enough\n getRawApp() {\n return this.app;\n }\n\n async close() {\n if (this.server && typeof this.server.close === 'function') {\n this.server.close();\n }\n }\n\n\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { Plugin, PluginContext, IHttpServer } from '@objectstack/core';\nimport {\n RestServerConfig,\n} from '@objectstack/spec/api';\nimport { HonoHttpServer } from './adapter';\nimport { serveStatic } from '@hono/node-server/serve-static';\nimport * as fs from 'fs';\nimport * as path from 'path';\n\nexport interface StaticMount {\n root: string;\n path?: string;\n rewrite?: boolean;\n spa?: boolean;\n}\n\nexport interface HonoPluginOptions {\n port?: number;\n staticRoot?: string;\n /**\n * Multiple static resource mounts\n */\n staticMounts?: StaticMount[];\n /**\n * REST server configuration\n * Controls automatic endpoint generation and API behavior\n */\n restConfig?: RestServerConfig;\n /**\n * Whether to register standard ObjectStack CRUD endpoints\n * @default true\n */\n registerStandardEndpoints?: boolean;\n /**\n * Whether to load endpoints from API Registry\n * @default true\n */\n useApiRegistry?: boolean;\n\n /**\n * Whether to enable SPA fallback\n * If true, returns index.html for non-API 404s\n * @default false\n */\n spaFallback?: boolean;\n}\n\n/**\n * Hono Server Plugin\n * \n * Provides HTTP server capabilities using Hono framework.\n * Registers the IHttpServer service so other plugins can register routes.\n * \n * Route registration is handled by plugins:\n * - `@objectstack/rest` → CRUD, metadata, discovery, UI, batch\n * - `createDispatcherPlugin()` → auth, graphql, analytics, packages, etc.\n */\nexport class HonoServerPlugin implements Plugin {\n name = 'com.objectstack.server.hono';\n type = 'server';\n version = '0.9.0';\n \n // Constants\n private static readonly DEFAULT_ENDPOINT_PRIORITY = 100;\n private static readonly CORE_ENDPOINT_PRIORITY = 950;\n private static readonly DISCOVERY_ENDPOINT_PRIORITY = 900;\n \n private options: HonoPluginOptions;\n private server: HonoHttpServer;\n\n constructor(options: HonoPluginOptions = {}) {\n this.options = { \n port: 3000,\n registerStandardEndpoints: true,\n useApiRegistry: true,\n spaFallback: false,\n ...options\n };\n // We handle static root manually in start() to support SPA fallback\n this.server = new HonoHttpServer(this.options.port);\n }\n\n /**\n * Init phase - Setup HTTP server and register as service\n */\n init = async (ctx: PluginContext) => {\n ctx.logger.debug('Initializing Hono server plugin', { \n port: this.options.port,\n staticRoot: this.options.staticRoot \n });\n \n // Register HTTP server service as IHttpServer\n // Register as 'http.server' to match core requirements\n ctx.registerService('http.server', this.server);\n // Alias 'http-server' for backward compatibility\n ctx.registerService('http-server', this.server);\n ctx.logger.debug('HTTP server service registered', { serviceName: 'http.server' });\n }\n\n /**\n * Start phase - Configure static files and start listening\n */\n start = async (ctx: PluginContext) => {\n ctx.logger.debug('Starting Hono server plugin');\n\n // Configure Static Files & SPA Fallback\n const mounts: StaticMount[] = this.options.staticMounts || [];\n\n // Auto-discover UI Plugins\n try {\n const rawKernel = ctx.getKernel() as any;\n if (rawKernel.plugins) {\n const loadedPlugins = rawKernel.plugins instanceof Map \n ? Array.from(rawKernel.plugins.values()) \n : Array.isArray(rawKernel.plugins) ? rawKernel.plugins : Object.values(rawKernel.plugins);\n\n for (const plugin of (loadedPlugins as any[])) {\n // Check for UI Plugin signature\n // Support legacy 'ui-plugin' and new 'ui' type\n if ((plugin.type === 'ui' || plugin.type === 'ui-plugin') && plugin.staticPath) {\n // Derive base route from name: @org/console -> console\n const slug = plugin.slug || plugin.name.split('/').pop();\n const baseRoute = `/${slug}`;\n \n ctx.logger.debug(`Auto-mounting UI Plugin: ${plugin.name}`, { \n path: baseRoute, \n root: plugin.staticPath \n });\n\n mounts.push({\n root: plugin.staticPath,\n path: baseRoute,\n rewrite: true, // Strip prefix: /console/assets/x -> /assets/x\n spa: true\n });\n\n // Handle Default Plugin Redirect\n if (plugin.default || plugin.isDefault) {\n const rawApp = this.server.getRawApp();\n rawApp.get('/', (c) => c.redirect(baseRoute));\n ctx.logger.debug(`Set default UI redirect: / -> ${baseRoute}`);\n }\n }\n }\n }\n } catch (err: any) {\n ctx.logger.warn('Failed to auto-discover UI plugins', { error: err.message || err });\n }\n\n // Backward compatibility for staticRoot\n if (this.options.staticRoot) {\n mounts.push({\n root: this.options.staticRoot,\n path: '/',\n rewrite: false,\n spa: this.options.spaFallback\n });\n }\n\n if (mounts.length > 0) {\n const rawApp = this.server.getRawApp();\n \n for (const mount of mounts) {\n const mountRoot = path.resolve(process.cwd(), mount.root);\n\n if (!fs.existsSync(mountRoot)) {\n ctx.logger.warn(`Static mount root not found: ${mountRoot}. Skipping.`);\n continue;\n }\n\n const mountPath = mount.path || '/';\n const normalizedPath = mountPath.startsWith('/') ? mountPath : `/${mountPath}`;\n const routePattern = normalizedPath === '/' ? '/*' : `${normalizedPath.replace(/\\/$/, '')}/*`;\n \n // Routes to register: both /mount and /mount/*\n const routes = normalizedPath === '/' ? [routePattern] : [normalizedPath, routePattern];\n\n ctx.logger.debug('Mounting static files', { \n to: routes, \n from: mountRoot, \n rewrite: mount.rewrite, \n spa: mount.spa \n });\n\n routes.forEach(route => {\n // 1. Serve Static Files\n rawApp.get(\n route, \n serveStatic({ \n root: mount.root,\n rewriteRequestPath: (reqPath) => {\n if (mount.rewrite && normalizedPath !== '/') {\n // /console/assets/style.css -> /assets/style.css\n if (reqPath.startsWith(normalizedPath)) {\n return reqPath.substring(normalizedPath.length) || '/';\n }\n }\n return reqPath;\n }\n })\n );\n\n // 2. SPA Fallback (Scoped)\n if (mount.spa) {\n rawApp.get(route, async (c, next) => {\n // Skip if API path check\n const config = this.options.restConfig || {};\n const basePath = config.api?.basePath || '/api';\n \n if (c.req.path.startsWith(basePath)) {\n return next();\n }\n\n return serveStatic({ \n root: mount.root,\n rewriteRequestPath: () => 'index.html'\n })(c, next);\n });\n }\n });\n }\n }\n\n // Start server on kernel:ready hook\n ctx.hook('kernel:ready', async () => {\n // Register standard endpoints before starting to listen\n if (this.options.registerStandardEndpoints) {\n this.registerDiscoveryAndCrudEndpoints(ctx);\n }\n\n const port = this.options.port ?? 3000;\n ctx.logger.debug('Starting HTTP server', { port });\n \n await this.server.listen(port);\n \n const actualPort = this.server.getPort();\n ctx.logger.info('HTTP server started successfully', { \n port: actualPort, \n url: `http://localhost:${actualPort}` \n });\n });\n }\n\n /**\n * Register discovery and basic CRUD endpoints.\n * Called when `registerStandardEndpoints` is true, before the server starts listening.\n */\n private registerDiscoveryAndCrudEndpoints(ctx: PluginContext) {\n const rawApp = this.server.getRawApp();\n const prefix = '/api/v1';\n\n // Build the standard discovery response\n const discovery = {\n version: 'v1',\n apiName: 'ObjectStack API',\n routes: {\n data: `${prefix}/data`,\n metadata: `${prefix}/meta`,\n auth: `${prefix}/auth`,\n packages: `${prefix}/packages`,\n analytics: `${prefix}/analytics`,\n realtime: `${prefix}/realtime`,\n workflow: `${prefix}/workflow`,\n automation: `${prefix}/automation`,\n ai: `${prefix}/ai`,\n notifications: `${prefix}/notifications`,\n i18n: `${prefix}/i18n`,\n storage: `${prefix}/storage`,\n ui: `${prefix}/ui`,\n },\n };\n\n // Discovery endpoints\n rawApp.get('/.well-known/objectstack', (c: any) => c.json({ data: discovery }));\n rawApp.get(prefix, (c: any) => c.json({ data: discovery }));\n\n ctx.logger.info('Registered discovery endpoints', { prefix });\n\n // Basic CRUD data endpoints — delegate to kernel.broker when available\n const getBroker = () => (ctx.getKernel() as any).broker;\n\n // Create\n rawApp.post(`${prefix}/data/:object`, async (c: any) => {\n const broker = getBroker();\n if (!broker) return c.json({ error: 'Broker not available' }, 500);\n const object = c.req.param('object');\n const data = await c.req.json().catch(() => ({}));\n const result = await broker.call('data.create', { object, data }, {});\n return c.json(result);\n });\n\n // Get by ID\n rawApp.get(`${prefix}/data/:object/:id`, async (c: any) => {\n const broker = getBroker();\n if (!broker) return c.json({ error: 'Broker not available' }, 500);\n const object = c.req.param('object');\n const id = c.req.param('id');\n const result = await broker.call('data.get', { object, id }, {});\n return result ? c.json(result) : c.json({ error: 'Not found' }, 404);\n });\n\n // Find / List\n rawApp.get(`${prefix}/data/:object`, async (c: any) => {\n const broker = getBroker();\n if (!broker) return c.json({ error: 'Broker not available' }, 500);\n const object = c.req.param('object');\n const filters = c.req.query();\n const result = await broker.call('data.find', { object, filters }, {});\n return c.json(result);\n });\n\n ctx.logger.debug('Registered standard CRUD data endpoints', { prefix });\n }\n\n /**\n * Destroy phase - Stop server\n */\n async destroy() {\n this.server.close();\n // Note: Can't use ctx.logger here since we're in destroy\n console.log('[HonoServerPlugin] Server stopped');\n }\n}\n"],"mappings":"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","names":["path","resolve","serveStatic","serveStatic"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/adapter.ts","../src/hono-plugin.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nexport * from './hono-plugin';\nexport * from './adapter';\n\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n// Export IHttpServer from core\nexport * from '@objectstack/core';\n\nimport { \n IHttpServer, \n RouteHandler, \n Middleware \n} from '@objectstack/core';\nimport { Hono } from 'hono';\nimport { serve } from '@hono/node-server';\nimport { serveStatic } from '@hono/node-server/serve-static';\n\n/**\n * Hono Implementation of IHttpServer\n */\nexport class HonoHttpServer implements IHttpServer {\n private app: Hono;\n private server: any;\n private listeningPort: number | undefined;\n\n constructor(\n private port: number = 3000,\n private staticRoot?: string\n ) {\n this.app = new Hono();\n }\n\n // internal helper to convert standard handler to Hono handler\n private wrap(handler: RouteHandler) {\n return async (c: any) => {\n let body: any = {};\n \n // Try to parse JSON body first if content-type is JSON\n if (c.req.header('content-type')?.includes('application/json')) {\n try { \n body = await c.req.json(); \n } catch(e) {\n // If JSON parsing fails, try parseBody\n try { \n body = await c.req.parseBody(); \n } catch(e2) {}\n }\n } else {\n // For non-JSON content types, use parseBody\n try { \n body = await c.req.parseBody(); \n } catch(e) {}\n }\n \n const req = {\n params: c.req.param(),\n query: c.req.query(),\n body,\n headers: c.req.header(),\n method: c.req.method,\n path: c.req.path\n };\n\n let capturedResponse: any;\n\n const res = {\n json: (data: any) => { capturedResponse = c.json(data); },\n send: (data: string) => { capturedResponse = c.html(data); },\n status: (code: number) => { c.status(code); return res; },\n header: (name: string, value: string) => { c.header(name, value); return res; }\n };\n\n await handler(req as any, res as any);\n return capturedResponse;\n };\n }\n\n get(path: string, handler: RouteHandler) {\n this.app.get(path, this.wrap(handler));\n }\n post(path: string, handler: RouteHandler) {\n this.app.post(path, this.wrap(handler));\n }\n put(path: string, handler: RouteHandler) {\n this.app.put(path, this.wrap(handler));\n }\n delete(path: string, handler: RouteHandler) {\n this.app.delete(path, this.wrap(handler));\n }\n patch(path: string, handler: RouteHandler) {\n this.app.patch(path, this.wrap(handler));\n }\n \n use(pathOrHandler: string | Middleware, handler?: Middleware) {\n if (typeof pathOrHandler === 'string' && handler) {\n // Path based middleware\n // Hono middleware signature is different (c, next) => ...\n this.app.use(pathOrHandler, async (c, next) => {\n // Simplistic conversion\n await handler({} as any, {} as any, next);\n });\n } else if (typeof pathOrHandler === 'function') {\n // Global middleware\n this.app.use('*', async (c, next) => {\n await pathOrHandler({} as any, {} as any, next);\n });\n }\n }\n\n /**\n * Mount a sub-application or router\n */\n mount(path: string, subApp: Hono) {\n this.app.route(path, subApp);\n }\n\n\n async listen(port: number) {\n if (this.staticRoot) {\n this.app.get('/*', serveStatic({ root: this.staticRoot }));\n }\n\n const targetPort = port || this.port;\n const maxRetries = 20;\n\n for (let attempt = 0; attempt < maxRetries; attempt++) {\n const tryPort = targetPort + attempt;\n try {\n await this.tryListen(tryPort);\n return;\n } catch (err: any) {\n if (err.code === 'EADDRINUSE' && attempt < maxRetries - 1) {\n if (this.server && typeof this.server.close === 'function') {\n this.server.close();\n }\n continue;\n }\n throw err;\n }\n }\n }\n\n private tryListen(port: number): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const server = serve({\n fetch: this.app.fetch,\n port\n }, (info) => {\n this.listeningPort = info.port;\n resolve();\n });\n this.server = server;\n server.on('error', (err: any) => {\n reject(err);\n });\n });\n }\n\n getPort() {\n return this.listeningPort || this.port;\n }\n\n // Expose raw app for scenarios where standard interface is not enough\n getRawApp() {\n return this.app;\n }\n\n async close() {\n if (this.server && typeof this.server.close === 'function') {\n this.server.close();\n }\n }\n\n\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { Plugin, PluginContext, IHttpServer } from '@objectstack/core';\nimport {\n RestServerConfig,\n} from '@objectstack/spec/api';\nimport { HonoHttpServer } from './adapter';\nimport { serveStatic } from '@hono/node-server/serve-static';\nimport * as fs from 'fs';\nimport * as path from 'path';\n\nexport interface StaticMount {\n root: string;\n path?: string;\n rewrite?: boolean;\n spa?: boolean;\n}\n\nexport interface HonoPluginOptions {\n port?: number;\n staticRoot?: string;\n /**\n * Multiple static resource mounts\n */\n staticMounts?: StaticMount[];\n /**\n * REST server configuration\n * Controls automatic endpoint generation and API behavior\n */\n restConfig?: RestServerConfig;\n /**\n * Whether to register standard ObjectStack CRUD endpoints\n * @default true\n */\n registerStandardEndpoints?: boolean;\n /**\n * Whether to load endpoints from API Registry\n * @default true\n */\n useApiRegistry?: boolean;\n\n /**\n * Whether to enable SPA fallback\n * If true, returns index.html for non-API 404s\n * @default false\n */\n spaFallback?: boolean;\n}\n\n/**\n * Hono Server Plugin\n * \n * Provides HTTP server capabilities using Hono framework.\n * Registers the IHttpServer service so other plugins can register routes.\n * \n * Route registration is handled by plugins:\n * - `@objectstack/rest` → CRUD, metadata, discovery, UI, batch\n * - `createDispatcherPlugin()` → auth, graphql, analytics, packages, etc.\n */\nexport class HonoServerPlugin implements Plugin {\n name = 'com.objectstack.server.hono';\n type = 'server';\n version = '0.9.0';\n \n // Constants\n private static readonly DEFAULT_ENDPOINT_PRIORITY = 100;\n private static readonly CORE_ENDPOINT_PRIORITY = 950;\n private static readonly DISCOVERY_ENDPOINT_PRIORITY = 900;\n \n private options: HonoPluginOptions;\n private server: HonoHttpServer;\n\n constructor(options: HonoPluginOptions = {}) {\n this.options = { \n port: 3000,\n registerStandardEndpoints: true,\n useApiRegistry: true,\n spaFallback: false,\n ...options\n };\n // We handle static root manually in start() to support SPA fallback\n this.server = new HonoHttpServer(this.options.port);\n }\n\n /**\n * Init phase - Setup HTTP server and register as service\n */\n init = async (ctx: PluginContext) => {\n ctx.logger.debug('Initializing Hono server plugin', { \n port: this.options.port,\n staticRoot: this.options.staticRoot \n });\n \n // Register HTTP server service as IHttpServer\n // Register as 'http.server' to match core requirements\n ctx.registerService('http.server', this.server);\n // Alias 'http-server' for backward compatibility\n ctx.registerService('http-server', this.server);\n ctx.logger.debug('HTTP server service registered', { serviceName: 'http.server' });\n }\n\n /**\n * Start phase - Configure static files and start listening\n */\n start = async (ctx: PluginContext) => {\n ctx.logger.debug('Starting Hono server plugin');\n\n // Configure Static Files & SPA Fallback\n const mounts: StaticMount[] = this.options.staticMounts || [];\n\n // Auto-discover UI Plugins\n try {\n const rawKernel = ctx.getKernel() as any;\n if (rawKernel.plugins) {\n const loadedPlugins = rawKernel.plugins instanceof Map \n ? Array.from(rawKernel.plugins.values()) \n : Array.isArray(rawKernel.plugins) ? rawKernel.plugins : Object.values(rawKernel.plugins);\n\n for (const plugin of (loadedPlugins as any[])) {\n // Check for UI Plugin signature\n // Support legacy 'ui-plugin' and new 'ui' type\n if ((plugin.type === 'ui' || plugin.type === 'ui-plugin') && plugin.staticPath) {\n // Derive base route from name: @org/console -> console\n const slug = plugin.slug || plugin.name.split('/').pop();\n const baseRoute = `/${slug}`;\n \n ctx.logger.debug(`Auto-mounting UI Plugin: ${plugin.name}`, { \n path: baseRoute, \n root: plugin.staticPath \n });\n\n mounts.push({\n root: plugin.staticPath,\n path: baseRoute,\n rewrite: true, // Strip prefix: /console/assets/x -> /assets/x\n spa: true\n });\n\n // Handle Default Plugin Redirect\n if (plugin.default || plugin.isDefault) {\n const rawApp = this.server.getRawApp();\n rawApp.get('/', (c) => c.redirect(baseRoute));\n ctx.logger.debug(`Set default UI redirect: / -> ${baseRoute}`);\n }\n }\n }\n }\n } catch (err: any) {\n ctx.logger.warn('Failed to auto-discover UI plugins', { error: err.message || err });\n }\n\n // Backward compatibility for staticRoot\n if (this.options.staticRoot) {\n mounts.push({\n root: this.options.staticRoot,\n path: '/',\n rewrite: false,\n spa: this.options.spaFallback\n });\n }\n\n if (mounts.length > 0) {\n const rawApp = this.server.getRawApp();\n \n for (const mount of mounts) {\n const mountRoot = path.resolve(process.cwd(), mount.root);\n\n if (!fs.existsSync(mountRoot)) {\n ctx.logger.warn(`Static mount root not found: ${mountRoot}. Skipping.`);\n continue;\n }\n\n const mountPath = mount.path || '/';\n const normalizedPath = mountPath.startsWith('/') ? mountPath : `/${mountPath}`;\n const routePattern = normalizedPath === '/' ? '/*' : `${normalizedPath.replace(/\\/$/, '')}/*`;\n \n // Routes to register: both /mount and /mount/*\n const routes = normalizedPath === '/' ? [routePattern] : [normalizedPath, routePattern];\n\n ctx.logger.debug('Mounting static files', { \n to: routes, \n from: mountRoot, \n rewrite: mount.rewrite, \n spa: mount.spa \n });\n\n routes.forEach(route => {\n // 1. Serve Static Files\n rawApp.get(\n route, \n serveStatic({ \n root: mount.root,\n rewriteRequestPath: (reqPath) => {\n if (mount.rewrite && normalizedPath !== '/') {\n // /console/assets/style.css -> /assets/style.css\n if (reqPath.startsWith(normalizedPath)) {\n return reqPath.substring(normalizedPath.length) || '/';\n }\n }\n return reqPath;\n }\n })\n );\n\n // 2. SPA Fallback (Scoped)\n if (mount.spa) {\n rawApp.get(route, async (c, next) => {\n // Skip if API path check\n const config = this.options.restConfig || {};\n const basePath = config.api?.basePath || '/api';\n \n if (c.req.path.startsWith(basePath)) {\n return next();\n }\n\n return serveStatic({ \n root: mount.root,\n rewriteRequestPath: () => 'index.html'\n })(c, next);\n });\n }\n });\n }\n }\n\n // Start server on kernel:ready hook\n ctx.hook('kernel:ready', async () => {\n // Register standard endpoints before starting to listen\n if (this.options.registerStandardEndpoints) {\n this.registerDiscoveryAndCrudEndpoints(ctx);\n }\n\n const port = this.options.port ?? 3000;\n ctx.logger.debug('Starting HTTP server', { port });\n \n await this.server.listen(port);\n\n const actualPort = this.server.getPort();\n if (actualPort !== port) {\n ctx.logger.warn(`Port ${port} is in use, using port ${actualPort} instead`);\n }\n ctx.logger.info('HTTP server started successfully', {\n port: actualPort,\n url: `http://localhost:${actualPort}`\n });\n });\n }\n\n /**\n * Register discovery and basic CRUD endpoints.\n * Called when `registerStandardEndpoints` is true, before the server starts listening.\n */\n private registerDiscoveryAndCrudEndpoints(ctx: PluginContext) {\n const rawApp = this.server.getRawApp();\n const prefix = '/api/v1';\n\n // Build the standard discovery response\n const discovery = {\n version: 'v1',\n apiName: 'ObjectStack API',\n routes: {\n data: `${prefix}/data`,\n metadata: `${prefix}/meta`,\n auth: `${prefix}/auth`,\n packages: `${prefix}/packages`,\n analytics: `${prefix}/analytics`,\n realtime: `${prefix}/realtime`,\n workflow: `${prefix}/workflow`,\n automation: `${prefix}/automation`,\n ai: `${prefix}/ai`,\n notifications: `${prefix}/notifications`,\n i18n: `${prefix}/i18n`,\n storage: `${prefix}/storage`,\n ui: `${prefix}/ui`,\n },\n };\n\n // Discovery endpoints\n rawApp.get('/.well-known/objectstack', (c: any) => c.json({ data: discovery }));\n rawApp.get(prefix, (c: any) => c.json({ data: discovery }));\n\n ctx.logger.info('Registered discovery endpoints', { prefix });\n\n // Basic CRUD data endpoints — delegate to kernel.broker when available\n const getBroker = () => (ctx.getKernel() as any).broker;\n\n // Create\n rawApp.post(`${prefix}/data/:object`, async (c: any) => {\n const broker = getBroker();\n if (!broker) return c.json({ error: 'Broker not available' }, 500);\n const object = c.req.param('object');\n const data = await c.req.json().catch(() => ({}));\n const result = await broker.call('data.create', { object, data }, {});\n return c.json(result);\n });\n\n // Get by ID\n rawApp.get(`${prefix}/data/:object/:id`, async (c: any) => {\n const broker = getBroker();\n if (!broker) return c.json({ error: 'Broker not available' }, 500);\n const object = c.req.param('object');\n const id = c.req.param('id');\n const result = await broker.call('data.get', { object, id }, {});\n return result ? c.json(result) : c.json({ error: 'Not found' }, 404);\n });\n\n // Find / List\n rawApp.get(`${prefix}/data/:object`, async (c: any) => {\n const broker = getBroker();\n if (!broker) return c.json({ error: 'Broker not available' }, 500);\n const object = c.req.param('object');\n const filters = c.req.query();\n const result = await broker.call('data.find', { object, filters }, {});\n return c.json(result);\n });\n\n ctx.logger.debug('Registered standard CRUD data endpoints', { prefix });\n }\n\n /**\n * Destroy phase - Stop server\n */\n async destroy() {\n this.server.close();\n // Note: Can't use ctx.logger here since we're in destroy\n console.log('[HonoServerPlugin] Server stopped');\n }\n}\n"],"mappings":"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","names":["path","resolve","serveStatic","serveStatic"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@objectstack/plugin-hono-server",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "Standard Hono Server Adapter for ObjectStack Runtime",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
"dependencies": {
|
|
9
9
|
"@hono/node-server": "^1.19.11",
|
|
10
10
|
"hono": "^4.12.8",
|
|
11
|
-
"@objectstack/core": "3.
|
|
12
|
-
"@objectstack/spec": "3.
|
|
11
|
+
"@objectstack/core": "3.3.0",
|
|
12
|
+
"@objectstack/spec": "3.3.0"
|
|
13
13
|
},
|
|
14
14
|
"devDependencies": {
|
|
15
15
|
"@types/node": "^25.5.0",
|
package/src/adapter.ts
CHANGED
|
@@ -113,19 +113,43 @@ export class HonoHttpServer implements IHttpServer {
|
|
|
113
113
|
|
|
114
114
|
|
|
115
115
|
async listen(port: number) {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
116
|
+
if (this.staticRoot) {
|
|
117
|
+
this.app.get('/*', serveStatic({ root: this.staticRoot }));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const targetPort = port || this.port;
|
|
121
|
+
const maxRetries = 20;
|
|
122
|
+
|
|
123
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
124
|
+
const tryPort = targetPort + attempt;
|
|
125
|
+
try {
|
|
126
|
+
await this.tryListen(tryPort);
|
|
127
|
+
return;
|
|
128
|
+
} catch (err: any) {
|
|
129
|
+
if (err.code === 'EADDRINUSE' && attempt < maxRetries - 1) {
|
|
130
|
+
if (this.server && typeof this.server.close === 'function') {
|
|
131
|
+
this.server.close();
|
|
132
|
+
}
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
throw err;
|
|
119
136
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
private tryListen(port: number): Promise<void> {
|
|
141
|
+
return new Promise<void>((resolve, reject) => {
|
|
142
|
+
const server = serve({
|
|
123
143
|
fetch: this.app.fetch,
|
|
124
|
-
port
|
|
144
|
+
port
|
|
125
145
|
}, (info) => {
|
|
126
146
|
this.listeningPort = info.port;
|
|
127
147
|
resolve();
|
|
128
148
|
});
|
|
149
|
+
this.server = server;
|
|
150
|
+
server.on('error', (err: any) => {
|
|
151
|
+
reject(err);
|
|
152
|
+
});
|
|
129
153
|
});
|
|
130
154
|
}
|
|
131
155
|
|
package/src/hono-plugin.ts
CHANGED
|
@@ -234,11 +234,14 @@ export class HonoServerPlugin implements Plugin {
|
|
|
234
234
|
ctx.logger.debug('Starting HTTP server', { port });
|
|
235
235
|
|
|
236
236
|
await this.server.listen(port);
|
|
237
|
-
|
|
237
|
+
|
|
238
238
|
const actualPort = this.server.getPort();
|
|
239
|
-
|
|
240
|
-
port
|
|
241
|
-
|
|
239
|
+
if (actualPort !== port) {
|
|
240
|
+
ctx.logger.warn(`Port ${port} is in use, using port ${actualPort} instead`);
|
|
241
|
+
}
|
|
242
|
+
ctx.logger.info('HTTP server started successfully', {
|
|
243
|
+
port: actualPort,
|
|
244
|
+
url: `http://localhost:${actualPort}`
|
|
242
245
|
});
|
|
243
246
|
});
|
|
244
247
|
}
|