@objectstack/hono 2.0.7 → 3.0.1
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/.turbo/turbo-build.log +10 -10
- package/CHANGELOG.md +17 -0
- package/dist/index.d.mts +1 -22
- package/dist/index.d.ts +1 -22
- package/dist/index.js +0 -150
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +0 -149
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/hono.test.ts +18 -387
- package/src/index.ts +1 -221
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @objectstack/hono@
|
|
2
|
+
> @objectstack/hono@3.0.1 build /home/runner/work/spec/spec/packages/adapters/hono
|
|
3
3
|
> tsup --config ../../../tsup.config.ts
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/index.ts
|
|
@@ -10,13 +10,13 @@
|
|
|
10
10
|
[34mCLI[39m Cleaning output folder
|
|
11
11
|
[34mESM[39m Build start
|
|
12
12
|
[34mCJS[39m Build start
|
|
13
|
-
[32mESM[39m [1mdist/index.mjs [22m[
|
|
14
|
-
[32mESM[39m [1mdist/index.mjs.map [22m[
|
|
15
|
-
[32mESM[39m ⚡️ Build success in
|
|
16
|
-
[32mCJS[39m [1mdist/index.js [22m[
|
|
17
|
-
[32mCJS[39m [1mdist/index.js.map [22m[
|
|
18
|
-
[32mCJS[39m ⚡️ Build success in
|
|
13
|
+
[32mESM[39m [1mdist/index.mjs [22m[32m216.00 B[39m
|
|
14
|
+
[32mESM[39m [1mdist/index.mjs.map [22m[32m648.00 B[39m
|
|
15
|
+
[32mESM[39m ⚡️ Build success in 25ms
|
|
16
|
+
[32mCJS[39m [1mdist/index.js [22m[32m1.22 KB[39m
|
|
17
|
+
[32mCJS[39m [1mdist/index.js.map [22m[32m692.00 B[39m
|
|
18
|
+
[32mCJS[39m ⚡️ Build success in 24ms
|
|
19
19
|
[34mDTS[39m Build start
|
|
20
|
-
[32mDTS[39m ⚡️ Build success in
|
|
21
|
-
[32mDTS[39m [1mdist/index.d.mts [22m[
|
|
22
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[
|
|
20
|
+
[32mDTS[39m ⚡️ Build success in 7379ms
|
|
21
|
+
[32mDTS[39m [1mdist/index.d.mts [22m[32m352.00 B[39m
|
|
22
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m352.00 B[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# @objectstack/hono
|
|
2
2
|
|
|
3
|
+
## 3.0.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- @objectstack/runtime@3.0.1
|
|
8
|
+
|
|
9
|
+
## 3.0.0
|
|
10
|
+
|
|
11
|
+
### Major Changes
|
|
12
|
+
|
|
13
|
+
- Release v3.0.0 — unified version bump for all ObjectStack packages.
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- Updated dependencies
|
|
18
|
+
- @objectstack/runtime@3.0.0
|
|
19
|
+
|
|
3
20
|
## 2.0.7
|
|
4
21
|
|
|
5
22
|
### Patch Changes
|
package/dist/index.d.mts
CHANGED
|
@@ -1,33 +1,12 @@
|
|
|
1
|
-
import * as hono_types from 'hono/types';
|
|
2
|
-
import { Hono } from 'hono';
|
|
3
1
|
import { ObjectKernel } from '@objectstack/runtime';
|
|
4
2
|
|
|
5
3
|
interface ObjectStackHonoOptions {
|
|
6
4
|
kernel: ObjectKernel;
|
|
7
5
|
prefix?: string;
|
|
8
6
|
}
|
|
9
|
-
/**
|
|
10
|
-
* @deprecated Use `HonoServerPlugin` + `createRestApiPlugin()` + `createDispatcherPlugin()` instead.
|
|
11
|
-
* This function bundles all routes into a single Hono app using the legacy HttpDispatcher.
|
|
12
|
-
* The plugin-based approach provides better modularity and separation of concerns.
|
|
13
|
-
*
|
|
14
|
-
* Migration:
|
|
15
|
-
* ```ts
|
|
16
|
-
* // Before:
|
|
17
|
-
* const app = createHonoApp({ kernel, prefix: '/api/v1' });
|
|
18
|
-
*
|
|
19
|
-
* // After:
|
|
20
|
-
* import { createRestApiPlugin } from '@objectstack/rest';
|
|
21
|
-
* import { createDispatcherPlugin } from '@objectstack/runtime';
|
|
22
|
-
* kernel.use(new HonoServerPlugin({ port: 3000 }));
|
|
23
|
-
* kernel.use(createRestApiPlugin());
|
|
24
|
-
* kernel.use(createDispatcherPlugin({ prefix: '/api/v1' }));
|
|
25
|
-
* ```
|
|
26
|
-
*/
|
|
27
|
-
declare function createHonoApp(options: ObjectStackHonoOptions): Hono<hono_types.BlankEnv, hono_types.BlankSchema, "/">;
|
|
28
7
|
/**
|
|
29
8
|
* Middleware mode for existing Hono apps
|
|
30
9
|
*/
|
|
31
10
|
declare function objectStackMiddleware(kernel: ObjectKernel): (c: any, next: any) => Promise<void>;
|
|
32
11
|
|
|
33
|
-
export { type ObjectStackHonoOptions,
|
|
12
|
+
export { type ObjectStackHonoOptions, objectStackMiddleware };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,33 +1,12 @@
|
|
|
1
|
-
import * as hono_types from 'hono/types';
|
|
2
|
-
import { Hono } from 'hono';
|
|
3
1
|
import { ObjectKernel } from '@objectstack/runtime';
|
|
4
2
|
|
|
5
3
|
interface ObjectStackHonoOptions {
|
|
6
4
|
kernel: ObjectKernel;
|
|
7
5
|
prefix?: string;
|
|
8
6
|
}
|
|
9
|
-
/**
|
|
10
|
-
* @deprecated Use `HonoServerPlugin` + `createRestApiPlugin()` + `createDispatcherPlugin()` instead.
|
|
11
|
-
* This function bundles all routes into a single Hono app using the legacy HttpDispatcher.
|
|
12
|
-
* The plugin-based approach provides better modularity and separation of concerns.
|
|
13
|
-
*
|
|
14
|
-
* Migration:
|
|
15
|
-
* ```ts
|
|
16
|
-
* // Before:
|
|
17
|
-
* const app = createHonoApp({ kernel, prefix: '/api/v1' });
|
|
18
|
-
*
|
|
19
|
-
* // After:
|
|
20
|
-
* import { createRestApiPlugin } from '@objectstack/rest';
|
|
21
|
-
* import { createDispatcherPlugin } from '@objectstack/runtime';
|
|
22
|
-
* kernel.use(new HonoServerPlugin({ port: 3000 }));
|
|
23
|
-
* kernel.use(createRestApiPlugin());
|
|
24
|
-
* kernel.use(createDispatcherPlugin({ prefix: '/api/v1' }));
|
|
25
|
-
* ```
|
|
26
|
-
*/
|
|
27
|
-
declare function createHonoApp(options: ObjectStackHonoOptions): Hono<hono_types.BlankEnv, hono_types.BlankSchema, "/">;
|
|
28
7
|
/**
|
|
29
8
|
* Middleware mode for existing Hono apps
|
|
30
9
|
*/
|
|
31
10
|
declare function objectStackMiddleware(kernel: ObjectKernel): (c: any, next: any) => Promise<void>;
|
|
32
11
|
|
|
33
|
-
export { type ObjectStackHonoOptions,
|
|
12
|
+
export { type ObjectStackHonoOptions, objectStackMiddleware };
|
package/dist/index.js
CHANGED
|
@@ -20,158 +20,9 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
-
createHonoApp: () => createHonoApp,
|
|
24
23
|
objectStackMiddleware: () => objectStackMiddleware
|
|
25
24
|
});
|
|
26
25
|
module.exports = __toCommonJS(index_exports);
|
|
27
|
-
var import_hono = require("hono");
|
|
28
|
-
var import_cors = require("hono/cors");
|
|
29
|
-
var import_runtime = require("@objectstack/runtime");
|
|
30
|
-
function createHonoApp(options) {
|
|
31
|
-
const app = new import_hono.Hono();
|
|
32
|
-
const { prefix = "/api" } = options;
|
|
33
|
-
const dispatcher = new import_runtime.HttpDispatcher(options.kernel);
|
|
34
|
-
app.use("*", (0, import_cors.cors)());
|
|
35
|
-
const normalizeResponse = (c, result) => {
|
|
36
|
-
if (result.handled) {
|
|
37
|
-
if (result.response) {
|
|
38
|
-
return c.json(result.response.body, result.response.status, result.response.headers);
|
|
39
|
-
}
|
|
40
|
-
if (result.result) {
|
|
41
|
-
const res = result.result;
|
|
42
|
-
if (res.type === "redirect" && res.url) {
|
|
43
|
-
return c.redirect(res.url);
|
|
44
|
-
}
|
|
45
|
-
if (res.type === "stream" && res.stream) {
|
|
46
|
-
return c.body(res.stream, 200, res.headers);
|
|
47
|
-
}
|
|
48
|
-
return res;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
return c.json({ success: false, error: { message: "Not Found", code: 404 } }, 404);
|
|
52
|
-
};
|
|
53
|
-
app.get(prefix, (c) => {
|
|
54
|
-
return c.json({ data: dispatcher.getDiscoveryInfo(prefix) });
|
|
55
|
-
});
|
|
56
|
-
app.all(`${prefix}/auth/*`, async (c) => {
|
|
57
|
-
try {
|
|
58
|
-
const authService = typeof options.kernel.getService === "function" ? options.kernel.getService("auth") : null;
|
|
59
|
-
if (authService && typeof authService.handleRequest === "function") {
|
|
60
|
-
const response = await authService.handleRequest(c.req.raw);
|
|
61
|
-
return response;
|
|
62
|
-
}
|
|
63
|
-
const path = c.req.path.substring(c.req.path.indexOf("/auth/") + 6);
|
|
64
|
-
const body = await c.req.parseBody().catch(() => ({}));
|
|
65
|
-
const result = await dispatcher.handleAuth(path, c.req.method, body, { request: c.req.raw });
|
|
66
|
-
return normalizeResponse(c, result);
|
|
67
|
-
} catch (err) {
|
|
68
|
-
return c.json({ success: false, error: { message: err.message, code: err.statusCode || 500 } }, err.statusCode || 500);
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
app.post(`${prefix}/graphql`, async (c) => {
|
|
72
|
-
try {
|
|
73
|
-
const body = await c.req.json();
|
|
74
|
-
const result = await dispatcher.handleGraphQL(body, { request: c.req.raw });
|
|
75
|
-
return c.json(result);
|
|
76
|
-
} catch (err) {
|
|
77
|
-
return c.json({ success: false, error: { message: err.message, code: err.statusCode || 500 } }, err.statusCode || 500);
|
|
78
|
-
}
|
|
79
|
-
});
|
|
80
|
-
app.all(`${prefix}/meta*`, async (c) => {
|
|
81
|
-
try {
|
|
82
|
-
const path = c.req.path.substring(c.req.path.indexOf("/meta") + 5);
|
|
83
|
-
const method = c.req.method;
|
|
84
|
-
let body = void 0;
|
|
85
|
-
if (method === "PUT" || method === "POST") {
|
|
86
|
-
try {
|
|
87
|
-
body = await c.req.json();
|
|
88
|
-
} catch (e) {
|
|
89
|
-
body = {};
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
const query = c.req.query();
|
|
93
|
-
const result = await dispatcher.handleMetadata(path, { request: c.req.raw }, method, body, query);
|
|
94
|
-
return normalizeResponse(c, result);
|
|
95
|
-
} catch (err) {
|
|
96
|
-
return c.json({ success: false, error: { message: err.message, code: err.statusCode || 500 } }, err.statusCode || 500);
|
|
97
|
-
}
|
|
98
|
-
});
|
|
99
|
-
app.all(`${prefix}/data*`, async (c) => {
|
|
100
|
-
try {
|
|
101
|
-
const path = c.req.path.substring(c.req.path.indexOf("/data") + 5);
|
|
102
|
-
const method = c.req.method;
|
|
103
|
-
let body = {};
|
|
104
|
-
if (method === "POST" || method === "PATCH") {
|
|
105
|
-
body = await c.req.json().catch(() => ({}));
|
|
106
|
-
}
|
|
107
|
-
const query = c.req.query();
|
|
108
|
-
const result = await dispatcher.handleData(path, method, body, query, { request: c.req.raw });
|
|
109
|
-
return normalizeResponse(c, result);
|
|
110
|
-
} catch (err) {
|
|
111
|
-
return c.json({ success: false, error: { message: err.message, code: err.statusCode || 500 } }, err.statusCode || 500);
|
|
112
|
-
}
|
|
113
|
-
});
|
|
114
|
-
app.all(`${prefix}/analytics*`, async (c) => {
|
|
115
|
-
try {
|
|
116
|
-
const path = c.req.path.substring(c.req.path.indexOf("/analytics") + 10);
|
|
117
|
-
const method = c.req.method;
|
|
118
|
-
let body = {};
|
|
119
|
-
if (method === "POST") {
|
|
120
|
-
body = await c.req.json().catch(() => ({}));
|
|
121
|
-
}
|
|
122
|
-
const result = await dispatcher.handleAnalytics(path, method, body, { request: c.req.raw });
|
|
123
|
-
return normalizeResponse(c, result);
|
|
124
|
-
} catch (err) {
|
|
125
|
-
return c.json({ success: false, error: { message: err.message, code: err.statusCode || 500 } }, err.statusCode || 500);
|
|
126
|
-
}
|
|
127
|
-
});
|
|
128
|
-
app.all(`${prefix}/automation*`, async (c) => {
|
|
129
|
-
try {
|
|
130
|
-
const path = c.req.path.substring(c.req.path.indexOf("/automation") + 11);
|
|
131
|
-
const method = c.req.method;
|
|
132
|
-
let body = {};
|
|
133
|
-
if (method === "POST") {
|
|
134
|
-
body = await c.req.json().catch(() => ({}));
|
|
135
|
-
}
|
|
136
|
-
const result = await dispatcher.handleAutomation(path, method, body, { request: c.req.raw });
|
|
137
|
-
return normalizeResponse(c, result);
|
|
138
|
-
} catch (err) {
|
|
139
|
-
return c.json({ success: false, error: { message: err.message, code: err.statusCode || 500 } }, err.statusCode || 500);
|
|
140
|
-
}
|
|
141
|
-
});
|
|
142
|
-
app.all(`${prefix}/storage*`, async (c) => {
|
|
143
|
-
try {
|
|
144
|
-
const path = c.req.path.substring(c.req.path.indexOf("/storage") + 8);
|
|
145
|
-
const method = c.req.method;
|
|
146
|
-
let file = void 0;
|
|
147
|
-
if (method === "POST" && path.includes("upload")) {
|
|
148
|
-
const body = await c.req.parseBody();
|
|
149
|
-
file = body["file"];
|
|
150
|
-
}
|
|
151
|
-
const result = await dispatcher.handleStorage(path, method, file, { request: c.req.raw });
|
|
152
|
-
return normalizeResponse(c, result);
|
|
153
|
-
} catch (err) {
|
|
154
|
-
return c.json({ success: false, error: { message: err.message, code: err.statusCode || 500 } }, err.statusCode || 500);
|
|
155
|
-
}
|
|
156
|
-
});
|
|
157
|
-
app.all(`${prefix}/packages*`, async (c) => {
|
|
158
|
-
try {
|
|
159
|
-
const packagesIndex = c.req.path.indexOf("/packages");
|
|
160
|
-
const path = c.req.path.substring(packagesIndex + 9);
|
|
161
|
-
const method = c.req.method;
|
|
162
|
-
let body = {};
|
|
163
|
-
if (method === "POST" || method === "PATCH" || method === "PUT") {
|
|
164
|
-
body = await c.req.json().catch(() => ({}));
|
|
165
|
-
}
|
|
166
|
-
const query = c.req.query();
|
|
167
|
-
const result = await dispatcher.handlePackages(path, method, body, query, { request: c.req.raw });
|
|
168
|
-
return normalizeResponse(c, result);
|
|
169
|
-
} catch (err) {
|
|
170
|
-
return c.json({ success: false, error: { message: err.message, code: err.statusCode || 500 } }, err.statusCode || 500);
|
|
171
|
-
}
|
|
172
|
-
});
|
|
173
|
-
return app;
|
|
174
|
-
}
|
|
175
26
|
function objectStackMiddleware(kernel) {
|
|
176
27
|
return async (c, next) => {
|
|
177
28
|
c.set("objectStack", kernel);
|
|
@@ -180,7 +31,6 @@ function objectStackMiddleware(kernel) {
|
|
|
180
31
|
}
|
|
181
32
|
// Annotate the CommonJS export names for ESM import in node:
|
|
182
33
|
0 && (module.exports = {
|
|
183
|
-
createHonoApp,
|
|
184
34
|
objectStackMiddleware
|
|
185
35
|
});
|
|
186
36
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { Hono } from 'hono';\nimport { cors } from 'hono/cors';\nimport { type ObjectKernel, HttpDispatcher, HttpDispatcherResult } from '@objectstack/runtime';\n\nexport interface ObjectStackHonoOptions {\n kernel: ObjectKernel;\n prefix?: string;\n}\n\n/**\n * Auth service interface with handleRequest method\n */\ninterface AuthService {\n handleRequest(request: Request): Promise<Response>;\n}\n\n/**\n * @deprecated Use `HonoServerPlugin` + `createRestApiPlugin()` + `createDispatcherPlugin()` instead.\n * This function bundles all routes into a single Hono app using the legacy HttpDispatcher.\n * The plugin-based approach provides better modularity and separation of concerns.\n *\n * Migration:\n * ```ts\n * // Before:\n * const app = createHonoApp({ kernel, prefix: '/api/v1' });\n *\n * // After:\n * import { createRestApiPlugin } from '@objectstack/rest';\n * import { createDispatcherPlugin } from '@objectstack/runtime';\n * kernel.use(new HonoServerPlugin({ port: 3000 }));\n * kernel.use(createRestApiPlugin());\n * kernel.use(createDispatcherPlugin({ prefix: '/api/v1' }));\n * ```\n */\nexport function createHonoApp(options: ObjectStackHonoOptions) {\n const app = new Hono();\n const { prefix = '/api' } = options;\n const dispatcher = new HttpDispatcher(options.kernel);\n\n app.use('*', cors());\n\n // --- Helper for Response Normalization ---\n const normalizeResponse = (c: any, result: HttpDispatcherResult) => {\n if (result.handled) {\n if (result.response) {\n return c.json(result.response.body, result.response.status as any, result.response.headers);\n }\n if (result.result) {\n const res = result.result;\n // Redirect\n if (res.type === 'redirect' && res.url) {\n return c.redirect(res.url);\n }\n // Stream\n if (res.type === 'stream' && res.stream) {\n return c.body(res.stream, 200, res.headers);\n }\n \n // Hono handles standard Response objects\n return res;\n }\n }\n return c.json({ success: false, error: { message: 'Not Found', code: 404 } }, 404);\n }\n\n // --- 0. Discovery Endpoint ---\n app.get(prefix, (c) => {\n return c.json({ data: dispatcher.getDiscoveryInfo(prefix) });\n });\n\n // --- 1. Auth ---\n app.all(`${prefix}/auth/*`, async (c) => {\n try {\n // Try AuthPlugin service first (preferred path)\n const authService = typeof options.kernel.getService === 'function'\n ? options.kernel.getService<AuthService>('auth')\n : null;\n\n if (authService && typeof authService.handleRequest === 'function') {\n const response = await authService.handleRequest(c.req.raw);\n return response;\n }\n\n // Fallback to legacy dispatcher\n const path = c.req.path.substring(c.req.path.indexOf('/auth/') + 6);\n const body = await c.req.parseBody().catch(() => ({})); \n \n const result = await dispatcher.handleAuth(path, c.req.method, body, { request: c.req.raw });\n return normalizeResponse(c, result);\n } catch (err: any) {\n return c.json({ success: false, error: { message: err.message, code: err.statusCode || 500 } }, err.statusCode || 500);\n }\n });\n\n // --- 2. GraphQL ---\n app.post(`${prefix}/graphql`, async (c) => {\n try {\n const body = await c.req.json();\n const result = await dispatcher.handleGraphQL(body, { request: c.req.raw });\n return c.json(result);\n } catch (err: any) {\n return c.json({ success: false, error: { message: err.message, code: err.statusCode || 500 } }, err.statusCode || 500);\n }\n });\n\n // --- 3. Metadata Endpoints ---\n app.all(`${prefix}/meta*`, async (c) => {\n try {\n const path = c.req.path.substring(c.req.path.indexOf('/meta') + 5);\n const method = c.req.method;\n let body = undefined;\n \n if (method === 'PUT' || method === 'POST') {\n // Attempt to parse JSON body\n try {\n body = await c.req.json();\n } catch (e) {\n // Ignore parse errors, body remains undefined or empty\n body = {};\n }\n }\n const query = c.req.query();\n\n const result = await dispatcher.handleMetadata(path, { request: c.req.raw }, method, body, query);\n return normalizeResponse(c, result);\n } catch (err: any) {\n return c.json({ success: false, error: { message: err.message, code: err.statusCode || 500 } }, err.statusCode || 500);\n }\n });\n\n // --- 4. Data Endpoints ---\n app.all(`${prefix}/data*`, async (c) => {\n try {\n const path = c.req.path.substring(c.req.path.indexOf('/data') + 5);\n const method = c.req.method;\n \n let body = {};\n if (method === 'POST' || method === 'PATCH') {\n body = await c.req.json().catch(() => ({}));\n }\n const query = c.req.query();\n\n const result = await dispatcher.handleData(path, method, body, query, { request: c.req.raw });\n return normalizeResponse(c, result);\n } catch (err: any) {\n return c.json({ success: false, error: { message: err.message, code: err.statusCode || 500 } }, err.statusCode || 500);\n }\n });\n\n // --- 5. Analytics Endpoints ---\n app.all(`${prefix}/analytics*`, async (c) => {\n try {\n const path = c.req.path.substring(c.req.path.indexOf('/analytics') + 10);\n const method = c.req.method;\n \n let body = {};\n if (method === 'POST') {\n body = await c.req.json().catch(() => ({}));\n }\n\n const result = await dispatcher.handleAnalytics(path, method, body, { request: c.req.raw });\n return normalizeResponse(c, result);\n } catch (err: any) {\n return c.json({ success: false, error: { message: err.message, code: err.statusCode || 500 } }, err.statusCode || 500);\n }\n });\n\n // --- 7. Automation Endpoints ---\n app.all(`${prefix}/automation*`, async (c) => {\n try {\n const path = c.req.path.substring(c.req.path.indexOf('/automation') + 11);\n const method = c.req.method;\n \n let body = {};\n if (method === 'POST') {\n body = await c.req.json().catch(() => ({}));\n }\n\n const result = await dispatcher.handleAutomation(path, method, body, { request: c.req.raw });\n return normalizeResponse(c, result);\n } catch (err: any) {\n return c.json({ success: false, error: { message: err.message, code: err.statusCode || 500 } }, err.statusCode || 500);\n }\n });\n\n // --- 8. Storage Endpoints ---\n app.all(`${prefix}/storage*`, async (c) => {\n try {\n const path = c.req.path.substring(c.req.path.indexOf('/storage') + 8);\n const method = c.req.method;\n \n let file: any = undefined;\n if (method === 'POST' && path.includes('upload')) {\n const body = await c.req.parseBody();\n file = body['file'];\n }\n\n const result = await dispatcher.handleStorage(path, method, file, { request: c.req.raw });\n return normalizeResponse(c, result);\n } catch (err: any) {\n return c.json({ success: false, error: { message: err.message, code: err.statusCode || 500 } }, err.statusCode || 500);\n }\n });\n\n // --- 9. Package Management Endpoints ---\n app.all(`${prefix}/packages*`, async (c) => {\n try {\n const packagesIndex = c.req.path.indexOf('/packages');\n const path = c.req.path.substring(packagesIndex + 9); // length of '/packages'\n const method = c.req.method;\n\n let body = {};\n if (method === 'POST' || method === 'PATCH' || method === 'PUT') {\n body = await c.req.json().catch(() => ({}));\n }\n const query = c.req.query();\n\n const result = await dispatcher.handlePackages(path, method, body, query, { request: c.req.raw });\n return normalizeResponse(c, result);\n } catch (err: any) {\n return c.json({ success: false, error: { message: err.message, code: err.statusCode || 500 } }, err.statusCode || 500);\n }\n });\n\n return app;\n}\n\n/**\n * Middleware mode for existing Hono apps\n */\nexport function objectStackMiddleware(kernel: ObjectKernel) {\n return async (c: any, next: any) => {\n c.set('objectStack', kernel);\n await next();\n };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { type ObjectKernel } from '@objectstack/runtime';\n\nexport interface ObjectStackHonoOptions {\n kernel: ObjectKernel;\n prefix?: string;\n}\n\n/**\n * Middleware mode for existing Hono apps\n */\nexport function objectStackMiddleware(kernel: ObjectKernel) {\n return async (c: any, next: any) => {\n c.set('objectStack', kernel);\n await next();\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAYO,SAAS,sBAAsB,QAAsB;AAC1D,SAAO,OAAO,GAAQ,SAAc;AAClC,MAAE,IAAI,eAAe,MAAM;AAC3B,UAAM,KAAK;AAAA,EACb;AACF;","names":[]}
|
package/dist/index.mjs
CHANGED
|
@@ -1,152 +1,4 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import { Hono } from "hono";
|
|
3
|
-
import { cors } from "hono/cors";
|
|
4
|
-
import { HttpDispatcher } from "@objectstack/runtime";
|
|
5
|
-
function createHonoApp(options) {
|
|
6
|
-
const app = new Hono();
|
|
7
|
-
const { prefix = "/api" } = options;
|
|
8
|
-
const dispatcher = new HttpDispatcher(options.kernel);
|
|
9
|
-
app.use("*", cors());
|
|
10
|
-
const normalizeResponse = (c, result) => {
|
|
11
|
-
if (result.handled) {
|
|
12
|
-
if (result.response) {
|
|
13
|
-
return c.json(result.response.body, result.response.status, result.response.headers);
|
|
14
|
-
}
|
|
15
|
-
if (result.result) {
|
|
16
|
-
const res = result.result;
|
|
17
|
-
if (res.type === "redirect" && res.url) {
|
|
18
|
-
return c.redirect(res.url);
|
|
19
|
-
}
|
|
20
|
-
if (res.type === "stream" && res.stream) {
|
|
21
|
-
return c.body(res.stream, 200, res.headers);
|
|
22
|
-
}
|
|
23
|
-
return res;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
return c.json({ success: false, error: { message: "Not Found", code: 404 } }, 404);
|
|
27
|
-
};
|
|
28
|
-
app.get(prefix, (c) => {
|
|
29
|
-
return c.json({ data: dispatcher.getDiscoveryInfo(prefix) });
|
|
30
|
-
});
|
|
31
|
-
app.all(`${prefix}/auth/*`, async (c) => {
|
|
32
|
-
try {
|
|
33
|
-
const authService = typeof options.kernel.getService === "function" ? options.kernel.getService("auth") : null;
|
|
34
|
-
if (authService && typeof authService.handleRequest === "function") {
|
|
35
|
-
const response = await authService.handleRequest(c.req.raw);
|
|
36
|
-
return response;
|
|
37
|
-
}
|
|
38
|
-
const path = c.req.path.substring(c.req.path.indexOf("/auth/") + 6);
|
|
39
|
-
const body = await c.req.parseBody().catch(() => ({}));
|
|
40
|
-
const result = await dispatcher.handleAuth(path, c.req.method, body, { request: c.req.raw });
|
|
41
|
-
return normalizeResponse(c, result);
|
|
42
|
-
} catch (err) {
|
|
43
|
-
return c.json({ success: false, error: { message: err.message, code: err.statusCode || 500 } }, err.statusCode || 500);
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
app.post(`${prefix}/graphql`, async (c) => {
|
|
47
|
-
try {
|
|
48
|
-
const body = await c.req.json();
|
|
49
|
-
const result = await dispatcher.handleGraphQL(body, { request: c.req.raw });
|
|
50
|
-
return c.json(result);
|
|
51
|
-
} catch (err) {
|
|
52
|
-
return c.json({ success: false, error: { message: err.message, code: err.statusCode || 500 } }, err.statusCode || 500);
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
app.all(`${prefix}/meta*`, async (c) => {
|
|
56
|
-
try {
|
|
57
|
-
const path = c.req.path.substring(c.req.path.indexOf("/meta") + 5);
|
|
58
|
-
const method = c.req.method;
|
|
59
|
-
let body = void 0;
|
|
60
|
-
if (method === "PUT" || method === "POST") {
|
|
61
|
-
try {
|
|
62
|
-
body = await c.req.json();
|
|
63
|
-
} catch (e) {
|
|
64
|
-
body = {};
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
const query = c.req.query();
|
|
68
|
-
const result = await dispatcher.handleMetadata(path, { request: c.req.raw }, method, body, query);
|
|
69
|
-
return normalizeResponse(c, result);
|
|
70
|
-
} catch (err) {
|
|
71
|
-
return c.json({ success: false, error: { message: err.message, code: err.statusCode || 500 } }, err.statusCode || 500);
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
app.all(`${prefix}/data*`, async (c) => {
|
|
75
|
-
try {
|
|
76
|
-
const path = c.req.path.substring(c.req.path.indexOf("/data") + 5);
|
|
77
|
-
const method = c.req.method;
|
|
78
|
-
let body = {};
|
|
79
|
-
if (method === "POST" || method === "PATCH") {
|
|
80
|
-
body = await c.req.json().catch(() => ({}));
|
|
81
|
-
}
|
|
82
|
-
const query = c.req.query();
|
|
83
|
-
const result = await dispatcher.handleData(path, method, body, query, { request: c.req.raw });
|
|
84
|
-
return normalizeResponse(c, result);
|
|
85
|
-
} catch (err) {
|
|
86
|
-
return c.json({ success: false, error: { message: err.message, code: err.statusCode || 500 } }, err.statusCode || 500);
|
|
87
|
-
}
|
|
88
|
-
});
|
|
89
|
-
app.all(`${prefix}/analytics*`, async (c) => {
|
|
90
|
-
try {
|
|
91
|
-
const path = c.req.path.substring(c.req.path.indexOf("/analytics") + 10);
|
|
92
|
-
const method = c.req.method;
|
|
93
|
-
let body = {};
|
|
94
|
-
if (method === "POST") {
|
|
95
|
-
body = await c.req.json().catch(() => ({}));
|
|
96
|
-
}
|
|
97
|
-
const result = await dispatcher.handleAnalytics(path, method, body, { request: c.req.raw });
|
|
98
|
-
return normalizeResponse(c, result);
|
|
99
|
-
} catch (err) {
|
|
100
|
-
return c.json({ success: false, error: { message: err.message, code: err.statusCode || 500 } }, err.statusCode || 500);
|
|
101
|
-
}
|
|
102
|
-
});
|
|
103
|
-
app.all(`${prefix}/automation*`, async (c) => {
|
|
104
|
-
try {
|
|
105
|
-
const path = c.req.path.substring(c.req.path.indexOf("/automation") + 11);
|
|
106
|
-
const method = c.req.method;
|
|
107
|
-
let body = {};
|
|
108
|
-
if (method === "POST") {
|
|
109
|
-
body = await c.req.json().catch(() => ({}));
|
|
110
|
-
}
|
|
111
|
-
const result = await dispatcher.handleAutomation(path, method, body, { request: c.req.raw });
|
|
112
|
-
return normalizeResponse(c, result);
|
|
113
|
-
} catch (err) {
|
|
114
|
-
return c.json({ success: false, error: { message: err.message, code: err.statusCode || 500 } }, err.statusCode || 500);
|
|
115
|
-
}
|
|
116
|
-
});
|
|
117
|
-
app.all(`${prefix}/storage*`, async (c) => {
|
|
118
|
-
try {
|
|
119
|
-
const path = c.req.path.substring(c.req.path.indexOf("/storage") + 8);
|
|
120
|
-
const method = c.req.method;
|
|
121
|
-
let file = void 0;
|
|
122
|
-
if (method === "POST" && path.includes("upload")) {
|
|
123
|
-
const body = await c.req.parseBody();
|
|
124
|
-
file = body["file"];
|
|
125
|
-
}
|
|
126
|
-
const result = await dispatcher.handleStorage(path, method, file, { request: c.req.raw });
|
|
127
|
-
return normalizeResponse(c, result);
|
|
128
|
-
} catch (err) {
|
|
129
|
-
return c.json({ success: false, error: { message: err.message, code: err.statusCode || 500 } }, err.statusCode || 500);
|
|
130
|
-
}
|
|
131
|
-
});
|
|
132
|
-
app.all(`${prefix}/packages*`, async (c) => {
|
|
133
|
-
try {
|
|
134
|
-
const packagesIndex = c.req.path.indexOf("/packages");
|
|
135
|
-
const path = c.req.path.substring(packagesIndex + 9);
|
|
136
|
-
const method = c.req.method;
|
|
137
|
-
let body = {};
|
|
138
|
-
if (method === "POST" || method === "PATCH" || method === "PUT") {
|
|
139
|
-
body = await c.req.json().catch(() => ({}));
|
|
140
|
-
}
|
|
141
|
-
const query = c.req.query();
|
|
142
|
-
const result = await dispatcher.handlePackages(path, method, body, query, { request: c.req.raw });
|
|
143
|
-
return normalizeResponse(c, result);
|
|
144
|
-
} catch (err) {
|
|
145
|
-
return c.json({ success: false, error: { message: err.message, code: err.statusCode || 500 } }, err.statusCode || 500);
|
|
146
|
-
}
|
|
147
|
-
});
|
|
148
|
-
return app;
|
|
149
|
-
}
|
|
150
2
|
function objectStackMiddleware(kernel) {
|
|
151
3
|
return async (c, next) => {
|
|
152
4
|
c.set("objectStack", kernel);
|
|
@@ -154,7 +6,6 @@ function objectStackMiddleware(kernel) {
|
|
|
154
6
|
};
|
|
155
7
|
}
|
|
156
8
|
export {
|
|
157
|
-
createHonoApp,
|
|
158
9
|
objectStackMiddleware
|
|
159
10
|
};
|
|
160
11
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { Hono } from 'hono';\nimport { cors } from 'hono/cors';\nimport { type ObjectKernel, HttpDispatcher, HttpDispatcherResult } from '@objectstack/runtime';\n\nexport interface ObjectStackHonoOptions {\n kernel: ObjectKernel;\n prefix?: string;\n}\n\n/**\n * Auth service interface with handleRequest method\n */\ninterface AuthService {\n handleRequest(request: Request): Promise<Response>;\n}\n\n/**\n * @deprecated Use `HonoServerPlugin` + `createRestApiPlugin()` + `createDispatcherPlugin()` instead.\n * This function bundles all routes into a single Hono app using the legacy HttpDispatcher.\n * The plugin-based approach provides better modularity and separation of concerns.\n *\n * Migration:\n * ```ts\n * // Before:\n * const app = createHonoApp({ kernel, prefix: '/api/v1' });\n *\n * // After:\n * import { createRestApiPlugin } from '@objectstack/rest';\n * import { createDispatcherPlugin } from '@objectstack/runtime';\n * kernel.use(new HonoServerPlugin({ port: 3000 }));\n * kernel.use(createRestApiPlugin());\n * kernel.use(createDispatcherPlugin({ prefix: '/api/v1' }));\n * ```\n */\nexport function createHonoApp(options: ObjectStackHonoOptions) {\n const app = new Hono();\n const { prefix = '/api' } = options;\n const dispatcher = new HttpDispatcher(options.kernel);\n\n app.use('*', cors());\n\n // --- Helper for Response Normalization ---\n const normalizeResponse = (c: any, result: HttpDispatcherResult) => {\n if (result.handled) {\n if (result.response) {\n return c.json(result.response.body, result.response.status as any, result.response.headers);\n }\n if (result.result) {\n const res = result.result;\n // Redirect\n if (res.type === 'redirect' && res.url) {\n return c.redirect(res.url);\n }\n // Stream\n if (res.type === 'stream' && res.stream) {\n return c.body(res.stream, 200, res.headers);\n }\n \n // Hono handles standard Response objects\n return res;\n }\n }\n return c.json({ success: false, error: { message: 'Not Found', code: 404 } }, 404);\n }\n\n // --- 0. Discovery Endpoint ---\n app.get(prefix, (c) => {\n return c.json({ data: dispatcher.getDiscoveryInfo(prefix) });\n });\n\n // --- 1. Auth ---\n app.all(`${prefix}/auth/*`, async (c) => {\n try {\n // Try AuthPlugin service first (preferred path)\n const authService = typeof options.kernel.getService === 'function'\n ? options.kernel.getService<AuthService>('auth')\n : null;\n\n if (authService && typeof authService.handleRequest === 'function') {\n const response = await authService.handleRequest(c.req.raw);\n return response;\n }\n\n // Fallback to legacy dispatcher\n const path = c.req.path.substring(c.req.path.indexOf('/auth/') + 6);\n const body = await c.req.parseBody().catch(() => ({})); \n \n const result = await dispatcher.handleAuth(path, c.req.method, body, { request: c.req.raw });\n return normalizeResponse(c, result);\n } catch (err: any) {\n return c.json({ success: false, error: { message: err.message, code: err.statusCode || 500 } }, err.statusCode || 500);\n }\n });\n\n // --- 2. GraphQL ---\n app.post(`${prefix}/graphql`, async (c) => {\n try {\n const body = await c.req.json();\n const result = await dispatcher.handleGraphQL(body, { request: c.req.raw });\n return c.json(result);\n } catch (err: any) {\n return c.json({ success: false, error: { message: err.message, code: err.statusCode || 500 } }, err.statusCode || 500);\n }\n });\n\n // --- 3. Metadata Endpoints ---\n app.all(`${prefix}/meta*`, async (c) => {\n try {\n const path = c.req.path.substring(c.req.path.indexOf('/meta') + 5);\n const method = c.req.method;\n let body = undefined;\n \n if (method === 'PUT' || method === 'POST') {\n // Attempt to parse JSON body\n try {\n body = await c.req.json();\n } catch (e) {\n // Ignore parse errors, body remains undefined or empty\n body = {};\n }\n }\n const query = c.req.query();\n\n const result = await dispatcher.handleMetadata(path, { request: c.req.raw }, method, body, query);\n return normalizeResponse(c, result);\n } catch (err: any) {\n return c.json({ success: false, error: { message: err.message, code: err.statusCode || 500 } }, err.statusCode || 500);\n }\n });\n\n // --- 4. Data Endpoints ---\n app.all(`${prefix}/data*`, async (c) => {\n try {\n const path = c.req.path.substring(c.req.path.indexOf('/data') + 5);\n const method = c.req.method;\n \n let body = {};\n if (method === 'POST' || method === 'PATCH') {\n body = await c.req.json().catch(() => ({}));\n }\n const query = c.req.query();\n\n const result = await dispatcher.handleData(path, method, body, query, { request: c.req.raw });\n return normalizeResponse(c, result);\n } catch (err: any) {\n return c.json({ success: false, error: { message: err.message, code: err.statusCode || 500 } }, err.statusCode || 500);\n }\n });\n\n // --- 5. Analytics Endpoints ---\n app.all(`${prefix}/analytics*`, async (c) => {\n try {\n const path = c.req.path.substring(c.req.path.indexOf('/analytics') + 10);\n const method = c.req.method;\n \n let body = {};\n if (method === 'POST') {\n body = await c.req.json().catch(() => ({}));\n }\n\n const result = await dispatcher.handleAnalytics(path, method, body, { request: c.req.raw });\n return normalizeResponse(c, result);\n } catch (err: any) {\n return c.json({ success: false, error: { message: err.message, code: err.statusCode || 500 } }, err.statusCode || 500);\n }\n });\n\n // --- 7. Automation Endpoints ---\n app.all(`${prefix}/automation*`, async (c) => {\n try {\n const path = c.req.path.substring(c.req.path.indexOf('/automation') + 11);\n const method = c.req.method;\n \n let body = {};\n if (method === 'POST') {\n body = await c.req.json().catch(() => ({}));\n }\n\n const result = await dispatcher.handleAutomation(path, method, body, { request: c.req.raw });\n return normalizeResponse(c, result);\n } catch (err: any) {\n return c.json({ success: false, error: { message: err.message, code: err.statusCode || 500 } }, err.statusCode || 500);\n }\n });\n\n // --- 8. Storage Endpoints ---\n app.all(`${prefix}/storage*`, async (c) => {\n try {\n const path = c.req.path.substring(c.req.path.indexOf('/storage') + 8);\n const method = c.req.method;\n \n let file: any = undefined;\n if (method === 'POST' && path.includes('upload')) {\n const body = await c.req.parseBody();\n file = body['file'];\n }\n\n const result = await dispatcher.handleStorage(path, method, file, { request: c.req.raw });\n return normalizeResponse(c, result);\n } catch (err: any) {\n return c.json({ success: false, error: { message: err.message, code: err.statusCode || 500 } }, err.statusCode || 500);\n }\n });\n\n // --- 9. Package Management Endpoints ---\n app.all(`${prefix}/packages*`, async (c) => {\n try {\n const packagesIndex = c.req.path.indexOf('/packages');\n const path = c.req.path.substring(packagesIndex + 9); // length of '/packages'\n const method = c.req.method;\n\n let body = {};\n if (method === 'POST' || method === 'PATCH' || method === 'PUT') {\n body = await c.req.json().catch(() => ({}));\n }\n const query = c.req.query();\n\n const result = await dispatcher.handlePackages(path, method, body, query, { request: c.req.raw });\n return normalizeResponse(c, result);\n } catch (err: any) {\n return c.json({ success: false, error: { message: err.message, code: err.statusCode || 500 } }, err.statusCode || 500);\n }\n });\n\n return app;\n}\n\n/**\n * Middleware mode for existing Hono apps\n */\nexport function objectStackMiddleware(kernel: ObjectKernel) {\n return async (c: any, next: any) => {\n c.set('objectStack', kernel);\n await next();\n };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { type ObjectKernel } from '@objectstack/runtime';\n\nexport interface ObjectStackHonoOptions {\n kernel: ObjectKernel;\n prefix?: string;\n}\n\n/**\n * Middleware mode for existing Hono apps\n */\nexport function objectStackMiddleware(kernel: ObjectKernel) {\n return async (c: any, next: any) => {\n c.set('objectStack', kernel);\n await next();\n };\n}\n"],"mappings":";AAYO,SAAS,sBAAsB,QAAsB;AAC1D,SAAO,OAAO,GAAQ,SAAc;AAClC,MAAE,IAAI,eAAe,MAAM;AAC3B,UAAM,KAAK;AAAA,EACb;AACF;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@objectstack/hono",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.1",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -13,13 +13,13 @@
|
|
|
13
13
|
},
|
|
14
14
|
"peerDependencies": {
|
|
15
15
|
"hono": "^4.11.9",
|
|
16
|
-
"@objectstack/runtime": "
|
|
16
|
+
"@objectstack/runtime": "3.0.1"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
19
|
"hono": "^4.11.9",
|
|
20
20
|
"typescript": "^5.0.0",
|
|
21
21
|
"vitest": "^4.0.18",
|
|
22
|
-
"@objectstack/runtime": "
|
|
22
|
+
"@objectstack/runtime": "3.0.1"
|
|
23
23
|
},
|
|
24
24
|
"scripts": {
|
|
25
25
|
"build": "tsup --config ../../../tsup.config.ts",
|
package/src/hono.test.ts
CHANGED
|
@@ -3,400 +3,15 @@
|
|
|
3
3
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
4
4
|
import { Hono } from 'hono';
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
const mockDispatcher = {
|
|
8
|
-
getDiscoveryInfo: vi.fn().mockReturnValue({ version: '1.0', endpoints: [] }),
|
|
9
|
-
handleAuth: vi.fn().mockResolvedValue({ handled: true, response: { body: { ok: true }, status: 200 } }),
|
|
10
|
-
handleGraphQL: vi.fn().mockResolvedValue({ data: {} }),
|
|
11
|
-
handleMetadata: vi.fn().mockResolvedValue({ handled: true, response: { body: { objects: [] }, status: 200 } }),
|
|
12
|
-
handleData: vi.fn().mockResolvedValue({ handled: true, response: { body: { records: [] }, status: 200 } }),
|
|
13
|
-
handleAnalytics: vi.fn().mockResolvedValue({ handled: true, response: { body: {}, status: 200 } }),
|
|
14
|
-
handleAutomation: vi.fn().mockResolvedValue({ handled: true, response: { body: {}, status: 200 } }),
|
|
15
|
-
handleStorage: vi.fn().mockResolvedValue({ handled: true, response: { body: {}, status: 200 } }),
|
|
16
|
-
handlePackages: vi.fn().mockResolvedValue({ handled: true, response: { body: {}, status: 200 } }),
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
vi.mock('@objectstack/runtime', () => {
|
|
20
|
-
return {
|
|
21
|
-
HttpDispatcher: function HttpDispatcher() {
|
|
22
|
-
return mockDispatcher;
|
|
23
|
-
},
|
|
24
|
-
};
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
import { createHonoApp, objectStackMiddleware } from './index';
|
|
6
|
+
import { objectStackMiddleware } from './index';
|
|
28
7
|
|
|
29
8
|
const mockKernel = { name: 'test-kernel' } as any;
|
|
30
9
|
|
|
31
|
-
describe('
|
|
10
|
+
describe('objectStackMiddleware', () => {
|
|
32
11
|
beforeEach(() => {
|
|
33
12
|
vi.clearAllMocks();
|
|
34
13
|
});
|
|
35
14
|
|
|
36
|
-
it('should return a Hono app instance', () => {
|
|
37
|
-
const app = createHonoApp({ kernel: mockKernel });
|
|
38
|
-
expect(app).toBeInstanceOf(Hono);
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
describe('Discovery Endpoint', () => {
|
|
42
|
-
it('GET /api returns discovery info', async () => {
|
|
43
|
-
const app = createHonoApp({ kernel: mockKernel });
|
|
44
|
-
const res = await app.request('/api');
|
|
45
|
-
expect(res.status).toBe(200);
|
|
46
|
-
const json = await res.json();
|
|
47
|
-
expect(json.data).toEqual({ version: '1.0', endpoints: [] });
|
|
48
|
-
expect(mockDispatcher.getDiscoveryInfo).toHaveBeenCalledWith('/api');
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('uses custom prefix for discovery', async () => {
|
|
52
|
-
const app = createHonoApp({ kernel: mockKernel, prefix: '/v2' });
|
|
53
|
-
const res = await app.request('/v2');
|
|
54
|
-
expect(res.status).toBe(200);
|
|
55
|
-
expect(mockDispatcher.getDiscoveryInfo).toHaveBeenCalledWith('/v2');
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
describe('Auth Endpoint', () => {
|
|
60
|
-
it('POST /api/auth/login calls handleAuth', async () => {
|
|
61
|
-
const app = createHonoApp({ kernel: mockKernel });
|
|
62
|
-
const res = await app.request('/api/auth/login', { method: 'POST' });
|
|
63
|
-
expect(res.status).toBe(200);
|
|
64
|
-
expect(mockDispatcher.handleAuth).toHaveBeenCalledWith(
|
|
65
|
-
'login',
|
|
66
|
-
'POST',
|
|
67
|
-
expect.anything(),
|
|
68
|
-
expect.objectContaining({ request: expect.any(Request) }),
|
|
69
|
-
);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it('GET /api/auth/callback calls handleAuth', async () => {
|
|
73
|
-
const app = createHonoApp({ kernel: mockKernel });
|
|
74
|
-
const res = await app.request('/api/auth/callback', { method: 'GET' });
|
|
75
|
-
expect(res.status).toBe(200);
|
|
76
|
-
expect(mockDispatcher.handleAuth).toHaveBeenCalledWith(
|
|
77
|
-
'callback',
|
|
78
|
-
'GET',
|
|
79
|
-
expect.anything(),
|
|
80
|
-
expect.objectContaining({ request: expect.any(Request) }),
|
|
81
|
-
);
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it('returns error on handleAuth exception', async () => {
|
|
85
|
-
mockDispatcher.handleAuth.mockRejectedValueOnce(
|
|
86
|
-
Object.assign(new Error('Unauthorized'), { statusCode: 401 }),
|
|
87
|
-
);
|
|
88
|
-
const app = createHonoApp({ kernel: mockKernel });
|
|
89
|
-
const res = await app.request('/api/auth/login', { method: 'POST' });
|
|
90
|
-
expect(res.status).toBe(401);
|
|
91
|
-
const json = await res.json();
|
|
92
|
-
expect(json.success).toBe(false);
|
|
93
|
-
expect(json.error.message).toBe('Unauthorized');
|
|
94
|
-
});
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
describe('Auth via AuthPlugin service', () => {
|
|
98
|
-
it('uses kernel.getService("auth") when available', async () => {
|
|
99
|
-
const mockHandleRequest = vi.fn().mockResolvedValue(
|
|
100
|
-
new Response(JSON.stringify({ user: { id: '1' } }), {
|
|
101
|
-
status: 200,
|
|
102
|
-
headers: { 'Content-Type': 'application/json' },
|
|
103
|
-
}),
|
|
104
|
-
);
|
|
105
|
-
const kernelWithAuth = {
|
|
106
|
-
...mockKernel,
|
|
107
|
-
getService: vi.fn().mockReturnValue({ handleRequest: mockHandleRequest }),
|
|
108
|
-
};
|
|
109
|
-
const app = createHonoApp({ kernel: kernelWithAuth });
|
|
110
|
-
const res = await app.request('/api/auth/sign-in/email', {
|
|
111
|
-
method: 'POST',
|
|
112
|
-
headers: { 'Content-Type': 'application/json' },
|
|
113
|
-
body: JSON.stringify({ email: 'a@b.com', password: 'pass' }),
|
|
114
|
-
});
|
|
115
|
-
expect(res.status).toBe(200);
|
|
116
|
-
const json = await res.json();
|
|
117
|
-
expect(json.user.id).toBe('1');
|
|
118
|
-
expect(kernelWithAuth.getService).toHaveBeenCalledWith('auth');
|
|
119
|
-
expect(mockHandleRequest).toHaveBeenCalledWith(expect.any(Request));
|
|
120
|
-
expect(mockDispatcher.handleAuth).not.toHaveBeenCalled();
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
it('falls back to dispatcher.handleAuth when auth service is not available', async () => {
|
|
124
|
-
const kernelWithoutAuth = {
|
|
125
|
-
...mockKernel,
|
|
126
|
-
getService: vi.fn().mockReturnValue(null),
|
|
127
|
-
};
|
|
128
|
-
const app = createHonoApp({ kernel: kernelWithoutAuth });
|
|
129
|
-
const res = await app.request('/api/auth/login', { method: 'POST' });
|
|
130
|
-
expect(res.status).toBe(200);
|
|
131
|
-
expect(mockDispatcher.handleAuth).toHaveBeenCalled();
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it('forwards GET requests to auth service', async () => {
|
|
135
|
-
const mockHandleRequest = vi.fn().mockResolvedValue(
|
|
136
|
-
new Response(JSON.stringify({ session: { token: 'abc' } }), {
|
|
137
|
-
status: 200,
|
|
138
|
-
headers: { 'Content-Type': 'application/json' },
|
|
139
|
-
}),
|
|
140
|
-
);
|
|
141
|
-
const kernelWithAuth = {
|
|
142
|
-
...mockKernel,
|
|
143
|
-
getService: vi.fn().mockReturnValue({ handleRequest: mockHandleRequest }),
|
|
144
|
-
};
|
|
145
|
-
const app = createHonoApp({ kernel: kernelWithAuth });
|
|
146
|
-
const res = await app.request('/api/auth/get-session', { method: 'GET' });
|
|
147
|
-
expect(res.status).toBe(200);
|
|
148
|
-
const json = await res.json();
|
|
149
|
-
expect(json.session.token).toBe('abc');
|
|
150
|
-
expect(mockHandleRequest).toHaveBeenCalled();
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
it('returns error when auth service throws', async () => {
|
|
154
|
-
const mockHandleRequest = vi.fn().mockRejectedValue(new Error('Auth failed'));
|
|
155
|
-
const kernelWithAuth = {
|
|
156
|
-
...mockKernel,
|
|
157
|
-
getService: vi.fn().mockReturnValue({ handleRequest: mockHandleRequest }),
|
|
158
|
-
};
|
|
159
|
-
const app = createHonoApp({ kernel: kernelWithAuth });
|
|
160
|
-
const res = await app.request('/api/auth/sign-in/email', { method: 'POST' });
|
|
161
|
-
expect(res.status).toBe(500);
|
|
162
|
-
const json = await res.json();
|
|
163
|
-
expect(json.success).toBe(false);
|
|
164
|
-
expect(json.error.message).toBe('Auth failed');
|
|
165
|
-
});
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
describe('GraphQL Endpoint', () => {
|
|
169
|
-
it('POST /api/graphql calls handleGraphQL', async () => {
|
|
170
|
-
const app = createHonoApp({ kernel: mockKernel });
|
|
171
|
-
const body = { query: '{ objects { name } }' };
|
|
172
|
-
const res = await app.request('/api/graphql', {
|
|
173
|
-
method: 'POST',
|
|
174
|
-
headers: { 'Content-Type': 'application/json' },
|
|
175
|
-
body: JSON.stringify(body),
|
|
176
|
-
});
|
|
177
|
-
expect(res.status).toBe(200);
|
|
178
|
-
expect(mockDispatcher.handleGraphQL).toHaveBeenCalledWith(
|
|
179
|
-
body,
|
|
180
|
-
expect.objectContaining({ request: expect.any(Request) }),
|
|
181
|
-
);
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
it('returns error on handleGraphQL exception', async () => {
|
|
185
|
-
mockDispatcher.handleGraphQL.mockRejectedValueOnce(new Error('Parse error'));
|
|
186
|
-
const app = createHonoApp({ kernel: mockKernel });
|
|
187
|
-
const res = await app.request('/api/graphql', {
|
|
188
|
-
method: 'POST',
|
|
189
|
-
headers: { 'Content-Type': 'application/json' },
|
|
190
|
-
body: JSON.stringify({ query: 'bad' }),
|
|
191
|
-
});
|
|
192
|
-
expect(res.status).toBe(500);
|
|
193
|
-
const json = await res.json();
|
|
194
|
-
expect(json.success).toBe(false);
|
|
195
|
-
});
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
describe('Metadata Endpoint', () => {
|
|
199
|
-
it('GET /api/meta/objects calls handleMetadata', async () => {
|
|
200
|
-
const app = createHonoApp({ kernel: mockKernel });
|
|
201
|
-
const res = await app.request('/api/meta/objects');
|
|
202
|
-
expect(res.status).toBe(200);
|
|
203
|
-
expect(mockDispatcher.handleMetadata).toHaveBeenCalledWith(
|
|
204
|
-
'/objects',
|
|
205
|
-
expect.objectContaining({ request: expect.any(Request) }),
|
|
206
|
-
'GET',
|
|
207
|
-
undefined,
|
|
208
|
-
expect.any(Object),
|
|
209
|
-
);
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
it('PUT /api/meta/objects parses JSON body', async () => {
|
|
213
|
-
const app = createHonoApp({ kernel: mockKernel });
|
|
214
|
-
const body = { name: 'test_object' };
|
|
215
|
-
const res = await app.request('/api/meta/objects', {
|
|
216
|
-
method: 'PUT',
|
|
217
|
-
headers: { 'Content-Type': 'application/json' },
|
|
218
|
-
body: JSON.stringify(body),
|
|
219
|
-
});
|
|
220
|
-
expect(res.status).toBe(200);
|
|
221
|
-
expect(mockDispatcher.handleMetadata).toHaveBeenCalledWith(
|
|
222
|
-
'/objects',
|
|
223
|
-
expect.objectContaining({ request: expect.any(Request) }),
|
|
224
|
-
'PUT',
|
|
225
|
-
body,
|
|
226
|
-
expect.any(Object),
|
|
227
|
-
);
|
|
228
|
-
});
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
describe('Data Endpoint', () => {
|
|
232
|
-
it('GET /api/data/account calls handleData', async () => {
|
|
233
|
-
const app = createHonoApp({ kernel: mockKernel });
|
|
234
|
-
const res = await app.request('/api/data/account');
|
|
235
|
-
expect(res.status).toBe(200);
|
|
236
|
-
expect(mockDispatcher.handleData).toHaveBeenCalledWith(
|
|
237
|
-
'/account',
|
|
238
|
-
'GET',
|
|
239
|
-
{},
|
|
240
|
-
expect.any(Object),
|
|
241
|
-
expect.objectContaining({ request: expect.any(Request) }),
|
|
242
|
-
);
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
it('POST /api/data/account parses JSON body', async () => {
|
|
246
|
-
const app = createHonoApp({ kernel: mockKernel });
|
|
247
|
-
const body = { name: 'Acme' };
|
|
248
|
-
const res = await app.request('/api/data/account', {
|
|
249
|
-
method: 'POST',
|
|
250
|
-
headers: { 'Content-Type': 'application/json' },
|
|
251
|
-
body: JSON.stringify(body),
|
|
252
|
-
});
|
|
253
|
-
expect(res.status).toBe(200);
|
|
254
|
-
expect(mockDispatcher.handleData).toHaveBeenCalledWith(
|
|
255
|
-
'/account',
|
|
256
|
-
'POST',
|
|
257
|
-
body,
|
|
258
|
-
expect.any(Object),
|
|
259
|
-
expect.objectContaining({ request: expect.any(Request) }),
|
|
260
|
-
);
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
it('returns 404 when result is not handled', async () => {
|
|
264
|
-
mockDispatcher.handleData.mockResolvedValueOnce({ handled: false });
|
|
265
|
-
const app = createHonoApp({ kernel: mockKernel });
|
|
266
|
-
const res = await app.request('/api/data/missing');
|
|
267
|
-
expect(res.status).toBe(404);
|
|
268
|
-
const json = await res.json();
|
|
269
|
-
expect(json.success).toBe(false);
|
|
270
|
-
});
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
describe('Analytics Endpoint', () => {
|
|
274
|
-
it('GET /api/analytics/report calls handleAnalytics', async () => {
|
|
275
|
-
const app = createHonoApp({ kernel: mockKernel });
|
|
276
|
-
const res = await app.request('/api/analytics/report');
|
|
277
|
-
expect(res.status).toBe(200);
|
|
278
|
-
expect(mockDispatcher.handleAnalytics).toHaveBeenCalled();
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
it('POST /api/analytics/report parses body', async () => {
|
|
282
|
-
const app = createHonoApp({ kernel: mockKernel });
|
|
283
|
-
const body = { metric: 'revenue' };
|
|
284
|
-
const res = await app.request('/api/analytics/report', {
|
|
285
|
-
method: 'POST',
|
|
286
|
-
headers: { 'Content-Type': 'application/json' },
|
|
287
|
-
body: JSON.stringify(body),
|
|
288
|
-
});
|
|
289
|
-
expect(res.status).toBe(200);
|
|
290
|
-
expect(mockDispatcher.handleAnalytics).toHaveBeenCalledWith(
|
|
291
|
-
'/report',
|
|
292
|
-
'POST',
|
|
293
|
-
body,
|
|
294
|
-
expect.objectContaining({ request: expect.any(Request) }),
|
|
295
|
-
);
|
|
296
|
-
});
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
describe('Automation Endpoint', () => {
|
|
300
|
-
it('GET /api/automation/flows calls handleAutomation', async () => {
|
|
301
|
-
const app = createHonoApp({ kernel: mockKernel });
|
|
302
|
-
const res = await app.request('/api/automation/flows');
|
|
303
|
-
expect(res.status).toBe(200);
|
|
304
|
-
expect(mockDispatcher.handleAutomation).toHaveBeenCalled();
|
|
305
|
-
});
|
|
306
|
-
|
|
307
|
-
it('POST /api/automation/flows parses body', async () => {
|
|
308
|
-
const app = createHonoApp({ kernel: mockKernel });
|
|
309
|
-
const body = { trigger: 'on_create' };
|
|
310
|
-
const res = await app.request('/api/automation/flows', {
|
|
311
|
-
method: 'POST',
|
|
312
|
-
headers: { 'Content-Type': 'application/json' },
|
|
313
|
-
body: JSON.stringify(body),
|
|
314
|
-
});
|
|
315
|
-
expect(res.status).toBe(200);
|
|
316
|
-
expect(mockDispatcher.handleAutomation).toHaveBeenCalledWith(
|
|
317
|
-
'/flows',
|
|
318
|
-
'POST',
|
|
319
|
-
body,
|
|
320
|
-
expect.objectContaining({ request: expect.any(Request) }),
|
|
321
|
-
);
|
|
322
|
-
});
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
describe('Storage Endpoint', () => {
|
|
326
|
-
it('GET /api/storage/files calls handleStorage', async () => {
|
|
327
|
-
const app = createHonoApp({ kernel: mockKernel });
|
|
328
|
-
const res = await app.request('/api/storage/files');
|
|
329
|
-
expect(res.status).toBe(200);
|
|
330
|
-
expect(mockDispatcher.handleStorage).toHaveBeenCalled();
|
|
331
|
-
});
|
|
332
|
-
});
|
|
333
|
-
|
|
334
|
-
describe('Packages Endpoint', () => {
|
|
335
|
-
it('GET /api/packages calls handlePackages', async () => {
|
|
336
|
-
const app = createHonoApp({ kernel: mockKernel });
|
|
337
|
-
const res = await app.request('/api/packages');
|
|
338
|
-
expect(res.status).toBe(200);
|
|
339
|
-
expect(mockDispatcher.handlePackages).toHaveBeenCalledWith(
|
|
340
|
-
'',
|
|
341
|
-
'GET',
|
|
342
|
-
{},
|
|
343
|
-
expect.any(Object),
|
|
344
|
-
expect.objectContaining({ request: expect.any(Request) }),
|
|
345
|
-
);
|
|
346
|
-
});
|
|
347
|
-
|
|
348
|
-
it('POST /api/packages/install parses body', async () => {
|
|
349
|
-
const app = createHonoApp({ kernel: mockKernel });
|
|
350
|
-
const body = { package: 'my-plugin' };
|
|
351
|
-
const res = await app.request('/api/packages/install', {
|
|
352
|
-
method: 'POST',
|
|
353
|
-
headers: { 'Content-Type': 'application/json' },
|
|
354
|
-
body: JSON.stringify(body),
|
|
355
|
-
});
|
|
356
|
-
expect(res.status).toBe(200);
|
|
357
|
-
expect(mockDispatcher.handlePackages).toHaveBeenCalledWith(
|
|
358
|
-
'/install',
|
|
359
|
-
'POST',
|
|
360
|
-
body,
|
|
361
|
-
expect.any(Object),
|
|
362
|
-
expect.objectContaining({ request: expect.any(Request) }),
|
|
363
|
-
);
|
|
364
|
-
});
|
|
365
|
-
});
|
|
366
|
-
|
|
367
|
-
describe('normalizeResponse', () => {
|
|
368
|
-
it('handles redirect result', async () => {
|
|
369
|
-
mockDispatcher.handleData.mockResolvedValueOnce({
|
|
370
|
-
handled: true,
|
|
371
|
-
result: { type: 'redirect', url: 'https://example.com' },
|
|
372
|
-
});
|
|
373
|
-
const app = createHonoApp({ kernel: mockKernel });
|
|
374
|
-
const res = await app.request('/api/data/redir', { redirect: 'manual' });
|
|
375
|
-
expect(res.status).toBe(302);
|
|
376
|
-
expect(res.headers.get('location')).toBe('https://example.com');
|
|
377
|
-
});
|
|
378
|
-
|
|
379
|
-
it('handles stream result', async () => {
|
|
380
|
-
const stream = new ReadableStream({
|
|
381
|
-
start(controller) {
|
|
382
|
-
controller.enqueue(new TextEncoder().encode('hello'));
|
|
383
|
-
controller.close();
|
|
384
|
-
},
|
|
385
|
-
});
|
|
386
|
-
mockDispatcher.handleData.mockResolvedValueOnce({
|
|
387
|
-
handled: true,
|
|
388
|
-
result: { type: 'stream', stream, headers: { 'Content-Type': 'text/plain' } },
|
|
389
|
-
});
|
|
390
|
-
const app = createHonoApp({ kernel: mockKernel });
|
|
391
|
-
const res = await app.request('/api/data/stream');
|
|
392
|
-
expect(res.status).toBe(200);
|
|
393
|
-
const text = await res.text();
|
|
394
|
-
expect(text).toBe('hello');
|
|
395
|
-
});
|
|
396
|
-
});
|
|
397
|
-
});
|
|
398
|
-
|
|
399
|
-
describe('objectStackMiddleware', () => {
|
|
400
15
|
it('sets kernel on context via c.set', async () => {
|
|
401
16
|
const app = new Hono();
|
|
402
17
|
const middleware = objectStackMiddleware(mockKernel);
|
|
@@ -429,4 +44,20 @@ describe('objectStackMiddleware', () => {
|
|
|
429
44
|
expect(res.status).toBe(200);
|
|
430
45
|
expect(spy).toHaveBeenCalled();
|
|
431
46
|
});
|
|
47
|
+
|
|
48
|
+
it('provides the correct kernel instance', async () => {
|
|
49
|
+
const app = new Hono();
|
|
50
|
+
const middleware = objectStackMiddleware(mockKernel);
|
|
51
|
+
|
|
52
|
+
app.use('*', middleware);
|
|
53
|
+
app.get('/test', (c) => {
|
|
54
|
+
const kernel = c.get('objectStack');
|
|
55
|
+
return c.json({ name: kernel.name });
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const res = await app.request('/test');
|
|
59
|
+
expect(res.status).toBe(200);
|
|
60
|
+
const json = await res.json();
|
|
61
|
+
expect(json.name).toBe('test-kernel');
|
|
62
|
+
});
|
|
432
63
|
});
|
package/src/index.ts
CHANGED
|
@@ -1,232 +1,12 @@
|
|
|
1
1
|
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import { cors } from 'hono/cors';
|
|
5
|
-
import { type ObjectKernel, HttpDispatcher, HttpDispatcherResult } from '@objectstack/runtime';
|
|
3
|
+
import { type ObjectKernel } from '@objectstack/runtime';
|
|
6
4
|
|
|
7
5
|
export interface ObjectStackHonoOptions {
|
|
8
6
|
kernel: ObjectKernel;
|
|
9
7
|
prefix?: string;
|
|
10
8
|
}
|
|
11
9
|
|
|
12
|
-
/**
|
|
13
|
-
* Auth service interface with handleRequest method
|
|
14
|
-
*/
|
|
15
|
-
interface AuthService {
|
|
16
|
-
handleRequest(request: Request): Promise<Response>;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* @deprecated Use `HonoServerPlugin` + `createRestApiPlugin()` + `createDispatcherPlugin()` instead.
|
|
21
|
-
* This function bundles all routes into a single Hono app using the legacy HttpDispatcher.
|
|
22
|
-
* The plugin-based approach provides better modularity and separation of concerns.
|
|
23
|
-
*
|
|
24
|
-
* Migration:
|
|
25
|
-
* ```ts
|
|
26
|
-
* // Before:
|
|
27
|
-
* const app = createHonoApp({ kernel, prefix: '/api/v1' });
|
|
28
|
-
*
|
|
29
|
-
* // After:
|
|
30
|
-
* import { createRestApiPlugin } from '@objectstack/rest';
|
|
31
|
-
* import { createDispatcherPlugin } from '@objectstack/runtime';
|
|
32
|
-
* kernel.use(new HonoServerPlugin({ port: 3000 }));
|
|
33
|
-
* kernel.use(createRestApiPlugin());
|
|
34
|
-
* kernel.use(createDispatcherPlugin({ prefix: '/api/v1' }));
|
|
35
|
-
* ```
|
|
36
|
-
*/
|
|
37
|
-
export function createHonoApp(options: ObjectStackHonoOptions) {
|
|
38
|
-
const app = new Hono();
|
|
39
|
-
const { prefix = '/api' } = options;
|
|
40
|
-
const dispatcher = new HttpDispatcher(options.kernel);
|
|
41
|
-
|
|
42
|
-
app.use('*', cors());
|
|
43
|
-
|
|
44
|
-
// --- Helper for Response Normalization ---
|
|
45
|
-
const normalizeResponse = (c: any, result: HttpDispatcherResult) => {
|
|
46
|
-
if (result.handled) {
|
|
47
|
-
if (result.response) {
|
|
48
|
-
return c.json(result.response.body, result.response.status as any, result.response.headers);
|
|
49
|
-
}
|
|
50
|
-
if (result.result) {
|
|
51
|
-
const res = result.result;
|
|
52
|
-
// Redirect
|
|
53
|
-
if (res.type === 'redirect' && res.url) {
|
|
54
|
-
return c.redirect(res.url);
|
|
55
|
-
}
|
|
56
|
-
// Stream
|
|
57
|
-
if (res.type === 'stream' && res.stream) {
|
|
58
|
-
return c.body(res.stream, 200, res.headers);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Hono handles standard Response objects
|
|
62
|
-
return res;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
return c.json({ success: false, error: { message: 'Not Found', code: 404 } }, 404);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// --- 0. Discovery Endpoint ---
|
|
69
|
-
app.get(prefix, (c) => {
|
|
70
|
-
return c.json({ data: dispatcher.getDiscoveryInfo(prefix) });
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
// --- 1. Auth ---
|
|
74
|
-
app.all(`${prefix}/auth/*`, async (c) => {
|
|
75
|
-
try {
|
|
76
|
-
// Try AuthPlugin service first (preferred path)
|
|
77
|
-
const authService = typeof options.kernel.getService === 'function'
|
|
78
|
-
? options.kernel.getService<AuthService>('auth')
|
|
79
|
-
: null;
|
|
80
|
-
|
|
81
|
-
if (authService && typeof authService.handleRequest === 'function') {
|
|
82
|
-
const response = await authService.handleRequest(c.req.raw);
|
|
83
|
-
return response;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Fallback to legacy dispatcher
|
|
87
|
-
const path = c.req.path.substring(c.req.path.indexOf('/auth/') + 6);
|
|
88
|
-
const body = await c.req.parseBody().catch(() => ({}));
|
|
89
|
-
|
|
90
|
-
const result = await dispatcher.handleAuth(path, c.req.method, body, { request: c.req.raw });
|
|
91
|
-
return normalizeResponse(c, result);
|
|
92
|
-
} catch (err: any) {
|
|
93
|
-
return c.json({ success: false, error: { message: err.message, code: err.statusCode || 500 } }, err.statusCode || 500);
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
// --- 2. GraphQL ---
|
|
98
|
-
app.post(`${prefix}/graphql`, async (c) => {
|
|
99
|
-
try {
|
|
100
|
-
const body = await c.req.json();
|
|
101
|
-
const result = await dispatcher.handleGraphQL(body, { request: c.req.raw });
|
|
102
|
-
return c.json(result);
|
|
103
|
-
} catch (err: any) {
|
|
104
|
-
return c.json({ success: false, error: { message: err.message, code: err.statusCode || 500 } }, err.statusCode || 500);
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
// --- 3. Metadata Endpoints ---
|
|
109
|
-
app.all(`${prefix}/meta*`, async (c) => {
|
|
110
|
-
try {
|
|
111
|
-
const path = c.req.path.substring(c.req.path.indexOf('/meta') + 5);
|
|
112
|
-
const method = c.req.method;
|
|
113
|
-
let body = undefined;
|
|
114
|
-
|
|
115
|
-
if (method === 'PUT' || method === 'POST') {
|
|
116
|
-
// Attempt to parse JSON body
|
|
117
|
-
try {
|
|
118
|
-
body = await c.req.json();
|
|
119
|
-
} catch (e) {
|
|
120
|
-
// Ignore parse errors, body remains undefined or empty
|
|
121
|
-
body = {};
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
const query = c.req.query();
|
|
125
|
-
|
|
126
|
-
const result = await dispatcher.handleMetadata(path, { request: c.req.raw }, method, body, query);
|
|
127
|
-
return normalizeResponse(c, result);
|
|
128
|
-
} catch (err: any) {
|
|
129
|
-
return c.json({ success: false, error: { message: err.message, code: err.statusCode || 500 } }, err.statusCode || 500);
|
|
130
|
-
}
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
// --- 4. Data Endpoints ---
|
|
134
|
-
app.all(`${prefix}/data*`, async (c) => {
|
|
135
|
-
try {
|
|
136
|
-
const path = c.req.path.substring(c.req.path.indexOf('/data') + 5);
|
|
137
|
-
const method = c.req.method;
|
|
138
|
-
|
|
139
|
-
let body = {};
|
|
140
|
-
if (method === 'POST' || method === 'PATCH') {
|
|
141
|
-
body = await c.req.json().catch(() => ({}));
|
|
142
|
-
}
|
|
143
|
-
const query = c.req.query();
|
|
144
|
-
|
|
145
|
-
const result = await dispatcher.handleData(path, method, body, query, { request: c.req.raw });
|
|
146
|
-
return normalizeResponse(c, result);
|
|
147
|
-
} catch (err: any) {
|
|
148
|
-
return c.json({ success: false, error: { message: err.message, code: err.statusCode || 500 } }, err.statusCode || 500);
|
|
149
|
-
}
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
// --- 5. Analytics Endpoints ---
|
|
153
|
-
app.all(`${prefix}/analytics*`, async (c) => {
|
|
154
|
-
try {
|
|
155
|
-
const path = c.req.path.substring(c.req.path.indexOf('/analytics') + 10);
|
|
156
|
-
const method = c.req.method;
|
|
157
|
-
|
|
158
|
-
let body = {};
|
|
159
|
-
if (method === 'POST') {
|
|
160
|
-
body = await c.req.json().catch(() => ({}));
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
const result = await dispatcher.handleAnalytics(path, method, body, { request: c.req.raw });
|
|
164
|
-
return normalizeResponse(c, result);
|
|
165
|
-
} catch (err: any) {
|
|
166
|
-
return c.json({ success: false, error: { message: err.message, code: err.statusCode || 500 } }, err.statusCode || 500);
|
|
167
|
-
}
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
// --- 7. Automation Endpoints ---
|
|
171
|
-
app.all(`${prefix}/automation*`, async (c) => {
|
|
172
|
-
try {
|
|
173
|
-
const path = c.req.path.substring(c.req.path.indexOf('/automation') + 11);
|
|
174
|
-
const method = c.req.method;
|
|
175
|
-
|
|
176
|
-
let body = {};
|
|
177
|
-
if (method === 'POST') {
|
|
178
|
-
body = await c.req.json().catch(() => ({}));
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const result = await dispatcher.handleAutomation(path, method, body, { request: c.req.raw });
|
|
182
|
-
return normalizeResponse(c, result);
|
|
183
|
-
} catch (err: any) {
|
|
184
|
-
return c.json({ success: false, error: { message: err.message, code: err.statusCode || 500 } }, err.statusCode || 500);
|
|
185
|
-
}
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
// --- 8. Storage Endpoints ---
|
|
189
|
-
app.all(`${prefix}/storage*`, async (c) => {
|
|
190
|
-
try {
|
|
191
|
-
const path = c.req.path.substring(c.req.path.indexOf('/storage') + 8);
|
|
192
|
-
const method = c.req.method;
|
|
193
|
-
|
|
194
|
-
let file: any = undefined;
|
|
195
|
-
if (method === 'POST' && path.includes('upload')) {
|
|
196
|
-
const body = await c.req.parseBody();
|
|
197
|
-
file = body['file'];
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
const result = await dispatcher.handleStorage(path, method, file, { request: c.req.raw });
|
|
201
|
-
return normalizeResponse(c, result);
|
|
202
|
-
} catch (err: any) {
|
|
203
|
-
return c.json({ success: false, error: { message: err.message, code: err.statusCode || 500 } }, err.statusCode || 500);
|
|
204
|
-
}
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
// --- 9. Package Management Endpoints ---
|
|
208
|
-
app.all(`${prefix}/packages*`, async (c) => {
|
|
209
|
-
try {
|
|
210
|
-
const packagesIndex = c.req.path.indexOf('/packages');
|
|
211
|
-
const path = c.req.path.substring(packagesIndex + 9); // length of '/packages'
|
|
212
|
-
const method = c.req.method;
|
|
213
|
-
|
|
214
|
-
let body = {};
|
|
215
|
-
if (method === 'POST' || method === 'PATCH' || method === 'PUT') {
|
|
216
|
-
body = await c.req.json().catch(() => ({}));
|
|
217
|
-
}
|
|
218
|
-
const query = c.req.query();
|
|
219
|
-
|
|
220
|
-
const result = await dispatcher.handlePackages(path, method, body, query, { request: c.req.raw });
|
|
221
|
-
return normalizeResponse(c, result);
|
|
222
|
-
} catch (err: any) {
|
|
223
|
-
return c.json({ success: false, error: { message: err.message, code: err.statusCode || 500 } }, err.statusCode || 500);
|
|
224
|
-
}
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
return app;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
10
|
/**
|
|
231
11
|
* Middleware mode for existing Hono apps
|
|
232
12
|
*/
|