@objectstack/plugin-hono-server 1.0.4 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,22 @@
1
+
2
+ > @objectstack/plugin-hono-server@1.0.5 build /home/runner/work/spec/spec/packages/plugins/plugin-hono-server
3
+ > tsup --config ../../../tsup.config.ts
4
+
5
+ CLI Building entry: src/index.ts
6
+ CLI Using tsconfig: tsconfig.json
7
+ CLI tsup v8.5.1
8
+ CLI Using tsup config: /home/runner/work/spec/spec/tsup.config.ts
9
+ CLI Target: es2020
10
+ CLI Cleaning output folder
11
+ ESM Build start
12
+ CJS Build start
13
+ ESM dist/index.mjs 7.97 KB
14
+ ESM dist/index.mjs.map 14.84 KB
15
+ ESM ⚡️ Build success in 23ms
16
+ CJS dist/index.js 8.32 KB
17
+ CJS dist/index.js.map 14.84 KB
18
+ CJS ⚡️ Build success in 23ms
19
+ DTS Build start
20
+ DTS ⚡️ Build success in 3391ms
21
+ DTS dist/index.d.mts 2.62 KB
22
+ DTS dist/index.d.ts 2.62 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # @objectstack/plugin-hono-server
2
2
 
3
+ ## 1.0.5
4
+
5
+ ### Patch Changes
6
+
7
+ - b1d24bd: refactor: migrate build system from tsc to tsup for faster builds
8
+ - Replaced `tsc` with `tsup` (using esbuild) across all packages
9
+ - Added shared `tsup.config.ts` in workspace root
10
+ - Added `tsup` as workspace dev dependency
11
+ - significantly improved build performance
12
+ - 877b864: fix: add SPA fallback to hono, fix msw context binding, improve runtime resilience, and fix client-react build types
13
+ - Updated dependencies [b1d24bd]
14
+ - Updated dependencies [877b864]
15
+ - @objectstack/core@1.0.5
16
+ - @objectstack/runtime@1.0.5
17
+ - @objectstack/hono@1.0.5
18
+ - @objectstack/types@1.0.5
19
+ - @objectstack/spec@1.0.5
20
+
3
21
  ## 1.0.4
4
22
 
5
23
  ### Patch Changes
