@objectstack/hono 4.0.4 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +83 -8
- package/dist/index.d.mts +26 -1
- package/dist/index.d.ts +26 -1
- package/dist/index.js +36 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +39 -5
- package/dist/index.mjs.map +1 -1
- package/package.json +35 -6
- package/.turbo/turbo-build.log +0 -22
- package/CHANGELOG.md +0 -371
- package/src/__mocks__/runtime.ts +0 -16
- package/src/hono.test.ts +0 -950
- package/src/index.ts +0 -283
- package/tsconfig.json +0 -26
- package/vitest.config.ts +0 -16
package/README.md
CHANGED
|
@@ -1,19 +1,94 @@
|
|
|
1
1
|
# @objectstack/hono
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> Hono adapter for ObjectStack — edge-compatible REST API server for Cloudflare Workers, Deno, Bun, Vercel Edge, and Node.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
-
|
|
7
|
-
- Edge Compatible (Cloudflare Workers, Deno, Bun)
|
|
8
|
-
- Built-in CORS
|
|
5
|
+
[](https://www.npmjs.com/package/@objectstack/hono)
|
|
6
|
+
[](https://opensource.org/licenses/Apache-2.0)
|
|
9
7
|
|
|
10
|
-
##
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
Creates a ready-to-serve Hono app that routes all ObjectStack traffic through `HttpDispatcher`. Built-in CORS with wildcard origin pattern support (`https://*.example.com`, `http://localhost:*`), compatible with better-auth bearer token rotation (automatically exposes `set-auth-token`).
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pnpm add @objectstack/hono hono
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Quick Start
|
|
11
19
|
|
|
12
20
|
```typescript
|
|
13
21
|
import { createHonoApp } from '@objectstack/hono';
|
|
14
22
|
import { kernel } from './my-kernel';
|
|
15
23
|
|
|
16
|
-
const app = createHonoApp({
|
|
24
|
+
const app = createHonoApp({
|
|
25
|
+
kernel,
|
|
26
|
+
prefix: '/api',
|
|
27
|
+
cors: {
|
|
28
|
+
origin: ['https://app.example.com', 'https://*.example.com'],
|
|
29
|
+
credentials: true,
|
|
30
|
+
},
|
|
31
|
+
});
|
|
17
32
|
|
|
18
|
-
export default app;
|
|
33
|
+
export default app; // Cloudflare Workers / Bun / Deno / Vercel Edge
|
|
19
34
|
```
|
|
35
|
+
|
|
36
|
+
### Embed into an existing Hono app
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
import { Hono } from 'hono';
|
|
40
|
+
import { objectStackMiddleware } from '@objectstack/hono';
|
|
41
|
+
|
|
42
|
+
const app = new Hono();
|
|
43
|
+
app.use('/api/*', objectStackMiddleware(kernel));
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Key Exports
|
|
47
|
+
|
|
48
|
+
| Export | Kind | Description |
|
|
49
|
+
|:---|:---|:---|
|
|
50
|
+
| `createHonoApp(options)` | function | New `Hono` instance with dispatcher + CORS installed. |
|
|
51
|
+
| `objectStackMiddleware(kernel)` | function | Reusable middleware for existing Hono apps. |
|
|
52
|
+
| `ObjectStackHonoOptions` | interface | Root options with `kernel`, `prefix`, `cors`. |
|
|
53
|
+
| `ObjectStackHonoCorsOptions` | interface | CORS configuration. |
|
|
54
|
+
|
|
55
|
+
## Configuration
|
|
56
|
+
|
|
57
|
+
| Option | Type | Default | Notes |
|
|
58
|
+
|:---|:---|:---|:---|
|
|
59
|
+
| `kernel` | `ObjectKernel` | — | Bootstrapped kernel. |
|
|
60
|
+
| `prefix` | `string` | `'/api'` | Base path. |
|
|
61
|
+
| `cors` | `ObjectStackHonoCorsOptions \| false` | Enabled | Set to `false` to disable. |
|
|
62
|
+
| `cors.origin` | `string \| string[]` | env `CORS_ORIGIN` or `'*'` | Supports wildcard patterns. |
|
|
63
|
+
| `cors.credentials` | `boolean` | `false` | Required for cookie sessions. |
|
|
64
|
+
| `cors.exposeHeaders` | `string[]` | `['set-auth-token']` merged | Always includes `set-auth-token` for better-auth. |
|
|
65
|
+
|
|
66
|
+
## Edge runtime notes
|
|
67
|
+
|
|
68
|
+
- Hono adapter is the **preferred** adapter for Cloudflare Workers, Deno Deploy, Bun, and Vercel Edge.
|
|
69
|
+
- Drivers differ by runtime: use [`@objectstack/driver-turso`](../../plugins/driver-turso) on edge; [`@objectstack/driver-sql`](../../plugins/driver-sql) on Node.
|
|
70
|
+
- Persist no long-lived state in module scope beyond the `kernel` instance.
|
|
71
|
+
|
|
72
|
+
## When to use
|
|
73
|
+
|
|
74
|
+
- ✅ Edge, serverless, and multi-runtime deployments.
|
|
75
|
+
- ✅ Projects wanting built-in CORS with wildcard patterns.
|
|
76
|
+
|
|
77
|
+
## When not to use
|
|
78
|
+
|
|
79
|
+
- ❌ Existing Express app — use [`@objectstack/express`](../express).
|
|
80
|
+
- ❌ NestJS enterprise stacks — use [`@objectstack/nestjs`](../nestjs).
|
|
81
|
+
|
|
82
|
+
## Related Packages
|
|
83
|
+
|
|
84
|
+
- [`@objectstack/plugin-hono-server`](../../plugins/plugin-hono-server) — optional plugin that hosts a Hono server inside the kernel.
|
|
85
|
+
- [`@objectstack/runtime`](../../runtime), [`@objectstack/rest`](../../rest).
|
|
86
|
+
|
|
87
|
+
## Links
|
|
88
|
+
|
|
89
|
+
- 📖 Docs: <https://objectstack.ai/docs>
|
|
90
|
+
- 📚 API Reference: <https://objectstack.ai/docs/references>
|
|
91
|
+
|
|
92
|
+
## License
|
|
93
|
+
|
|
94
|
+
Apache-2.0 © ObjectStack
|
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import { Hono } from 'hono';
|
|
2
2
|
import { ObjectKernel } from '@objectstack/runtime';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Minimal structural interface matching KernelManager from @objectstack/service-cloud.
|
|
6
|
+
* Declared locally to avoid a circular build dependency.
|
|
7
|
+
*/
|
|
8
|
+
type KernelManager = any;
|
|
9
|
+
/**
|
|
10
|
+
* Opaque reference to an EnvironmentDriverRegistry from @objectstack/service-cloud.
|
|
11
|
+
* Declared locally to avoid a circular build dependency. Pass an instance
|
|
12
|
+
* of DefaultEnvironmentDriverRegistry from @objectstack/service-cloud at runtime.
|
|
13
|
+
*/
|
|
14
|
+
type EnvironmentDriverRegistry = any;
|
|
4
15
|
interface ObjectStackHonoCorsOptions {
|
|
5
16
|
/** Enable or disable CORS. Defaults to true. */
|
|
6
17
|
enabled?: boolean;
|
|
@@ -22,6 +33,20 @@ interface ObjectStackHonoOptions {
|
|
|
22
33
|
prefix?: string;
|
|
23
34
|
/** CORS configuration. Set to `false` to disable entirely. */
|
|
24
35
|
cors?: ObjectStackHonoCorsOptions | false;
|
|
36
|
+
/**
|
|
37
|
+
* Optional {@link KernelManager}. When provided, the dispatcher will route
|
|
38
|
+
* per-project requests to a project-scoped kernel resolved via
|
|
39
|
+
* `kernelManager.getOrCreate(projectId)`. When absent (self-hosted mode),
|
|
40
|
+
* all requests use the single `kernel` passed above.
|
|
41
|
+
*/
|
|
42
|
+
kernelManager?: KernelManager;
|
|
43
|
+
/**
|
|
44
|
+
* Optional {@link EnvironmentDriverRegistry}. When provided, the dispatcher
|
|
45
|
+
* resolves incoming requests to a project via hostname / `X-Project-Id`
|
|
46
|
+
* header / session before invoking the KernelManager. Required for
|
|
47
|
+
* host-based routing in cloud / multi-project mode.
|
|
48
|
+
*/
|
|
49
|
+
envRegistry?: EnvironmentDriverRegistry;
|
|
25
50
|
}
|
|
26
51
|
/**
|
|
27
52
|
* Middleware mode for existing Hono apps
|
|
@@ -48,4 +73,4 @@ declare function objectStackMiddleware(kernel: ObjectKernel): (c: any, next: any
|
|
|
48
73
|
*/
|
|
49
74
|
declare function createHonoApp(options: ObjectStackHonoOptions): Hono;
|
|
50
75
|
|
|
51
|
-
export { type ObjectStackHonoCorsOptions, type ObjectStackHonoOptions, createHonoApp, objectStackMiddleware };
|
|
76
|
+
export { type EnvironmentDriverRegistry, type KernelManager, type ObjectStackHonoCorsOptions, type ObjectStackHonoOptions, createHonoApp, objectStackMiddleware };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import { Hono } from 'hono';
|
|
2
2
|
import { ObjectKernel } from '@objectstack/runtime';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Minimal structural interface matching KernelManager from @objectstack/service-cloud.
|
|
6
|
+
* Declared locally to avoid a circular build dependency.
|
|
7
|
+
*/
|
|
8
|
+
type KernelManager = any;
|
|
9
|
+
/**
|
|
10
|
+
* Opaque reference to an EnvironmentDriverRegistry from @objectstack/service-cloud.
|
|
11
|
+
* Declared locally to avoid a circular build dependency. Pass an instance
|
|
12
|
+
* of DefaultEnvironmentDriverRegistry from @objectstack/service-cloud at runtime.
|
|
13
|
+
*/
|
|
14
|
+
type EnvironmentDriverRegistry = any;
|
|
4
15
|
interface ObjectStackHonoCorsOptions {
|
|
5
16
|
/** Enable or disable CORS. Defaults to true. */
|
|
6
17
|
enabled?: boolean;
|
|
@@ -22,6 +33,20 @@ interface ObjectStackHonoOptions {
|
|
|
22
33
|
prefix?: string;
|
|
23
34
|
/** CORS configuration. Set to `false` to disable entirely. */
|
|
24
35
|
cors?: ObjectStackHonoCorsOptions | false;
|
|
36
|
+
/**
|
|
37
|
+
* Optional {@link KernelManager}. When provided, the dispatcher will route
|
|
38
|
+
* per-project requests to a project-scoped kernel resolved via
|
|
39
|
+
* `kernelManager.getOrCreate(projectId)`. When absent (self-hosted mode),
|
|
40
|
+
* all requests use the single `kernel` passed above.
|
|
41
|
+
*/
|
|
42
|
+
kernelManager?: KernelManager;
|
|
43
|
+
/**
|
|
44
|
+
* Optional {@link EnvironmentDriverRegistry}. When provided, the dispatcher
|
|
45
|
+
* resolves incoming requests to a project via hostname / `X-Project-Id`
|
|
46
|
+
* header / session before invoking the KernelManager. Required for
|
|
47
|
+
* host-based routing in cloud / multi-project mode.
|
|
48
|
+
*/
|
|
49
|
+
envRegistry?: EnvironmentDriverRegistry;
|
|
25
50
|
}
|
|
26
51
|
/**
|
|
27
52
|
* Middleware mode for existing Hono apps
|
|
@@ -48,4 +73,4 @@ declare function objectStackMiddleware(kernel: ObjectKernel): (c: any, next: any
|
|
|
48
73
|
*/
|
|
49
74
|
declare function createHonoApp(options: ObjectStackHonoOptions): Hono;
|
|
50
75
|
|
|
51
|
-
export { type ObjectStackHonoCorsOptions, type ObjectStackHonoOptions, createHonoApp, objectStackMiddleware };
|
|
76
|
+
export { type EnvironmentDriverRegistry, type KernelManager, type ObjectStackHonoCorsOptions, type ObjectStackHonoOptions, createHonoApp, objectStackMiddleware };
|
package/dist/index.js
CHANGED
|
@@ -27,6 +27,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
27
27
|
var import_hono = require("hono");
|
|
28
28
|
var import_cors = require("hono/cors");
|
|
29
29
|
var import_runtime = require("@objectstack/runtime");
|
|
30
|
+
var import_plugin_hono_server = require("@objectstack/plugin-hono-server");
|
|
30
31
|
function objectStackMiddleware(kernel) {
|
|
31
32
|
return async (c, next) => {
|
|
32
33
|
c.set("objectStack", kernel);
|
|
@@ -36,7 +37,11 @@ function objectStackMiddleware(kernel) {
|
|
|
36
37
|
function createHonoApp(options) {
|
|
37
38
|
const app = new import_hono.Hono();
|
|
38
39
|
const prefix = options.prefix || "/api";
|
|
39
|
-
const dispatcher = new import_runtime.HttpDispatcher(
|
|
40
|
+
const dispatcher = new import_runtime.HttpDispatcher(
|
|
41
|
+
options.kernel,
|
|
42
|
+
options.envRegistry,
|
|
43
|
+
options.kernelManager ? { kernelManager: options.kernelManager } : void 0
|
|
44
|
+
);
|
|
40
45
|
const corsDisabledByEnv = process.env.CORS_ENABLED === "false";
|
|
41
46
|
if (options.cors !== false && !corsDisabledByEnv) {
|
|
42
47
|
const corsOpts = typeof options.cors === "object" ? options.cors : {};
|
|
@@ -54,16 +59,23 @@ function createHonoApp(options) {
|
|
|
54
59
|
const credentials = corsOpts.credentials ?? process.env.CORS_CREDENTIALS !== "false";
|
|
55
60
|
const maxAge = corsOpts.maxAge ?? (process.env.CORS_MAX_AGE ? parseInt(process.env.CORS_MAX_AGE, 10) : 86400);
|
|
56
61
|
let origin;
|
|
57
|
-
if (
|
|
62
|
+
if (configuredOrigin === "*" && credentials) {
|
|
58
63
|
origin = (requestOrigin) => requestOrigin || "*";
|
|
64
|
+
} else if ((0, import_plugin_hono_server.hasWildcardPattern)(configuredOrigin)) {
|
|
65
|
+
origin = (0, import_plugin_hono_server.createOriginMatcher)(configuredOrigin);
|
|
59
66
|
} else {
|
|
60
67
|
origin = configuredOrigin;
|
|
61
68
|
}
|
|
69
|
+
const defaultExposeHeaders = ["set-auth-token"];
|
|
70
|
+
const exposeHeaders = Array.from(/* @__PURE__ */ new Set([
|
|
71
|
+
...defaultExposeHeaders,
|
|
72
|
+
...corsOpts.exposeHeaders ?? []
|
|
73
|
+
]));
|
|
62
74
|
app.use("*", (0, import_cors.cors)({
|
|
63
75
|
origin,
|
|
64
76
|
allowMethods: corsOpts.methods || ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"],
|
|
65
|
-
allowHeaders: corsOpts.allowHeaders || ["Content-Type", "Authorization", "X-Requested-With"],
|
|
66
|
-
exposeHeaders
|
|
77
|
+
allowHeaders: corsOpts.allowHeaders || ["Content-Type", "Authorization", "X-Requested-With", "X-Tenant-ID", "X-Project-Id"],
|
|
78
|
+
exposeHeaders,
|
|
67
79
|
credentials,
|
|
68
80
|
maxAge
|
|
69
81
|
}));
|
|
@@ -144,6 +156,26 @@ function createHonoApp(options) {
|
|
|
144
156
|
} catch {
|
|
145
157
|
authService = null;
|
|
146
158
|
}
|
|
159
|
+
if (path === "config" && method === "GET" && authService) {
|
|
160
|
+
try {
|
|
161
|
+
const config = authService.getPublicConfig?.();
|
|
162
|
+
if (config) {
|
|
163
|
+
return c.json({
|
|
164
|
+
success: true,
|
|
165
|
+
data: config
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
} catch (error) {
|
|
169
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
170
|
+
return c.json({
|
|
171
|
+
success: false,
|
|
172
|
+
error: {
|
|
173
|
+
code: "auth_config_error",
|
|
174
|
+
message: err.message
|
|
175
|
+
}
|
|
176
|
+
}, 500);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
147
179
|
if (authService && typeof authService.handleRequest === "function") {
|
|
148
180
|
const response = await authService.handleRequest(c.req.raw);
|
|
149
181
|
return new Response(response.body, {
|
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 ObjectStackHonoCorsOptions {\n /** Enable or disable CORS. Defaults to true. */\n enabled?: boolean;\n /** Allowed origins. Defaults to env `CORS_ORIGIN` or '*'. Comma-separated string or array. */\n origin?: string | string[];\n /** Allowed methods. */\n methods?: string[];\n /** Allow credentials (cookies, authorization headers). */\n credentials?: boolean;\n /** Preflight cache max-age in seconds. */\n maxAge?: number;\n /** Allowed headers. */\n allowHeaders?: string[];\n /** Exposed headers. */\n exposeHeaders?: string[];\n}\n\nexport interface ObjectStackHonoOptions {\n kernel: ObjectKernel;\n prefix?: string;\n /** CORS configuration. Set to `false` to disable entirely. */\n cors?: ObjectStackHonoCorsOptions | false;\n}\n\n/**\n * Auth service interface with handleRequest method\n */\ninterface AuthService {\n handleRequest(request: Request): Promise<Response>;\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\n/**\n * Creates a full-featured Hono app with all ObjectStack route dispatchers.\n *\n * Only routes that need framework-specific handling (auth service, storage\n * formData, GraphQL raw result, discovery wrapper) are registered explicitly.\n * All other routes (meta, data, packages, analytics, automation, i18n, ui,\n * openapi, custom endpoints, and any future routes) are handled by a\n * catch-all that delegates to `HttpDispatcher.dispatch()`.\n *\n * This means new routes added to `HttpDispatcher` automatically work in\n * every adapter without any adapter-side code changes.\n *\n * @example\n * ```ts\n * import { createHonoApp } from '@objectstack/hono';\n * const app = createHonoApp({ kernel });\n * export default app;\n * ```\n */\nexport function createHonoApp(options: ObjectStackHonoOptions): Hono {\n const app = new Hono();\n const prefix = options.prefix || '/api';\n const dispatcher = new HttpDispatcher(options.kernel);\n\n // ─── CORS Middleware ──────────────────────────────────────────────────────\n // Enabled by default. Controlled via options.cors or environment variables:\n // CORS_ENABLED – \"false\" to disable (default: true)\n // CORS_ORIGIN – comma-separated origins or \"*\" (default: \"*\")\n // CORS_CREDENTIALS – \"false\" to disallow credentials (default: true)\n // CORS_MAX_AGE – preflight cache seconds (default: 86400)\n const corsDisabledByEnv = process.env.CORS_ENABLED === 'false';\n if (options.cors !== false && !corsDisabledByEnv) {\n const corsOpts = typeof options.cors === 'object' ? options.cors : {};\n const enabled = corsOpts.enabled ?? true;\n\n if (enabled) {\n // Resolve origins: options > env > default '*'\n let configuredOrigin: string | string[];\n if (corsOpts.origin) {\n configuredOrigin = corsOpts.origin;\n } else if (process.env.CORS_ORIGIN) {\n const envOrigin = process.env.CORS_ORIGIN.trim();\n configuredOrigin = envOrigin.includes(',') ? envOrigin.split(',').map(s => s.trim()) : envOrigin;\n } else {\n configuredOrigin = '*';\n }\n\n const credentials = corsOpts.credentials ?? (process.env.CORS_CREDENTIALS !== 'false');\n const maxAge = corsOpts.maxAge ?? (process.env.CORS_MAX_AGE ? parseInt(process.env.CORS_MAX_AGE, 10) : 86400);\n\n // When credentials is true, browsers reject wildcard '*' for Access-Control-Allow-Origin.\n // Use a function to reflect the request's Origin header instead.\n let origin: string | string[] | ((origin: string) => string | undefined | null);\n if (credentials && configuredOrigin === '*') {\n origin = (requestOrigin: string) => requestOrigin || '*';\n } else {\n origin = configuredOrigin;\n }\n\n app.use('*', cors({\n origin: origin as any,\n allowMethods: corsOpts.methods || ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'],\n allowHeaders: corsOpts.allowHeaders || ['Content-Type', 'Authorization', 'X-Requested-With'],\n exposeHeaders: corsOpts.exposeHeaders || [],\n credentials,\n maxAge,\n }));\n }\n }\n\n const errorJson = (c: any, message: string, code: number = 500) => {\n return c.json({ success: false, error: { message, code } }, code);\n };\n\n const toResponse = (c: any, result: HttpDispatcherResult) => {\n if (result.handled) {\n if (result.response) {\n if (result.response.headers) {\n Object.entries(result.response.headers).forEach(([k, v]) => c.header(k, v as string));\n }\n return c.json(result.response.body, result.response.status);\n }\n if (result.result) {\n const res = result.result;\n if (res.type === 'redirect' && res.url) {\n return c.redirect(res.url);\n }\n if (res.type === 'stream' && res.events) {\n // SSE / Vercel Data Stream streaming response\n const headers: Record<string, string> = {\n 'Content-Type': res.contentType || 'text/event-stream',\n 'Cache-Control': 'no-cache',\n 'Connection': 'keep-alive',\n ...(res.headers || {}),\n };\n const stream = new ReadableStream({\n async start(controller) {\n try {\n const encoder = new TextEncoder();\n for await (const event of res.events) {\n const chunk = res.vercelDataStream\n ? (typeof event === 'string' ? event : JSON.stringify(event) + '\\n')\n : `data: ${JSON.stringify(event)}\\n\\n`;\n controller.enqueue(encoder.encode(chunk));\n }\n } catch (err) {\n // Stream error — close gracefully\n } finally {\n controller.close();\n }\n },\n });\n return new Response(stream, { status: 200, headers });\n }\n if (res.type === 'stream' && res.stream) {\n if (res.headers) {\n Object.entries(res.headers).forEach(([k, v]) => c.header(k, v as string));\n }\n return new Response(res.stream, { status: 200 });\n }\n return c.json(res, 200);\n }\n }\n return errorJson(c, 'Not Found', 404);\n };\n\n // ─── Explicit routes (framework-specific handling required) ────────────────\n\n // --- Discovery ---\n app.get(prefix, async (c) => {\n return c.json({ data: await dispatcher.getDiscoveryInfo(prefix) });\n });\n\n app.get(`${prefix}/discovery`, async (c) => {\n return c.json({ data: await dispatcher.getDiscoveryInfo(prefix) });\n });\n\n // --- .well-known ---\n app.get('/.well-known/objectstack', (c) => {\n return c.redirect(prefix);\n });\n\n // --- Auth (needs auth service integration) ---\n app.all(`${prefix}/auth/*`, async (c) => {\n try {\n const path = c.req.path.substring(`${prefix}/auth/`.length);\n const method = c.req.method;\n\n // Try AuthPlugin service first (prefer async to support factory-based services)\n let authService: AuthService | null = null;\n try {\n if (typeof options.kernel.getServiceAsync === 'function') {\n authService = await options.kernel.getServiceAsync<AuthService>('auth');\n } else if (typeof options.kernel.getService === 'function') {\n authService = options.kernel.getService<AuthService>('auth');\n }\n } catch {\n // Service not registered — fall through to dispatcher\n authService = null;\n }\n\n if (authService && typeof authService.handleRequest === 'function') {\n const response = await authService.handleRequest(c.req.raw);\n return new Response(response.body, {\n status: response.status,\n headers: response.headers,\n });\n }\n\n // Fallback to legacy dispatcher\n const body = method === 'GET' || method === 'HEAD'\n ? {}\n : await c.req.json().catch(() => ({}));\n const result = await dispatcher.handleAuth(path, method, body, { request: c.req.raw });\n return toResponse(c, result);\n } catch (err: any) {\n return errorJson(c, err.message || 'Internal Server Error', err.statusCode || 500);\n }\n });\n\n // --- GraphQL (returns raw result, not HttpDispatcherResult) ---\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 errorJson(c, err.message || 'Internal Server Error', err.statusCode || 500);\n }\n });\n\n // --- Storage (needs formData parsing) ---\n app.all(`${prefix}/storage/*`, async (c) => {\n try {\n const subPath = c.req.path.substring(`${prefix}/storage`.length);\n const method = c.req.method;\n\n let file: any = undefined;\n if (method === 'POST' && subPath === '/upload') {\n const formData = await c.req.formData();\n file = formData.get('file');\n }\n\n const result = await dispatcher.handleStorage(subPath, method, file, { request: c.req.raw });\n return toResponse(c, result);\n } catch (err: any) {\n return errorJson(c, err.message || 'Internal Server Error', err.statusCode || 500);\n }\n });\n\n // ─── Catch-all: delegate to dispatcher.dispatch() ─────────────────────────\n // Handles meta, data, packages, analytics, automation, i18n, ui, openapi,\n // custom API endpoints, and any future routes added to HttpDispatcher.\n app.all(`${prefix}/*`, async (c) => {\n try {\n const subPath = c.req.path.substring(prefix.length);\n const method = c.req.method;\n\n let body: any = undefined;\n if (method === 'POST' || method === 'PUT' || method === 'PATCH') {\n body = await c.req.json().catch(() => ({}));\n }\n\n const queryParams: Record<string, any> = {};\n const url = new URL(c.req.url);\n url.searchParams.forEach((val, key) => { queryParams[key] = val; });\n\n const result = await dispatcher.dispatch(method, subPath, body, queryParams, { request: c.req.raw }, prefix);\n return toResponse(c, result);\n } catch (err: any) {\n return errorJson(c, err.message || 'Internal Server Error', err.statusCode || 500);\n }\n });\n\n return app;\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 { Hono } from 'hono';\nimport { cors } from 'hono/cors';\nimport {\n type ObjectKernel,\n HttpDispatcher,\n HttpDispatcherResult,\n} from '@objectstack/runtime';\n\n/**\n * Minimal structural interface matching KernelManager from @objectstack/service-cloud.\n * Declared locally to avoid a circular build dependency.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type KernelManager = any;\n\n/**\n * Opaque reference to an EnvironmentDriverRegistry from @objectstack/service-cloud.\n * Declared locally to avoid a circular build dependency. Pass an instance\n * of DefaultEnvironmentDriverRegistry from @objectstack/service-cloud at runtime.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type EnvironmentDriverRegistry = any;\nimport { createOriginMatcher, hasWildcardPattern } from '@objectstack/plugin-hono-server';\n\nexport interface ObjectStackHonoCorsOptions {\n /** Enable or disable CORS. Defaults to true. */\n enabled?: boolean;\n /** Allowed origins. Defaults to env `CORS_ORIGIN` or '*'. Comma-separated string or array. */\n origin?: string | string[];\n /** Allowed methods. */\n methods?: string[];\n /** Allow credentials (cookies, authorization headers). */\n credentials?: boolean;\n /** Preflight cache max-age in seconds. */\n maxAge?: number;\n /** Allowed headers. */\n allowHeaders?: string[];\n /** Exposed headers. */\n exposeHeaders?: string[];\n}\n\nexport interface ObjectStackHonoOptions {\n kernel: ObjectKernel;\n prefix?: string;\n /** CORS configuration. Set to `false` to disable entirely. */\n cors?: ObjectStackHonoCorsOptions | false;\n /**\n * Optional {@link KernelManager}. When provided, the dispatcher will route\n * per-project requests to a project-scoped kernel resolved via\n * `kernelManager.getOrCreate(projectId)`. When absent (self-hosted mode),\n * all requests use the single `kernel` passed above.\n */\n kernelManager?: KernelManager;\n /**\n * Optional {@link EnvironmentDriverRegistry}. When provided, the dispatcher\n * resolves incoming requests to a project via hostname / `X-Project-Id`\n * header / session before invoking the KernelManager. Required for\n * host-based routing in cloud / multi-project mode.\n */\n envRegistry?: EnvironmentDriverRegistry;\n}\n\n/**\n * Auth service interface with handleRequest method\n */\ninterface AuthService {\n handleRequest(request: Request): Promise<Response>;\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\n/**\n * Creates a full-featured Hono app with all ObjectStack route dispatchers.\n *\n * Only routes that need framework-specific handling (auth service, storage\n * formData, GraphQL raw result, discovery wrapper) are registered explicitly.\n * All other routes (meta, data, packages, analytics, automation, i18n, ui,\n * openapi, custom endpoints, and any future routes) are handled by a\n * catch-all that delegates to `HttpDispatcher.dispatch()`.\n *\n * This means new routes added to `HttpDispatcher` automatically work in\n * every adapter without any adapter-side code changes.\n *\n * @example\n * ```ts\n * import { createHonoApp } from '@objectstack/hono';\n * const app = createHonoApp({ kernel });\n * export default app;\n * ```\n */\nexport function createHonoApp(options: ObjectStackHonoOptions): Hono {\n const app = new Hono();\n const prefix = options.prefix || '/api';\n const dispatcher = new HttpDispatcher(\n options.kernel,\n options.envRegistry,\n options.kernelManager ? { kernelManager: options.kernelManager } : undefined,\n );\n\n // ─── CORS Middleware ──────────────────────────────────────────────────────\n // Enabled by default. Controlled via options.cors or environment variables:\n // CORS_ENABLED – \"false\" to disable (default: true)\n // CORS_ORIGIN – comma-separated origins or \"*\" (default: \"*\")\n // CORS_CREDENTIALS – \"false\" to disallow credentials (default: true)\n // CORS_MAX_AGE – preflight cache seconds (default: 86400)\n const corsDisabledByEnv = process.env.CORS_ENABLED === 'false';\n if (options.cors !== false && !corsDisabledByEnv) {\n const corsOpts = typeof options.cors === 'object' ? options.cors : {};\n const enabled = corsOpts.enabled ?? true;\n\n if (enabled) {\n // Resolve origins: options > env > default '*'\n let configuredOrigin: string | string[];\n if (corsOpts.origin) {\n configuredOrigin = corsOpts.origin;\n } else if (process.env.CORS_ORIGIN) {\n const envOrigin = process.env.CORS_ORIGIN.trim();\n configuredOrigin = envOrigin.includes(',') ? envOrigin.split(',').map(s => s.trim()) : envOrigin;\n } else {\n configuredOrigin = '*';\n }\n\n const credentials = corsOpts.credentials ?? (process.env.CORS_CREDENTIALS !== 'false');\n const maxAge = corsOpts.maxAge ?? (process.env.CORS_MAX_AGE ? parseInt(process.env.CORS_MAX_AGE, 10) : 86400);\n\n // When credentials is true, browsers reject wildcard '*' for Access-Control-Allow-Origin.\n // For wildcard patterns (like \"https://*.example.com\" or \"http://localhost:*\") we must\n // use a matcher function — Hono's cors() middleware does exact-string matching only and\n // treats '*' in patterns as a literal character, so passing wildcard strings straight\n // through would silently drop the Access-Control-Allow-Origin header on every real\n // request (preflight can still succeed via apps/objectos's short-circuit, but the\n // subsequent POST/GET would be blocked by the browser).\n //\n // This mirrors `plugin-hono-server`'s CORS wiring and uses the shared pattern matcher\n // from `@objectstack/plugin-hono-server` so all Hono-based code paths stay in sync.\n let origin: string | string[] | ((origin: string) => string | undefined | null);\n if (configuredOrigin === '*' && credentials) {\n // Credentials mode with '*' — reflect the request origin\n origin = (requestOrigin: string) => requestOrigin || '*';\n } else if (hasWildcardPattern(configuredOrigin)) {\n // Wildcard patterns (e.g., \"https://*.objectui.org\", \"http://localhost:*\")\n origin = createOriginMatcher(configuredOrigin);\n } else {\n // Exact origin(s) — pass through as-is\n origin = configuredOrigin;\n }\n\n // Always include `set-auth-token` in exposed headers so that the\n // better-auth `bearer()` plugin (registered by plugin-auth) can\n // deliver rotated session tokens to cross-origin clients. Without\n // this, browsers strip the header from every response, the client\n // never sees the new token, and cross-origin sessions silently\n // break even when preflight and the actual request both succeed.\n //\n // This mirrors `plugin-hono-server`'s CORS wiring — all three\n // Hono-based CORS sites must stay in lockstep on this default.\n const defaultExposeHeaders = ['set-auth-token'];\n const exposeHeaders = Array.from(new Set([\n ...defaultExposeHeaders,\n ...(corsOpts.exposeHeaders ?? []),\n ]));\n\n app.use('*', cors({\n origin: origin as any,\n allowMethods: corsOpts.methods || ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'],\n allowHeaders: corsOpts.allowHeaders || ['Content-Type', 'Authorization', 'X-Requested-With', 'X-Tenant-ID', 'X-Project-Id'],\n exposeHeaders,\n credentials,\n maxAge,\n }));\n }\n }\n\n const errorJson = (c: any, message: string, code: number = 500) => {\n return c.json({ success: false, error: { message, code } }, code);\n };\n\n const toResponse = (c: any, result: HttpDispatcherResult) => {\n if (result.handled) {\n if (result.response) {\n if (result.response.headers) {\n Object.entries(result.response.headers).forEach(([k, v]) => c.header(k, v as string));\n }\n return c.json(result.response.body, result.response.status);\n }\n if (result.result) {\n const res = result.result;\n if (res.type === 'redirect' && res.url) {\n return c.redirect(res.url);\n }\n if (res.type === 'stream' && res.events) {\n // SSE / Vercel Data Stream streaming response\n const headers: Record<string, string> = {\n 'Content-Type': res.contentType || 'text/event-stream',\n 'Cache-Control': 'no-cache',\n 'Connection': 'keep-alive',\n ...(res.headers || {}),\n };\n const stream = new ReadableStream({\n async start(controller) {\n try {\n const encoder = new TextEncoder();\n for await (const event of res.events) {\n const chunk = res.vercelDataStream\n ? (typeof event === 'string' ? event : JSON.stringify(event) + '\\n')\n : `data: ${JSON.stringify(event)}\\n\\n`;\n controller.enqueue(encoder.encode(chunk));\n }\n } catch (err) {\n // Stream error — close gracefully\n } finally {\n controller.close();\n }\n },\n });\n return new Response(stream, { status: 200, headers });\n }\n if (res.type === 'stream' && res.stream) {\n if (res.headers) {\n Object.entries(res.headers).forEach(([k, v]) => c.header(k, v as string));\n }\n return new Response(res.stream, { status: 200 });\n }\n return c.json(res, 200);\n }\n }\n return errorJson(c, 'Not Found', 404);\n };\n\n // ─── Explicit routes (framework-specific handling required) ────────────────\n\n // --- Discovery ---\n app.get(prefix, async (c) => {\n return c.json({ data: await dispatcher.getDiscoveryInfo(prefix) });\n });\n\n app.get(`${prefix}/discovery`, async (c) => {\n return c.json({ data: await dispatcher.getDiscoveryInfo(prefix) });\n });\n\n // --- .well-known ---\n app.get('/.well-known/objectstack', (c) => {\n return c.redirect(prefix);\n });\n\n // --- Auth (needs auth service integration) ---\n app.all(`${prefix}/auth/*`, async (c) => {\n try {\n const path = c.req.path.substring(`${prefix}/auth/`.length);\n const method = c.req.method;\n\n // Try AuthPlugin service first (prefer async to support factory-based services)\n let authService: AuthService | null = null;\n try {\n if (typeof options.kernel.getServiceAsync === 'function') {\n authService = await options.kernel.getServiceAsync<AuthService>('auth');\n } else if (typeof options.kernel.getService === 'function') {\n authService = options.kernel.getService<AuthService>('auth');\n }\n } catch {\n // Service not registered — fall through to dispatcher\n authService = null;\n }\n\n // Handle /auth/config endpoint specifically (not handled by better-auth)\n if (path === 'config' && method === 'GET' && authService) {\n try {\n const config = (authService as any).getPublicConfig?.();\n if (config) {\n return c.json({\n success: true,\n data: config,\n });\n }\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n return c.json({\n success: false,\n error: {\n code: 'auth_config_error',\n message: err.message,\n },\n }, 500);\n }\n }\n\n if (authService && typeof authService.handleRequest === 'function') {\n const response = await authService.handleRequest(c.req.raw);\n return new Response(response.body, {\n status: response.status,\n headers: response.headers,\n });\n }\n\n // Fallback to legacy dispatcher\n const body = method === 'GET' || method === 'HEAD'\n ? {}\n : await c.req.json().catch(() => ({}));\n const result = await dispatcher.handleAuth(path, method, body, { request: c.req.raw });\n return toResponse(c, result);\n } catch (err: any) {\n return errorJson(c, err.message || 'Internal Server Error', err.statusCode || 500);\n }\n });\n\n // --- GraphQL (returns raw result, not HttpDispatcherResult) ---\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 errorJson(c, err.message || 'Internal Server Error', err.statusCode || 500);\n }\n });\n\n // --- Storage (needs formData parsing) ---\n app.all(`${prefix}/storage/*`, async (c) => {\n try {\n const subPath = c.req.path.substring(`${prefix}/storage`.length);\n const method = c.req.method;\n\n let file: any = undefined;\n if (method === 'POST' && subPath === '/upload') {\n const formData = await c.req.formData();\n file = formData.get('file');\n }\n\n const result = await dispatcher.handleStorage(subPath, method, file, { request: c.req.raw });\n return toResponse(c, result);\n } catch (err: any) {\n return errorJson(c, err.message || 'Internal Server Error', err.statusCode || 500);\n }\n });\n\n // ─── Catch-all: delegate to dispatcher.dispatch() ─────────────────────────\n // Handles meta, data, packages, analytics, automation, i18n, ui, openapi,\n // custom API endpoints, and any future routes added to HttpDispatcher.\n app.all(`${prefix}/*`, async (c) => {\n try {\n const subPath = c.req.path.substring(prefix.length);\n const method = c.req.method;\n\n let body: any = undefined;\n if (method === 'POST' || method === 'PUT' || method === 'PATCH') {\n body = await c.req.json().catch(() => ({}));\n }\n\n const queryParams: Record<string, any> = {};\n const url = new URL(c.req.url);\n url.searchParams.forEach((val, key) => { queryParams[key] = val; });\n\n const result = await dispatcher.dispatch(method, subPath, body, queryParams, { request: c.req.raw }, prefix);\n return toResponse(c, result);\n } catch (err: any) {\n return errorJson(c, err.message || 'Internal Server Error', err.statusCode || 500);\n }\n });\n\n return app;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA,kBAAqB;AACrB,kBAAqB;AACrB,qBAIO;AAgBP,gCAAwD;AAkDjD,SAAS,sBAAsB,QAAsB;AAC1D,SAAO,OAAO,GAAQ,SAAc;AAClC,MAAE,IAAI,eAAe,MAAM;AAC3B,UAAM,KAAK;AAAA,EACb;AACF;AAqBO,SAAS,cAAc,SAAuC;AACnE,QAAM,MAAM,IAAI,iBAAK;AACrB,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,aAAa,IAAI;AAAA,IACrB,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ,gBAAgB,EAAE,eAAe,QAAQ,cAAc,IAAI;AAAA,EACrE;AAQA,QAAM,oBAAoB,QAAQ,IAAI,iBAAiB;AACvD,MAAI,QAAQ,SAAS,SAAS,CAAC,mBAAmB;AAChD,UAAM,WAAW,OAAO,QAAQ,SAAS,WAAW,QAAQ,OAAO,CAAC;AACpE,UAAM,UAAU,SAAS,WAAW;AAEpC,QAAI,SAAS;AAEX,UAAI;AACJ,UAAI,SAAS,QAAQ;AACnB,2BAAmB,SAAS;AAAA,MAC9B,WAAW,QAAQ,IAAI,aAAa;AAClC,cAAM,YAAY,QAAQ,IAAI,YAAY,KAAK;AAC/C,2BAAmB,UAAU,SAAS,GAAG,IAAI,UAAU,MAAM,GAAG,EAAE,IAAI,OAAK,EAAE,KAAK,CAAC,IAAI;AAAA,MACzF,OAAO;AACL,2BAAmB;AAAA,MACrB;AAEA,YAAM,cAAc,SAAS,eAAgB,QAAQ,IAAI,qBAAqB;AAC9E,YAAM,SAAS,SAAS,WAAW,QAAQ,IAAI,eAAe,SAAS,QAAQ,IAAI,cAAc,EAAE,IAAI;AAYvG,UAAI;AACJ,UAAI,qBAAqB,OAAO,aAAa;AAE3C,iBAAS,CAAC,kBAA0B,iBAAiB;AAAA,MACvD,eAAW,8CAAmB,gBAAgB,GAAG;AAE/C,qBAAS,+CAAoB,gBAAgB;AAAA,MAC/C,OAAO;AAEL,iBAAS;AAAA,MACX;AAWA,YAAM,uBAAuB,CAAC,gBAAgB;AAC9C,YAAM,gBAAgB,MAAM,KAAK,oBAAI,IAAI;AAAA,QACvC,GAAG;AAAA,QACH,GAAI,SAAS,iBAAiB,CAAC;AAAA,MACjC,CAAC,CAAC;AAEF,UAAI,IAAI,SAAK,kBAAK;AAAA,QAChB;AAAA,QACA,cAAc,SAAS,WAAW,CAAC,OAAO,QAAQ,OAAO,UAAU,SAAS,QAAQ,SAAS;AAAA,QAC7F,cAAc,SAAS,gBAAgB,CAAC,gBAAgB,iBAAiB,oBAAoB,eAAe,cAAc;AAAA,QAC1H;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC,CAAC;AAAA,IACJ;AAAA,EACF;AAEA,QAAM,YAAY,CAAC,GAAQ,SAAiB,OAAe,QAAQ;AACjE,WAAO,EAAE,KAAK,EAAE,SAAS,OAAO,OAAO,EAAE,SAAS,KAAK,EAAE,GAAG,IAAI;AAAA,EAClE;AAEA,QAAM,aAAa,CAAC,GAAQ,WAAiC;AAC3D,QAAI,OAAO,SAAS;AAClB,UAAI,OAAO,UAAU;AACnB,YAAI,OAAO,SAAS,SAAS;AAC3B,iBAAO,QAAQ,OAAO,SAAS,OAAO,EAAE,QAAQ,CAAC,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,GAAG,CAAW,CAAC;AAAA,QACtF;AACA,eAAO,EAAE,KAAK,OAAO,SAAS,MAAM,OAAO,SAAS,MAAM;AAAA,MAC5D;AACA,UAAI,OAAO,QAAQ;AACjB,cAAM,MAAM,OAAO;AACnB,YAAI,IAAI,SAAS,cAAc,IAAI,KAAK;AACtC,iBAAO,EAAE,SAAS,IAAI,GAAG;AAAA,QAC3B;AACA,YAAI,IAAI,SAAS,YAAY,IAAI,QAAQ;AAEvC,gBAAM,UAAkC;AAAA,YACtC,gBAAgB,IAAI,eAAe;AAAA,YACnC,iBAAiB;AAAA,YACjB,cAAc;AAAA,YACd,GAAI,IAAI,WAAW,CAAC;AAAA,UACtB;AACA,gBAAM,SAAS,IAAI,eAAe;AAAA,YAChC,MAAM,MAAM,YAAY;AACtB,kBAAI;AACF,sBAAM,UAAU,IAAI,YAAY;AAChC,iCAAiB,SAAS,IAAI,QAAQ;AACpC,wBAAM,QAAQ,IAAI,mBACb,OAAO,UAAU,WAAW,QAAQ,KAAK,UAAU,KAAK,IAAI,OAC7D,SAAS,KAAK,UAAU,KAAK,CAAC;AAAA;AAAA;AAClC,6BAAW,QAAQ,QAAQ,OAAO,KAAK,CAAC;AAAA,gBAC1C;AAAA,cACF,SAAS,KAAK;AAAA,cAEd,UAAE;AACA,2BAAW,MAAM;AAAA,cACnB;AAAA,YACF;AAAA,UACF,CAAC;AACD,iBAAO,IAAI,SAAS,QAAQ,EAAE,QAAQ,KAAK,QAAQ,CAAC;AAAA,QACtD;AACA,YAAI,IAAI,SAAS,YAAY,IAAI,QAAQ;AACvC,cAAI,IAAI,SAAS;AACf,mBAAO,QAAQ,IAAI,OAAO,EAAE,QAAQ,CAAC,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,GAAG,CAAW,CAAC;AAAA,UAC1E;AACA,iBAAO,IAAI,SAAS,IAAI,QAAQ,EAAE,QAAQ,IAAI,CAAC;AAAA,QACjD;AACA,eAAO,EAAE,KAAK,KAAK,GAAG;AAAA,MACxB;AAAA,IACF;AACA,WAAO,UAAU,GAAG,aAAa,GAAG;AAAA,EACtC;AAKA,MAAI,IAAI,QAAQ,OAAO,MAAM;AAC3B,WAAO,EAAE,KAAK,EAAE,MAAM,MAAM,WAAW,iBAAiB,MAAM,EAAE,CAAC;AAAA,EACnE,CAAC;AAED,MAAI,IAAI,GAAG,MAAM,cAAc,OAAO,MAAM;AAC1C,WAAO,EAAE,KAAK,EAAE,MAAM,MAAM,WAAW,iBAAiB,MAAM,EAAE,CAAC;AAAA,EACnE,CAAC;AAGD,MAAI,IAAI,4BAA4B,CAAC,MAAM;AACzC,WAAO,EAAE,SAAS,MAAM;AAAA,EAC1B,CAAC;AAGD,MAAI,IAAI,GAAG,MAAM,WAAW,OAAO,MAAM;AACvC,QAAI;AACF,YAAM,OAAO,EAAE,IAAI,KAAK,UAAU,GAAG,MAAM,SAAS,MAAM;AAC1D,YAAM,SAAS,EAAE,IAAI;AAGrB,UAAI,cAAkC;AACtC,UAAI;AACF,YAAI,OAAO,QAAQ,OAAO,oBAAoB,YAAY;AACxD,wBAAc,MAAM,QAAQ,OAAO,gBAA6B,MAAM;AAAA,QACxE,WAAW,OAAO,QAAQ,OAAO,eAAe,YAAY;AAC1D,wBAAc,QAAQ,OAAO,WAAwB,MAAM;AAAA,QAC7D;AAAA,MACF,QAAQ;AAEN,sBAAc;AAAA,MAChB;AAGA,UAAI,SAAS,YAAY,WAAW,SAAS,aAAa;AACxD,YAAI;AACF,gBAAM,SAAU,YAAoB,kBAAkB;AACtD,cAAI,QAAQ;AACV,mBAAO,EAAE,KAAK;AAAA,cACZ,SAAS;AAAA,cACT,MAAM;AAAA,YACR,CAAC;AAAA,UACH;AAAA,QACF,SAAS,OAAO;AACd,gBAAM,MAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AACpE,iBAAO,EAAE,KAAK;AAAA,YACZ,SAAS;AAAA,YACT,OAAO;AAAA,cACL,MAAM;AAAA,cACN,SAAS,IAAI;AAAA,YACf;AAAA,UACF,GAAG,GAAG;AAAA,QACR;AAAA,MACF;AAEA,UAAI,eAAe,OAAO,YAAY,kBAAkB,YAAY;AAClE,cAAM,WAAW,MAAM,YAAY,cAAc,EAAE,IAAI,GAAG;AAC1D,eAAO,IAAI,SAAS,SAAS,MAAM;AAAA,UACjC,QAAQ,SAAS;AAAA,UACjB,SAAS,SAAS;AAAA,QACpB,CAAC;AAAA,MACH;AAGA,YAAM,OAAO,WAAW,SAAS,WAAW,SACxC,CAAC,IACD,MAAM,EAAE,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AACvC,YAAM,SAAS,MAAM,WAAW,WAAW,MAAM,QAAQ,MAAM,EAAE,SAAS,EAAE,IAAI,IAAI,CAAC;AACrF,aAAO,WAAW,GAAG,MAAM;AAAA,IAC7B,SAAS,KAAU;AACjB,aAAO,UAAU,GAAG,IAAI,WAAW,yBAAyB,IAAI,cAAc,GAAG;AAAA,IACnF;AAAA,EACF,CAAC;AAGD,MAAI,KAAK,GAAG,MAAM,YAAY,OAAO,MAAM;AACzC,QAAI;AACF,YAAM,OAAO,MAAM,EAAE,IAAI,KAAK;AAC9B,YAAM,SAAS,MAAM,WAAW,cAAc,MAAM,EAAE,SAAS,EAAE,IAAI,IAAI,CAAC;AAC1E,aAAO,EAAE,KAAK,MAAM;AAAA,IACtB,SAAS,KAAU;AACjB,aAAO,UAAU,GAAG,IAAI,WAAW,yBAAyB,IAAI,cAAc,GAAG;AAAA,IACnF;AAAA,EACF,CAAC;AAGD,MAAI,IAAI,GAAG,MAAM,cAAc,OAAO,MAAM;AAC1C,QAAI;AACF,YAAM,UAAU,EAAE,IAAI,KAAK,UAAU,GAAG,MAAM,WAAW,MAAM;AAC/D,YAAM,SAAS,EAAE,IAAI;AAErB,UAAI,OAAY;AAChB,UAAI,WAAW,UAAU,YAAY,WAAW;AAC9C,cAAM,WAAW,MAAM,EAAE,IAAI,SAAS;AACtC,eAAO,SAAS,IAAI,MAAM;AAAA,MAC5B;AAEA,YAAM,SAAS,MAAM,WAAW,cAAc,SAAS,QAAQ,MAAM,EAAE,SAAS,EAAE,IAAI,IAAI,CAAC;AAC3F,aAAO,WAAW,GAAG,MAAM;AAAA,IAC7B,SAAS,KAAU;AACjB,aAAO,UAAU,GAAG,IAAI,WAAW,yBAAyB,IAAI,cAAc,GAAG;AAAA,IACnF;AAAA,EACF,CAAC;AAKD,MAAI,IAAI,GAAG,MAAM,MAAM,OAAO,MAAM;AAClC,QAAI;AACF,YAAM,UAAU,EAAE,IAAI,KAAK,UAAU,OAAO,MAAM;AAClD,YAAM,SAAS,EAAE,IAAI;AAErB,UAAI,OAAY;AAChB,UAAI,WAAW,UAAU,WAAW,SAAS,WAAW,SAAS;AAC/D,eAAO,MAAM,EAAE,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAAA,MAC5C;AAEA,YAAM,cAAmC,CAAC;AAC1C,YAAM,MAAM,IAAI,IAAI,EAAE,IAAI,GAAG;AAC7B,UAAI,aAAa,QAAQ,CAAC,KAAK,QAAQ;AAAE,oBAAY,GAAG,IAAI;AAAA,MAAK,CAAC;AAElE,YAAM,SAAS,MAAM,WAAW,SAAS,QAAQ,SAAS,MAAM,aAAa,EAAE,SAAS,EAAE,IAAI,IAAI,GAAG,MAAM;AAC3G,aAAO,WAAW,GAAG,MAAM;AAAA,IAC7B,SAAS,KAAU;AACjB,aAAO,UAAU,GAAG,IAAI,WAAW,yBAAyB,IAAI,cAAc,GAAG;AAAA,IACnF;AAAA,EACF,CAAC;AAED,SAAO;AACT;","names":[]}
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import { Hono } from "hono";
|
|
3
3
|
import { cors } from "hono/cors";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
HttpDispatcher
|
|
6
|
+
} from "@objectstack/runtime";
|
|
7
|
+
import { createOriginMatcher, hasWildcardPattern } from "@objectstack/plugin-hono-server";
|
|
5
8
|
function objectStackMiddleware(kernel) {
|
|
6
9
|
return async (c, next) => {
|
|
7
10
|
c.set("objectStack", kernel);
|
|
@@ -11,7 +14,11 @@ function objectStackMiddleware(kernel) {
|
|
|
11
14
|
function createHonoApp(options) {
|
|
12
15
|
const app = new Hono();
|
|
13
16
|
const prefix = options.prefix || "/api";
|
|
14
|
-
const dispatcher = new HttpDispatcher(
|
|
17
|
+
const dispatcher = new HttpDispatcher(
|
|
18
|
+
options.kernel,
|
|
19
|
+
options.envRegistry,
|
|
20
|
+
options.kernelManager ? { kernelManager: options.kernelManager } : void 0
|
|
21
|
+
);
|
|
15
22
|
const corsDisabledByEnv = process.env.CORS_ENABLED === "false";
|
|
16
23
|
if (options.cors !== false && !corsDisabledByEnv) {
|
|
17
24
|
const corsOpts = typeof options.cors === "object" ? options.cors : {};
|
|
@@ -29,16 +36,23 @@ function createHonoApp(options) {
|
|
|
29
36
|
const credentials = corsOpts.credentials ?? process.env.CORS_CREDENTIALS !== "false";
|
|
30
37
|
const maxAge = corsOpts.maxAge ?? (process.env.CORS_MAX_AGE ? parseInt(process.env.CORS_MAX_AGE, 10) : 86400);
|
|
31
38
|
let origin;
|
|
32
|
-
if (
|
|
39
|
+
if (configuredOrigin === "*" && credentials) {
|
|
33
40
|
origin = (requestOrigin) => requestOrigin || "*";
|
|
41
|
+
} else if (hasWildcardPattern(configuredOrigin)) {
|
|
42
|
+
origin = createOriginMatcher(configuredOrigin);
|
|
34
43
|
} else {
|
|
35
44
|
origin = configuredOrigin;
|
|
36
45
|
}
|
|
46
|
+
const defaultExposeHeaders = ["set-auth-token"];
|
|
47
|
+
const exposeHeaders = Array.from(/* @__PURE__ */ new Set([
|
|
48
|
+
...defaultExposeHeaders,
|
|
49
|
+
...corsOpts.exposeHeaders ?? []
|
|
50
|
+
]));
|
|
37
51
|
app.use("*", cors({
|
|
38
52
|
origin,
|
|
39
53
|
allowMethods: corsOpts.methods || ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"],
|
|
40
|
-
allowHeaders: corsOpts.allowHeaders || ["Content-Type", "Authorization", "X-Requested-With"],
|
|
41
|
-
exposeHeaders
|
|
54
|
+
allowHeaders: corsOpts.allowHeaders || ["Content-Type", "Authorization", "X-Requested-With", "X-Tenant-ID", "X-Project-Id"],
|
|
55
|
+
exposeHeaders,
|
|
42
56
|
credentials,
|
|
43
57
|
maxAge
|
|
44
58
|
}));
|
|
@@ -119,6 +133,26 @@ function createHonoApp(options) {
|
|
|
119
133
|
} catch {
|
|
120
134
|
authService = null;
|
|
121
135
|
}
|
|
136
|
+
if (path === "config" && method === "GET" && authService) {
|
|
137
|
+
try {
|
|
138
|
+
const config = authService.getPublicConfig?.();
|
|
139
|
+
if (config) {
|
|
140
|
+
return c.json({
|
|
141
|
+
success: true,
|
|
142
|
+
data: config
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
} catch (error) {
|
|
146
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
147
|
+
return c.json({
|
|
148
|
+
success: false,
|
|
149
|
+
error: {
|
|
150
|
+
code: "auth_config_error",
|
|
151
|
+
message: err.message
|
|
152
|
+
}
|
|
153
|
+
}, 500);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
122
156
|
if (authService && typeof authService.handleRequest === "function") {
|
|
123
157
|
const response = await authService.handleRequest(c.req.raw);
|
|
124
158
|
return new Response(response.body, {
|
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 ObjectStackHonoCorsOptions {\n /** Enable or disable CORS. Defaults to true. */\n enabled?: boolean;\n /** Allowed origins. Defaults to env `CORS_ORIGIN` or '*'. Comma-separated string or array. */\n origin?: string | string[];\n /** Allowed methods. */\n methods?: string[];\n /** Allow credentials (cookies, authorization headers). */\n credentials?: boolean;\n /** Preflight cache max-age in seconds. */\n maxAge?: number;\n /** Allowed headers. */\n allowHeaders?: string[];\n /** Exposed headers. */\n exposeHeaders?: string[];\n}\n\nexport interface ObjectStackHonoOptions {\n kernel: ObjectKernel;\n prefix?: string;\n /** CORS configuration. Set to `false` to disable entirely. */\n cors?: ObjectStackHonoCorsOptions | false;\n}\n\n/**\n * Auth service interface with handleRequest method\n */\ninterface AuthService {\n handleRequest(request: Request): Promise<Response>;\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\n/**\n * Creates a full-featured Hono app with all ObjectStack route dispatchers.\n *\n * Only routes that need framework-specific handling (auth service, storage\n * formData, GraphQL raw result, discovery wrapper) are registered explicitly.\n * All other routes (meta, data, packages, analytics, automation, i18n, ui,\n * openapi, custom endpoints, and any future routes) are handled by a\n * catch-all that delegates to `HttpDispatcher.dispatch()`.\n *\n * This means new routes added to `HttpDispatcher` automatically work in\n * every adapter without any adapter-side code changes.\n *\n * @example\n * ```ts\n * import { createHonoApp } from '@objectstack/hono';\n * const app = createHonoApp({ kernel });\n * export default app;\n * ```\n */\nexport function createHonoApp(options: ObjectStackHonoOptions): Hono {\n const app = new Hono();\n const prefix = options.prefix || '/api';\n const dispatcher = new HttpDispatcher(options.kernel);\n\n // ─── CORS Middleware ──────────────────────────────────────────────────────\n // Enabled by default. Controlled via options.cors or environment variables:\n // CORS_ENABLED – \"false\" to disable (default: true)\n // CORS_ORIGIN – comma-separated origins or \"*\" (default: \"*\")\n // CORS_CREDENTIALS – \"false\" to disallow credentials (default: true)\n // CORS_MAX_AGE – preflight cache seconds (default: 86400)\n const corsDisabledByEnv = process.env.CORS_ENABLED === 'false';\n if (options.cors !== false && !corsDisabledByEnv) {\n const corsOpts = typeof options.cors === 'object' ? options.cors : {};\n const enabled = corsOpts.enabled ?? true;\n\n if (enabled) {\n // Resolve origins: options > env > default '*'\n let configuredOrigin: string | string[];\n if (corsOpts.origin) {\n configuredOrigin = corsOpts.origin;\n } else if (process.env.CORS_ORIGIN) {\n const envOrigin = process.env.CORS_ORIGIN.trim();\n configuredOrigin = envOrigin.includes(',') ? envOrigin.split(',').map(s => s.trim()) : envOrigin;\n } else {\n configuredOrigin = '*';\n }\n\n const credentials = corsOpts.credentials ?? (process.env.CORS_CREDENTIALS !== 'false');\n const maxAge = corsOpts.maxAge ?? (process.env.CORS_MAX_AGE ? parseInt(process.env.CORS_MAX_AGE, 10) : 86400);\n\n // When credentials is true, browsers reject wildcard '*' for Access-Control-Allow-Origin.\n // Use a function to reflect the request's Origin header instead.\n let origin: string | string[] | ((origin: string) => string | undefined | null);\n if (credentials && configuredOrigin === '*') {\n origin = (requestOrigin: string) => requestOrigin || '*';\n } else {\n origin = configuredOrigin;\n }\n\n app.use('*', cors({\n origin: origin as any,\n allowMethods: corsOpts.methods || ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'],\n allowHeaders: corsOpts.allowHeaders || ['Content-Type', 'Authorization', 'X-Requested-With'],\n exposeHeaders: corsOpts.exposeHeaders || [],\n credentials,\n maxAge,\n }));\n }\n }\n\n const errorJson = (c: any, message: string, code: number = 500) => {\n return c.json({ success: false, error: { message, code } }, code);\n };\n\n const toResponse = (c: any, result: HttpDispatcherResult) => {\n if (result.handled) {\n if (result.response) {\n if (result.response.headers) {\n Object.entries(result.response.headers).forEach(([k, v]) => c.header(k, v as string));\n }\n return c.json(result.response.body, result.response.status);\n }\n if (result.result) {\n const res = result.result;\n if (res.type === 'redirect' && res.url) {\n return c.redirect(res.url);\n }\n if (res.type === 'stream' && res.events) {\n // SSE / Vercel Data Stream streaming response\n const headers: Record<string, string> = {\n 'Content-Type': res.contentType || 'text/event-stream',\n 'Cache-Control': 'no-cache',\n 'Connection': 'keep-alive',\n ...(res.headers || {}),\n };\n const stream = new ReadableStream({\n async start(controller) {\n try {\n const encoder = new TextEncoder();\n for await (const event of res.events) {\n const chunk = res.vercelDataStream\n ? (typeof event === 'string' ? event : JSON.stringify(event) + '\\n')\n : `data: ${JSON.stringify(event)}\\n\\n`;\n controller.enqueue(encoder.encode(chunk));\n }\n } catch (err) {\n // Stream error — close gracefully\n } finally {\n controller.close();\n }\n },\n });\n return new Response(stream, { status: 200, headers });\n }\n if (res.type === 'stream' && res.stream) {\n if (res.headers) {\n Object.entries(res.headers).forEach(([k, v]) => c.header(k, v as string));\n }\n return new Response(res.stream, { status: 200 });\n }\n return c.json(res, 200);\n }\n }\n return errorJson(c, 'Not Found', 404);\n };\n\n // ─── Explicit routes (framework-specific handling required) ────────────────\n\n // --- Discovery ---\n app.get(prefix, async (c) => {\n return c.json({ data: await dispatcher.getDiscoveryInfo(prefix) });\n });\n\n app.get(`${prefix}/discovery`, async (c) => {\n return c.json({ data: await dispatcher.getDiscoveryInfo(prefix) });\n });\n\n // --- .well-known ---\n app.get('/.well-known/objectstack', (c) => {\n return c.redirect(prefix);\n });\n\n // --- Auth (needs auth service integration) ---\n app.all(`${prefix}/auth/*`, async (c) => {\n try {\n const path = c.req.path.substring(`${prefix}/auth/`.length);\n const method = c.req.method;\n\n // Try AuthPlugin service first (prefer async to support factory-based services)\n let authService: AuthService | null = null;\n try {\n if (typeof options.kernel.getServiceAsync === 'function') {\n authService = await options.kernel.getServiceAsync<AuthService>('auth');\n } else if (typeof options.kernel.getService === 'function') {\n authService = options.kernel.getService<AuthService>('auth');\n }\n } catch {\n // Service not registered — fall through to dispatcher\n authService = null;\n }\n\n if (authService && typeof authService.handleRequest === 'function') {\n const response = await authService.handleRequest(c.req.raw);\n return new Response(response.body, {\n status: response.status,\n headers: response.headers,\n });\n }\n\n // Fallback to legacy dispatcher\n const body = method === 'GET' || method === 'HEAD'\n ? {}\n : await c.req.json().catch(() => ({}));\n const result = await dispatcher.handleAuth(path, method, body, { request: c.req.raw });\n return toResponse(c, result);\n } catch (err: any) {\n return errorJson(c, err.message || 'Internal Server Error', err.statusCode || 500);\n }\n });\n\n // --- GraphQL (returns raw result, not HttpDispatcherResult) ---\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 errorJson(c, err.message || 'Internal Server Error', err.statusCode || 500);\n }\n });\n\n // --- Storage (needs formData parsing) ---\n app.all(`${prefix}/storage/*`, async (c) => {\n try {\n const subPath = c.req.path.substring(`${prefix}/storage`.length);\n const method = c.req.method;\n\n let file: any = undefined;\n if (method === 'POST' && subPath === '/upload') {\n const formData = await c.req.formData();\n file = formData.get('file');\n }\n\n const result = await dispatcher.handleStorage(subPath, method, file, { request: c.req.raw });\n return toResponse(c, result);\n } catch (err: any) {\n return errorJson(c, err.message || 'Internal Server Error', err.statusCode || 500);\n }\n });\n\n // ─── Catch-all: delegate to dispatcher.dispatch() ─────────────────────────\n // Handles meta, data, packages, analytics, automation, i18n, ui, openapi,\n // custom API endpoints, and any future routes added to HttpDispatcher.\n app.all(`${prefix}/*`, async (c) => {\n try {\n const subPath = c.req.path.substring(prefix.length);\n const method = c.req.method;\n\n let body: any = undefined;\n if (method === 'POST' || method === 'PUT' || method === 'PATCH') {\n body = await c.req.json().catch(() => ({}));\n }\n\n const queryParams: Record<string, any> = {};\n const url = new URL(c.req.url);\n url.searchParams.forEach((val, key) => { queryParams[key] = val; });\n\n const result = await dispatcher.dispatch(method, subPath, body, queryParams, { request: c.req.raw }, prefix);\n return toResponse(c, result);\n } catch (err: any) {\n return errorJson(c, err.message || 'Internal Server Error', err.statusCode || 500);\n }\n });\n\n return app;\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 { Hono } from 'hono';\nimport { cors } from 'hono/cors';\nimport {\n type ObjectKernel,\n HttpDispatcher,\n HttpDispatcherResult,\n} from '@objectstack/runtime';\n\n/**\n * Minimal structural interface matching KernelManager from @objectstack/service-cloud.\n * Declared locally to avoid a circular build dependency.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type KernelManager = any;\n\n/**\n * Opaque reference to an EnvironmentDriverRegistry from @objectstack/service-cloud.\n * Declared locally to avoid a circular build dependency. Pass an instance\n * of DefaultEnvironmentDriverRegistry from @objectstack/service-cloud at runtime.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type EnvironmentDriverRegistry = any;\nimport { createOriginMatcher, hasWildcardPattern } from '@objectstack/plugin-hono-server';\n\nexport interface ObjectStackHonoCorsOptions {\n /** Enable or disable CORS. Defaults to true. */\n enabled?: boolean;\n /** Allowed origins. Defaults to env `CORS_ORIGIN` or '*'. Comma-separated string or array. */\n origin?: string | string[];\n /** Allowed methods. */\n methods?: string[];\n /** Allow credentials (cookies, authorization headers). */\n credentials?: boolean;\n /** Preflight cache max-age in seconds. */\n maxAge?: number;\n /** Allowed headers. */\n allowHeaders?: string[];\n /** Exposed headers. */\n exposeHeaders?: string[];\n}\n\nexport interface ObjectStackHonoOptions {\n kernel: ObjectKernel;\n prefix?: string;\n /** CORS configuration. Set to `false` to disable entirely. */\n cors?: ObjectStackHonoCorsOptions | false;\n /**\n * Optional {@link KernelManager}. When provided, the dispatcher will route\n * per-project requests to a project-scoped kernel resolved via\n * `kernelManager.getOrCreate(projectId)`. When absent (self-hosted mode),\n * all requests use the single `kernel` passed above.\n */\n kernelManager?: KernelManager;\n /**\n * Optional {@link EnvironmentDriverRegistry}. When provided, the dispatcher\n * resolves incoming requests to a project via hostname / `X-Project-Id`\n * header / session before invoking the KernelManager. Required for\n * host-based routing in cloud / multi-project mode.\n */\n envRegistry?: EnvironmentDriverRegistry;\n}\n\n/**\n * Auth service interface with handleRequest method\n */\ninterface AuthService {\n handleRequest(request: Request): Promise<Response>;\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\n/**\n * Creates a full-featured Hono app with all ObjectStack route dispatchers.\n *\n * Only routes that need framework-specific handling (auth service, storage\n * formData, GraphQL raw result, discovery wrapper) are registered explicitly.\n * All other routes (meta, data, packages, analytics, automation, i18n, ui,\n * openapi, custom endpoints, and any future routes) are handled by a\n * catch-all that delegates to `HttpDispatcher.dispatch()`.\n *\n * This means new routes added to `HttpDispatcher` automatically work in\n * every adapter without any adapter-side code changes.\n *\n * @example\n * ```ts\n * import { createHonoApp } from '@objectstack/hono';\n * const app = createHonoApp({ kernel });\n * export default app;\n * ```\n */\nexport function createHonoApp(options: ObjectStackHonoOptions): Hono {\n const app = new Hono();\n const prefix = options.prefix || '/api';\n const dispatcher = new HttpDispatcher(\n options.kernel,\n options.envRegistry,\n options.kernelManager ? { kernelManager: options.kernelManager } : undefined,\n );\n\n // ─── CORS Middleware ──────────────────────────────────────────────────────\n // Enabled by default. Controlled via options.cors or environment variables:\n // CORS_ENABLED – \"false\" to disable (default: true)\n // CORS_ORIGIN – comma-separated origins or \"*\" (default: \"*\")\n // CORS_CREDENTIALS – \"false\" to disallow credentials (default: true)\n // CORS_MAX_AGE – preflight cache seconds (default: 86400)\n const corsDisabledByEnv = process.env.CORS_ENABLED === 'false';\n if (options.cors !== false && !corsDisabledByEnv) {\n const corsOpts = typeof options.cors === 'object' ? options.cors : {};\n const enabled = corsOpts.enabled ?? true;\n\n if (enabled) {\n // Resolve origins: options > env > default '*'\n let configuredOrigin: string | string[];\n if (corsOpts.origin) {\n configuredOrigin = corsOpts.origin;\n } else if (process.env.CORS_ORIGIN) {\n const envOrigin = process.env.CORS_ORIGIN.trim();\n configuredOrigin = envOrigin.includes(',') ? envOrigin.split(',').map(s => s.trim()) : envOrigin;\n } else {\n configuredOrigin = '*';\n }\n\n const credentials = corsOpts.credentials ?? (process.env.CORS_CREDENTIALS !== 'false');\n const maxAge = corsOpts.maxAge ?? (process.env.CORS_MAX_AGE ? parseInt(process.env.CORS_MAX_AGE, 10) : 86400);\n\n // When credentials is true, browsers reject wildcard '*' for Access-Control-Allow-Origin.\n // For wildcard patterns (like \"https://*.example.com\" or \"http://localhost:*\") we must\n // use a matcher function — Hono's cors() middleware does exact-string matching only and\n // treats '*' in patterns as a literal character, so passing wildcard strings straight\n // through would silently drop the Access-Control-Allow-Origin header on every real\n // request (preflight can still succeed via apps/objectos's short-circuit, but the\n // subsequent POST/GET would be blocked by the browser).\n //\n // This mirrors `plugin-hono-server`'s CORS wiring and uses the shared pattern matcher\n // from `@objectstack/plugin-hono-server` so all Hono-based code paths stay in sync.\n let origin: string | string[] | ((origin: string) => string | undefined | null);\n if (configuredOrigin === '*' && credentials) {\n // Credentials mode with '*' — reflect the request origin\n origin = (requestOrigin: string) => requestOrigin || '*';\n } else if (hasWildcardPattern(configuredOrigin)) {\n // Wildcard patterns (e.g., \"https://*.objectui.org\", \"http://localhost:*\")\n origin = createOriginMatcher(configuredOrigin);\n } else {\n // Exact origin(s) — pass through as-is\n origin = configuredOrigin;\n }\n\n // Always include `set-auth-token` in exposed headers so that the\n // better-auth `bearer()` plugin (registered by plugin-auth) can\n // deliver rotated session tokens to cross-origin clients. Without\n // this, browsers strip the header from every response, the client\n // never sees the new token, and cross-origin sessions silently\n // break even when preflight and the actual request both succeed.\n //\n // This mirrors `plugin-hono-server`'s CORS wiring — all three\n // Hono-based CORS sites must stay in lockstep on this default.\n const defaultExposeHeaders = ['set-auth-token'];\n const exposeHeaders = Array.from(new Set([\n ...defaultExposeHeaders,\n ...(corsOpts.exposeHeaders ?? []),\n ]));\n\n app.use('*', cors({\n origin: origin as any,\n allowMethods: corsOpts.methods || ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'],\n allowHeaders: corsOpts.allowHeaders || ['Content-Type', 'Authorization', 'X-Requested-With', 'X-Tenant-ID', 'X-Project-Id'],\n exposeHeaders,\n credentials,\n maxAge,\n }));\n }\n }\n\n const errorJson = (c: any, message: string, code: number = 500) => {\n return c.json({ success: false, error: { message, code } }, code);\n };\n\n const toResponse = (c: any, result: HttpDispatcherResult) => {\n if (result.handled) {\n if (result.response) {\n if (result.response.headers) {\n Object.entries(result.response.headers).forEach(([k, v]) => c.header(k, v as string));\n }\n return c.json(result.response.body, result.response.status);\n }\n if (result.result) {\n const res = result.result;\n if (res.type === 'redirect' && res.url) {\n return c.redirect(res.url);\n }\n if (res.type === 'stream' && res.events) {\n // SSE / Vercel Data Stream streaming response\n const headers: Record<string, string> = {\n 'Content-Type': res.contentType || 'text/event-stream',\n 'Cache-Control': 'no-cache',\n 'Connection': 'keep-alive',\n ...(res.headers || {}),\n };\n const stream = new ReadableStream({\n async start(controller) {\n try {\n const encoder = new TextEncoder();\n for await (const event of res.events) {\n const chunk = res.vercelDataStream\n ? (typeof event === 'string' ? event : JSON.stringify(event) + '\\n')\n : `data: ${JSON.stringify(event)}\\n\\n`;\n controller.enqueue(encoder.encode(chunk));\n }\n } catch (err) {\n // Stream error — close gracefully\n } finally {\n controller.close();\n }\n },\n });\n return new Response(stream, { status: 200, headers });\n }\n if (res.type === 'stream' && res.stream) {\n if (res.headers) {\n Object.entries(res.headers).forEach(([k, v]) => c.header(k, v as string));\n }\n return new Response(res.stream, { status: 200 });\n }\n return c.json(res, 200);\n }\n }\n return errorJson(c, 'Not Found', 404);\n };\n\n // ─── Explicit routes (framework-specific handling required) ────────────────\n\n // --- Discovery ---\n app.get(prefix, async (c) => {\n return c.json({ data: await dispatcher.getDiscoveryInfo(prefix) });\n });\n\n app.get(`${prefix}/discovery`, async (c) => {\n return c.json({ data: await dispatcher.getDiscoveryInfo(prefix) });\n });\n\n // --- .well-known ---\n app.get('/.well-known/objectstack', (c) => {\n return c.redirect(prefix);\n });\n\n // --- Auth (needs auth service integration) ---\n app.all(`${prefix}/auth/*`, async (c) => {\n try {\n const path = c.req.path.substring(`${prefix}/auth/`.length);\n const method = c.req.method;\n\n // Try AuthPlugin service first (prefer async to support factory-based services)\n let authService: AuthService | null = null;\n try {\n if (typeof options.kernel.getServiceAsync === 'function') {\n authService = await options.kernel.getServiceAsync<AuthService>('auth');\n } else if (typeof options.kernel.getService === 'function') {\n authService = options.kernel.getService<AuthService>('auth');\n }\n } catch {\n // Service not registered — fall through to dispatcher\n authService = null;\n }\n\n // Handle /auth/config endpoint specifically (not handled by better-auth)\n if (path === 'config' && method === 'GET' && authService) {\n try {\n const config = (authService as any).getPublicConfig?.();\n if (config) {\n return c.json({\n success: true,\n data: config,\n });\n }\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n return c.json({\n success: false,\n error: {\n code: 'auth_config_error',\n message: err.message,\n },\n }, 500);\n }\n }\n\n if (authService && typeof authService.handleRequest === 'function') {\n const response = await authService.handleRequest(c.req.raw);\n return new Response(response.body, {\n status: response.status,\n headers: response.headers,\n });\n }\n\n // Fallback to legacy dispatcher\n const body = method === 'GET' || method === 'HEAD'\n ? {}\n : await c.req.json().catch(() => ({}));\n const result = await dispatcher.handleAuth(path, method, body, { request: c.req.raw });\n return toResponse(c, result);\n } catch (err: any) {\n return errorJson(c, err.message || 'Internal Server Error', err.statusCode || 500);\n }\n });\n\n // --- GraphQL (returns raw result, not HttpDispatcherResult) ---\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 errorJson(c, err.message || 'Internal Server Error', err.statusCode || 500);\n }\n });\n\n // --- Storage (needs formData parsing) ---\n app.all(`${prefix}/storage/*`, async (c) => {\n try {\n const subPath = c.req.path.substring(`${prefix}/storage`.length);\n const method = c.req.method;\n\n let file: any = undefined;\n if (method === 'POST' && subPath === '/upload') {\n const formData = await c.req.formData();\n file = formData.get('file');\n }\n\n const result = await dispatcher.handleStorage(subPath, method, file, { request: c.req.raw });\n return toResponse(c, result);\n } catch (err: any) {\n return errorJson(c, err.message || 'Internal Server Error', err.statusCode || 500);\n }\n });\n\n // ─── Catch-all: delegate to dispatcher.dispatch() ─────────────────────────\n // Handles meta, data, packages, analytics, automation, i18n, ui, openapi,\n // custom API endpoints, and any future routes added to HttpDispatcher.\n app.all(`${prefix}/*`, async (c) => {\n try {\n const subPath = c.req.path.substring(prefix.length);\n const method = c.req.method;\n\n let body: any = undefined;\n if (method === 'POST' || method === 'PUT' || method === 'PATCH') {\n body = await c.req.json().catch(() => ({}));\n }\n\n const queryParams: Record<string, any> = {};\n const url = new URL(c.req.url);\n url.searchParams.forEach((val, key) => { queryParams[key] = val; });\n\n const result = await dispatcher.dispatch(method, subPath, body, queryParams, { request: c.req.raw }, prefix);\n return toResponse(c, result);\n } catch (err: any) {\n return errorJson(c, err.message || 'Internal Server Error', err.statusCode || 500);\n }\n });\n\n return app;\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@objectstack/hono",
|
|
3
|
-
"version": "4.0
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -11,15 +11,44 @@
|
|
|
11
11
|
"require": "./dist/index.js"
|
|
12
12
|
}
|
|
13
13
|
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@objectstack/plugin-hono-server": "4.1.0"
|
|
16
|
+
},
|
|
14
17
|
"peerDependencies": {
|
|
15
18
|
"hono": "^4.12.8",
|
|
16
|
-
"@objectstack/runtime": "^4.0
|
|
19
|
+
"@objectstack/runtime": "^4.1.0"
|
|
17
20
|
},
|
|
18
21
|
"devDependencies": {
|
|
19
|
-
"hono": "^4.12.
|
|
20
|
-
"typescript": "^6.0.
|
|
21
|
-
"vitest": "^4.1.
|
|
22
|
-
"@objectstack/runtime": "4.0
|
|
22
|
+
"hono": "^4.12.21",
|
|
23
|
+
"typescript": "^6.0.3",
|
|
24
|
+
"vitest": "^4.1.7",
|
|
25
|
+
"@objectstack/runtime": "4.1.0"
|
|
26
|
+
},
|
|
27
|
+
"description": "Hono adapter for ObjectStack — edge-compatible REST API server for Cloudflare Workers, Deno, Bun, and Node.",
|
|
28
|
+
"keywords": [
|
|
29
|
+
"objectstack",
|
|
30
|
+
"hono",
|
|
31
|
+
"adapter",
|
|
32
|
+
"edge",
|
|
33
|
+
"rest"
|
|
34
|
+
],
|
|
35
|
+
"author": "ObjectStack",
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "https://github.com/objectstack-ai/framework.git",
|
|
39
|
+
"directory": "packages/adapters/hono"
|
|
40
|
+
},
|
|
41
|
+
"homepage": "https://objectstack.ai/docs",
|
|
42
|
+
"bugs": "https://github.com/objectstack-ai/framework/issues",
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"access": "public"
|
|
45
|
+
},
|
|
46
|
+
"files": [
|
|
47
|
+
"dist",
|
|
48
|
+
"README.md"
|
|
49
|
+
],
|
|
50
|
+
"engines": {
|
|
51
|
+
"node": ">=18.0.0"
|
|
23
52
|
},
|
|
24
53
|
"scripts": {
|
|
25
54
|
"build": "tsup --config ../../../tsup.config.ts",
|
package/.turbo/turbo-build.log
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
> @objectstack/hono@4.0.4 build /home/runner/work/framework/framework/packages/adapters/hono
|
|
3
|
-
> tsup --config ../../../tsup.config.ts
|
|
4
|
-
|
|
5
|
-
[34mCLI[39m Building entry: src/index.ts
|
|
6
|
-
[34mCLI[39m Using tsconfig: tsconfig.json
|
|
7
|
-
[34mCLI[39m tsup v8.5.1
|
|
8
|
-
[34mCLI[39m Using tsup config: /home/runner/work/framework/framework/tsup.config.ts
|
|
9
|
-
[34mCLI[39m Target: es2020
|
|
10
|
-
[34mCLI[39m Cleaning output folder
|
|
11
|
-
[34mESM[39m Build start
|
|
12
|
-
[34mCJS[39m Build start
|
|
13
|
-
[32mCJS[39m [1mdist/index.js [22m[32m7.85 KB[39m
|
|
14
|
-
[32mCJS[39m [1mdist/index.js.map [22m[32m15.58 KB[39m
|
|
15
|
-
[32mCJS[39m ⚡️ Build success in 54ms
|
|
16
|
-
[32mESM[39m [1mdist/index.mjs [22m[32m6.75 KB[39m
|
|
17
|
-
[32mESM[39m [1mdist/index.mjs.map [22m[32m15.55 KB[39m
|
|
18
|
-
[32mESM[39m ⚡️ Build success in 54ms
|
|
19
|
-
[34mDTS[39m Build start
|
|
20
|
-
[32mDTS[39m ⚡️ Build success in 13070ms
|
|
21
|
-
[32mDTS[39m [1mdist/index.d.mts [22m[32m1.87 KB[39m
|
|
22
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[32m1.87 KB[39m
|