@objectstack/plugin-hono-server 0.8.2 → 0.9.0
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 +175 -0
- package/dist/hono-plugin.d.ts +43 -0
- package/dist/hono-plugin.js +792 -125
- package/package.json +8 -6
- package/src/hono-plugin.test.ts +190 -0
- package/src/hono-plugin.ts +878 -159
- package/tsconfig.json +2 -1
package/package.json
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@objectstack/plugin-hono-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Standard Hono Server Adapter for ObjectStack Runtime",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"dependencies": {
|
|
8
8
|
"@hono/node-server": "^1.2.0",
|
|
9
9
|
"hono": "^4.0.0",
|
|
10
|
-
"@objectstack/
|
|
11
|
-
"@objectstack/
|
|
12
|
-
"@objectstack/types": "0.
|
|
10
|
+
"@objectstack/spec": "0.9.0",
|
|
11
|
+
"@objectstack/core": "0.9.0",
|
|
12
|
+
"@objectstack/types": "0.9.0"
|
|
13
13
|
},
|
|
14
14
|
"devDependencies": {
|
|
15
15
|
"@types/node": "^25.1.0",
|
|
16
|
-
"typescript": "^5.0.0"
|
|
16
|
+
"typescript": "^5.0.0",
|
|
17
|
+
"vitest": "^4.0.18"
|
|
17
18
|
},
|
|
18
19
|
"scripts": {
|
|
19
|
-
"build": "tsc"
|
|
20
|
+
"build": "tsc",
|
|
21
|
+
"test": "vitest run"
|
|
20
22
|
}
|
|
21
23
|
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { HonoServerPlugin } from './hono-plugin';
|
|
3
|
+
import { PluginContext, ApiRegistry } from '@objectstack/core';
|
|
4
|
+
|
|
5
|
+
describe('HonoServerPlugin', () => {
|
|
6
|
+
let context: any;
|
|
7
|
+
let logger: any;
|
|
8
|
+
let protocol: any;
|
|
9
|
+
let apiRegistry: any;
|
|
10
|
+
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
logger = {
|
|
13
|
+
info: vi.fn(),
|
|
14
|
+
debug: vi.fn(),
|
|
15
|
+
warn: vi.fn(),
|
|
16
|
+
error: vi.fn()
|
|
17
|
+
};
|
|
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
|
+
|
|
36
|
+
apiRegistry = {
|
|
37
|
+
registerApi: vi.fn(),
|
|
38
|
+
getRegistry: vi.fn().mockReturnValue({
|
|
39
|
+
version: '1.0.0',
|
|
40
|
+
conflictResolution: 'error',
|
|
41
|
+
apis: [],
|
|
42
|
+
totalApis: 0,
|
|
43
|
+
totalEndpoints: 0
|
|
44
|
+
})
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
context = {
|
|
48
|
+
logger,
|
|
49
|
+
getService: vi.fn((service) => {
|
|
50
|
+
if (service === 'protocol') return protocol;
|
|
51
|
+
if (service === 'api-registry') throw new Error('Not found');
|
|
52
|
+
return null;
|
|
53
|
+
}),
|
|
54
|
+
registerService: vi.fn(),
|
|
55
|
+
hook: vi.fn()
|
|
56
|
+
};
|
|
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
|
+
|
|
66
|
+
it('should register hook on start', async () => {
|
|
67
|
+
const plugin = new HonoServerPlugin();
|
|
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;
|
|
90
|
+
});
|
|
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
|
+
});
|
|
99
|
+
|
|
100
|
+
it('should register standard endpoints to API Registry', async () => {
|
|
101
|
+
context.getService = vi.fn((service) => {
|
|
102
|
+
if (service === 'protocol') return protocol;
|
|
103
|
+
if (service === 'api-registry') return apiRegistry;
|
|
104
|
+
return null;
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const plugin = new HonoServerPlugin();
|
|
108
|
+
await plugin.init(context as PluginContext);
|
|
109
|
+
await plugin.start(context as PluginContext);
|
|
110
|
+
|
|
111
|
+
expect(apiRegistry.registerApi).toHaveBeenCalledWith(
|
|
112
|
+
expect.objectContaining({
|
|
113
|
+
id: 'objectstack_core_api',
|
|
114
|
+
name: 'ObjectStack Core API',
|
|
115
|
+
type: 'rest',
|
|
116
|
+
version: 'v1'
|
|
117
|
+
})
|
|
118
|
+
);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('should skip standard endpoint registration when disabled', async () => {
|
|
122
|
+
context.getService = vi.fn((service) => {
|
|
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 });
|
|
129
|
+
await plugin.init(context as PluginContext);
|
|
130
|
+
await plugin.start(context as PluginContext);
|
|
131
|
+
|
|
132
|
+
expect(apiRegistry.registerApi).not.toHaveBeenCalled();
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('should use legacy routes when useApiRegistry is disabled', async () => {
|
|
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);
|
|
145
|
+
|
|
146
|
+
expect(apiRegistry.getRegistry).not.toHaveBeenCalled();
|
|
147
|
+
expect(logger.debug).toHaveBeenCalledWith('Using legacy route registration');
|
|
148
|
+
});
|
|
149
|
+
|
|
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
|
+
|
|
157
|
+
const plugin = new HonoServerPlugin({
|
|
158
|
+
restConfig: {
|
|
159
|
+
api: {
|
|
160
|
+
version: 'v2',
|
|
161
|
+
basePath: '/custom',
|
|
162
|
+
enableCrud: true,
|
|
163
|
+
enableMetadata: true,
|
|
164
|
+
enableBatch: true
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
await plugin.init(context as PluginContext);
|
|
170
|
+
await plugin.start(context as PluginContext);
|
|
171
|
+
|
|
172
|
+
expect(apiRegistry.registerApi).toHaveBeenCalledWith(
|
|
173
|
+
expect.objectContaining({
|
|
174
|
+
version: 'v2'
|
|
175
|
+
})
|
|
176
|
+
);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('should handle protocol service not found gracefully', async () => {
|
|
180
|
+
context.getService = vi.fn(() => {
|
|
181
|
+
throw new Error('Service not found');
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
const plugin = new HonoServerPlugin();
|
|
185
|
+
await plugin.init(context as PluginContext);
|
|
186
|
+
await plugin.start(context as PluginContext);
|
|
187
|
+
|
|
188
|
+
expect(logger.warn).toHaveBeenCalledWith('Protocol service not found, skipping protocol routes');
|
|
189
|
+
});
|
|
190
|
+
});
|