@@ -0,0 +1,88 @@
1
+ import { Plugin, PluginContext, IHttpServer, RouteHandler, Middleware } from '@objectstack/core';
2
+ export * from '@objectstack/core';
3
+ import { RestServerConfig } from '@objectstack/spec/api';
4
+ import * as hono_types from 'hono/types';
5
+ import { Hono } from 'hono';
6
+
7
+ interface HonoPluginOptions {
8
+ port?: number;
9
+ staticRoot?: string;
10
+ /**
11
+ * REST server configuration
12
+ * Controls automatic endpoint generation and API behavior
13
+ */
14
+ restConfig?: RestServerConfig;
15
+ /**
16
+ * Whether to register standard ObjectStack CRUD endpoints
17
+ * @default true
18
+ */
19
+ registerStandardEndpoints?: boolean;
20
+ /**
21
+ * Whether to load endpoints from API Registry
22
+ * @default true
23
+ */
24
+ useApiRegistry?: boolean;
25
+ /**
26
+ * Whether to enable SPA fallback
27
+ * If true, returns index.html for non-API 404s
28
+ * @default false
29
+ */
30
+ spaFallback?: boolean;
31
+ }
32
+ /**
33
+ * Hono Server Plugin
34
+ *
35
+ * Provides HTTP server capabilities using Hono framework.
36
+ * Registers routes for ObjectStack Runtime Protocol.
37
+ */
38
+ declare class HonoServerPlugin implements Plugin {
39
+ name: string;
40
+ version: string;
41
+ private static readonly DEFAULT_ENDPOINT_PRIORITY;
42
+ private static readonly CORE_ENDPOINT_PRIORITY;
43
+ private static readonly DISCOVERY_ENDPOINT_PRIORITY;
44
+ private options;
45
+ private server;
46
+ constructor(options?: HonoPluginOptions);
47
+ /**
48
+ * Init phase - Setup HTTP server and register as service
49
+ */
50
+ init: (ctx: PluginContext) => Promise<void>;
51
+ /**
52
+ * Start phase - Bind routes and start listening
53
+ */
54
+ start: (ctx: PluginContext) => Promise<void>;
55
+ /**
56
+ * Destroy phase - Stop server
57
+ */
58
+ destroy(): Promise<void>;
59
+ }
60
+
61
+ /**
62
+ * Hono Implementation of IHttpServer
63
+ */
64
+ declare class HonoHttpServer implements IHttpServer {
65
+ private port;
66
+ private staticRoot?;
67
+ private app;
68
+ private server;
69
+ private listeningPort;
70
+ constructor(port?: number, staticRoot?: string | undefined);
71
+ private wrap;
72
+ get(path: string, handler: RouteHandler): void;
73
+ post(path: string, handler: RouteHandler): void;
74
+ put(path: string, handler: RouteHandler): void;
75
+ delete(path: string, handler: RouteHandler): void;
76
+ patch(path: string, handler: RouteHandler): void;
77
+ use(pathOrHandler: string | Middleware, handler?: Middleware): void;
78
+ /**
79
+ * Mount a sub-application or router
80
+ */
81
+ mount(path: string, subApp: Hono): void;
82
+ listen(port: number): Promise<void>;
83
+ getPort(): number;
84
+ getRawApp(): Hono<hono_types.BlankEnv, hono_types.BlankSchema, "/">;
85
+ close(): Promise<void>;
86
+ }
87
+
88
+ export { HonoHttpServer, type HonoPluginOptions, HonoServerPlugin };
package/dist/index.d.ts CHANGED
@@ -1,2 +1,88 @@
1
- export * from './hono-plugin';
2
- export * from './adapter';
1
+ import { Plugin, PluginContext, IHttpServer, RouteHandler, Middleware } from '@objectstack/core';
2
+ export * from '@objectstack/core';
3
+ import { RestServerConfig } from '@objectstack/spec/api';
4
+ import * as hono_types from 'hono/types';
5
+ import { Hono } from 'hono';
6
+
7
+ interface HonoPluginOptions {
8
+ port?: number;
9
+ staticRoot?: string;
10
+ /**
11
+ * REST server configuration
12
+ * Controls automatic endpoint generation and API behavior
13
+ */
14
+ restConfig?: RestServerConfig;
15
+ /**
16
+ * Whether to register standard ObjectStack CRUD endpoints
17
+ * @default true
18
+ */
19
+ registerStandardEndpoints?: boolean;
20
+ /**
21
+ * Whether to load endpoints from API Registry
22
+ * @default true
23
+ */
24
+ useApiRegistry?: boolean;
25
+ /**
26
+ * Whether to enable SPA fallback
27
+ * If true, returns index.html for non-API 404s
28
+ * @default false
29
+ */
30
+ spaFallback?: boolean;
31
+ }
32
+ /**
33
+ * Hono Server Plugin
34
+ *
35
+ * Provides HTTP server capabilities using Hono framework.
36
+ * Registers routes for ObjectStack Runtime Protocol.
37
+ */
38
+ declare class HonoServerPlugin implements Plugin {
39
+ name: string;
40
+ version: string;
41
+ private static readonly DEFAULT_ENDPOINT_PRIORITY;
42
+ private static readonly CORE_ENDPOINT_PRIORITY;
43
+ private static readonly DISCOVERY_ENDPOINT_PRIORITY;
44
+ private options;
45
+ private server;
46
+ constructor(options?: HonoPluginOptions);
47
+ /**
48
+ * Init phase - Setup HTTP server and register as service
49
+ */
50
+ init: (ctx: PluginContext) => Promise<void>;
51
+ /**
52
+ * Start phase - Bind routes and start listening
53
+ */
54
+ start: (ctx: PluginContext) => Promise<void>;
55
+ /**
56
+ * Destroy phase - Stop server
57
+ */
58
+ destroy(): Promise<void>;
59
+ }
60
+
61
+ /**
62
+ * Hono Implementation of IHttpServer
63
+ */
64
+ declare class HonoHttpServer implements IHttpServer {
65
+ private port;
66
+ private staticRoot?;
67
+ private app;
68
+ private server;
69
+ private listeningPort;
70
+ constructor(port?: number, staticRoot?: string | undefined);
71
+ private wrap;
72
+ get(path: string, handler: RouteHandler): void;
73
+ post(path: string, handler: RouteHandler): void;
74
+ put(path: string, handler: RouteHandler): void;
75
+ delete(path: string, handler: RouteHandler): void;
76
+ patch(path: string, handler: RouteHandler): void;
77
+ use(pathOrHandler: string | Middleware, handler?: Middleware): void;
78
+ /**
79
+ * Mount a sub-application or router
80
+ */
81
+ mount(path: string, subApp: Hono): void;
82
+ listen(port: number): Promise<void>;
83
+ getPort(): number;
84
+ getRawApp(): Hono<hono_types.BlankEnv, hono_types.BlankSchema, "/">;
85
+ close(): Promise<void>;
86
+ }
87
+
88
+ export { HonoHttpServer, type HonoPluginOptions, HonoServerPlugin };
package/dist/index.js CHANGED
@@ -1,18 +1,259 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
20
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
21
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
22
+
23
+ // src/index.ts
24
+ var index_exports = {};
25
+ __export(index_exports, {
26
+ HonoHttpServer: () => HonoHttpServer,
27
+ HonoServerPlugin: () => HonoServerPlugin
28
+ });
29
+ module.exports = __toCommonJS(index_exports);
30
+
31
+ // src/adapter.ts
32
+ var adapter_exports = {};
33
+ __export(adapter_exports, {
34
+ HonoHttpServer: () => HonoHttpServer
35
+ });
36
+ __reExport(adapter_exports, require("@objectstack/core"));
37
+ var import_hono = require("hono");
38
+ var import_node_server = require("@hono/node-server");
39
+ var import_serve_static = require("@hono/node-server/serve-static");
40
+ var HonoHttpServer = class {
41
+ constructor(port = 3e3, staticRoot) {
42
+ this.port = port;
43
+ this.staticRoot = staticRoot;
44
+ __publicField(this, "app");
45
+ __publicField(this, "server");
46
+ __publicField(this, "listeningPort");
47
+ this.app = new import_hono.Hono();
48
+ }
49
+ // internal helper to convert standard handler to Hono handler
50
+ wrap(handler) {
51
+ return async (c) => {
52
+ let body = {};
53
+ if (c.req.header("content-type")?.includes("application/json")) {
54
+ try {
55
+ body = await c.req.json();
56
+ } catch (e) {
57
+ try {
58
+ body = await c.req.parseBody();
59
+ } catch (e2) {
60
+ }
61
+ }
62
+ } else {
63
+ try {
64
+ body = await c.req.parseBody();
65
+ } catch (e) {
66
+ }
67
+ }
68
+ const req = {
69
+ params: c.req.param(),
70
+ query: c.req.query(),
71
+ body,
72
+ headers: c.req.header(),
73
+ method: c.req.method,
74
+ path: c.req.path
75
+ };
76
+ let capturedResponse;
77
+ const res = {
78
+ json: (data) => {
79
+ capturedResponse = c.json(data);
80
+ },
81
+ send: (data) => {
82
+ capturedResponse = c.html(data);
83
+ },
84
+ status: (code) => {
85
+ c.status(code);
86
+ return res;
87
+ },
88
+ header: (name, value) => {
89
+ c.header(name, value);
90
+ return res;
91
+ }
92
+ };
93
+ await handler(req, res);
94
+ return capturedResponse;
95
+ };
96
+ }
97
+ get(path, handler) {
98
+ this.app.get(path, this.wrap(handler));
99
+ }
100
+ post(path, handler) {
101
+ this.app.post(path, this.wrap(handler));
102
+ }
103
+ put(path, handler) {
104
+ this.app.put(path, this.wrap(handler));
105
+ }
106
+ delete(path, handler) {
107
+ this.app.delete(path, this.wrap(handler));
108
+ }
109
+ patch(path, handler) {
110
+ this.app.patch(path, this.wrap(handler));
111
+ }
112
+ use(pathOrHandler, handler) {
113
+ if (typeof pathOrHandler === "string" && handler) {
114
+ this.app.use(pathOrHandler, async (c, next) => {
115
+ await handler({}, {}, next);
116
+ });
117
+ } else if (typeof pathOrHandler === "function") {
118
+ this.app.use("*", async (c, next) => {
119
+ await pathOrHandler({}, {}, next);
120
+ });
7
121
  }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
122
+ }
123
+ /**
124
+ * Mount a sub-application or router
125
+ */
126
+ mount(path, subApp) {
127
+ this.app.route(path, subApp);
128
+ }
129
+ async listen(port) {
130
+ return new Promise((resolve) => {
131
+ if (this.staticRoot) {
132
+ this.app.get("/*", (0, import_serve_static.serveStatic)({ root: this.staticRoot }));
133
+ }
134
+ const targetPort = port || this.port;
135
+ this.server = (0, import_node_server.serve)({
136
+ fetch: this.app.fetch,
137
+ port: targetPort
138
+ }, (info) => {
139
+ this.listeningPort = info.port;
140
+ resolve();
141
+ });
142
+ });
143
+ }
144
+ getPort() {
145
+ return this.listeningPort || this.port;
146
+ }
147
+ // Expose raw app for scenarios where standard interface is not enough
148
+ getRawApp() {
149
+ return this.app;
150
+ }
151
+ async close() {
152
+ if (this.server && typeof this.server.close === "function") {
153
+ this.server.close();
154
+ }
155
+ }
156
+ };
157
+
158
+ // src/hono-plugin.ts
159
+ var import_hono2 = require("@objectstack/hono");
160
+ var import_serve_static2 = require("@hono/node-server/serve-static");
161
+ var HonoServerPlugin = class {
162
+ constructor(options = {}) {
163
+ __publicField(this, "name", "com.objectstack.server.hono");
164
+ __publicField(this, "version", "0.9.0");
165
+ __publicField(this, "options");
166
+ __publicField(this, "server");
167
+ /**
168
+ * Init phase - Setup HTTP server and register as service
169
+ */
170
+ __publicField(this, "init", async (ctx) => {
171
+ ctx.logger.debug("Initializing Hono server plugin", {
172
+ port: this.options.port,
173
+ staticRoot: this.options.staticRoot
174
+ });
175
+ ctx.registerService("http.server", this.server);
176
+ ctx.registerService("http-server", this.server);
177
+ ctx.logger.debug("HTTP server service registered", { serviceName: "http.server" });
178
+ });
179
+ /**
180
+ * Start phase - Bind routes and start listening
181
+ */
182
+ __publicField(this, "start", async (ctx) => {
183
+ ctx.logger.debug("Starting Hono server plugin");
184
+ try {
185
+ const kernel = ctx.getKernel();
186
+ const config = this.options.restConfig || {};
187
+ const apiVersion = config.api?.version || "v1";
188
+ const basePath = config.api?.basePath || "/api";
189
+ const apiPath = config.api?.apiPath || `${basePath}/${apiVersion}`;
190
+ const app = (0, import_hono2.createHonoApp)({
191
+ kernel,
192
+ prefix: apiPath
193
+ // Use the calculated path
194
+ });
195
+ ctx.logger.debug("Mounting ObjectStack Runtime App", { prefix: apiPath });
196
+ this.server.mount("/", app);
197
+ } catch (e) {
198
+ ctx.logger.error("Failed to create standard Hono app", e);
199
+ }
200
+ if (this.options.staticRoot) {
201
+ const rawApp = this.server.getRawApp();
202
+ const staticRoot = this.options.staticRoot;
203
+ ctx.logger.debug("Configuring static files", { root: staticRoot, spa: this.options.spaFallback });
204
+ rawApp.get("/*", (0, import_serve_static2.serveStatic)({ root: staticRoot }));
205
+ if (this.options.spaFallback) {
206
+ rawApp.get("*", async (c, next) => {
207
+ const config = this.options.restConfig || {};
208
+ const basePath = config.api?.basePath || "/api";
209
+ if (c.req.path.startsWith(basePath)) {
210
+ return next();
211
+ }
212
+ return (0, import_serve_static2.serveStatic)({
213
+ root: staticRoot,
214
+ rewriteRequestPath: () => "index.html"
215
+ })(c, next);
216
+ });
217
+ }
218
+ }
219
+ ctx.hook("kernel:ready", async () => {
220
+ const port = this.options.port || 3e3;
221
+ ctx.logger.debug("Starting HTTP server", { port });
222
+ await this.server.listen(port);
223
+ const actualPort = this.server.getPort();
224
+ ctx.logger.info("HTTP server started successfully", {
225
+ port: actualPort,
226
+ url: `http://localhost:${actualPort}`
227
+ });
228
+ });
229
+ });
230
+ this.options = {
231
+ port: 3e3,
232
+ registerStandardEndpoints: true,
233
+ useApiRegistry: true,
234
+ spaFallback: false,
235
+ ...options
236
+ };
237
+ this.server = new HonoHttpServer(this.options.port);
238
+ }
239
+ /**
240
+ * Destroy phase - Stop server
241
+ */
242
+ async destroy() {
243
+ this.server.close();
244
+ console.log("[HonoServerPlugin] Server stopped");
245
+ }
15
246
  };
