@objectstack/plugin-hono-server 1.0.1 → 1.0.2
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 +22 -0
- package/README.md +21 -4
- package/dist/adapter.d.ts +4 -0
- package/dist/adapter.js +6 -0
- package/dist/hono-plugin.d.ts +0 -24
- package/dist/hono-plugin.js +16 -1010
- package/package.json +6 -4
- package/src/adapter.ts +8 -0
- package/src/hono-plugin.test.ts +50 -133
- package/src/hono-plugin.ts +19 -1059
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.2",
|
|
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.2",
|
|
12
|
+
"@objectstack/hono": "1.0.2",
|
|
13
|
+
"@objectstack/runtime": "1.0.2",
|
|
14
|
+
"@objectstack/spec": "1.0.2",
|
|
15
|
+
"@objectstack/types": "1.0.2"
|
|
14
16
|
},
|
|
15
17
|
"devDependencies": {
|
|
16
18
|
"@types/node": "^25.1.0",
|
package/src/adapter.ts
CHANGED
|
@@ -101,6 +101,14 @@ export class HonoHttpServer implements IHttpServer {
|
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
+
/**
|
|
105
|
+
* Mount a sub-application or router
|
|
106
|
+
*/
|
|
107
|
+
mount(path: string, subApp: Hono) {
|
|
108
|
+
this.app.route(path, subApp);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
|
|
104
112
|
async listen(port: number) {
|
|
105
113
|
return new Promise<void>((resolve) => {
|
|
106
114
|
if (this.staticRoot) {
|
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
|
});
|