@ontrails/hono 0.2.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/CHANGELOG.md ADDED
@@ -0,0 +1,321 @@
1
+ # @ontrails/hono
2
+
3
+ ## 0.2.0
4
+
5
+ ### Patch Changes
6
+
7
+ - [`664e8bb`](https://github.com/outfitter-dev/trails/commit/664e8bb049e61f1d47cec1dd246606707af5d1a8): Point public installation examples at exact approved 0.2.0 versions and distinguish the prepared source release from pending npm publication.
8
+ - [`b3ddf91`](https://github.com/outfitter-dev/trails/commit/b3ddf918ada8211f44983512e2bbdfbc5b66d722): Prepare the first normal Trails release at `0.2.0` on `latest`, replacing the unpublished 1.0.0 source release. The target advances the original `0.1.0` source minor, which was never published under the current package names. Public packages remain in lockstep. Update consumer installation guidance and provide a temporary manifest bridge for old 1.0 beta sources. Published beta versions remain unchanged; minor 0.x releases may carry documented breaking changes.
9
+
10
+ The `1.0.0` section retained below records an unpublished preparation, not an npm release. Its accumulated changes are included in `0.2.0`; the section remains as source history alongside the published beta entries.
11
+
12
+ ## 1.0.0
13
+
14
+ ### Minor Changes
15
+
16
+ - [`5adb995`](https://github.com/outfitter-dev/trails/commit/5adb99551c2dda6190d46cce7f60bb08d63c99aa): Complete the v1 hard cutover from the authored `blaze` field to
17
+ `implementation` across trail contracts, surface projections, tests, examples,
18
+ and public source-analysis helpers. Existing applications must rename authored
19
+ trail behavior fields and direct trail-object access before upgrading.
20
+
21
+ ### Patch Changes
22
+
23
+ - [`f5c6777`](https://github.com/outfitter-dev/trails/commit/f5c6777c39ea959d2123f17bb683151fe265386c): Move adapter package workspaces from `connectors/*` to `adapters/*` as part of
24
+ the package-boundary taxonomy cutover. Package names and public APIs are
25
+ unchanged.
26
+ - [`e41c382`](https://github.com/outfitter-dev/trails/commit/e41c3829c2d692683b78c730e67fd5b17ac0ff4e): Document beta-channel install guidance in package and adapter README install snippets so consumers use explicit `@beta` (or pinned `1.0.0-beta.N`) tags instead of accidental `latest` resolution during the prerelease line. Adds the policy doc at `docs/releases/beta-channel-policy.md`, prints both `latest` and `beta` dist-tags in `bun run publish:registry-check`, and aligns plugin/skill install snippets.
27
+ - [`ed5926b`](https://github.com/outfitter-dev/trails/commit/ed5926bddebed7be19a902d69be58f2d5e8b4c51): Add missing TSDoc for public adapter and sink boundary types.
28
+ - [`6300f70`](https://github.com/outfitter-dev/trails/commit/6300f709bb6dffc0e6cc82479fe8d0204c52bbba): Refresh source comments and test labels for retired connector terminology as adapter guardrails become strict.
29
+ - [`20d7a5c`](https://github.com/outfitter-dev/trails/commit/20d7a5c8e675fd3ecd8c29441bbd8a99b5c64ed0): Enforce the shared safe error projection policy for public error bodies, diagnostics, serialized payloads, and CLI stderr.
30
+ - [`95bf132`](https://github.com/outfitter-dev/trails/commit/95bf132a15d6bd0f3a9e5fa597697393014f1e21): Wire HTTP permit resolution through the Hono adapter, including request headers for Bearer Authorization handling.
31
+ - [`729f957`](https://github.com/outfitter-dev/trails/commit/729f957a11dfa54474c20262dc2c37866f8b437e): Harden the Hono surface by capping JSON request bodies at 1 MiB by default and
32
+ redacting generic internal errors while preserving server-side diagnostics.
33
+ - [`49c2e7d`](https://github.com/outfitter-dev/trails/commit/49c2e7d5c7c063b9aa6abee1d2932bf3003133cc): Refresh published package README taxonomy to use adapter language instead of retired connector vocabulary.
34
+ - [`88a6a62`](https://github.com/outfitter-dev/trails/commit/88a6a62a9e9e230ca6d368fa78dc3ece6c816204): Complete the v1 classification-first cutover from projection/project vocabulary
35
+ to derive/derived for contract-owned fact production and render/rendered for
36
+ surface presentation. Public type, helper, rule, relation, and report names move
37
+ without compatibility aliases; ordinary repository/project nouns remain
38
+ explicit preserves or structured review inventory.
39
+ - [`b1fbe57`](https://github.com/outfitter-dev/trails/commit/b1fbe574e6f44d1fecb5e3a000270955c0a77b7b): Publish Bun-validated package tarballs through an npm trusted-publishing adapter
40
+ binding, add exact repository metadata for each public workspace package, and
41
+ correct the native Bun release descriptor to its pack-only runtime boundary.
42
+ - [`bc2d327`](https://github.com/outfitter-dev/trails/commit/bc2d3276cf0af2c7217365a28b436ebf3023c09b): Close HTTP package documentation around the shared `@ontrails/http/fetch` kernel, Bun-native `@ontrails/http/bun` subpath, and Hono adapter boundary before versioning.
43
+ - [`20cb72c`](https://github.com/outfitter-dev/trails/commit/20cb72cc91e48a4683f8832e18b0f55855943583): Refactor Hono route handling to delegate Web request parsing, response
44
+ projection, diagnostics, permits, and webhook handling through
45
+ `@ontrails/http/fetch`.
46
+ - [`8105f53`](https://github.com/outfitter-dev/trails/commit/8105f538ee3a0768a21482e6b7385122c2ed90dd): Declare the Hono package as an extracted HTTP adapter and dogfood the shared
47
+ adapter authoring check path against its owner conformance test.
48
+ - [`df9a7d0`](https://github.com/outfitter-dev/trails/commit/df9a7d00fe4d9ebec948b6ebed6dc4525fc8e0dc): Add project-aware public export-map governance for @ontrails workspace docs,
49
+ imports, root barrels, and bin-only package surfaces.
50
+ - [`61497c5`](https://github.com/outfitter-dev/trails/commit/61497c54deaaae2d067af88d0be6db0a5acb5faf): Add v1-minimum public API examples for shipped surface entrypoints.
51
+
52
+ ## 1.0.0-beta.50
53
+
54
+ ## 1.0.0-beta.49
55
+
56
+ ## 1.0.0-beta.48
57
+
58
+ ## 1.0.0-beta.47
59
+
60
+ ## 1.0.0-beta.46
61
+
62
+ ## 1.0.0-beta.45
63
+
64
+ ## 1.0.0-beta.44
65
+
66
+ ### Patch Changes
67
+
68
+ - [`b1fbe57`](https://github.com/outfitter-dev/trails/commit/b1fbe574e6f44d1fecb5e3a000270955c0a77b7b): Publish Bun-validated package tarballs through an npm trusted-publishing adapter
69
+ binding, add exact repository metadata for each public workspace package, and
70
+ correct the native Bun release descriptor to its pack-only runtime boundary.
71
+
72
+ ## 1.0.0-beta.43
73
+
74
+ ### Patch Changes
75
+
76
+ - [`88a6a62`](https://github.com/outfitter-dev/trails/commit/88a6a62a9e9e230ca6d368fa78dc3ece6c816204): Complete the v1 classification-first cutover from projection/project vocabulary
77
+ to derive/derived for contract-owned fact production and render/rendered for
78
+ surface presentation. Public type, helper, rule, relation, and report names move
79
+ without compatibility aliases; ordinary repository/project nouns remain
80
+ explicit preserves or structured review inventory.
81
+
82
+ ## 1.0.0-beta.42
83
+
84
+ ## 1.0.0-beta.41
85
+
86
+ ## 1.0.0-beta.40
87
+
88
+ ### Minor Changes
89
+
90
+ - [`5adb995`](https://github.com/outfitter-dev/trails/commit/5adb99551c2dda6190d46cce7f60bb08d63c99aa): Complete the v1 hard cutover from the authored `blaze` field to
91
+ `implementation` across trail contracts, surface projections, tests, examples,
92
+ and public source-analysis helpers. Existing applications must rename authored
93
+ trail behavior fields and direct trail-object access before upgrading.
94
+
95
+ ## 1.0.0-beta.39
96
+
97
+ ## 1.0.0-beta.38
98
+
99
+ ## 1.0.0-beta.37
100
+
101
+ ## 1.0.0-beta.36
102
+
103
+ ## 1.0.0-beta.35
104
+
105
+ ## 1.0.0-beta.34
106
+
107
+ ## 1.0.0-beta.33
108
+
109
+ ## 1.0.0-beta.32
110
+
111
+ ### Patch Changes
112
+
113
+ - Updated dependencies [3e5c0fc]
114
+ - Updated dependencies [f3c4fef]
115
+ - Updated dependencies [cb0a9d8]
116
+ - Updated dependencies [21c6dda]
117
+ - Updated dependencies [fe72b84]
118
+ - @ontrails/core@1.0.0-beta.32
119
+ - @ontrails/http@1.0.0-beta.32
120
+
121
+ ## 1.0.0-beta.31
122
+
123
+ ### Patch Changes
124
+
125
+ - Updated dependencies [4cd5d4e]
126
+ - Updated dependencies [38907cc]
127
+ - @ontrails/core@1.0.0-beta.31
128
+ - @ontrails/http@1.0.0-beta.31
129
+
130
+ ## 1.0.0-beta.30
131
+
132
+ ### Patch Changes
133
+
134
+ - @ontrails/core@1.0.0-beta.30
135
+ - @ontrails/http@1.0.0-beta.30
136
+
137
+ ## 1.0.0-beta.29
138
+
139
+ ### Patch Changes
140
+
141
+ - @ontrails/core@1.0.0-beta.29
142
+ - @ontrails/http@1.0.0-beta.29
143
+
144
+ ## 1.0.0-beta.28
145
+
146
+ ### Patch Changes
147
+
148
+ - @ontrails/core@1.0.0-beta.28
149
+ - @ontrails/http@1.0.0-beta.28
150
+
151
+ ## 1.0.0-beta.27
152
+
153
+ ### Patch Changes
154
+
155
+ - @ontrails/core@1.0.0-beta.27
156
+ - @ontrails/http@1.0.0-beta.27
157
+
158
+ ## 1.0.0-beta.26
159
+
160
+ ### Patch Changes
161
+
162
+ - Updated dependencies [1307568]
163
+ - Updated dependencies [371d19e]
164
+ - @ontrails/core@1.0.0-beta.26
165
+ - @ontrails/http@1.0.0-beta.26
166
+
167
+ ## 1.0.0-beta.25
168
+
169
+ ### Patch Changes
170
+
171
+ - Updated dependencies [c36aca9]
172
+ - Updated dependencies [3befcf1]
173
+ - Updated dependencies [a4f9cf6]
174
+ - Updated dependencies [9bcf34e]
175
+ - @ontrails/core@1.0.0-beta.25
176
+ - @ontrails/http@1.0.0-beta.25
177
+
178
+ ## 1.0.0-beta.24
179
+
180
+ ### Patch Changes
181
+
182
+ - @ontrails/core@1.0.0-beta.24
183
+ - @ontrails/http@1.0.0-beta.24
184
+
185
+ ## 1.0.0-beta.23
186
+
187
+ ### Patch Changes
188
+
189
+ - Updated dependencies [9c5ecdc]
190
+ - @ontrails/http@1.0.0-beta.23
191
+ - @ontrails/core@1.0.0-beta.23
192
+
193
+ ## 1.0.0-beta.22
194
+
195
+ ### Patch Changes
196
+
197
+ - @ontrails/core@1.0.0-beta.22
198
+ - @ontrails/http@1.0.0-beta.22
199
+
200
+ ## 1.0.0-beta.21
201
+
202
+ ### Patch Changes
203
+
204
+ - Updated dependencies [99523f2]
205
+ - @ontrails/core@1.0.0-beta.21
206
+ - @ontrails/http@1.0.0-beta.21
207
+
208
+ ## 1.0.0-beta.20
209
+
210
+ ### Patch Changes
211
+
212
+ - Updated dependencies [851a2a3]
213
+ - @ontrails/core@1.0.0-beta.20
214
+ - @ontrails/http@1.0.0-beta.20
215
+
216
+ ## 1.0.0-beta.19
217
+
218
+ ### Patch Changes
219
+
220
+ - e41c382: Document beta-channel install guidance in package and adapter README install snippets so consumers use explicit `@beta` (or pinned `1.0.0-beta.N`) tags instead of accidental `latest` resolution during the prerelease line. Adds the policy doc at `docs/releases/beta-channel-policy.md`, prints both `latest` and `beta` dist-tags in `bun run publish:registry-check`, and aligns plugin/skill install snippets.
221
+ - ed5926b: Add missing TSDoc for public adapter and sink boundary types.
222
+ - 8105f53: Declare the Hono package as an extracted HTTP adapter and dogfood the shared
223
+ adapter authoring check path against its owner conformance test.
224
+ - Updated dependencies [e41c382]
225
+ - Updated dependencies [1eb5bdc]
226
+ - Updated dependencies [f8d80b9]
227
+ - Updated dependencies [94a8380]
228
+ - Updated dependencies [94a8380]
229
+ - Updated dependencies [846a597]
230
+ - Updated dependencies [223aaad]
231
+ - Updated dependencies [3125f4d]
232
+ - Updated dependencies [2494dc6]
233
+ - Updated dependencies [2d53717]
234
+ - Updated dependencies [16cb740]
235
+ - Updated dependencies [8894ecb]
236
+ - Updated dependencies [fdf7ec9]
237
+ - Updated dependencies [d76be13]
238
+ - Updated dependencies [84f56a5]
239
+ - Updated dependencies [431b04c]
240
+ - Updated dependencies [5d88104]
241
+ - Updated dependencies [f04a9ef]
242
+ - Updated dependencies [fc00aeb]
243
+ - Updated dependencies [ab1c77c]
244
+ - Updated dependencies [8ca5b85]
245
+ - @ontrails/core@1.0.0-beta.19
246
+ - @ontrails/http@1.0.0-beta.19
247
+
248
+ ## 1.0.0-beta.18
249
+
250
+ ### Patch Changes
251
+
252
+ - bc2d327: Close HTTP package documentation around the shared `@ontrails/http/fetch` kernel, Bun-native `@ontrails/http/bun` subpath, and Hono adapter boundary before versioning.
253
+ - 20cb72c: Refactor Hono route handling to delegate Web request parsing, response
254
+ projection, diagnostics, permits, and webhook handling through
255
+ `@ontrails/http/fetch`.
256
+ - Updated dependencies [c0b2948]
257
+ - Updated dependencies [fc3219c]
258
+ - Updated dependencies [bc2d327]
259
+ - @ontrails/http@1.0.0-beta.18
260
+ - @ontrails/core@1.0.0-beta.18
261
+
262
+ ## 1.0.0-beta.17
263
+
264
+ ### Patch Changes
265
+
266
+ - 61497c5: Add v1-minimum public API examples for shipped surface entrypoints.
267
+ - Updated dependencies [3dc8254]
268
+ - Updated dependencies [61497c5]
269
+ - @ontrails/core@1.0.0-beta.17
270
+ - @ontrails/http@1.0.0-beta.17
271
+
272
+ ## 1.0.0-beta.16
273
+
274
+ ### Patch Changes
275
+
276
+ - f5c6777: Move adapter package workspaces from `connectors/*` to `adapters/*` as part of
277
+ the package-boundary taxonomy cutover. Package names and public APIs are
278
+ unchanged.
279
+ - 6300f70: Refresh source comments and test labels for retired connector terminology as adapter guardrails become strict.
280
+ - 20d7a5c: Enforce the shared safe error projection policy for public error bodies, diagnostics, serialized payloads, and CLI stderr.
281
+ - 95bf132: Wire HTTP permit resolution through the Hono adapter, including request headers for Bearer Authorization handling.
282
+ - 729f957: Harden the Hono surface by capping JSON request bodies at 1 MiB by default and
283
+ redacting generic internal errors while preserving server-side diagnostics.
284
+ - 49c2e7d: Refresh published package README taxonomy to use adapter language instead of retired connector vocabulary.
285
+ - df9a7d0: Add project-aware public export-map governance for @ontrails workspace docs,
286
+ imports, root barrels, and bin-only package surfaces.
287
+ - Updated dependencies [73622ae]
288
+ - Updated dependencies [6300f70]
289
+ - Updated dependencies [d172013]
290
+ - Updated dependencies [c3fc5c3]
291
+ - Updated dependencies [20d7a5c]
292
+ - Updated dependencies [be5fb46]
293
+ - Updated dependencies [95bf132]
294
+ - Updated dependencies [e898cc4]
295
+ - Updated dependencies [2bf239e]
296
+ - Updated dependencies [3395234]
297
+ - Updated dependencies [bcdc484]
298
+ - Updated dependencies [49c2e7d]
299
+ - Updated dependencies [331e3a9]
300
+ - Updated dependencies [4399fdb]
301
+ - Updated dependencies [4b8d13b]
302
+ - Updated dependencies [112b9f2]
303
+ - Updated dependencies [893025e]
304
+ - Updated dependencies [eec5e9d]
305
+ - Updated dependencies [ebd4434]
306
+ - Updated dependencies [863d473]
307
+ - Updated dependencies [344f2f7]
308
+ - Updated dependencies [26f9ffd]
309
+ - Updated dependencies [10eae9a]
310
+ - Updated dependencies [22c6c06]
311
+ - Updated dependencies [df9a7d0]
312
+ - @ontrails/core@1.0.0-beta.16
313
+ - @ontrails/http@1.0.0-beta.16
314
+
315
+ ## 1.0.0-beta.15
316
+
317
+ ### Patch Changes
318
+
319
+ - Updated dependencies [4ad6b25]
320
+ - @ontrails/core@1.0.0-beta.15
321
+ - @ontrails/http@1.0.0-beta.15
package/README.md ADDED
@@ -0,0 +1,44 @@
1
+ # @ontrails/hono
2
+
3
+ Hono surface adapter for Trails. Use this package when you want to serve a topo over HTTP with Hono while keeping `@ontrails/http` focused on framework-agnostic route building and the shared Web Fetch kernel.
4
+
5
+ ## Usage
6
+
7
+ ```typescript
8
+ import { surface } from '@ontrails/hono';
9
+ import { graph } from './app';
10
+
11
+ await surface(graph, { port: 3000 });
12
+ ```
13
+
14
+ JSON request bodies are capped at 1 MiB by default. Override the cap with `maxJsonBodyBytes` when a surface intentionally accepts larger JSON payloads:
15
+
16
+ ```typescript
17
+ await surface(graph, {
18
+ maxJsonBodyBytes: 2 * 1024 * 1024,
19
+ port: 3000,
20
+ });
21
+ ```
22
+
23
+ Pass `resolvePermit` to resolve HTTP `Authorization: Bearer ...` credentials into `ctx.permit` before protected trails execute. The adapter forwards request headers to the framework-agnostic HTTP route executor, so malformed Authorization headers return `401` and resolved permits with insufficient scopes return `403`.
24
+
25
+ Generic non-TrailsError failures return a redacted 500 response while a redacted diagnostic rendering is written to server diagnostics. `TrailsError` responses keep their taxonomy category and class name but redact sensitive message fragments before writing the public body.
26
+
27
+ For custom HTTP integrations or route inspection, keep using `deriveHttpRoutes()` from `@ontrails/http`. For a framework-neutral runtime handler, use `createRouteHandler()` or `createFetchHandler()` from `@ontrails/http/fetch`. For Bun-native serving without Hono, use `@ontrails/http/bun`.
28
+
29
+ ## Installation
30
+
31
+ These commands target stable `0.2.0`. Run them after that version is published to npm.
32
+
33
+ ```bash
34
+ bun add --exact @ontrails/http@0.2.0 @ontrails/hono@0.2.0
35
+ ```
36
+
37
+ ## Migration
38
+
39
+ <!-- warden-ignore-next-line -->
40
+ This package replaces the old `@ontrails/http/hono` subpath.
41
+
42
+ <!-- warden-ignore-next-line -->
43
+ - Before: `import { trailhead } from '@ontrails/http/hono'`
44
+ - After: `import { surface } from '@ontrails/hono'`
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@ontrails/hono",
3
+ "version": "0.2.0",
4
+ "repository": {
5
+ "type": "git",
6
+ "url": "git+https://github.com/outfitter-dev/trails.git",
7
+ "directory": "adapters/hono"
8
+ },
9
+ "files": [
10
+ "src/**/*.ts",
11
+ "!src/**/__tests__/**",
12
+ "!src/**/*.test.ts",
13
+ "!src/**/*.test-d.ts",
14
+ "README.md",
15
+ "CHANGELOG.md"
16
+ ],
17
+ "type": "module",
18
+ "exports": {
19
+ ".": "./src/index.ts",
20
+ "./package.json": "./package.json"
21
+ },
22
+ "scripts": {
23
+ "build": "tsc -b",
24
+ "test": "bun test",
25
+ "typecheck": "tsc --noEmit",
26
+ "lint": "oxlint ./src",
27
+ "clean": "rm -rf dist *.tsbuildinfo"
28
+ },
29
+ "dependencies": {
30
+ "@ontrails/core": "^0.2.0",
31
+ "hono": "^4.7.0"
32
+ },
33
+ "peerDependencies": {
34
+ "@ontrails/http": "^0.2.0",
35
+ "zod": "^4.3.5"
36
+ },
37
+ "trails": {
38
+ "adapter": {
39
+ "target": "http"
40
+ }
41
+ }
42
+ }
@@ -0,0 +1,65 @@
1
+ import { InternalError, Result, trail } from '@ontrails/core';
2
+ import type { Trail } from '@ontrails/core';
3
+ import { createRouteHandler } from '@ontrails/http';
4
+ import type { HttpRouteDefinition } from '@ontrails/http';
5
+ import { z } from 'zod';
6
+
7
+ const caughtErrors = new Map<string, Error>();
8
+ const caughtErrorInput = z.object({ errorId: z.string() });
9
+ const caughtErrorTrail = trail('__ontrails.hono.error', {
10
+ implementation: () =>
11
+ Result.err(new InternalError('Hono error fallback executed directly')),
12
+ input: caughtErrorInput,
13
+ intent: 'read',
14
+ output: z.object({}),
15
+ }) as Trail<unknown, unknown, unknown>;
16
+
17
+ const caughtErrorRoute: HttpRouteDefinition = {
18
+ execute: async (input) => {
19
+ const parsed = caughtErrorInput.safeParse(input);
20
+ if (!parsed.success) {
21
+ return Result.err(
22
+ new InternalError('Hono error fallback missing error id')
23
+ );
24
+ }
25
+ const error =
26
+ caughtErrors.get(parsed.data.errorId) ??
27
+ new Error('Hono error fallback missing caught error');
28
+ return Result.err(error);
29
+ },
30
+ inputSource: 'query',
31
+ method: 'GET',
32
+ path: '/__ontrails/hono/error',
33
+ trail: caughtErrorTrail,
34
+ trailId: '__ontrails.hono.error',
35
+ };
36
+ const caughtErrorHandler = createRouteHandler(caughtErrorRoute);
37
+
38
+ const materializeCaughtErrorRequest = (
39
+ request: Request,
40
+ errorId: string
41
+ ): Request => {
42
+ const url = new URL('/__ontrails/hono/error', request.url);
43
+ url.searchParams.set('errorId', errorId);
44
+ return new Request(url, {
45
+ headers: request.headers,
46
+ method: 'GET',
47
+ signal: request.signal,
48
+ });
49
+ };
50
+
51
+ export const handleCaughtHonoError = async (
52
+ error: unknown,
53
+ request: Request
54
+ ): Promise<Response> => {
55
+ const err = error instanceof Error ? error : new Error(String(error));
56
+ const errorId = crypto.randomUUID();
57
+ caughtErrors.set(errorId, err);
58
+ try {
59
+ return await caughtErrorHandler(
60
+ materializeCaughtErrorRequest(request, errorId)
61
+ );
62
+ } finally {
63
+ caughtErrors.delete(errorId);
64
+ }
65
+ };
package/src/index.ts ADDED
@@ -0,0 +1,6 @@
1
+ export {
2
+ createApp,
3
+ surface,
4
+ type CreateAppOptions,
5
+ type SurfaceHttpResult,
6
+ } from './surface.js';
package/src/surface.ts ADDED
@@ -0,0 +1,242 @@
1
+ /**
2
+ * Hono adapter for Trails HTTP routes.
3
+ *
4
+ * Takes framework-agnostic HttpRouteDefinition[] and wires them into a
5
+ * Hono application, handling request parsing, response mapping, and errors.
6
+ *
7
+ * ```ts
8
+ * const graph = topo("myapp", entity);
9
+ * await surface(graph, { port: 3000 });
10
+ * ```
11
+ */
12
+
13
+ import type {
14
+ BaseSurfaceOptions,
15
+ Layer,
16
+ ResourceOverrideMap,
17
+ Topo,
18
+ TrailContextInit,
19
+ } from '@ontrails/core';
20
+ import { Hono } from 'hono';
21
+ import type { Context as HonoContext } from 'hono';
22
+ import { createRouteHandler, deriveHttpRoutes } from '@ontrails/http';
23
+ import type {
24
+ HttpMethod,
25
+ HttpRouteDefinition,
26
+ ResolveHttpPermit,
27
+ } from '@ontrails/http';
28
+ import { handleCaughtHonoError } from './caught-error.js';
29
+
30
+ // ---------------------------------------------------------------------------
31
+ // Options
32
+ // ---------------------------------------------------------------------------
33
+
34
+ /**
35
+ * Options for building a Trails HTTP app on Hono.
36
+ */
37
+ export interface CreateAppOptions extends BaseSurfaceOptions {
38
+ readonly basePath?: string | undefined;
39
+ readonly createContext?:
40
+ | (() => TrailContextInit | Promise<TrailContextInit>)
41
+ | undefined;
42
+ readonly hostname?: string | undefined;
43
+ readonly layers?: readonly Layer[] | undefined;
44
+ /** Maximum JSON request body size in bytes. Defaults to 1 MiB. */
45
+ readonly maxJsonBodyBytes?: number | undefined;
46
+ readonly name?: string | undefined;
47
+ readonly port?: number | undefined;
48
+ readonly resources?: ResourceOverrideMap | undefined;
49
+ readonly resolvePermit?: ResolveHttpPermit | undefined;
50
+ }
51
+
52
+ interface RuntimeOptions {
53
+ readonly maxJsonBodyBytes?: number | undefined;
54
+ }
55
+
56
+ /**
57
+ * Runtime handle returned by the Hono surface.
58
+ */
59
+ export interface SurfaceHttpResult {
60
+ readonly close: () => Promise<void>;
61
+ readonly url: string;
62
+ }
63
+
64
+ // ---------------------------------------------------------------------------
65
+ // Route registration
66
+ // ---------------------------------------------------------------------------
67
+
68
+ const materializeHonoRequest = async (c: HonoContext): Promise<Request> => {
69
+ if (!c.req.raw.bodyUsed) {
70
+ return c.req.raw;
71
+ }
72
+
73
+ const body = await c.req.text();
74
+ return new Request(c.req.raw.url, {
75
+ body,
76
+ headers: c.req.raw.headers,
77
+ method: c.req.raw.method,
78
+ signal: c.req.raw.signal,
79
+ });
80
+ };
81
+
82
+ /** Create a Hono handler from a route definition. */
83
+ const createHonoHandler = (
84
+ route: HttpRouteDefinition,
85
+ options: RuntimeOptions
86
+ ): ((c: HonoContext) => Promise<Response>) => {
87
+ const handler = createRouteHandler(route, {
88
+ maxJsonBodyBytes: options.maxJsonBodyBytes,
89
+ });
90
+ return async (c) => handler(await materializeHonoRequest(c));
91
+ };
92
+
93
+ /** Route registration keyed by HTTP method. */
94
+ const routeRegistrars: Record<
95
+ HttpMethod,
96
+ (
97
+ hono: Hono,
98
+ path: string,
99
+ handler: (c: HonoContext) => Promise<Response>
100
+ ) => void
101
+ > = {
102
+ DELETE: (hono, path, handler) => {
103
+ hono.delete(path, handler);
104
+ },
105
+ GET: (hono, path, handler) => {
106
+ hono.get(path, handler);
107
+ },
108
+ PATCH: (hono, path, handler) => {
109
+ hono.patch(path, handler);
110
+ },
111
+ POST: (hono, path, handler) => {
112
+ hono.post(path, handler);
113
+ },
114
+ PUT: (hono, path, handler) => {
115
+ hono.put(path, handler);
116
+ },
117
+ };
118
+
119
+ const registerRoutes = (
120
+ hono: Hono,
121
+ routes: HttpRouteDefinition[],
122
+ options: RuntimeOptions
123
+ ): void => {
124
+ for (const route of routes) {
125
+ const handler = createHonoHandler(route, options);
126
+ routeRegistrars[route.method](hono, route.path, handler);
127
+ }
128
+ };
129
+
130
+ // ---------------------------------------------------------------------------
131
+ // Global error handler
132
+ // ---------------------------------------------------------------------------
133
+
134
+ const handleCaughtError = async (
135
+ error: unknown,
136
+ c: HonoContext
137
+ ): Promise<Response> => handleCaughtHonoError(error, c.req.raw);
138
+
139
+ const registerErrorHandler = (hono: Hono): void => {
140
+ // oxlint-disable-next-line prefer-await-to-callbacks -- Hono's onError API requires a callback
141
+ hono.onError((err, c) => handleCaughtError(err, c));
142
+ };
143
+
144
+ // ---------------------------------------------------------------------------
145
+ // Validation
146
+ // ---------------------------------------------------------------------------
147
+
148
+ // ---------------------------------------------------------------------------
149
+ // createApp
150
+ // ---------------------------------------------------------------------------
151
+
152
+ /**
153
+ * Build HTTP routes from a topo and register them on a Hono app.
154
+ *
155
+ * @remarks This is a host materialization boundary. Derivation failures are
156
+ * thrown for HTTP bootstrap code after `deriveHttpRoutes` has already
157
+ * represented the framework error as a Result.
158
+ *
159
+ * @example
160
+ * ```ts
161
+ * import { createApp } from '@ontrails/hono';
162
+ *
163
+ * const app = createApp(graph, { basePath: '/api' });
164
+ * Bun.serve({ fetch: app.fetch, port: 3000 });
165
+ * ```
166
+ */
167
+ export const createApp = (
168
+ graph: Topo,
169
+ options: CreateAppOptions = {}
170
+ ): Hono => {
171
+ const hono = new Hono();
172
+ const runtimeOptions = {
173
+ maxJsonBodyBytes: options.maxJsonBodyBytes,
174
+ };
175
+
176
+ registerErrorHandler(hono);
177
+
178
+ const routesResult = deriveHttpRoutes(graph, {
179
+ basePath: options.basePath,
180
+ configValues: options.configValues,
181
+ createContext: options.createContext,
182
+ exclude: options.exclude,
183
+ include: options.include,
184
+ intent: options.intent,
185
+ layers: options.layers,
186
+ resolvePermit: options.resolvePermit,
187
+ resources: options.resources,
188
+ validate: options.validate,
189
+ });
190
+
191
+ if (routesResult.isErr()) {
192
+ throw routesResult.error;
193
+ }
194
+
195
+ registerRoutes(hono, routesResult.value, runtimeOptions);
196
+ return hono;
197
+ };
198
+
199
+ const startServer = (
200
+ hono: Hono,
201
+ options: CreateAppOptions
202
+ ): SurfaceHttpResult => {
203
+ const server = Bun.serve({
204
+ fetch: hono.fetch,
205
+ hostname: options.hostname ?? '0.0.0.0',
206
+ port: options.port ?? 3000,
207
+ });
208
+
209
+ return {
210
+ close: async () => {
211
+ await server.stop(true);
212
+ },
213
+ url: String(server.url),
214
+ };
215
+ };
216
+
217
+ // ---------------------------------------------------------------------------
218
+ // surface
219
+ // ---------------------------------------------------------------------------
220
+
221
+ /**
222
+ * Build a Hono app from a topo and start serving it with Bun.
223
+ *
224
+ * @remarks Always starts a Bun server. Use `createApp(graph)` for an
225
+ * unserved Hono app that you can wire into your own server.
226
+ *
227
+ * @example
228
+ * ```ts
229
+ * import { surface } from '@ontrails/hono';
230
+ *
231
+ * const server = await surface(graph, { port: 3000 });
232
+ * console.log(server.url);
233
+ * ```
234
+ */
235
+ export const surface = async (
236
+ graph: Topo,
237
+ options: CreateAppOptions = {}
238
+ ): Promise<SurfaceHttpResult> => {
239
+ // oxlint-disable-next-line require-await -- async ensures createApp() throws become rejected promises, not uncaught exceptions
240
+ const hono = createApp(graph, options);
241
+ return startServer(hono, options);
242
+ };