16
- Object.defineProperty(exports, "__esModule", { value: true });
17
- __exportStar(require("./hono-plugin"), exports);
18
- __exportStar(require("./adapter"), exports);
247
+ // Constants
248
+ __publicField(HonoServerPlugin, "DEFAULT_ENDPOINT_PRIORITY", 100);
249
+ __publicField(HonoServerPlugin, "CORE_ENDPOINT_PRIORITY", 950);
250
+ __publicField(HonoServerPlugin, "DISCOVERY_ENDPOINT_PRIORITY", 900);
251
+
252
+ // src/index.ts
253
+ __reExport(index_exports, adapter_exports, module.exports);
254
+ // Annotate the CommonJS export names for ESM import in node:
255
+ 0 && (module.exports = {
256
+ HonoHttpServer,
257
+ HonoServerPlugin
258
+ });
259
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/adapter.ts","../src/hono-plugin.ts"],"sourcesContent":["export * from './hono-plugin';\nexport * from './adapter';\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","import { Plugin, PluginContext, IHttpServer, ApiRegistry } from '@objectstack/core';\nimport { ObjectStackProtocol } from '@objectstack/spec/api';\nimport { \n ApiRegistryEntryInput,\n ApiEndpointRegistrationInput,\n RestServerConfig,\n} from '@objectstack/spec/api';\nimport { HonoHttpServer } from './adapter';\nimport { createHonoApp } from '@objectstack/hono';\nimport { serveStatic } from '@hono/node-server/serve-static';\n\nexport interface HonoPluginOptions {\n port?: number;\n staticRoot?: string;\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 routes for ObjectStack Runtime Protocol.\n */\nexport class HonoServerPlugin implements Plugin {\n name = 'com.objectstack.server.hono';\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 - Bind routes and start listening\n */\n start = async (ctx: PluginContext) => {\n ctx.logger.debug('Starting Hono server plugin');\n \n // Use Standard ObjectStack Runtime Hono App\n try {\n const kernel = ctx.getKernel();\n const config = this.options.restConfig || {};\n // Calculate prefix similar to before\n const apiVersion = config.api?.version || 'v1';\n const basePath = config.api?.basePath || '/api';\n const apiPath = config.api?.apiPath || `${basePath}/${apiVersion}`;\n \n const app = createHonoApp({ \n kernel,\n prefix: apiPath // Use the calculated path\n });\n \n ctx.logger.debug('Mounting ObjectStack Runtime App', { prefix: apiPath });\n // Use the mount method we added to HonoHttpServer\n this.server.mount('/', app as any);\n\n } catch (e: any) {\n ctx.logger.error('Failed to create standard Hono app', e);\n }\n\n // Configure Static Files & SPA Fallback\n if (this.options.staticRoot) {\n const rawApp = this.server.getRawApp();\n const staticRoot = this.options.staticRoot;\n \n ctx.logger.debug('Configuring static files', { root: staticRoot, spa: this.options.spaFallback });\n \n // 1. Static Files\n rawApp.get('/*', serveStatic({ root: staticRoot }));\n \n // 2. SPA Fallback\n if (this.options.spaFallback) {\n rawApp.get('*', async (c, next) => {\n // Skip API paths\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 // Fallback to index.html\n return serveStatic({ \n root: staticRoot,\n rewriteRequestPath: () => 'index.html'\n })(c, next);\n });\n }\n }\n\n // Start server on kernel:ready hook\n ctx.hook('kernel:ready', async () => {\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 * 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":["import_hono","import_serve_static"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,256 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
19
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
20
+
21
+ // src/index.ts
22
+ var index_exports = {};
23
+ __export(index_exports, {
24
+ HonoHttpServer: () => HonoHttpServer,
25
+ HonoServerPlugin: () => HonoServerPlugin
26
+ });
27
+
28
+ // src/adapter.ts
29
+ var adapter_exports = {};
30
+ __export(adapter_exports, {
31
+ HonoHttpServer: () => HonoHttpServer
32
+ });
33
+ __reExport(adapter_exports, core_star);
34
+ import * as core_star from "@objectstack/core";
35
+ import { Hono } from "hono";
36
+ import { serve } from "@hono/node-server";
37
+ import { serveStatic } from "@hono/node-server/serve-static";
38
+ var HonoHttpServer = class {
39
+ constructor(port = 3e3, staticRoot) {
40
+ this.port = port;
41
+ this.staticRoot = staticRoot;
42
+ __publicField(this, "app");
43
+ __publicField(this, "server");
44
+ __publicField(this, "listeningPort");
45
+ this.app = new Hono();
46
+ }
47
+ // internal helper to convert standard handler to Hono handler
48
+ wrap(handler) {
49
+ return async (c) => {
50
+ let body = {};
51
+ if (c.req.header("content-type")?.includes("application/json")) {
52
+ try {
53
+ body = await c.req.json();
54
+ } catch (e) {
55
+ try {
56
+ body = await c.req.parseBody();
57
+ } catch (e2) {
58
+ }
59
+ }
60
+ } else {
61
+ try {
62
+ body = await c.req.parseBody();
63
+ } catch (e) {
64
+ }
65
+ }
66
+ const req = {
67
+ params: c.req.param(),
68
+ query: c.req.query(),
69
+ body,
70
+ headers: c.req.header(),
71
+ method: c.req.method,
72
+ path: c.req.path
73
+ };
74
+ let capturedResponse;
75
+ const res = {
76
+ json: (data) => {
77
+ capturedResponse = c.json(data);
78
+ },
79
+ send: (data) => {
80
+ capturedResponse = c.html(data);
81
+ },
82
+ status: (code) => {
83
+ c.status(code);
84
+ return res;
85
+ },
86
+ header: (name, value) => {
87
+ c.header(name, value);
88
+ return res;
89
+ }
90
+ };
91
+ await handler(req, res);
92
+ return capturedResponse;
93
+ };
94
+ }
95
+ get(path, handler) {
96
+ this.app.get(path, this.wrap(handler));
97
+ }
98
+ post(path, handler) {
99
+ this.app.post(path, this.wrap(handler));
100
+ }
101
+ put(path, handler) {
102
+ this.app.put(path, this.wrap(handler));
103
+ }
104
+ delete(path, handler) {
105
+ this.app.delete(path, this.wrap(handler));
106
+ }
107
+ patch(path, handler) {
108
+ this.app.patch(path, this.wrap(handler));
109
+ }
110
+ use(pathOrHandler, handler) {
111
+ if (typeof pathOrHandler === "string" && handler) {
112
+ this.app.use(pathOrHandler, async (c, next) => {
113
+ await handler({}, {}, next);
114
+ });
115
+ } else if (typeof pathOrHandler === "function") {
116
+ this.app.use("*", async (c, next) => {
117
+ await pathOrHandler({}, {}, next);
118
+ });
119
+ }
120
+ }
121
+ /**
122
+ * Mount a sub-application or router
123
+ */
124
+ mount(path, subApp) {
125
+ this.app.route(path, subApp);
126
+ }
127
+ async listen(port) {
128
+ return new Promise((resolve) => {
129
+ if (this.staticRoot) {
130
+ this.app.get("/*", serveStatic({ root: this.staticRoot }));
131
+ }
132
+ const targetPort = port || this.port;
133
+ this.server = serve({
134
+ fetch: this.app.fetch,
135
+ port: targetPort
136
+ }, (info) => {
137
+ this.listeningPort = info.port;
138
+ resolve();
139
+ });
140
+ });
141
+ }
142
+ getPort() {
143
+ return this.listeningPort || this.port;
144
+ }
145
+ // Expose raw app for scenarios where standard interface is not enough
146
+ getRawApp() {
147
+ return this.app;
148
+ }
149
+ async close() {
150
+ if (this.server && typeof this.server.close === "function") {
151
+ this.server.close();
152
+ }
153
+ }
154
+ };
155
+
156
+ // src/hono-plugin.ts
157
+ import { createHonoApp } from "@objectstack/hono";
158
+ import { serveStatic as serveStatic2 } from "@hono/node-server/serve-static";
159
+ var HonoServerPlugin = class {
160
+ constructor(options = {}) {
161
+ __publicField(this, "name", "com.objectstack.server.hono");
162
+ __publicField(this, "version", "0.9.0");
163
+ __publicField(this, "options");
164
+ __publicField(this, "server");
165
+ /**
166
+ * Init phase - Setup HTTP server and register as service
167
+ */
168
+ __publicField(this, "init", async (ctx) => {
169
+ ctx.logger.debug("Initializing Hono server plugin", {
170
+ port: this.options.port,
171
+ staticRoot: this.options.staticRoot
172
+ });
173
+ ctx.registerService("http.server", this.server);
174
+ ctx.registerService("http-server", this.server);
175
+ ctx.logger.debug("HTTP server service registered", { serviceName: "http.server" });
176
+ });
177
+ /**
178
+ * Start phase - Bind routes and start listening
179
+ */
180
+ __publicField(this, "start", async (ctx) => {
181
+ ctx.logger.debug("Starting Hono server plugin");
182
+ try {
183
+ const kernel = ctx.getKernel();
184
+ const config = this.options.restConfig || {};
185
+ const apiVersion = config.api?.version || "v1";
186
+ const basePath = config.api?.basePath || "/api";
187
+ const apiPath = config.api?.apiPath || `${basePath}/${apiVersion}`;
188
+ const app = createHonoApp({
189
+ kernel,
190
+ prefix: apiPath
191
+ // Use the calculated path
192
+ });
193
+ ctx.logger.debug("Mounting ObjectStack Runtime App", { prefix: apiPath });
194
+ this.server.mount("/", app);
195
+ } catch (e) {
196
+ ctx.logger.error("Failed to create standard Hono app", e);
197
+ }
198
+ if (this.options.staticRoot) {
199
+ const rawApp = this.server.getRawApp();
200
+ const staticRoot = this.options.staticRoot;
201
+ ctx.logger.debug("Configuring static files", { root: staticRoot, spa: this.options.spaFallback });
202
+ rawApp.get("/*", serveStatic2({ root: staticRoot }));
203
+ if (this.options.spaFallback) {
204
+ rawApp.get("*", async (c, next) => {
205
+ const config = this.options.restConfig || {};
206
+ const basePath = config.api?.basePath || "/api";
207
+ if (c.req.path.startsWith(basePath)) {
208
+ return next();
209
+ }
210
+ return serveStatic2({
211
+ root: staticRoot,
212
+ rewriteRequestPath: () => "index.html"
213
+ })(c, next);
214
+ });
215
+ }
216
+ }
217
+ ctx.hook("kernel:ready", async () => {
218
+ const port = this.options.port || 3e3;
219
+ ctx.logger.debug("Starting HTTP server", { port });
220
+ await this.server.listen(port);
221
+ const actualPort = this.server.getPort();
222
+ ctx.logger.info("HTTP server started successfully", {
223
+ port: actualPort,
224
+ url: `http://localhost:${actualPort}`
225
+ });
226
+ });
227
+ });
228
+ this.options = {
229
+ port: 3e3,
230
+ registerStandardEndpoints: true,
231
+ useApiRegistry: true,
232
+ spaFallback: false,
233
+ ...options
234
+ };
235
+ this.server = new HonoHttpServer(this.options.port);
236
+ }
237
+ /**
238
+ * Destroy phase - Stop server
239
+ */
240
+ async destroy() {
241
+ this.server.close();
242
+ console.log("[HonoServerPlugin] Server stopped");
243
+ }
244
+ };
245
+ // Constants
246
+ __publicField(HonoServerPlugin, "DEFAULT_ENDPOINT_PRIORITY", 100);
247
+ __publicField(HonoServerPlugin, "CORE_ENDPOINT_PRIORITY", 950);
248
+ __publicField(HonoServerPlugin, "DISCOVERY_ENDPOINT_PRIORITY", 900);
249
+
250
+ // src/index.ts
251
+ __reExport(index_exports, adapter_exports);
252
+ export {
253
+ HonoHttpServer,
254
+ HonoServerPlugin
255
+ };
256
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/adapter.ts","../src/hono-plugin.ts"],"sourcesContent":["export * from './hono-plugin';\nexport * from './adapter';\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","import { Plugin, PluginContext, IHttpServer, ApiRegistry } from '@objectstack/core';\nimport { ObjectStackProtocol } from '@objectstack/spec/api';\nimport { \n ApiRegistryEntryInput,\n ApiEndpointRegistrationInput,\n RestServerConfig,\n} from '@objectstack/spec/api';\nimport { HonoHttpServer } from './adapter';\nimport { createHonoApp } from '@objectstack/hono';\nimport { serveStatic } from '@hono/node-server/serve-static';\n\nexport interface HonoPluginOptions {\n port?: number;\n staticRoot?: string;\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 routes for ObjectStack Runtime Protocol.\n */\nexport class HonoServerPlugin implements Plugin {\n name = 'com.objectstack.server.hono';\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 - Bind routes and start listening\n */\n start = async (ctx: PluginContext) => {\n ctx.logger.debug('Starting Hono server plugin');\n \n // Use Standard ObjectStack Runtime Hono App\n try {\n const kernel = ctx.getKernel();\n const config = this.options.restConfig || {};\n // Calculate prefix similar to before\n const apiVersion = config.api?.version || 'v1';\n const basePath = config.api?.basePath || '/api';\n const apiPath = config.api?.apiPath || `${basePath}/${apiVersion}`;\n \n const app = createHonoApp({ \n kernel,\n prefix: apiPath // Use the calculated path\n });\n \n ctx.logger.debug('Mounting ObjectStack Runtime App', { prefix: apiPath });\n // Use the mount method we added to HonoHttpServer\n this.server.mount('/', app as any);\n\n } catch (e: any) {\n ctx.logger.error('Failed to create standard Hono app', e);\n }\n\n // Configure Static Files & SPA Fallback\n if (this.options.staticRoot) {\n const rawApp = this.server.getRawApp();\n const staticRoot = this.options.staticRoot;\n \n ctx.logger.debug('Configuring static files', { root: staticRoot, spa: this.options.spaFallback });\n \n // 1. Static Files\n rawApp.get('/*', serveStatic({ root: staticRoot }));\n \n // 2. SPA Fallback\n if (this.options.spaFallback) {\n rawApp.get('*', async (c, next) => {\n // Skip API paths\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 // Fallback to index.html\n return serveStatic({ \n root: staticRoot,\n rewriteRequestPath: () => 'index.html'\n })(c, next);\n });\n }\n }\n\n // Start server on kernel:ready hook\n ctx.hook('kernel:ready', async () => {\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 * 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":["serveStatic","serveStatic"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@objectstack/plugin-hono-server",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Standard Hono Server Adapter for ObjectStack Runtime",
6
6
  "main": "dist/index.js",
@@ -8,11 +8,11 @@
8
8
  "dependencies": {
9
9
  "@hono/node-server": "^1.2.0",
10
10
  "hono": "^4.0.0",
11
- "@objectstack/core": "1.0.4",
12
- "@objectstack/hono": "1.0.4",
13
- "@objectstack/runtime": "1.0.4",
14
- "@objectstack/spec": "1.0.4",
15
- "@objectstack/types": "1.0.4"
11
+ "@objectstack/core": "1.0.5",
12
+ "@objectstack/hono": "1.0.5",
13
+ "@objectstack/runtime": "1.0.5",
14
+ "@objectstack/spec": "1.0.5",
15
+ "@objectstack/types": "1.0.5"
16
16
  },
17
17
  "devDependencies": {
18
18
  "@types/node": "^25.1.0",
@@ -20,7 +20,7 @@
20
20
  "vitest": "^4.0.18"
21
21
  },
22
22
  "scripts": {
23
- "build": "tsc",
23
+ "build": "tsup --config ../../../tsup.config.ts",
24
24
  "test": "vitest run"
25
25
  }
26
26
  }
@@ -9,13 +9,23 @@ vi.mock('@objectstack/hono', () => ({
9
9
  createHonoApp: vi.fn(),
10
10
  }));
11
11
 
12
+ vi.mock('@hono/node-server/serve-static', () => ({
13
+ serveStatic: vi.fn(() => (c: any, next: any) => next())
14
+ }));
15
+
12
16
  vi.mock('./adapter', () => ({
13
17
  HonoHttpServer: vi.fn(function() {
14
18
  return {
15
19
  mount: vi.fn(),
16
20
  start: vi.fn(),
17
21
  stop: vi.fn(),
18
- getApp: vi.fn()
22
+ getApp: vi.fn(),
23
+ listen: vi.fn(),
24
+ getPort: vi.fn().mockReturnValue(3000),
25
+ close: vi.fn(),
26
+ getRawApp: vi.fn().mockReturnValue({
27
+ get: vi.fn(),
28
+ })
19
29
  };
20
30
  })
21
31
  }));
@@ -104,4 +114,23 @@ describe('HonoServerPlugin', () => {
104
114
 
105
115
  expect(logger.error).toHaveBeenCalledWith('Failed to create standard Hono app', expect.any(Error));
106
116
  });
117
+
118
+ it('should configure static files and SPA fallback when enabled', async () => {
119
+ const plugin = new HonoServerPlugin({
120
+ staticRoot: './public',
121
+ spaFallback: true
122
+ });
123
+
124
+ await plugin.init(context as PluginContext);
125
+ await plugin.start(context as PluginContext);
126
+
127
+ const serverInstance = (HonoHttpServer as any).mock.instances[0];
128
+ const rawApp = serverInstance.getRawApp();
129
+
130
+ expect(serverInstance.getRawApp).toHaveBeenCalled();
131
+ // Should register static files middleware
132
+ expect(rawApp.get).toHaveBeenCalledWith('/*', expect.anything());
133
+ // Should register SPA fallback middleware
134
+ expect(rawApp.get).toHaveBeenCalledWith('*', expect.anything());
135
+ });
107
136
  });
@@ -7,6 +7,7 @@ import {
7
7
  } from '@objectstack/spec/api';
8
8
  import { HonoHttpServer } from './adapter';
9
9
  import { createHonoApp } from '@objectstack/hono';
10
+ import { serveStatic } from '@hono/node-server/serve-static';
10
11
 
11
12
  export interface HonoPluginOptions {
12
13
  port?: number;
@@ -26,6 +27,13 @@ export interface HonoPluginOptions {
26
27
  * @default true
27
28
  */
28
29
  useApiRegistry?: boolean;
30
+
31
+ /**
32
+ * Whether to enable SPA fallback
33
+ * If true, returns index.html for non-API 404s
34
+ * @default false
35
+ */
36
+ spaFallback?: boolean;
29
37
  }
30
38
 
31
39
  /**
@@ -51,9 +59,11 @@ export class HonoServerPlugin implements Plugin {
51
59
  port: 3000,
52
60
  registerStandardEndpoints: true,
53
61
  useApiRegistry: true,
62
+ spaFallback: false,
54
63
  ...options
55
64
  };
56
- this.server = new HonoHttpServer(this.options.port, this.options.staticRoot);
65
+ // We handle static root manually in start() to support SPA fallback
66
+ this.server = new HonoHttpServer(this.options.port);
57
67
  }
58
68
 
59
69
  /**
@@ -101,6 +111,36 @@ export class HonoServerPlugin implements Plugin {
101
111
  ctx.logger.error('Failed to create standard Hono app', e);
102
112
  }
103
113
 
114
+ // Configure Static Files & SPA Fallback
115
+ if (this.options.staticRoot) {
116
+ const rawApp = this.server.getRawApp();
117
+ const staticRoot = this.options.staticRoot;
118
+
119
+ ctx.logger.debug('Configuring static files', { root: staticRoot, spa: this.options.spaFallback });
120
+
121
+ // 1. Static Files
122
+ rawApp.get('/*', serveStatic({ root: staticRoot }));
123
+
124
+ // 2. SPA Fallback
125
+ if (this.options.spaFallback) {
126
+ rawApp.get('*', async (c, next) => {
127
+ // Skip API paths
128
+ const config = this.options.restConfig || {};
129
+ const basePath = config.api?.basePath || '/api';
130
+
131
+ if (c.req.path.startsWith(basePath)) {
132
+ return next();
133
+ }
134
+
135
+ // Fallback to index.html
136
+ return serveStatic({
137
+ root: staticRoot,
138
+ rewriteRequestPath: () => 'index.html'
139
+ })(c, next);
140
+ });
141
+ }
142
+ }
143
+
104
144
  // Start server on kernel:ready hook
105
145
  ctx.hook('kernel:ready', async () => {
106
146
  const port = this.options.port || 3000;
package/dist/adapter.d.ts DELETED
@@ -1,29 +0,0 @@
1
- export * from '@objectstack/core';
2
- import { IHttpServer, RouteHandler, Middleware } from '@objectstack/core';
3
- import { Hono } from 'hono';
4
- /**
5
- * Hono Implementation of IHttpServer
6
- */
7
- export declare class HonoHttpServer implements IHttpServer {
8
- private port;
9
- private staticRoot?;
10
- private app;
11
- private server;
12
- private listeningPort;
13
- constructor(port?: number, staticRoot?: string | undefined);
14
- private wrap;
15
- get(path: string, handler: RouteHandler): void;
16
- post(path: string, handler: RouteHandler): void;
17
- put(path: string, handler: RouteHandler): void;
18
- delete(path: string, handler: RouteHandler): void;
19
- patch(path: string, handler: RouteHandler): void;
20
- use(pathOrHandler: string | Middleware, handler?: Middleware): void;
21
- /**
22
- * Mount a sub-application or router
23
- */
24
- mount(path: string, subApp: Hono): void;
25
- listen(port: number): Promise<void>;
26
- getPort(): number;
27
- getRawApp(): Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
28
- close(): Promise<void>;
29
- }
package/dist/adapter.js DELETED
@@ -1,145 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
- };
16
- Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.HonoHttpServer = void 0;
18
- // Export IHttpServer from core
19
- __exportStar(require("@objectstack/core"), exports);
20
- const hono_1 = require("hono");
21
- const node_server_1 = require("@hono/node-server");
22
- const serve_static_1 = require("@hono/node-server/serve-static");
23
- /**
24
- * Hono Implementation of IHttpServer
25
- */
26
- class HonoHttpServer {
27
- port;
28
- staticRoot;
29
- app;
30
- server;
31
- listeningPort;
32
- constructor(port = 3000, staticRoot) {
33
- this.port = port;
34
- this.staticRoot = staticRoot;
35
- this.app = new hono_1.Hono();
36
- }
37
- // internal helper to convert standard handler to Hono handler
38
- wrap(handler) {
39
- return async (c) => {
40
- let body = {};
41
- // Try to parse JSON body first if content-type is JSON
42
- if (c.req.header('content-type')?.includes('application/json')) {
43
- try {
44
- body = await c.req.json();
45
- }
46
- catch (e) {
47
- // If JSON parsing fails, try parseBody
48
- try {
49
- body = await c.req.parseBody();
50
- }
51
- catch (e2) { }
52
- }
53
- }
54
- else {
55
- // For non-JSON content types, use parseBody
56
- try {
57
- body = await c.req.parseBody();
58
- }
59
- catch (e) { }
60
- }
61
- const req = {
62
- params: c.req.param(),
63
- query: c.req.query(),
64
- body,
65
- headers: c.req.header(),
66
- method: c.req.method,
67
- path: c.req.path
68
- };
69
- let capturedResponse;
70
- const res = {
71
- json: (data) => { capturedResponse = c.json(data); },
72
- send: (data) => { capturedResponse = c.html(data); },
73
- status: (code) => { c.status(code); return res; },
74
- header: (name, value) => { c.header(name, value); return res; }
75
- };
76
- await handler(req, res);
77
- return capturedResponse;
78
- };
79
- }
80
- get(path, handler) {
81
- this.app.get(path, this.wrap(handler));
82
- }
83
- post(path, handler) {
84
- this.app.post(path, this.wrap(handler));
85
- }
86
- put(path, handler) {
87
- this.app.put(path, this.wrap(handler));
88
- }
89
- delete(path, handler) {
90
- this.app.delete(path, this.wrap(handler));
91
- }
92
- patch(path, handler) {
93
- this.app.patch(path, this.wrap(handler));
94
- }
95
- use(pathOrHandler, handler) {
96
- if (typeof pathOrHandler === 'string' && handler) {
97
- // Path based middleware
98
- // Hono middleware signature is different (c, next) => ...
99
- this.app.use(pathOrHandler, async (c, next) => {
100
- // Simplistic conversion
101
- await handler({}, {}, next);
102
- });
103
- }
104
- else if (typeof pathOrHandler === 'function') {
105
- // Global middleware
106
- this.app.use('*', async (c, next) => {
107
- await pathOrHandler({}, {}, next);
108
- });
109
- }
110
- }
111
- /**
112
- * Mount a sub-application or router
113
- */
114
- mount(path, subApp) {
115
- this.app.route(path, subApp);
116
- }
117
- async listen(port) {
118
- return new Promise((resolve) => {
119
- if (this.staticRoot) {
120
- this.app.get('/*', (0, serve_static_1.serveStatic)({ root: this.staticRoot }));
121
- }
122
- const targetPort = port || this.port;
123
- this.server = (0, node_server_1.serve)({
124
- fetch: this.app.fetch,
125
- port: targetPort
126
- }, (info) => {
127
- this.listeningPort = info.port;
128
- resolve();
129
- });
130
- });
131
- }
132
- getPort() {
133
- return this.listeningPort || this.port;
134
- }
135
- // Expose raw app for scenarios where standard interface is not enough
136
- getRawApp() {
137
- return this.app;
138
- }
139
- async close() {
140
- if (this.server && typeof this.server.close === 'function') {
141
- this.server.close();
142
- }
143
- }
144
- }
145
- exports.HonoHttpServer = HonoHttpServer;
@@ -1,49 +0,0 @@
1
- import { Plugin, PluginContext } from '@objectstack/core';
2
- import { RestServerConfig } from '@objectstack/spec/api';
3
- export interface HonoPluginOptions {
4
- port?: number;
5
- staticRoot?: string;
6
- /**
7
- * REST server configuration
8
- * Controls automatic endpoint generation and API behavior
9
- */
10
- restConfig?: RestServerConfig;
11
- /**
12
- * Whether to register standard ObjectStack CRUD endpoints
13
- * @default true
14
- */
15
- registerStandardEndpoints?: boolean;
16
- /**
17
- * Whether to load endpoints from API Registry
18
- * @default true
19
- */
20
- useApiRegistry?: boolean;
21
- }
22
- /**
23
- * Hono Server Plugin
24
- *
25
- * Provides HTTP server capabilities using Hono framework.
26
- * Registers routes for ObjectStack Runtime Protocol.
27
- */
28
- export declare class HonoServerPlugin implements Plugin {
29
- name: string;
30
- version: string;
31
- private static readonly DEFAULT_ENDPOINT_PRIORITY;
32
- private static readonly CORE_ENDPOINT_PRIORITY;
33
- private static readonly DISCOVERY_ENDPOINT_PRIORITY;
34
- private options;
35
- private server;
36
- constructor(options?: HonoPluginOptions);
37
- /**
38
- * Init phase - Setup HTTP server and register as service
39
- */
40
- init: (ctx: PluginContext) => Promise<void>;
41
- /**
42
- * Start phase - Bind routes and start listening
43
- */
44
- start: (ctx: PluginContext) => Promise<void>;
45
- /**
46
- * Destroy phase - Stop server
47
- */
48
- destroy(): Promise<void>;
49
- }
@@ -1,90 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.HonoServerPlugin = void 0;
4
- const adapter_1 = require("./adapter");
5
- const hono_1 = require("@objectstack/hono");
6
- /**
7
- * Hono Server Plugin
8
- *
9
- * Provides HTTP server capabilities using Hono framework.
10
- * Registers routes for ObjectStack Runtime Protocol.
11
- */
12
- class HonoServerPlugin {
13
- name = 'com.objectstack.server.hono';
14
- version = '0.9.0';
15
- // Constants
16
- static DEFAULT_ENDPOINT_PRIORITY = 100;
17
- static CORE_ENDPOINT_PRIORITY = 950;
18
- static DISCOVERY_ENDPOINT_PRIORITY = 900;
19
- options;
20
- server;
21
- constructor(options = {}) {
22
- this.options = {
23
- port: 3000,
24
- registerStandardEndpoints: true,
25
- useApiRegistry: true,
26
- ...options
27
- };
28
- this.server = new adapter_1.HonoHttpServer(this.options.port, this.options.staticRoot);
29
- }
30
- /**
31
- * Init phase - Setup HTTP server and register as service
32
- */
33
- init = async (ctx) => {
34
- ctx.logger.debug('Initializing Hono server plugin', {
35
- port: this.options.port,
36
- staticRoot: this.options.staticRoot
37
- });
38
- // Register HTTP server service as IHttpServer
39
- // Register as 'http.server' to match core requirements
40
- ctx.registerService('http.server', this.server);
41
- // Alias 'http-server' for backward compatibility
42
- ctx.registerService('http-server', this.server);
43
- ctx.logger.debug('HTTP server service registered', { serviceName: 'http.server' });
44
- };
45
- /**
46
- * Start phase - Bind routes and start listening
47
- */
48
- start = async (ctx) => {
49
- ctx.logger.debug('Starting Hono server plugin');
50
- // Use Standard ObjectStack Runtime Hono App
51
- try {
52
- const kernel = ctx.getKernel();
53
- const config = this.options.restConfig || {};
54
- // Calculate prefix similar to before
55
- const apiVersion = config.api?.version || 'v1';
56
- const basePath = config.api?.basePath || '/api';
57
- const apiPath = config.api?.apiPath || `${basePath}/${apiVersion}`;
58
- const app = (0, hono_1.createHonoApp)({
59
- kernel,
60
- prefix: apiPath // Use the calculated path
61
- });
62
- ctx.logger.debug('Mounting ObjectStack Runtime App', { prefix: apiPath });
63
- // Use the mount method we added to HonoHttpServer
64
- this.server.mount('/', app);
65
- }
66
- catch (e) {
67
- ctx.logger.error('Failed to create standard Hono app', e);
68
- }
69
- // Start server on kernel:ready hook
70
- ctx.hook('kernel:ready', async () => {
71
- const port = this.options.port || 3000;
72
- ctx.logger.debug('Starting HTTP server', { port });
73
- await this.server.listen(port);
74
- const actualPort = this.server.getPort();
75
- ctx.logger.info('HTTP server started successfully', {
76
- port: actualPort,
77
- url: `http://localhost:${actualPort}`
78
- });
79
- });
80
- };
81
- /**
82
- * Destroy phase - Stop server
83
- */
84
- async destroy() {
85
- this.server.close();
86
- // Note: Can't use ctx.logger here since we're in destroy
87
- console.log('[HonoServerPlugin] Server stopped');
88
- }
89
- }
90
- exports.HonoServerPlugin = HonoServerPlugin;