@objectstack/plugin-hono-server 1.0.1 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +54 -0
- package/README.md +21 -4
- package/dist/adapter.d.ts +6 -0
- package/dist/adapter.js +13 -1
- package/dist/hono-plugin.d.ts +0 -24
- package/dist/hono-plugin.js +24 -1014
- package/package.json +6 -4
- package/src/adapter.ts +16 -1
- package/src/hono-plugin.test.ts +50 -133
- package/src/hono-plugin.ts +28 -1063
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.4",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "Standard Hono Server Adapter for ObjectStack Runtime",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -8,9 +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/
|
|
13
|
-
"@objectstack/
|
|
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"
|
|
14
16
|
},
|
|
15
17
|
"devDependencies": {
|
|
16
18
|
"@types/node": "^25.1.0",
|
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,
|
|
@@ -101,21 +102,35 @@ export class HonoHttpServer implements IHttpServer {
|
|
|
101
102
|
}
|
|
102
103
|
}
|
|
103
104
|
|
|
105
|
+
/**
|
|
106
|
+
* Mount a sub-application or router
|
|
107
|
+
*/
|
|
108
|
+
mount(path: string, subApp: Hono) {
|
|
109
|
+
this.app.route(path, subApp);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
|
|
104
113
|
async listen(port: number) {
|
|
105
114
|
return new Promise<void>((resolve) => {
|
|
106
115
|
if (this.staticRoot) {
|
|
107
116
|
this.app.get('/*', serveStatic({ root: this.staticRoot }));
|
|
108
117
|
}
|
|
109
118
|
|
|
119
|
+
const targetPort = port || this.port;
|
|
110
120
|
this.server = serve({
|
|
111
121
|
fetch: this.app.fetch,
|
|
112
|
-
port:
|
|
122
|
+
port: targetPort
|
|
113
123
|
}, (info) => {
|
|
124
|
+
this.listeningPort = info.port;
|
|
114
125
|
resolve();
|
|
115
126
|
});
|
|
116
127
|
});
|
|
117
128
|
}
|
|
118
129
|
|
|
130
|
+
getPort() {
|
|
131
|
+
return this.listeningPort || this.port;
|
|
132
|
+
}
|
|
133
|
+
|
|
119
134
|
// Expose raw app for scenarios where standard interface is not enough
|
|
120
135
|
getRawApp() {
|
|
121
136
|
return this.app;
|
package/src/hono-plugin.test.ts
CHANGED
|
@@ -1,167 +1,86 @@
|
|
|
1
1
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
2
|
import { HonoServerPlugin } from './hono-plugin';
|
|
3
|
-
import { PluginContext
|
|
3
|
+
import { PluginContext } from '@objectstack/core';
|
|
4
|
+
import { createHonoApp } from '@objectstack/hono';
|
|
5
|
+
import { HonoHttpServer } from './adapter';
|
|
6
|
+
|
|
7
|
+
// Mock dependencies
|
|
8
|
+
vi.mock('@objectstack/hono', () => ({
|
|
9
|
+
createHonoApp: vi.fn(),
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
vi.mock('./adapter', () => ({
|
|
13
|
+
HonoHttpServer: vi.fn(function() {
|
|
14
|
+
return {
|
|
15
|
+
mount: vi.fn(),
|
|
16
|
+
start: vi.fn(),
|
|
17
|
+
stop: vi.fn(),
|
|
18
|
+
getApp: vi.fn()
|
|
19
|
+
};
|
|
20
|
+
})
|
|
21
|
+
}));
|
|
4
22
|
|
|
5
23
|
describe('HonoServerPlugin', () => {
|
|
6
24
|
let context: any;
|
|
7
25
|
let logger: any;
|
|
8
|
-
let
|
|
9
|
-
let apiRegistry: any;
|
|
26
|
+
let kernel: any;
|
|
10
27
|
|
|
11
28
|
beforeEach(() => {
|
|
29
|
+
vi.clearAllMocks();
|
|
30
|
+
|
|
12
31
|
logger = {
|
|
13
32
|
info: vi.fn(),
|
|
14
33
|
debug: vi.fn(),
|
|
15
34
|
warn: vi.fn(),
|
|
16
35
|
error: vi.fn()
|
|
17
36
|
};
|
|
18
|
-
|
|
19
|
-
protocol = {
|
|
20
|
-
getDiscovery: vi.fn().mockResolvedValue({ version: 'v1', apiName: 'ObjectStack' }),
|
|
21
|
-
getMetaTypes: vi.fn().mockResolvedValue({ types: ['object', 'plugin'] }),
|
|
22
|
-
getMetaItems: vi.fn().mockResolvedValue({ type: 'object', items: [] }),
|
|
23
|
-
findData: vi.fn().mockResolvedValue({ object: 'test', records: [] }),
|
|
24
|
-
getData: vi.fn().mockResolvedValue({ object: 'test', id: '1', record: {} }),
|
|
25
|
-
createData: vi.fn().mockResolvedValue({ object: 'test', id: '1', record: {} }),
|
|
26
|
-
updateData: vi.fn().mockResolvedValue({ object: 'test', id: '1', record: {} }),
|
|
27
|
-
deleteData: vi.fn().mockResolvedValue({ object: 'test', id: '1', success: true }),
|
|
28
|
-
batchData: vi.fn().mockResolvedValue({ total: 0, succeeded: 0, failed: 0 }),
|
|
29
|
-
createManyData: vi.fn().mockResolvedValue({ object: 'test', records: [], count: 0 }),
|
|
30
|
-
updateManyData: vi.fn().mockResolvedValue({ total: 0, succeeded: 0, failed: 0 }),
|
|
31
|
-
deleteManyData: vi.fn().mockResolvedValue({ total: 0, succeeded: 0, failed: 0 }),
|
|
32
|
-
getMetaItemCached: vi.fn().mockResolvedValue({ data: {}, notModified: false }),
|
|
33
|
-
getUiView: vi.fn().mockResolvedValue({ object: 'test', type: 'list' })
|
|
34
|
-
};
|
|
35
37
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
getRegistry: vi.fn().mockReturnValue({
|
|
39
|
-
version: '1.0.0',
|
|
40
|
-
conflictResolution: 'error',
|
|
41
|
-
apis: [],
|
|
42
|
-
totalApis: 0,
|
|
43
|
-
totalEndpoints: 0
|
|
44
|
-
})
|
|
38
|
+
kernel = {
|
|
39
|
+
getService: vi.fn(),
|
|
45
40
|
};
|
|
46
41
|
|
|
47
42
|
context = {
|
|
48
43
|
logger,
|
|
49
|
-
|
|
50
|
-
if (service === 'protocol') return protocol;
|
|
51
|
-
if (service === 'api-registry') throw new Error('Not found');
|
|
52
|
-
return null;
|
|
53
|
-
}),
|
|
44
|
+
getKernel: vi.fn().mockReturnValue(kernel),
|
|
54
45
|
registerService: vi.fn(),
|
|
55
|
-
hook: vi.fn()
|
|
46
|
+
hook: vi.fn(),
|
|
47
|
+
getService: vi.fn()
|
|
56
48
|
};
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it('should initialize and register server', async () => {
|
|
60
|
-
const plugin = new HonoServerPlugin();
|
|
61
|
-
await plugin.init(context as PluginContext);
|
|
62
|
-
|
|
63
|
-
expect(context.registerService).toHaveBeenCalledWith('http-server', expect.anything());
|
|
64
|
-
});
|
|
65
49
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
await plugin.init(context as PluginContext);
|
|
69
|
-
await plugin.start(context as PluginContext);
|
|
70
|
-
|
|
71
|
-
// Should wait for kernel:ready to start server
|
|
72
|
-
expect(context.hook).toHaveBeenCalledWith('kernel:ready', expect.any(Function));
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it('should register CRUD routes in legacy mode when API Registry not available', async () => {
|
|
76
|
-
const plugin = new HonoServerPlugin();
|
|
77
|
-
await plugin.init(context as PluginContext);
|
|
78
|
-
await plugin.start(context as PluginContext);
|
|
79
|
-
|
|
80
|
-
expect(context.getService).toHaveBeenCalledWith('protocol');
|
|
81
|
-
expect(context.getService).toHaveBeenCalledWith('api-registry');
|
|
82
|
-
expect(logger.debug).toHaveBeenCalledWith('API Registry not found, using legacy route registration');
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it('should use API Registry when available', async () => {
|
|
86
|
-
context.getService = vi.fn((service) => {
|
|
87
|
-
if (service === 'protocol') return protocol;
|
|
88
|
-
if (service === 'api-registry') return apiRegistry;
|
|
89
|
-
return null;
|
|
50
|
+
(createHonoApp as any).mockReturnValue({
|
|
51
|
+
// Mock Hono App structure if needed
|
|
90
52
|
});
|
|
91
|
-
|
|
92
|
-
const plugin = new HonoServerPlugin();
|
|
93
|
-
await plugin.init(context as PluginContext);
|
|
94
|
-
await plugin.start(context as PluginContext);
|
|
95
|
-
|
|
96
|
-
expect(context.getService).toHaveBeenCalledWith('api-registry');
|
|
97
|
-
expect(apiRegistry.registerApi).toHaveBeenCalled();
|
|
98
53
|
});
|
|
99
54
|
|
|
100
|
-
it('should
|
|
101
|
-
context.getService = vi.fn((service) => {
|
|
102
|
-
if (service === 'protocol') return protocol;
|
|
103
|
-
if (service === 'api-registry') return apiRegistry;
|
|
104
|
-
return null;
|
|
105
|
-
});
|
|
106
|
-
|
|
55
|
+
it('should initialize and register server', async () => {
|
|
107
56
|
const plugin = new HonoServerPlugin();
|
|
108
57
|
await plugin.init(context as PluginContext);
|
|
109
|
-
await plugin.start(context as PluginContext);
|
|
110
58
|
|
|
111
|
-
expect(
|
|
112
|
-
|
|
113
|
-
id: 'objectstack_core_api',
|
|
114
|
-
name: 'ObjectStack Core API',
|
|
115
|
-
type: 'rest',
|
|
116
|
-
version: 'v1'
|
|
117
|
-
})
|
|
118
|
-
);
|
|
59
|
+
expect(context.registerService).toHaveBeenCalledWith('http-server', expect.any(Object));
|
|
60
|
+
expect(HonoHttpServer).toHaveBeenCalled();
|
|
119
61
|
});
|
|
120
62
|
|
|
121
|
-
it('should
|
|
122
|
-
|
|
123
|
-
if (service === 'protocol') return protocol;
|
|
124
|
-
if (service === 'api-registry') return apiRegistry;
|
|
125
|
-
return null;
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
const plugin = new HonoServerPlugin({ registerStandardEndpoints: false });
|
|
63
|
+
it('should create and mount Hono app on start', async () => {
|
|
64
|
+
const plugin = new HonoServerPlugin();
|
|
129
65
|
await plugin.init(context as PluginContext);
|
|
130
66
|
await plugin.start(context as PluginContext);
|
|
131
67
|
|
|
132
|
-
expect(
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
context.getService = vi.fn((service) => {
|
|
137
|
-
if (service === 'protocol') return protocol;
|
|
138
|
-
if (service === 'api-registry') return apiRegistry;
|
|
139
|
-
return null;
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
const plugin = new HonoServerPlugin({ useApiRegistry: false });
|
|
143
|
-
await plugin.init(context as PluginContext);
|
|
144
|
-
await plugin.start(context as PluginContext);
|
|
68
|
+
expect(createHonoApp).toHaveBeenCalledWith(expect.objectContaining({
|
|
69
|
+
kernel: kernel,
|
|
70
|
+
prefix: '/api/v1'
|
|
71
|
+
}));
|
|
145
72
|
|
|
146
|
-
|
|
147
|
-
|
|
73
|
+
// Access the mocked server instance
|
|
74
|
+
const serverInstance = (HonoHttpServer as any).mock.instances[0];
|
|
75
|
+
expect(serverInstance.mount).toHaveBeenCalledWith('/', expect.anything());
|
|
148
76
|
});
|
|
149
77
|
|
|
150
|
-
it('should respect REST server configuration', async () => {
|
|
151
|
-
context.getService = vi.fn((service) => {
|
|
152
|
-
if (service === 'protocol') return protocol;
|
|
153
|
-
if (service === 'api-registry') return apiRegistry;
|
|
154
|
-
return null;
|
|
155
|
-
});
|
|
156
|
-
|
|
78
|
+
it('should respect REST server configuration for prefix', async () => {
|
|
157
79
|
const plugin = new HonoServerPlugin({
|
|
158
80
|
restConfig: {
|
|
159
81
|
api: {
|
|
160
82
|
version: 'v2',
|
|
161
|
-
basePath: '/custom'
|
|
162
|
-
enableCrud: true,
|
|
163
|
-
enableMetadata: true,
|
|
164
|
-
enableBatch: true
|
|
83
|
+
basePath: '/custom'
|
|
165
84
|
}
|
|
166
85
|
}
|
|
167
86
|
});
|
|
@@ -169,22 +88,20 @@ describe('HonoServerPlugin', () => {
|
|
|
169
88
|
await plugin.init(context as PluginContext);
|
|
170
89
|
await plugin.start(context as PluginContext);
|
|
171
90
|
|
|
172
|
-
expect(
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
})
|
|
176
|
-
);
|
|
91
|
+
expect(createHonoApp).toHaveBeenCalledWith(expect.objectContaining({
|
|
92
|
+
prefix: '/custom/v2'
|
|
93
|
+
}));
|
|
177
94
|
});
|
|
178
95
|
|
|
179
|
-
it('should handle
|
|
180
|
-
|
|
181
|
-
throw new Error('
|
|
96
|
+
it('should handle errors during app creation', async () => {
|
|
97
|
+
(createHonoApp as any).mockImplementation(() => {
|
|
98
|
+
throw new Error('Creation failed');
|
|
182
99
|
});
|
|
183
100
|
|
|
184
101
|
const plugin = new HonoServerPlugin();
|
|
185
102
|
await plugin.init(context as PluginContext);
|
|
186
103
|
await plugin.start(context as PluginContext);
|
|
187
104
|
|
|
188
|
-
expect(logger.
|
|
105
|
+
expect(logger.error).toHaveBeenCalledWith('Failed to create standard Hono app', expect.any(Error));
|
|
189
106
|
});
|
|
190
107
|
});
|