@objectstack/plugin-hono-server 0.6.1 → 0.7.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.
@@ -24,97 +24,349 @@ class HonoServerPlugin {
24
24
  * Init phase - Setup HTTP server and register as service
25
25
  */
26
26
  async init(ctx) {
27
+ ctx.logger.debug('Initializing Hono server plugin', {
28
+ port: this.options.port,
29
+ staticRoot: this.options.staticRoot
30
+ });
27
31
  // Register HTTP server service as IHttpServer
28
32
  ctx.registerService('http-server', this.server);
29
- ctx.logger.log('[HonoServerPlugin] HTTP server service registered');
33
+ ctx.logger.info('HTTP server service registered', { serviceName: 'http-server' });
30
34
  }
31
35
  /**
32
36
  * Start phase - Bind routes and start listening
33
37
  */
34
38
  async start(ctx) {
39
+ ctx.logger.debug('Starting Hono server plugin');
35
40
  // Get protocol implementation instance
36
41
  let protocol = null;
37
42
  try {
38
43
  protocol = ctx.getService('protocol');
44
+ ctx.logger.debug('Protocol service found, registering protocol routes');
39
45
  }
40
46
  catch (e) {
41
- ctx.logger.log('[HonoServerPlugin] Protocol service not found, skipping protocol routes');
47
+ ctx.logger.warn('Protocol service not found, skipping protocol routes');
42
48
  }
43
49
  // Register protocol routes if available
44
50
  if (protocol) {
45
51
  const p = protocol;
46
- this.server.get('/api/v1', (req, res) => res.json(p.getDiscovery()));
52
+ ctx.logger.debug('Registering API routes');
53
+ this.server.get('/api/v1', async (req, res) => {
54
+ ctx.logger.debug('API discovery request');
55
+ res.json(await p.getDiscovery({}));
56
+ });
47
57
  // Meta Protocol
48
- this.server.get('/api/v1/meta', (req, res) => res.json(p.getMetaTypes()));
49
- this.server.get('/api/v1/meta/:type', (req, res) => res.json(p.getMetaItems(req.params.type)));
50
- this.server.get('/api/v1/meta/:type/:name', (req, res) => {
51
- try {
52
- res.json(p.getMetaItem(req.params.type, req.params.name));
53
- }
54
- catch (e) {
55
- res.status(404).json({ error: e.message });
56
- }
58
+ this.server.get('/api/v1/meta', async (req, res) => {
59
+ ctx.logger.debug('Meta types request');
60
+ res.json(await p.getMetaTypes({}));
61
+ });
62
+ this.server.get('/api/v1/meta/:type', async (req, res) => {
63
+ ctx.logger.debug('Meta items request', { type: req.params.type });
64
+ res.json(await p.getMetaItems({ type: req.params.type }));
57
65
  });
58
66
  // Data Protocol
59
67
  this.server.get('/api/v1/data/:object', async (req, res) => {
68
+ ctx.logger.debug('Data find request', { object: req.params.object, query: req.query });
60
69
  try {
61
- res.json(await p.findData(req.params.object, req.query));
70
+ const result = await p.findData({ object: req.params.object, query: req.query });
71
+ ctx.logger.debug('Data find completed', { object: req.params.object, count: result?.records?.length ?? 0 });
72
+ res.json(result);
62
73
  }
63
74
  catch (e) {
75
+ ctx.logger.error('Data find failed', e, { object: req.params.object });
64
76
  res.status(400).json({ error: e.message });
65
77
  }
66
78
  });
67
79
  this.server.get('/api/v1/data/:object/:id', async (req, res) => {
80
+ ctx.logger.debug('Data get request', { object: req.params.object, id: req.params.id });
68
81
  try {
69
- res.json(await p.getData(req.params.object, req.params.id));
82
+ const result = await p.getData({ object: req.params.object, id: req.params.id });
83
+ ctx.logger.debug('Data get completed', { object: req.params.object, id: req.params.id });
84
+ res.json(result);
70
85
  }
71
86
  catch (e) {
87
+ ctx.logger.warn('Data get failed - not found', { object: req.params.object, id: req.params.id });
72
88
  res.status(404).json({ error: e.message });
73
89
  }
74
90
  });
75
91
  this.server.post('/api/v1/data/:object', async (req, res) => {
92
+ ctx.logger.debug('Data create request', { object: req.params.object });
76
93
  try {
77
- res.status(201).json(await p.createData(req.params.object, req.body));
94
+ const result = await p.createData({ object: req.params.object, data: req.body });
95
+ ctx.logger.info('Data created', { object: req.params.object, id: result?.id });
96
+ res.status(201).json(result);
78
97
  }
79
98
  catch (e) {
99
+ ctx.logger.error('Data create failed', e, { object: req.params.object });
80
100
  res.status(400).json({ error: e.message });
81
101
  }
82
102
  });
83
103
  this.server.patch('/api/v1/data/:object/:id', async (req, res) => {
104
+ ctx.logger.debug('Data update request', { object: req.params.object, id: req.params.id });
84
105
  try {
85
- res.json(await p.updateData(req.params.object, req.params.id, req.body));
106
+ const result = await p.updateData({ object: req.params.object, id: req.params.id, data: req.body });
107
+ ctx.logger.info('Data updated', { object: req.params.object, id: req.params.id });
108
+ res.json(result);
86
109
  }
87
110
  catch (e) {
111
+ ctx.logger.error('Data update failed', e, { object: req.params.object, id: req.params.id });
88
112
  res.status(400).json({ error: e.message });
89
113
  }
90
114
  });
91
115
  this.server.delete('/api/v1/data/:object/:id', async (req, res) => {
116
+ ctx.logger.debug('Data delete request', { object: req.params.object, id: req.params.id });
92
117
  try {
93
- res.json(await p.deleteData(req.params.object, req.params.id));
118
+ const result = await p.deleteData({ object: req.params.object, id: req.params.id });
119
+ ctx.logger.info('Data deleted', { object: req.params.object, id: req.params.id, success: result?.success });
120
+ res.json(result);
94
121
  }
95
122
  catch (e) {
123
+ ctx.logger.error('Data delete failed', e, { object: req.params.object, id: req.params.id });
96
124
  res.status(400).json({ error: e.message });
97
125
  }
98
126
  });
99
127
  // UI Protocol
100
- // @ts-ignore
101
- this.server.get('/api/v1/ui/view/:object', (req, res) => {
128
+ this.server.get('/api/v1/ui/view/:object', async (req, res) => {
129
+ const viewType = (req.query.type) || 'list';
130
+ const qt = Array.isArray(viewType) ? viewType[0] : viewType;
131
+ ctx.logger.debug('UI view request', { object: req.params.object, viewType: qt });
132
+ try {
133
+ res.json(await p.getUiView({ object: req.params.object, type: qt }));
134
+ }
135
+ catch (e) {
136
+ ctx.logger.warn('UI view not found', { object: req.params.object, viewType: qt });
137
+ res.status(404).json({ error: e.message });
138
+ }
139
+ });
140
+ // Batch Operations
141
+ this.server.post('/api/v1/data/:object/batch', async (req, res) => {
142
+ ctx.logger.info('Batch operation request', {
143
+ object: req.params.object,
144
+ operation: req.body?.operation,
145
+ hasBody: !!req.body,
146
+ bodyType: typeof req.body,
147
+ bodyKeys: req.body ? Object.keys(req.body) : []
148
+ });
149
+ try {
150
+ const result = await p.batchData({ object: req.params.object, request: req.body });
151
+ ctx.logger.info('Batch operation completed', {
152
+ object: req.params.object,
153
+ operation: req.body?.operation,
154
+ total: result.total,
155
+ succeeded: result.succeeded,
156
+ failed: result.failed
157
+ });
158
+ res.json(result);
159
+ }
160
+ catch (e) {
161
+ ctx.logger.error('Batch operation failed', e, { object: req.params.object });
162
+ res.status(400).json({ error: e.message });
163
+ }
164
+ });
165
+ this.server.post('/api/v1/data/:object/createMany', async (req, res) => {
166
+ ctx.logger.debug('Create many request', { object: req.params.object, count: req.body?.length });
167
+ try {
168
+ const result = await p.createManyData({ object: req.params.object, records: req.body || [] });
169
+ ctx.logger.info('Create many completed', { object: req.params.object, count: result.records?.length ?? 0 });
170
+ res.status(201).json(result);
171
+ }
172
+ catch (e) {
173
+ ctx.logger.error('Create many failed', e, { object: req.params.object });
174
+ res.status(400).json({ error: e.message });
175
+ }
176
+ });
177
+ this.server.post('/api/v1/data/:object/updateMany', async (req, res) => {
178
+ ctx.logger.debug('Update many request', { object: req.params.object, count: req.body?.records?.length });
179
+ try {
180
+ const result = await p.updateManyData({ object: req.params.object, records: req.body?.records, options: req.body?.options });
181
+ ctx.logger.info('Update many completed', {
182
+ object: req.params.object,
183
+ total: result.total,
184
+ succeeded: result.succeeded,
185
+ failed: result.failed
186
+ });
187
+ res.json(result);
188
+ }
189
+ catch (e) {
190
+ ctx.logger.error('Update many failed', e, { object: req.params.object });
191
+ res.status(400).json({ error: e.message });
192
+ }
193
+ });
194
+ this.server.post('/api/v1/data/:object/deleteMany', async (req, res) => {
195
+ ctx.logger.debug('Delete many request', { object: req.params.object, count: req.body?.ids?.length });
102
196
  try {
103
- const viewType = (req.query.type) || 'list';
104
- const qt = Array.isArray(viewType) ? viewType[0] : viewType;
105
- res.json(p.getUiView(req.params.object, qt));
197
+ const result = await p.deleteManyData({ object: req.params.object, ids: req.body?.ids, options: req.body?.options });
198
+ ctx.logger.info('Delete many completed', {
199
+ object: req.params.object,
200
+ total: result.total,
201
+ succeeded: result.succeeded,
202
+ failed: result.failed
203
+ });
204
+ res.json(result);
106
205
  }
107
206
  catch (e) {
207
+ ctx.logger.error('Delete many failed', e, { object: req.params.object });
208
+ res.status(400).json({ error: e.message });
209
+ }
210
+ });
211
+ // Enhanced Metadata Route with ETag Support
212
+ this.server.get('/api/v1/meta/:type/:name', async (req, res) => {
213
+ ctx.logger.debug('Meta item request with cache support', {
214
+ type: req.params.type,
215
+ name: req.params.name,
216
+ ifNoneMatch: req.headers['if-none-match']
217
+ });
218
+ try {
219
+ const cacheRequest = {
220
+ ifNoneMatch: req.headers['if-none-match'],
221
+ ifModifiedSince: req.headers['if-modified-since'],
222
+ };
223
+ const result = await p.getMetaItemCached({
224
+ type: req.params.type,
225
+ name: req.params.name,
226
+ cacheRequest
227
+ });
228
+ if (result.notModified) {
229
+ ctx.logger.debug('Meta item not modified (304)', { type: req.params.type, name: req.params.name });
230
+ res.status(304).json({});
231
+ }
232
+ else {
233
+ // Set cache headers
234
+ if (result.etag) {
235
+ const etagValue = result.etag.weak ? `W/"${result.etag.value}"` : `"${result.etag.value}"`;
236
+ res.header('ETag', etagValue);
237
+ }
238
+ if (result.lastModified) {
239
+ res.header('Last-Modified', new Date(result.lastModified).toUTCString());
240
+ }
241
+ if (result.cacheControl) {
242
+ const directives = result.cacheControl.directives.join(', ');
243
+ const maxAge = result.cacheControl.maxAge ? `, max-age=${result.cacheControl.maxAge}` : '';
244
+ res.header('Cache-Control', directives + maxAge);
245
+ }
246
+ ctx.logger.debug('Meta item returned with cache headers', {
247
+ type: req.params.type,
248
+ name: req.params.name,
249
+ etag: result.etag?.value
250
+ });
251
+ res.json(result.data);
252
+ }
253
+ }
254
+ catch (e) {
255
+ ctx.logger.warn('Meta item not found', { type: req.params.type, name: req.params.name });
108
256
  res.status(404).json({ error: e.message });
109
257
  }
110
258
  });
259
+ // View Storage Routes
260
+ this.server.post('/api/v1/ui/views', async (req, res) => {
261
+ ctx.logger.debug('Create view request', { name: req.body?.name, object: req.body?.object });
262
+ try {
263
+ const result = await p.createView(req.body);
264
+ if (result.success) {
265
+ ctx.logger.info('View created', { id: result.data?.id, name: result.data?.name });
266
+ res.status(201).json(result);
267
+ }
268
+ else {
269
+ ctx.logger.warn('View creation failed', { error: result.error });
270
+ res.status(400).json(result);
271
+ }
272
+ }
273
+ catch (e) {
274
+ ctx.logger.error('View creation error', e);
275
+ res.status(500).json({ success: false, error: { code: 'internal_error', message: e.message } });
276
+ }
277
+ });
278
+ this.server.get('/api/v1/ui/views/:id', async (req, res) => {
279
+ ctx.logger.debug('Get view request', { id: req.params.id });
280
+ try {
281
+ const result = await p.getView({ id: req.params.id });
282
+ if (result.success) {
283
+ ctx.logger.debug('View retrieved', { id: req.params.id });
284
+ res.json(result);
285
+ }
286
+ else {
287
+ ctx.logger.warn('View not found', { id: req.params.id });
288
+ res.status(404).json(result);
289
+ }
290
+ }
291
+ catch (e) {
292
+ ctx.logger.error('Get view error', e, { id: req.params.id });
293
+ res.status(500).json({ success: false, error: { code: 'internal_error', message: e.message } });
294
+ }
295
+ });
296
+ this.server.get('/api/v1/ui/views', async (req, res) => {
297
+ ctx.logger.debug('List views request', { query: req.query });
298
+ try {
299
+ const request = {};
300
+ if (req.query.object)
301
+ request.object = req.query.object;
302
+ if (req.query.type)
303
+ request.type = req.query.type;
304
+ if (req.query.visibility)
305
+ request.visibility = req.query.visibility;
306
+ if (req.query.createdBy)
307
+ request.createdBy = req.query.createdBy;
308
+ if (req.query.isDefault !== undefined)
309
+ request.isDefault = req.query.isDefault === 'true';
310
+ if (req.query.limit)
311
+ request.limit = parseInt(req.query.limit);
312
+ if (req.query.offset)
313
+ request.offset = parseInt(req.query.offset);
314
+ const result = await p.listViews(request);
315
+ ctx.logger.debug('Views listed', { count: result.data?.length, total: result.pagination?.total });
316
+ res.json(result);
317
+ }
318
+ catch (e) {
319
+ ctx.logger.error('List views error', e);
320
+ res.status(500).json({ success: false, error: { code: 'internal_error', message: e.message } });
321
+ }
322
+ });
323
+ this.server.patch('/api/v1/ui/views/:id', async (req, res) => {
324
+ ctx.logger.debug('Update view request', { id: req.params.id });
325
+ try {
326
+ const result = await p.updateView({ ...req.body, id: req.params.id });
327
+ if (result.success) {
328
+ ctx.logger.info('View updated', { id: req.params.id });
329
+ res.json(result);
330
+ }
331
+ else {
332
+ ctx.logger.warn('View update failed', { id: req.params.id, error: result.error });
333
+ res.status(result.error?.code === 'resource_not_found' ? 404 : 400).json(result);
334
+ }
335
+ }
336
+ catch (e) {
337
+ ctx.logger.error('Update view error', e, { id: req.params.id });
338
+ res.status(500).json({ success: false, error: { code: 'internal_error', message: e.message } });
339
+ }
340
+ });
341
+ this.server.delete('/api/v1/ui/views/:id', async (req, res) => {
342
+ ctx.logger.debug('Delete view request', { id: req.params.id });
343
+ try {
344
+ const result = await p.deleteView({ id: req.params.id });
345
+ if (result.success) {
346
+ ctx.logger.info('View deleted', { id: req.params.id });
347
+ res.json(result);
348
+ }
349
+ else {
350
+ ctx.logger.warn('View deletion failed', { id: req.params.id });
351
+ res.status(404).json(result);
352
+ }
353
+ }
354
+ catch (e) {
355
+ ctx.logger.error('Delete view error', e, { id: req.params.id });
356
+ res.status(500).json({ success: false, error: { code: 'internal_error', message: e.message } });
357
+ }
358
+ });
359
+ ctx.logger.info('All API routes registered');
111
360
  }
112
361
  // Start server on kernel:ready hook
113
362
  ctx.hook('kernel:ready', async () => {
114
363
  const port = this.options.port || 3000;
115
- ctx.logger.log('[HonoServerPlugin] Starting server...');
364
+ ctx.logger.info('Starting HTTP server', { port });
116
365
  await this.server.listen(port);
117
- ctx.logger.log(`✅ Server is running on http://localhost:${port}`);
366
+ ctx.logger.info('HTTP server started successfully', {
367
+ port,
368
+ url: `http://localhost:${port}`
369
+ });
118
370
  });
119
371
  }
120
372
  /**
@@ -122,6 +374,7 @@ class HonoServerPlugin {
122
374
  */
123
375
  async destroy() {
124
376
  this.server.close();
377
+ // Note: Can't use ctx.logger here since we're in destroy
125
378
  console.log('[HonoServerPlugin] Server stopped');
126
379
  }
127
380
  }
@@ -0,0 +1,238 @@
1
+ import { ObjectStackManifest } from '@objectstack/spec/system';
2
+
3
+ /**
4
+ * Hono Server Plugin Manifest
5
+ *
6
+ * HTTP server adapter plugin using the Hono framework.
7
+ * Provides northbound HTTP/REST API gateway capabilities.
8
+ */
9
+ const HonoServerPlugin: ObjectStackManifest = {
10
+ id: 'com.objectstack.server.hono',
11
+ name: 'Hono Server Adapter',
12
+ version: '1.0.0',
13
+ type: 'adapter',
14
+ description: 'HTTP server adapter using Hono framework. Exposes ObjectStack Runtime Protocol via REST API endpoints.',
15
+
16
+ configuration: {
17
+ title: 'Hono Server Configuration',
18
+ properties: {
19
+ port: {
20
+ type: 'number',
21
+ default: 3000,
22
+ description: 'HTTP server port',
23
+ },
24
+ staticRoot: {
25
+ type: 'string',
26
+ description: 'Path to static files directory (optional)',
27
+ },
28
+ },
29
+ },
30
+
31
+ // Plugin Capability Declaration
32
+ capabilities: {
33
+ // Protocols This Plugin Implements
34
+ implements: [
35
+ {
36
+ protocol: {
37
+ id: 'com.objectstack.protocol.http.v1',
38
+ label: 'HTTP Server Protocol v1',
39
+ version: { major: 1, minor: 0, patch: 0 },
40
+ description: 'Standard HTTP server capabilities',
41
+ },
42
+ conformance: 'full',
43
+ certified: false,
44
+ },
45
+ {
46
+ protocol: {
47
+ id: 'com.objectstack.protocol.api.rest.v1',
48
+ label: 'REST API Protocol v1',
49
+ version: { major: 1, minor: 0, patch: 0 },
50
+ description: 'RESTful API endpoint implementation',
51
+ },
52
+ conformance: 'full',
53
+ features: [
54
+ {
55
+ name: 'meta_protocol',
56
+ enabled: true,
57
+ description: 'Metadata discovery endpoints',
58
+ },
59
+ {
60
+ name: 'data_protocol',
61
+ enabled: true,
62
+ description: 'CRUD data operations',
63
+ },
64
+ {
65
+ name: 'ui_protocol',
66
+ enabled: true,
67
+ description: 'UI view metadata endpoints',
68
+ },
69
+ ],
70
+ certified: false,
71
+ },
72
+ ],
73
+
74
+ // Interfaces This Plugin Provides
75
+ provides: [
76
+ {
77
+ id: 'com.objectstack.server.hono.interface.http_server',
78
+ name: 'IHttpServer',
79
+ description: 'HTTP server service interface',
80
+ version: { major: 1, minor: 0, patch: 0 },
81
+ stability: 'stable',
82
+ methods: [
83
+ {
84
+ name: 'get',
85
+ description: 'Register GET route handler',
86
+ parameters: [
87
+ {
88
+ name: 'path',
89
+ type: 'string',
90
+ required: true,
91
+ description: 'Route path pattern',
92
+ },
93
+ {
94
+ name: 'handler',
95
+ type: 'Function',
96
+ required: true,
97
+ description: 'Route handler function',
98
+ },
99
+ ],
100
+ returnType: 'void',
101
+ async: false,
102
+ },
103
+ {
104
+ name: 'post',
105
+ description: 'Register POST route handler',
106
+ parameters: [
107
+ {
108
+ name: 'path',
109
+ type: 'string',
110
+ required: true,
111
+ description: 'Route path pattern',
112
+ },
113
+ {
114
+ name: 'handler',
115
+ type: 'Function',
116
+ required: true,
117
+ description: 'Route handler function',
118
+ },
119
+ ],
120
+ returnType: 'void',
121
+ async: false,
122
+ },
123
+ {
124
+ name: 'patch',
125
+ description: 'Register PATCH route handler',
126
+ parameters: [
127
+ {
128
+ name: 'path',
129
+ type: 'string',
130
+ required: true,
131
+ description: 'Route path pattern',
132
+ },
133
+ {
134
+ name: 'handler',
135
+ type: 'Function',
136
+ required: true,
137
+ description: 'Route handler function',
138
+ },
139
+ ],
140
+ returnType: 'void',
141
+ async: false,
142
+ },
143
+ {
144
+ name: 'delete',
145
+ description: 'Register DELETE route handler',
146
+ parameters: [
147
+ {
148
+ name: 'path',
149
+ type: 'string',
150
+ required: true,
151
+ description: 'Route path pattern',
152
+ },
153
+ {
154
+ name: 'handler',
155
+ type: 'Function',
156
+ required: true,
157
+ description: 'Route handler function',
158
+ },
159
+ ],
160
+ returnType: 'void',
161
+ async: false,
162
+ },
163
+ {
164
+ name: 'listen',
165
+ description: 'Start the HTTP server',
166
+ parameters: [
167
+ {
168
+ name: 'port',
169
+ type: 'number',
170
+ required: true,
171
+ description: 'Port number',
172
+ },
173
+ ],
174
+ returnType: 'Promise<void>',
175
+ async: true,
176
+ },
177
+ {
178
+ name: 'close',
179
+ description: 'Stop the HTTP server',
180
+ parameters: [],
181
+ returnType: 'void',
182
+ async: false,
183
+ },
184
+ ],
185
+ },
186
+ ],
187
+
188
+ // Dependencies on Other Plugins/Services
189
+ requires: [
190
+ {
191
+ pluginId: 'com.objectstack.engine.objectql',
192
+ version: '^0.6.0',
193
+ optional: true,
194
+ reason: 'ObjectStack Runtime Protocol implementation service',
195
+ requiredCapabilities: [
196
+ 'com.objectstack.protocol.runtime.v1',
197
+ ],
198
+ },
199
+ ],
200
+
201
+ // Extension Points This Plugin Defines
202
+ extensionPoints: [
203
+ {
204
+ id: 'com.objectstack.server.hono.extension.middleware',
205
+ name: 'HTTP Middleware',
206
+ description: 'Register custom HTTP middleware',
207
+ type: 'hook',
208
+ cardinality: 'multiple',
209
+ contract: {
210
+ signature: '(req: Request, res: Response, next: Function) => void | Promise<void>',
211
+ },
212
+ },
213
+ {
214
+ id: 'com.objectstack.server.hono.extension.route',
215
+ name: 'Custom Routes',
216
+ description: 'Register custom API routes',
217
+ type: 'action',
218
+ cardinality: 'multiple',
219
+ contract: {
220
+ input: 'RouteDefinition',
221
+ signature: '(app: HonoApp) => void',
222
+ },
223
+ },
224
+ ],
225
+
226
+ // No extensions contributed to other plugins
227
+ extensions: [],
228
+ },
229
+
230
+ contributes: {
231
+ // System Events
232
+ events: [
233
+ 'kernel:ready',
234
+ ],
235
+ },
236
+ };
237
+
238
+ export default HonoServerPlugin;
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@objectstack/plugin-hono-server",
3
- "version": "0.6.1",
3
+ "version": "0.7.2",
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/core": "0.6.1",
11
- "@objectstack/spec": "0.6.1",
12
- "@objectstack/types": "0.6.1"
10
+ "@objectstack/core": "0.7.2",
11
+ "@objectstack/spec": "0.7.2",
12
+ "@objectstack/types": "0.7.2"
13
13
  },
14
14
  "devDependencies": {
15
15
  "@types/node": "^20.0.0",
package/src/adapter.ts CHANGED
@@ -27,19 +27,33 @@ export class HonoHttpServer implements IHttpServer {
27
27
  // internal helper to convert standard handler to Hono handler
28
28
  private wrap(handler: RouteHandler) {
29
29
  return async (c: any) => {
30
+ let body: any = {};
31
+
32
+ // Try to parse JSON body first if content-type is JSON
33
+ if (c.req.header('content-type')?.includes('application/json')) {
34
+ try {
35
+ body = await c.req.json();
36
+ } catch(e) {
37
+ // If JSON parsing fails, try parseBody
38
+ try {
39
+ body = await c.req.parseBody();
40
+ } catch(e2) {}
41
+ }
42
+ } else {
43
+ // For non-JSON content types, use parseBody
44
+ try {
45
+ body = await c.req.parseBody();
46
+ } catch(e) {}
47
+ }
48
+
30
49
  const req = {
31
50
  params: c.req.param(),
32
51
  query: c.req.query(),
33
- body: await c.req.parseBody().catch(() => {}), // fallback
52
+ body,
34
53
  headers: c.req.header(),
35
54
  method: c.req.method,
36
55
  path: c.req.path
37
56
  };
38
-
39
- // Try to parse JSON body if possible
40
- if (c.req.header('content-type')?.includes('application/json')) {
41
- try { req.body = await c.req.json(); } catch(e) {}
42
- }
43
57
 
44
58
  let capturedResponse: any;
45
59