Skip to content

Commit ce72c60

Browse files
committed
Refactor AppManifestPlugin to support both direct and nested manifest structures
1 parent 6d48ab7 commit ce72c60

File tree

2 files changed

+11
-109
lines changed

2 files changed

+11
-109
lines changed
Lines changed: 1 addition & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -1,104 +1 @@
1-
import { serve } from '@hono/node-server';
2-
import { serveStatic } from '@hono/node-server/serve-static';
3-
import { Hono } from 'hono';
4-
import { cors } from 'hono/cors';
5-
import { logger } from 'hono/logger';
6-
import { ObjectStackRuntimeProtocol } from '@objectstack/runtime';
7-
import { RuntimePlugin, IKernel } from '@objectstack/types';
8-
9-
export interface HonoServerOptions {
10-
port?: number;
11-
staticRoot?: string;
12-
cors?: boolean;
13-
logger?: boolean;
14-
}
15-
16-
/**
17-
* Hono Server Runtime Plugin
18-
*
19-
* Exposes the ObjectStack Kernel via standard HTTP Protocol using Hono.
20-
* Can be used for Production (Standalone) or Development.
21-
*/
22-
export class HonoServerPlugin implements RuntimePlugin {
23-
name = 'com.objectstack.server.hono';
24-
25-
private options: HonoServerOptions;
26-
27-
constructor(options: HonoServerOptions = {}) {
28-
this.options = {
29-
port: 3000,
30-
cors: true,
31-
logger: true,
32-
...options
33-
};
34-
}
35-
36-
async onStart(ctx: { engine: IKernel }) {
37-
const app = new Hono();
38-
// TODO: Protocol needs access to actual Kernel instance or we make Protocol an interface too
39-
const protocol = new ObjectStackRuntimeProtocol(ctx.engine as any);
40-
41-
// 1. Middlewares
42-
if (this.options.logger) app.use('*', logger());
43-
if (this.options.cors) app.use('*', cors());
44-
45-
// 2. Wiring Protocol (Automatic)
46-
// Discovery
47-
app.get('/api/v1', (c) => c.json(protocol.getDiscovery()));
48-
49-
// Meta Protocol
50-
app.get('/api/v1/meta', (c) => c.json(protocol.getMetaTypes()));
51-
app.get('/api/v1/meta/:type', (c) => c.json(protocol.getMetaItems(c.req.param('type'))));
52-
app.get('/api/v1/meta/:type/:name', (c) => {
53-
try {
54-
return c.json(protocol.getMetaItem(c.req.param('type'), c.req.param('name')));
55-
} catch(e:any) {
56-
return c.json({error: e.message}, 404);
57-
}
58-
});
59-
60-
// Data Protocol
61-
app.get('/api/v1/data/:object', async (c) => {
62-
try { return c.json(await protocol.findData(c.req.param('object'), c.req.query())); }
63-
catch(e:any) { return c.json({error:e.message}, 400); }
64-
});
65-
app.get('/api/v1/data/:object/:id', async (c) => {
66-
try { return c.json(await protocol.getData(c.req.param('object'), c.req.param('id'))); }
67-
catch(e:any) { return c.json({error:e.message}, 404); }
68-
});
69-
app.post('/api/v1/data/:object', async (c) => {
70-
try { return c.json(await protocol.createData(c.req.param('object'), await c.req.json()), 201); }
71-
catch(e:any) { return c.json({error:e.message}, 400); }
72-
});
73-
app.patch('/api/v1/data/:object/:id', async (c) => {
74-
try { return c.json(await protocol.updateData(c.req.param('object'), c.req.param('id'), await c.req.json())); }
75-
catch(e:any) { return c.json({error:e.message}, 400); }
76-
});
77-
app.delete('/api/v1/data/:object/:id', async (c) => {
78-
try { return c.json(await protocol.deleteData(c.req.param('object'), c.req.param('id'))); }
79-
catch(e:any) { return c.json({error:e.message}, 400); }
80-
});
81-
82-
// UI Protocol
83-
// @ts-ignore
84-
app.get('/api/v1/ui/view/:object', (c) => {
85-
try {
86-
const viewType = (c.req.query('type') as 'list' | 'form') || 'list';
87-
return c.json(protocol.getUiView(c.req.param('object'), viewType));
88-
}
89-
catch(e:any) { return c.json({error:e.message}, 404); }
90-
});
91-
92-
// 3. Static Files (Optional)
93-
if (this.options.staticRoot) {
94-
app.get('/', serveStatic({ root: this.options.staticRoot, path: 'index.html' }));
95-
app.get('/*', serveStatic({ root: this.options.staticRoot }));
96-
}
97-
98-
console.log('');
99-
console.log(`🌍 ObjectStack Server (Hono) running at: http://localhost:${this.options.port}`);
100-
console.log('');
101-
102-
serve({ fetch: app.fetch, port: this.options.port });
103-
}
104-
}
1+
export * from './hono-plugin';

packages/runtime/src/app-manifest-plugin.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,19 @@ export class AppManifestPlugin implements Plugin {
2323

2424
constructor(manifest: any) {
2525
this.manifest = manifest;
26-
this.name = manifest.id || manifest.name || 'unnamed-app';
27-
this.version = manifest.version;
26+
// Support both direct manifest (legacy) and Stack Definition (nested manifest)
27+
const sys = manifest.manifest || manifest;
28+
this.name = sys.id || sys.name || 'unnamed-app';
29+
this.version = sys.version;
2830
}
2931

3032
async init(ctx: PluginContext) {
31-
ctx.logger.log(`[AppManifestPlugin] Loading App Manifest: ${this.manifest.id || this.manifest.name}`);
33+
// Support both direct manifest (legacy) and Stack Definition (nested manifest)
34+
const sys = this.manifest.manifest || this.manifest;
35+
ctx.logger.log(`[AppManifestPlugin] Loading App Manifest: ${sys.id || sys.name}`);
3236

3337
// Register the app/plugin in the schema registry
34-
SchemaRegistry.registerPlugin(this.manifest);
38+
SchemaRegistry.registerPlugin(sys);
3539

3640
// Register all objects defined in the manifest
3741
if (this.manifest.objects) {
@@ -45,7 +49,8 @@ export class AppManifestPlugin implements Plugin {
4549
async start(ctx: PluginContext) {
4650
// Seed data if provided
4751
if (this.manifest.data && Array.isArray(this.manifest.data)) {
48-
ctx.logger.log(`[AppManifestPlugin] Seeding data for ${this.manifest.name || this.manifest.id}...`);
52+
const sys = this.manifest.manifest || this.manifest;
53+
ctx.logger.log(`[AppManifestPlugin] Seeding data for ${sys.name || sys.id}...`);
4954

5055
const objectql = ctx.getService<ObjectQL>('objectql');
5156

0 commit comments

Comments
 (0)