@objectstack/plugin-hono-server 1.0.2 → 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.
- package/.turbo/turbo-build.log +22 -0
- package/CHANGELOG.md +50 -0
- package/dist/index.d.mts +88 -0
- package/dist/index.d.ts +88 -2
- package/dist/index.js +256 -15
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +256 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +7 -7
- package/src/adapter.ts +8 -1
- package/src/hono-plugin.test.ts +30 -1
- package/src/hono-plugin.ts +51 -6
- package/dist/adapter.d.ts +0 -27
- package/dist/adapter.js +0 -139
- package/dist/hono-plugin.d.ts +0 -49
- package/dist/hono-plugin.js +0 -86
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.
|
|
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.
|
|
12
|
-
"@objectstack/hono": "1.0.
|
|
13
|
-
"@objectstack/runtime": "1.0.
|
|
14
|
-
"@objectstack/spec": "1.0.
|
|
15
|
-
"@objectstack/types": "1.0.
|
|
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": "
|
|
23
|
+
"build": "tsup --config ../../../tsup.config.ts",
|
|
24
24
|
"test": "vitest run"
|
|
25
25
|
}
|
|
26
26
|
}
|
package/src/adapter.ts
CHANGED
|
@@ -16,6 +16,7 @@ import { serveStatic } from '@hono/node-server/serve-static';
|
|
|
16
16
|
export class HonoHttpServer implements IHttpServer {
|
|
17
17
|
private app: Hono;
|
|
18
18
|
private server: any;
|
|
19
|
+
private listeningPort: number | undefined;
|
|
19
20
|
|
|
20
21
|
constructor(
|
|
21
22
|
private port: number = 3000,
|
|
@@ -115,15 +116,21 @@ export class HonoHttpServer implements IHttpServer {
|
|
|
115
116
|
this.app.get('/*', serveStatic({ root: this.staticRoot }));
|
|
116
117
|
}
|
|
117
118
|
|
|
119
|
+
const targetPort = port || this.port;
|
|
118
120
|
this.server = serve({
|
|
119
121
|
fetch: this.app.fetch,
|
|
120
|
-
port:
|
|
122
|
+
port: targetPort
|
|
121
123
|
}, (info) => {
|
|
124
|
+
this.listeningPort = info.port;
|
|
122
125
|
resolve();
|
|
123
126
|
});
|
|
124
127
|
});
|
|
125
128
|
}
|
|
126
129
|
|
|
130
|
+
getPort() {
|
|
131
|
+
return this.listeningPort || this.port;
|
|
132
|
+
}
|
|
133
|
+
|
|
127
134
|
// Expose raw app for scenarios where standard interface is not enough
|
|
128
135
|
getRawApp() {
|
|
129
136
|
return this.app;
|
package/src/hono-plugin.test.ts
CHANGED
|
@@ -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
|
});
|
package/src/hono-plugin.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
/**
|
|
@@ -66,8 +76,11 @@ export class HonoServerPlugin implements Plugin {
|
|
|
66
76
|
});
|
|
67
77
|
|
|
68
78
|
// Register HTTP server service as IHttpServer
|
|
79
|
+
// Register as 'http.server' to match core requirements
|
|
80
|
+
ctx.registerService('http.server', this.server);
|
|
81
|
+
// Alias 'http-server' for backward compatibility
|
|
69
82
|
ctx.registerService('http-server', this.server);
|
|
70
|
-
ctx.logger.
|
|
83
|
+
ctx.logger.debug('HTTP server service registered', { serviceName: 'http.server' });
|
|
71
84
|
}
|
|
72
85
|
|
|
73
86
|
/**
|
|
@@ -90,7 +103,7 @@ export class HonoServerPlugin implements Plugin {
|
|
|
90
103
|
prefix: apiPath // Use the calculated path
|
|
91
104
|
});
|
|
92
105
|
|
|
93
|
-
ctx.logger.
|
|
106
|
+
ctx.logger.debug('Mounting ObjectStack Runtime App', { prefix: apiPath });
|
|
94
107
|
// Use the mount method we added to HonoHttpServer
|
|
95
108
|
this.server.mount('/', app as any);
|
|
96
109
|
|
|
@@ -98,15 +111,47 @@ export class HonoServerPlugin implements Plugin {
|
|
|
98
111
|
ctx.logger.error('Failed to create standard Hono app', e);
|
|
99
112
|
}
|
|
100
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
|
+
|
|
101
144
|
// Start server on kernel:ready hook
|
|
102
145
|
ctx.hook('kernel:ready', async () => {
|
|
103
146
|
const port = this.options.port || 3000;
|
|
104
|
-
ctx.logger.
|
|
147
|
+
ctx.logger.debug('Starting HTTP server', { port });
|
|
105
148
|
|
|
106
149
|
await this.server.listen(port);
|
|
150
|
+
|
|
151
|
+
const actualPort = this.server.getPort();
|
|
107
152
|
ctx.logger.info('HTTP server started successfully', {
|
|
108
|
-
port,
|
|
109
|
-
url: `http://localhost:${
|
|
153
|
+
port: actualPort,
|
|
154
|
+
url: `http://localhost:${actualPort}`
|
|
110
155
|
});
|
|
111
156
|
});
|
|
112
157
|
}
|
package/dist/adapter.d.ts
DELETED
|
@@ -1,27 +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
|
-
constructor(port?: number, staticRoot?: string | undefined);
|
|
13
|
-
private wrap;
|
|
14
|
-
get(path: string, handler: RouteHandler): void;
|
|
15
|
-
post(path: string, handler: RouteHandler): void;
|
|
16
|
-
put(path: string, handler: RouteHandler): void;
|
|
17
|
-
delete(path: string, handler: RouteHandler): void;
|
|
18
|
-
patch(path: string, handler: RouteHandler): void;
|
|
19
|
-
use(pathOrHandler: string | Middleware, handler?: Middleware): void;
|
|
20
|
-
/**
|
|
21
|
-
* Mount a sub-application or router
|
|
22
|
-
*/
|
|
23
|
-
mount(path: string, subApp: Hono): void;
|
|
24
|
-
listen(port: number): Promise<void>;
|
|
25
|
-
getRawApp(): Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
|
|
26
|
-
close(): Promise<void>;
|
|
27
|
-
}
|
package/dist/adapter.js
DELETED
|
@@ -1,139 +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
|
-
constructor(port = 3000, staticRoot) {
|
|
32
|
-
this.port = port;
|
|
33
|
-
this.staticRoot = staticRoot;
|
|
34
|
-
this.app = new hono_1.Hono();
|
|
35
|
-
}
|
|
36
|
-
// internal helper to convert standard handler to Hono handler
|
|
37
|
-
wrap(handler) {
|
|
38
|
-
return async (c) => {
|
|
39
|
-
let body = {};
|
|
40
|
-
// Try to parse JSON body first if content-type is JSON
|
|
41
|
-
if (c.req.header('content-type')?.includes('application/json')) {
|
|
42
|
-
try {
|
|
43
|
-
body = await c.req.json();
|
|
44
|
-
}
|
|
45
|
-
catch (e) {
|
|
46
|
-
// If JSON parsing fails, try parseBody
|
|
47
|
-
try {
|
|
48
|
-
body = await c.req.parseBody();
|
|
49
|
-
}
|
|
50
|
-
catch (e2) { }
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
else {
|
|
54
|
-
// For non-JSON content types, use parseBody
|
|
55
|
-
try {
|
|
56
|
-
body = await c.req.parseBody();
|
|
57
|
-
}
|
|
58
|
-
catch (e) { }
|
|
59
|
-
}
|
|
60
|
-
const req = {
|
|
61
|
-
params: c.req.param(),
|
|
62
|
-
query: c.req.query(),
|
|
63
|
-
body,
|
|
64
|
-
headers: c.req.header(),
|
|
65
|
-
method: c.req.method,
|
|
66
|
-
path: c.req.path
|
|
67
|
-
};
|
|
68
|
-
let capturedResponse;
|
|
69
|
-
const res = {
|
|
70
|
-
json: (data) => { capturedResponse = c.json(data); },
|
|
71
|
-
send: (data) => { capturedResponse = c.html(data); },
|
|
72
|
-
status: (code) => { c.status(code); return res; },
|
|
73
|
-
header: (name, value) => { c.header(name, value); return res; }
|
|
74
|
-
};
|
|
75
|
-
await handler(req, res);
|
|
76
|
-
return capturedResponse;
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
get(path, handler) {
|
|
80
|
-
this.app.get(path, this.wrap(handler));
|
|
81
|
-
}
|
|
82
|
-
post(path, handler) {
|
|
83
|
-
this.app.post(path, this.wrap(handler));
|
|
84
|
-
}
|
|
85
|
-
put(path, handler) {
|
|
86
|
-
this.app.put(path, this.wrap(handler));
|
|
87
|
-
}
|
|
88
|
-
delete(path, handler) {
|
|
89
|
-
this.app.delete(path, this.wrap(handler));
|
|
90
|
-
}
|
|
91
|
-
patch(path, handler) {
|
|
92
|
-
this.app.patch(path, this.wrap(handler));
|
|
93
|
-
}
|
|
94
|
-
use(pathOrHandler, handler) {
|
|
95
|
-
if (typeof pathOrHandler === 'string' && handler) {
|
|
96
|
-
// Path based middleware
|
|
97
|
-
// Hono middleware signature is different (c, next) => ...
|
|
98
|
-
this.app.use(pathOrHandler, async (c, next) => {
|
|
99
|
-
// Simplistic conversion
|
|
100
|
-
await handler({}, {}, next);
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
else if (typeof pathOrHandler === 'function') {
|
|
104
|
-
// Global middleware
|
|
105
|
-
this.app.use('*', async (c, next) => {
|
|
106
|
-
await pathOrHandler({}, {}, next);
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
/**
|
|
111
|
-
* Mount a sub-application or router
|
|
112
|
-
*/
|
|
113
|
-
mount(path, subApp) {
|
|
114
|
-
this.app.route(path, subApp);
|
|
115
|
-
}
|
|
116
|
-
async listen(port) {
|
|
117
|
-
return new Promise((resolve) => {
|
|
118
|
-
if (this.staticRoot) {
|
|
119
|
-
this.app.get('/*', (0, serve_static_1.serveStatic)({ root: this.staticRoot }));
|
|
120
|
-
}
|
|
121
|
-
this.server = (0, node_server_1.serve)({
|
|
122
|
-
fetch: this.app.fetch,
|
|
123
|
-
port: port || this.port
|
|
124
|
-
}, (info) => {
|
|
125
|
-
resolve();
|
|
126
|
-
});
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
// Expose raw app for scenarios where standard interface is not enough
|
|
130
|
-
getRawApp() {
|
|
131
|
-
return this.app;
|
|
132
|
-
}
|
|
133
|
-
async close() {
|
|
134
|
-
if (this.server && typeof this.server.close === 'function') {
|
|
135
|
-
this.server.close();
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
exports.HonoHttpServer = HonoHttpServer;
|