@objectstack/plugin-hono-server 4.0.3 → 4.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/README.md +93 -1
- package/dist/index.d.mts +118 -27
- package/dist/index.d.ts +118 -27
- package/dist/index.js +247 -26
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +247 -26
- package/dist/index.mjs.map +1 -1
- package/package.json +33 -8
- package/.turbo/turbo-build.log +0 -22
- package/CHANGELOG.md +0 -680
- package/objectstack.config.ts +0 -240
- package/src/adapter.ts +0 -222
- package/src/hono-plugin.test.ts +0 -113
- package/src/hono-plugin.ts +0 -328
- package/src/index.ts +0 -5
- package/tsconfig.json +0 -24
package/objectstack.config.ts
DELETED
|
@@ -1,240 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
-
|
|
3
|
-
import { ObjectStackManifest } from '@objectstack/spec/system';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Hono Server Plugin Manifest
|
|
7
|
-
*
|
|
8
|
-
* HTTP server adapter plugin using the Hono framework.
|
|
9
|
-
* Provides northbound HTTP/REST API gateway capabilities.
|
|
10
|
-
*/
|
|
11
|
-
const HonoServerPlugin: ObjectStackManifest = {
|
|
12
|
-
id: 'com.objectstack.server.hono',
|
|
13
|
-
name: 'Hono Server Adapter',
|
|
14
|
-
version: '1.0.0',
|
|
15
|
-
type: 'adapter',
|
|
16
|
-
description: 'HTTP server adapter using Hono framework. Exposes ObjectStack Runtime Protocol via REST API endpoints.',
|
|
17
|
-
|
|
18
|
-
configuration: {
|
|
19
|
-
title: 'Hono Server Configuration',
|
|
20
|
-
properties: {
|
|
21
|
-
port: {
|
|
22
|
-
type: 'number',
|
|
23
|
-
default: 3000,
|
|
24
|
-
description: 'HTTP server port',
|
|
25
|
-
},
|
|
26
|
-
staticRoot: {
|
|
27
|
-
type: 'string',
|
|
28
|
-
description: 'Path to static files directory (optional)',
|
|
29
|
-
},
|
|
30
|
-
},
|
|
31
|
-
},
|
|
32
|
-
|
|
33
|
-
// Plugin Capability Declaration
|
|
34
|
-
capabilities: {
|
|
35
|
-
// Protocols This Plugin Implements
|
|
36
|
-
implements: [
|
|
37
|
-
{
|
|
38
|
-
protocol: {
|
|
39
|
-
id: 'com.objectstack.protocol.http.v1',
|
|
40
|
-
label: 'HTTP Server Protocol v1',
|
|
41
|
-
version: { major: 1, minor: 0, patch: 0 },
|
|
42
|
-
description: 'Standard HTTP server capabilities',
|
|
43
|
-
},
|
|
44
|
-
conformance: 'full',
|
|
45
|
-
certified: false,
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
protocol: {
|
|
49
|
-
id: 'com.objectstack.protocol.api.rest.v1',
|
|
50
|
-
label: 'REST API Protocol v1',
|
|
51
|
-
version: { major: 1, minor: 0, patch: 0 },
|
|
52
|
-
description: 'RESTful API endpoint implementation',
|
|
53
|
-
},
|
|
54
|
-
conformance: 'full',
|
|
55
|
-
features: [
|
|
56
|
-
{
|
|
57
|
-
name: 'meta_protocol',
|
|
58
|
-
enabled: true,
|
|
59
|
-
description: 'Metadata discovery endpoints',
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
name: 'data_protocol',
|
|
63
|
-
enabled: true,
|
|
64
|
-
description: 'CRUD data operations',
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
name: 'ui_protocol',
|
|
68
|
-
enabled: true,
|
|
69
|
-
description: 'UI view metadata endpoints',
|
|
70
|
-
},
|
|
71
|
-
],
|
|
72
|
-
certified: false,
|
|
73
|
-
},
|
|
74
|
-
],
|
|
75
|
-
|
|
76
|
-
// Interfaces This Plugin Provides
|
|
77
|
-
provides: [
|
|
78
|
-
{
|
|
79
|
-
id: 'com.objectstack.server.hono.interface.http_server',
|
|
80
|
-
name: 'IHttpServer',
|
|
81
|
-
description: 'HTTP server service interface',
|
|
82
|
-
version: { major: 1, minor: 0, patch: 0 },
|
|
83
|
-
stability: 'stable',
|
|
84
|
-
methods: [
|
|
85
|
-
{
|
|
86
|
-
name: 'get',
|
|
87
|
-
description: 'Register GET route handler',
|
|
88
|
-
parameters: [
|
|
89
|
-
{
|
|
90
|
-
name: 'path',
|
|
91
|
-
type: 'string',
|
|
92
|
-
required: true,
|
|
93
|
-
description: 'Route path pattern',
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
name: 'handler',
|
|
97
|
-
type: 'Function',
|
|
98
|
-
required: true,
|
|
99
|
-
description: 'Route handler function',
|
|
100
|
-
},
|
|
101
|
-
],
|
|
102
|
-
returnType: 'void',
|
|
103
|
-
async: false,
|
|
104
|
-
},
|
|
105
|
-
{
|
|
106
|
-
name: 'post',
|
|
107
|
-
description: 'Register POST route handler',
|
|
108
|
-
parameters: [
|
|
109
|
-
{
|
|
110
|
-
name: 'path',
|
|
111
|
-
type: 'string',
|
|
112
|
-
required: true,
|
|
113
|
-
description: 'Route path pattern',
|
|
114
|
-
},
|
|
115
|
-
{
|
|
116
|
-
name: 'handler',
|
|
117
|
-
type: 'Function',
|
|
118
|
-
required: true,
|
|
119
|
-
description: 'Route handler function',
|
|
120
|
-
},
|
|
121
|
-
],
|
|
122
|
-
returnType: 'void',
|
|
123
|
-
async: false,
|
|
124
|
-
},
|
|
125
|
-
{
|
|
126
|
-
name: 'patch',
|
|
127
|
-
description: 'Register PATCH route handler',
|
|
128
|
-
parameters: [
|
|
129
|
-
{
|
|
130
|
-
name: 'path',
|
|
131
|
-
type: 'string',
|
|
132
|
-
required: true,
|
|
133
|
-
description: 'Route path pattern',
|
|
134
|
-
},
|
|
135
|
-
{
|
|
136
|
-
name: 'handler',
|
|
137
|
-
type: 'Function',
|
|
138
|
-
required: true,
|
|
139
|
-
description: 'Route handler function',
|
|
140
|
-
},
|
|
141
|
-
],
|
|
142
|
-
returnType: 'void',
|
|
143
|
-
async: false,
|
|
144
|
-
},
|
|
145
|
-
{
|
|
146
|
-
name: 'delete',
|
|
147
|
-
description: 'Register DELETE route handler',
|
|
148
|
-
parameters: [
|
|
149
|
-
{
|
|
150
|
-
name: 'path',
|
|
151
|
-
type: 'string',
|
|
152
|
-
required: true,
|
|
153
|
-
description: 'Route path pattern',
|
|
154
|
-
},
|
|
155
|
-
{
|
|
156
|
-
name: 'handler',
|
|
157
|
-
type: 'Function',
|
|
158
|
-
required: true,
|
|
159
|
-
description: 'Route handler function',
|
|
160
|
-
},
|
|
161
|
-
],
|
|
162
|
-
returnType: 'void',
|
|
163
|
-
async: false,
|
|
164
|
-
},
|
|
165
|
-
{
|
|
166
|
-
name: 'listen',
|
|
167
|
-
description: 'Start the HTTP server',
|
|
168
|
-
parameters: [
|
|
169
|
-
{
|
|
170
|
-
name: 'port',
|
|
171
|
-
type: 'number',
|
|
172
|
-
required: true,
|
|
173
|
-
description: 'Port number',
|
|
174
|
-
},
|
|
175
|
-
],
|
|
176
|
-
returnType: 'Promise<void>',
|
|
177
|
-
async: true,
|
|
178
|
-
},
|
|
179
|
-
{
|
|
180
|
-
name: 'close',
|
|
181
|
-
description: 'Stop the HTTP server',
|
|
182
|
-
parameters: [],
|
|
183
|
-
returnType: 'void',
|
|
184
|
-
async: false,
|
|
185
|
-
},
|
|
186
|
-
],
|
|
187
|
-
},
|
|
188
|
-
],
|
|
189
|
-
|
|
190
|
-
// Dependencies on Other Plugins/Services
|
|
191
|
-
requires: [
|
|
192
|
-
{
|
|
193
|
-
pluginId: 'com.objectstack.engine.objectql',
|
|
194
|
-
version: '^0.6.0',
|
|
195
|
-
optional: true,
|
|
196
|
-
reason: 'ObjectStack Runtime Protocol implementation service',
|
|
197
|
-
requiredCapabilities: [
|
|
198
|
-
'com.objectstack.protocol.runtime.v1',
|
|
199
|
-
],
|
|
200
|
-
},
|
|
201
|
-
],
|
|
202
|
-
|
|
203
|
-
// Extension Points This Plugin Defines
|
|
204
|
-
extensionPoints: [
|
|
205
|
-
{
|
|
206
|
-
id: 'com.objectstack.server.hono.extension.middleware',
|
|
207
|
-
name: 'HTTP Middleware',
|
|
208
|
-
description: 'Register custom HTTP middleware',
|
|
209
|
-
type: 'hook',
|
|
210
|
-
cardinality: 'multiple',
|
|
211
|
-
contract: {
|
|
212
|
-
signature: '(req: Request, res: Response, next: Function) => void | Promise<void>',
|
|
213
|
-
},
|
|
214
|
-
},
|
|
215
|
-
{
|
|
216
|
-
id: 'com.objectstack.server.hono.extension.route',
|
|
217
|
-
name: 'Custom Routes',
|
|
218
|
-
description: 'Register custom API routes',
|
|
219
|
-
type: 'action',
|
|
220
|
-
cardinality: 'multiple',
|
|
221
|
-
contract: {
|
|
222
|
-
input: 'RouteDefinition',
|
|
223
|
-
signature: '(app: HonoApp) => void',
|
|
224
|
-
},
|
|
225
|
-
},
|
|
226
|
-
],
|
|
227
|
-
|
|
228
|
-
// No extensions contributed to other plugins
|
|
229
|
-
extensions: [],
|
|
230
|
-
},
|
|
231
|
-
|
|
232
|
-
contributes: {
|
|
233
|
-
// System Events
|
|
234
|
-
events: [
|
|
235
|
-
'kernel:ready',
|
|
236
|
-
],
|
|
237
|
-
},
|
|
238
|
-
};
|
|
239
|
-
|
|
240
|
-
export default HonoServerPlugin;
|
package/src/adapter.ts
DELETED
|
@@ -1,222 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
-
|
|
3
|
-
// Export IHttpServer from core
|
|
4
|
-
export * from '@objectstack/core';
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
IHttpServer,
|
|
8
|
-
RouteHandler,
|
|
9
|
-
Middleware
|
|
10
|
-
} from '@objectstack/core';
|
|
11
|
-
import { Hono } from 'hono';
|
|
12
|
-
import { serve } from '@hono/node-server';
|
|
13
|
-
import { serveStatic } from '@hono/node-server/serve-static';
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Hono Implementation of IHttpServer
|
|
17
|
-
*/
|
|
18
|
-
export class HonoHttpServer implements IHttpServer {
|
|
19
|
-
private app: Hono;
|
|
20
|
-
private server: any;
|
|
21
|
-
private listeningPort: number | undefined;
|
|
22
|
-
|
|
23
|
-
constructor(
|
|
24
|
-
private port: number = 3000,
|
|
25
|
-
private staticRoot?: string
|
|
26
|
-
) {
|
|
27
|
-
this.app = new Hono();
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// internal helper to convert standard handler to Hono handler
|
|
31
|
-
private wrap(handler: RouteHandler) {
|
|
32
|
-
return async (c: any) => {
|
|
33
|
-
let body: any = {};
|
|
34
|
-
|
|
35
|
-
// Try to parse JSON body first if content-type is JSON
|
|
36
|
-
if (c.req.header('content-type')?.includes('application/json')) {
|
|
37
|
-
try {
|
|
38
|
-
body = await c.req.json();
|
|
39
|
-
} catch(e) {
|
|
40
|
-
// If JSON parsing fails, try parseBody
|
|
41
|
-
try {
|
|
42
|
-
body = await c.req.parseBody();
|
|
43
|
-
} catch(e2) {}
|
|
44
|
-
}
|
|
45
|
-
} else {
|
|
46
|
-
// For non-JSON content types, use parseBody
|
|
47
|
-
try {
|
|
48
|
-
body = await c.req.parseBody();
|
|
49
|
-
} catch(e) {}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const req = {
|
|
53
|
-
params: c.req.param(),
|
|
54
|
-
query: c.req.query(),
|
|
55
|
-
body,
|
|
56
|
-
headers: c.req.header(),
|
|
57
|
-
method: c.req.method,
|
|
58
|
-
path: c.req.path
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
let capturedResponse: any;
|
|
62
|
-
let streamController: ReadableStreamDefaultController | null = null;
|
|
63
|
-
let streamEncoder: TextEncoder | null = null;
|
|
64
|
-
let streamHeaders: Record<string, string> = {};
|
|
65
|
-
let isStreaming = false;
|
|
66
|
-
|
|
67
|
-
const res = {
|
|
68
|
-
json: (data: any) => { capturedResponse = c.json(data); },
|
|
69
|
-
send: (data: string) => { capturedResponse = c.html(data); },
|
|
70
|
-
status: (code: number) => { c.status(code); return res; },
|
|
71
|
-
header: (name: string, value: string) => {
|
|
72
|
-
c.header(name, value);
|
|
73
|
-
streamHeaders[name] = value;
|
|
74
|
-
return res;
|
|
75
|
-
},
|
|
76
|
-
write: (chunk: string | Uint8Array) => {
|
|
77
|
-
isStreaming = true;
|
|
78
|
-
if (streamController && streamEncoder) {
|
|
79
|
-
const data = typeof chunk === 'string' ? streamEncoder.encode(chunk) : chunk;
|
|
80
|
-
streamController.enqueue(data);
|
|
81
|
-
}
|
|
82
|
-
},
|
|
83
|
-
end: () => {
|
|
84
|
-
if (streamController) {
|
|
85
|
-
streamController.close();
|
|
86
|
-
}
|
|
87
|
-
},
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
// Create a streaming response wrapper — if handler calls res.write(),
|
|
91
|
-
// we return a ReadableStream; otherwise fall back to capturedResponse.
|
|
92
|
-
const streamPromise = new Promise<Response | null>((resolve) => {
|
|
93
|
-
const stream = new ReadableStream({
|
|
94
|
-
start(controller) {
|
|
95
|
-
streamController = controller;
|
|
96
|
-
streamEncoder = new TextEncoder();
|
|
97
|
-
},
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
// Run the handler; once it's done, check if streaming was used
|
|
101
|
-
const result = handler(req as any, res as any);
|
|
102
|
-
const done = result instanceof Promise ? result : Promise.resolve(result);
|
|
103
|
-
done.then(() => {
|
|
104
|
-
if (isStreaming) {
|
|
105
|
-
resolve(new Response(stream, {
|
|
106
|
-
status: 200,
|
|
107
|
-
headers: streamHeaders,
|
|
108
|
-
}));
|
|
109
|
-
} else {
|
|
110
|
-
// Not streaming — close the unused stream and return null
|
|
111
|
-
streamController?.close();
|
|
112
|
-
resolve(null);
|
|
113
|
-
}
|
|
114
|
-
}).catch(() => {
|
|
115
|
-
streamController?.close();
|
|
116
|
-
resolve(null);
|
|
117
|
-
});
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
const streamResponse = await streamPromise;
|
|
121
|
-
return streamResponse ?? capturedResponse;
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
get(path: string, handler: RouteHandler) {
|
|
126
|
-
this.app.get(path, this.wrap(handler));
|
|
127
|
-
}
|
|
128
|
-
post(path: string, handler: RouteHandler) {
|
|
129
|
-
this.app.post(path, this.wrap(handler));
|
|
130
|
-
}
|
|
131
|
-
put(path: string, handler: RouteHandler) {
|
|
132
|
-
this.app.put(path, this.wrap(handler));
|
|
133
|
-
}
|
|
134
|
-
delete(path: string, handler: RouteHandler) {
|
|
135
|
-
this.app.delete(path, this.wrap(handler));
|
|
136
|
-
}
|
|
137
|
-
patch(path: string, handler: RouteHandler) {
|
|
138
|
-
this.app.patch(path, this.wrap(handler));
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
use(pathOrHandler: string | Middleware, handler?: Middleware) {
|
|
142
|
-
if (typeof pathOrHandler === 'string' && handler) {
|
|
143
|
-
// Path based middleware
|
|
144
|
-
// Hono middleware signature is different (c, next) => ...
|
|
145
|
-
this.app.use(pathOrHandler, async (c, next) => {
|
|
146
|
-
// Simplistic conversion
|
|
147
|
-
await handler({} as any, {} as any, next);
|
|
148
|
-
});
|
|
149
|
-
} else if (typeof pathOrHandler === 'function') {
|
|
150
|
-
// Global middleware
|
|
151
|
-
this.app.use('*', async (c, next) => {
|
|
152
|
-
await pathOrHandler({} as any, {} as any, next);
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Mount a sub-application or router
|
|
159
|
-
*/
|
|
160
|
-
mount(path: string, subApp: Hono) {
|
|
161
|
-
this.app.route(path, subApp);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
async listen(port: number) {
|
|
166
|
-
if (this.staticRoot) {
|
|
167
|
-
this.app.get('/*', serveStatic({ root: this.staticRoot }));
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const targetPort = port || this.port;
|
|
171
|
-
const maxRetries = 20;
|
|
172
|
-
|
|
173
|
-
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
174
|
-
const tryPort = targetPort + attempt;
|
|
175
|
-
try {
|
|
176
|
-
await this.tryListen(tryPort);
|
|
177
|
-
return;
|
|
178
|
-
} catch (err: any) {
|
|
179
|
-
if (err.code === 'EADDRINUSE' && attempt < maxRetries - 1) {
|
|
180
|
-
if (this.server && typeof this.server.close === 'function') {
|
|
181
|
-
this.server.close();
|
|
182
|
-
}
|
|
183
|
-
continue;
|
|
184
|
-
}
|
|
185
|
-
throw err;
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
private tryListen(port: number): Promise<void> {
|
|
191
|
-
return new Promise<void>((resolve, reject) => {
|
|
192
|
-
const server = serve({
|
|
193
|
-
fetch: this.app.fetch,
|
|
194
|
-
port
|
|
195
|
-
}, (info) => {
|
|
196
|
-
this.listeningPort = info.port;
|
|
197
|
-
resolve();
|
|
198
|
-
});
|
|
199
|
-
this.server = server;
|
|
200
|
-
server.on('error', (err: any) => {
|
|
201
|
-
reject(err);
|
|
202
|
-
});
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
getPort() {
|
|
207
|
-
return this.listeningPort || this.port;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// Expose raw app for scenarios where standard interface is not enough
|
|
211
|
-
getRawApp() {
|
|
212
|
-
return this.app;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
async close() {
|
|
216
|
-
if (this.server && typeof this.server.close === 'function') {
|
|
217
|
-
this.server.close();
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
}
|
package/src/hono-plugin.test.ts
DELETED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
-
import { HonoServerPlugin } from './hono-plugin';
|
|
3
|
-
import { PluginContext } from '@objectstack/core';
|
|
4
|
-
import { HonoHttpServer } from './adapter';
|
|
5
|
-
|
|
6
|
-
vi.mock('fs', async (importOriginal) => {
|
|
7
|
-
const actual = await importOriginal<typeof import('fs')>();
|
|
8
|
-
return {
|
|
9
|
-
...actual,
|
|
10
|
-
existsSync: vi.fn().mockReturnValue(true)
|
|
11
|
-
};
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
vi.mock('@hono/node-server/serve-static', () => ({
|
|
15
|
-
serveStatic: vi.fn(() => (c: any, next: any) => next())
|
|
16
|
-
}));
|
|
17
|
-
|
|
18
|
-
vi.mock('./adapter', () => ({
|
|
19
|
-
HonoHttpServer: vi.fn(function() {
|
|
20
|
-
return {
|
|
21
|
-
mount: vi.fn(),
|
|
22
|
-
start: vi.fn(),
|
|
23
|
-
stop: vi.fn(),
|
|
24
|
-
getApp: vi.fn(),
|
|
25
|
-
listen: vi.fn(),
|
|
26
|
-
getPort: vi.fn().mockReturnValue(3000),
|
|
27
|
-
close: vi.fn(),
|
|
28
|
-
getRawApp: vi.fn().mockReturnValue({
|
|
29
|
-
get: vi.fn(),
|
|
30
|
-
})
|
|
31
|
-
};
|
|
32
|
-
})
|
|
33
|
-
}));
|
|
34
|
-
|
|
35
|
-
describe('HonoServerPlugin', () => {
|
|
36
|
-
let context: any;
|
|
37
|
-
let logger: any;
|
|
38
|
-
let kernel: any;
|
|
39
|
-
|
|
40
|
-
beforeEach(() => {
|
|
41
|
-
vi.clearAllMocks();
|
|
42
|
-
|
|
43
|
-
logger = {
|
|
44
|
-
info: vi.fn(),
|
|
45
|
-
debug: vi.fn(),
|
|
46
|
-
warn: vi.fn(),
|
|
47
|
-
error: vi.fn()
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
kernel = {
|
|
51
|
-
getService: vi.fn(),
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
context = {
|
|
55
|
-
logger,
|
|
56
|
-
getKernel: vi.fn().mockReturnValue(kernel),
|
|
57
|
-
registerService: vi.fn(),
|
|
58
|
-
hook: vi.fn(),
|
|
59
|
-
getService: vi.fn()
|
|
60
|
-
};
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('should initialize and register server', async () => {
|
|
64
|
-
const plugin = new HonoServerPlugin();
|
|
65
|
-
await plugin.init(context as PluginContext);
|
|
66
|
-
|
|
67
|
-
expect(context.registerService).toHaveBeenCalledWith('http-server', expect.any(Object));
|
|
68
|
-
expect(HonoHttpServer).toHaveBeenCalled();
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it('should register IHttpServer service on init', async () => {
|
|
72
|
-
const plugin = new HonoServerPlugin();
|
|
73
|
-
await plugin.init(context as PluginContext);
|
|
74
|
-
|
|
75
|
-
expect(context.registerService).toHaveBeenCalledWith('http.server', expect.any(Object));
|
|
76
|
-
expect(context.registerService).toHaveBeenCalledWith('http-server', expect.any(Object));
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('should start without errors', async () => {
|
|
80
|
-
const plugin = new HonoServerPlugin();
|
|
81
|
-
await plugin.init(context as PluginContext);
|
|
82
|
-
await plugin.start(context as PluginContext);
|
|
83
|
-
|
|
84
|
-
// Plugin should register kernel:ready hook to start listening
|
|
85
|
-
expect(context.hook).toHaveBeenCalledWith('kernel:ready', expect.any(Function));
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it('should handle errors gracefully on start', async () => {
|
|
89
|
-
// Simulate a start that doesn't crash even without routes
|
|
90
|
-
const plugin = new HonoServerPlugin();
|
|
91
|
-
await plugin.init(context as PluginContext);
|
|
92
|
-
await expect(plugin.start(context as PluginContext)).resolves.not.toThrow();
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it('should configure static files and SPA fallback when enabled', async () => {
|
|
96
|
-
const plugin = new HonoServerPlugin({
|
|
97
|
-
staticRoot: './public',
|
|
98
|
-
spaFallback: true
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
await plugin.init(context as PluginContext);
|
|
102
|
-
await plugin.start(context as PluginContext);
|
|
103
|
-
|
|
104
|
-
const serverInstance = (HonoHttpServer as any).mock.instances[0];
|
|
105
|
-
const rawApp = serverInstance.getRawApp();
|
|
106
|
-
|
|
107
|
-
expect(serverInstance.getRawApp).toHaveBeenCalled();
|
|
108
|
-
// Should register static files middleware
|
|
109
|
-
expect(rawApp.get).toHaveBeenCalledWith('/*', expect.anything());
|
|
110
|
-
// Should register SPA fallback middleware
|
|
111
|
-
expect(rawApp.get).toHaveBeenCalledWith('/*', expect.anything());
|
|
112
|
-
});
|
|
113
|
-
});
|