honertia 0.1.16 → 0.1.18

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 CHANGED
@@ -84,18 +84,7 @@ class BindingsService extends Context.Tag('app/Bindings')<
84
84
  { KV: KVNamespace }
85
85
  >() {}
86
86
 
87
- // Request-scoped setup: put db/auth on c.var so Honertia/Effect can read them.
88
- app.use('*', async (c, next) => {
89
- c.set('db', createDb(c.env.DATABASE_URL))
90
- c.set('auth', createAuth({
91
- db: c.var.db,
92
- secret: c.env.BETTER_AUTH_SECRET,
93
- baseURL: new URL(c.req.url).origin,
94
- }))
95
- await next()
96
- })
97
-
98
- // Honertia bundles the core middleware + auth loading + Effect runtime setup.
87
+ // Honertia bundles db/auth setup + core middleware + Effect runtime.
99
88
  app.use('*', setupHonertia<Env, BindingsService>({
100
89
  honertia: {
101
90
  // Use your asset manifest hash so Inertia reloads on deploy.
@@ -109,9 +98,19 @@ app.use('*', setupHonertia<Env, BindingsService>({
109
98
  head: isProd ? '' : vite.hmrHead(),
110
99
  }
111
100
  }),
101
+ // Database factory (creates c.var.db for each request)
102
+ database: (c) => createDb(c.env.DATABASE_URL),
103
+ // Auth factory (can access c.var.db since database runs first)
104
+ auth: (c) => createAuth({
105
+ db: c.var.db,
106
+ secret: c.env.BETTER_AUTH_SECRET,
107
+ baseURL: new URL(c.req.url).origin,
108
+ }),
109
+ // Schema for route model binding (optional)
110
+ schema,
112
111
  },
113
112
  effect: {
114
- // Expose Cloudflare bindings to Effect handlers via a service layer.
113
+ // Custom Effect services (e.g., Cloudflare bindings)
115
114
  services: (c) => Layer.succeed(BindingsService, {
116
115
  KV: c.env.MY_KV,
117
116
  }),
@@ -119,7 +118,6 @@ app.use('*', setupHonertia<Env, BindingsService>({
119
118
  // Optional: extra Hono middleware in the same chain.
120
119
  middleware: [
121
120
  logger(),
122
- // register additional middleware here...
123
121
  ],
124
122
  }))
125
123
 
@@ -963,13 +961,19 @@ declare module 'honertia/effect' {
963
961
  }
964
962
  ```
965
963
 
966
- 2. Pass your schema to `effectRoutes`:
964
+ 2. Pass your schema to `setupHonertia`:
967
965
 
968
966
  ```typescript
969
967
  import * as schema from '~/db/schema'
970
968
 
971
- effectRoutes(app, { schema })
972
- .get('/projects/{project}', showProject)
969
+ app.use('*', setupHonertia({
970
+ honertia: {
971
+ version: '1.0.0',
972
+ render: createTemplate({ ... }),
973
+ database: (c) => createDb(c.env.DATABASE_URL),
974
+ schema, // Schema is shared with all effectRoutes
975
+ },
976
+ }))
973
977
  ```
974
978
 
975
979
  **Basic Usage:**
@@ -980,7 +984,7 @@ import { bound } from 'honertia/effect'
980
984
  // Route: /projects/{project}
981
985
  // Automatically queries: SELECT * FROM projects WHERE id = :project
982
986
 
983
- effectRoutes(app, { schema }).get('/projects/{project}', showProject)
987
+ effectRoutes(app).get('/projects/{project}', showProject)
984
988
 
985
989
  const showProject = Effect.gen(function* () {
986
990
  const project = yield* bound('project') // Already fetched, guaranteed to exist
@@ -994,7 +998,7 @@ By default, bindings query the `id` column. Use `{param:column}` syntax to bind
994
998
 
995
999
  ```typescript
996
1000
  // Bind by slug instead of id
997
- effectRoutes(app, { schema }).get('/projects/{project:slug}', showProject)
1001
+ effectRoutes(app).get('/projects/{project:slug}', showProject)
998
1002
  // Queries: SELECT * FROM projects WHERE slug = :project
999
1003
  ```
1000
1004
 
@@ -1004,7 +1008,7 @@ For nested routes, Honertia automatically scopes child models to their parents u
1004
1008
 
1005
1009
  ```typescript
1006
1010
  // Route: /users/{user}/posts/{post}
1007
- effectRoutes(app, { schema }).get('/users/{user}/posts/{post}', showUserPost)
1011
+ effectRoutes(app).get('/users/{user}/posts/{post}', showUserPost)
1008
1012
 
1009
1013
  // Queries:
1010
1014
  // 1. SELECT * FROM users WHERE id = :user
@@ -1039,7 +1043,7 @@ If no relation is found, the child is resolved without scoping (useful for unrel
1039
1043
  Route model binding and param validation work together. Validation runs first:
1040
1044
 
1041
1045
  ```typescript
1042
- effectRoutes(app, { schema }).get(
1046
+ effectRoutes(app).get(
1043
1047
  '/projects/{project}',
1044
1048
  showProject,
1045
1049
  { params: S.Struct({ project: uuid }) } // Validates UUID format first
@@ -1058,7 +1062,7 @@ You can mix Laravel-style `{binding}` with Hono-style `:param` in the same route
1058
1062
  ```typescript
1059
1063
  // :version is a regular Hono param (not bound)
1060
1064
  // {project} is resolved from the database
1061
- effectRoutes(app, { schema }).get(
1065
+ effectRoutes(app).get(
1062
1066
  '/api/:version/projects/{project}',
1063
1067
  showProject
1064
1068
  )
@@ -1243,7 +1247,204 @@ return yield* forbidden('You cannot edit this project')
1243
1247
 
1244
1248
  ## Error Handling
1245
1249
 
1246
- Honertia provides typed errors:
1250
+ Honertia provides typed errors that integrate with Effect's error channel. Each error type has specific handling behavior designed for Inertia-style applications.
1251
+
1252
+ ### Built-in Error Types
1253
+
1254
+ | Error Type | HTTP Status | Handling Behavior |
1255
+ |------------|-------------|-------------------|
1256
+ | `ValidationError` | 422 / redirect | Re-renders form with field errors, or redirects back |
1257
+ | `UnauthorizedError` | 302/303 | Redirects to login page |
1258
+ | `NotFoundError` | 404 | Uses Hono's `notFound()` handler → renders via Honertia |
1259
+ | `ForbiddenError` | 403 | Returns JSON response (for API compatibility) |
1260
+ | `HttpError` | Custom | Returns JSON with custom status (developer-controlled) |
1261
+ | `RouteConfigurationError` | 500 | Throws to Hono's `onError` → renders error page |
1262
+ | Unexpected errors | 500 | Throws to Hono's `onError` → renders error page |
1263
+
1264
+ ### Error Type Details
1265
+
1266
+ #### `ValidationError`
1267
+
1268
+ Thrown when request validation fails. Automatically re-renders the form with field-level errors.
1269
+
1270
+ ```typescript
1271
+ import { ValidationError, validateRequest } from 'honertia/effect'
1272
+
1273
+ // Automatic: validateRequest throws ValidationError on failure
1274
+ const input = yield* validateRequest(schema, {
1275
+ errorComponent: 'Projects/Create', // Re-renders this component with errors
1276
+ })
1277
+
1278
+ // Manual: throw ValidationError directly
1279
+ yield* Effect.fail(new ValidationError({
1280
+ errors: { email: 'Invalid email format' },
1281
+ component: 'Auth/Register', // Optional: component to re-render
1282
+ }))
1283
+ ```
1284
+
1285
+ **Behavior:**
1286
+ - If request prefers JSON (API calls): returns `{ errors: {...} }` with 422 status
1287
+ - If `component` is set: re-renders that component with errors in props
1288
+ - Otherwise: redirects back to referer with errors in session
1289
+
1290
+ #### `UnauthorizedError`
1291
+
1292
+ Thrown when authentication is required but the user is not logged in.
1293
+
1294
+ ```typescript
1295
+ import { UnauthorizedError, authorize } from 'honertia/effect'
1296
+
1297
+ // Automatic: authorize() throws UnauthorizedError if no user
1298
+ const auth = yield* authorize()
1299
+
1300
+ // Manual: throw with custom redirect
1301
+ yield* Effect.fail(new UnauthorizedError({
1302
+ message: 'Please log in to continue',
1303
+ redirectTo: '/login', // Defaults to '/login'
1304
+ }))
1305
+ ```
1306
+
1307
+ **Behavior:** Redirects to the specified URL (302 for regular requests, 303 for Inertia requests).
1308
+
1309
+ #### `NotFoundError`
1310
+
1311
+ Thrown when a requested resource doesn't exist.
1312
+
1313
+ ```typescript
1314
+ import { NotFoundError, notFound } from 'honertia/effect'
1315
+
1316
+ // Helper function
1317
+ return yield* notFound('Project', projectId)
1318
+
1319
+ // Manual
1320
+ yield* Effect.fail(new NotFoundError({
1321
+ resource: 'Project',
1322
+ id: projectId,
1323
+ }))
1324
+ ```
1325
+
1326
+ **Behavior:** Triggers Hono's `notFound()` handler. If you've set up `registerErrorHandlers()`, this renders your error component with status 404.
1327
+
1328
+ #### `ForbiddenError`
1329
+
1330
+ Thrown when the user is authenticated but not authorized to perform an action.
1331
+
1332
+ ```typescript
1333
+ import { ForbiddenError, forbidden, authorize } from 'honertia/effect'
1334
+
1335
+ // Automatic: authorize() throws ForbiddenError if check fails
1336
+ const auth = yield* authorize((a) => a.user.role === 'admin')
1337
+
1338
+ // Helper function
1339
+ return yield* forbidden('You cannot edit this project')
1340
+
1341
+ // Manual
1342
+ yield* Effect.fail(new ForbiddenError({
1343
+ message: 'Admin access required',
1344
+ }))
1345
+ ```
1346
+
1347
+ **Behavior:** Returns JSON `{ message: "..." }` with 403 status. This is intentionally JSON for API compatibility.
1348
+
1349
+ #### `HttpError`
1350
+
1351
+ A generic error for custom HTTP responses. Use when you need precise control over the response.
1352
+
1353
+ ```typescript
1354
+ import { HttpError, httpError } from 'honertia/effect'
1355
+
1356
+ // Helper function
1357
+ return yield* httpError(429, 'Rate limited', { retryAfter: 60 })
1358
+
1359
+ // Manual
1360
+ yield* Effect.fail(new HttpError({
1361
+ status: 429,
1362
+ message: 'Too many requests',
1363
+ body: { retryAfter: 60 }, // Optional additional data
1364
+ }))
1365
+ ```
1366
+
1367
+ **Behavior:** Returns JSON `{ message: "...", ...body }` with the specified status code.
1368
+
1369
+ #### `RouteConfigurationError`
1370
+
1371
+ Thrown when there's a developer configuration error, such as using route model binding without providing a schema.
1372
+
1373
+ ```typescript
1374
+ import { RouteConfigurationError } from 'honertia/effect'
1375
+
1376
+ // This error is thrown automatically when:
1377
+ // - You use bound('project') but didn't pass schema to effectRoutes()
1378
+ // - Other route configuration mistakes
1379
+
1380
+ // You typically don't throw this manually
1381
+ ```
1382
+
1383
+ **Behavior:** Re-throws to Hono's `onError` handler, which renders your error component. The error message and hint are logged to the console for debugging.
1384
+
1385
+ ### Setting Up Error Pages
1386
+
1387
+ To render errors via Honertia instead of returning plain text/JSON, use `registerErrorHandlers`:
1388
+
1389
+ ```typescript
1390
+ import { registerErrorHandlers } from 'honertia'
1391
+
1392
+ // In your app setup
1393
+ registerErrorHandlers(app, {
1394
+ component: 'Error', // Your error page component
1395
+ showDevErrors: true, // Show detailed errors in development
1396
+ envKey: 'ENVIRONMENT', // Env var to check
1397
+ devValue: 'development', // Value that enables dev errors
1398
+ })
1399
+ ```
1400
+
1401
+ Your `Error` component receives:
1402
+
1403
+ ```tsx
1404
+ // src/pages/Error.tsx
1405
+ interface ErrorProps {
1406
+ status: number // 404, 500, etc.
1407
+ message: string // Error message (detailed in dev, generic in prod)
1408
+ }
1409
+
1410
+ export default function Error({ status, message }: ErrorProps) {
1411
+ return (
1412
+ <div className="error-page">
1413
+ <h1>{status}</h1>
1414
+ <p>{message}</p>
1415
+ </div>
1416
+ )
1417
+ }
1418
+ ```
1419
+
1420
+ ### Error Handling Flow
1421
+
1422
+ ```
1423
+ Effect Handler
1424
+
1425
+
1426
+ ┌─────────────────────────────────────────────────────────┐
1427
+ │ errorToResponse() │
1428
+ ├─────────────────────────────────────────────────────────┤
1429
+ │ ValidationError → Re-render form / redirect back │
1430
+ │ UnauthorizedError → Redirect to login │
1431
+ │ NotFoundError → c.notFound() → Hono notFound handler │
1432
+ │ ForbiddenError → JSON 403 │
1433
+ │ HttpError → JSON with custom status │
1434
+ │ Other errors → throw → Hono onError handler │
1435
+ └─────────────────────────────────────────────────────────┘
1436
+
1437
+ ▼ (for thrown errors)
1438
+ ┌─────────────────────────────────────────────────────────┐
1439
+ │ Hono onError handler │
1440
+ │ (from registerErrorHandlers) │
1441
+ ├─────────────────────────────────────────────────────────┤
1442
+ │ Renders error component via Honertia │
1443
+ │ Shows detailed message in dev, generic in prod │
1444
+ └─────────────────────────────────────────────────────────┘
1445
+ ```
1446
+
1447
+ ### Usage Examples
1247
1448
 
1248
1449
  ```typescript
1249
1450
  import {
@@ -1252,7 +1453,7 @@ import {
1252
1453
  NotFoundError,
1253
1454
  ForbiddenError,
1254
1455
  HttpError,
1255
- } from 'honertia'
1456
+ } from 'honertia/effect'
1256
1457
 
1257
1458
  // Validation errors automatically re-render with field errors
1258
1459
  const input = yield* validateRequest(schema, {
@@ -65,7 +65,7 @@ export const bound = (key) => Effect.gen(function* () {
65
65
  if (models.has('__schema_not_configured__')) {
66
66
  return yield* new RouteConfigurationError({
67
67
  message: `Route model binding requires schema configuration. Cannot resolve bound('${key}') without schema.`,
68
- hint: `Pass your schema to effectRoutes: effectRoutes(app, { schema })`
68
+ hint: `Pass your schema to setupHonertia: setupHonertia({ honertia: { schema } })`
69
69
  });
70
70
  }
71
71
  const model = models.get(key);
@@ -15,7 +15,6 @@ import { DatabaseService, AuthService, AuthUserService, HonertiaService, Request
15
15
  * @example
16
16
  * // Provide Cloudflare Worker bindings as a service
17
17
  * effectBridge<Env, BindingsService>({
18
- * database: (c) => createDb(c.env.DATABASE_URL),
19
18
  * services: (c) => Layer.succeed(BindingsService, c.env),
20
19
  * })
21
20
  *
@@ -29,7 +28,6 @@ import { DatabaseService, AuthService, AuthUserService, HonertiaService, Request
29
28
  * })
30
29
  */
31
30
  export interface EffectBridgeConfig<E extends Env, CustomServices = never> {
32
- database?: (c: HonoContext<E>) => unknown;
33
31
  /**
34
32
  * Custom services to provide to all Effect handlers.
35
33
  * Return a Layer that provides your custom services.
@@ -37,14 +35,8 @@ export interface EffectBridgeConfig<E extends Env, CustomServices = never> {
37
35
  services?: (c: HonoContext<E>) => Layer.Layer<CustomServices, never, never>;
38
36
  /**
39
37
  * Drizzle schema for route model binding.
40
- * Required if using Laravel-style route model binding.
41
- *
42
- * @example
43
- * ```typescript
44
- * import * as schema from '~/db/schema'
45
- *
46
- * effectRoutes(app, { schema })
47
- * ```
38
+ * Usually configured via `setupHonertia({ honertia: { schema } })`.
39
+ * Can also be passed here for standalone effectBridge usage.
48
40
  */
49
41
  schema?: Record<string, unknown>;
50
42
  }
@@ -53,11 +45,16 @@ export interface EffectBridgeConfig<E extends Env, CustomServices = never> {
53
45
  */
54
46
  declare const EFFECT_RUNTIME: unique symbol;
55
47
  /**
56
- * Extend Hono context with Effect runtime.
48
+ * Symbol for storing schema in Hono context.
49
+ */
50
+ declare const EFFECT_SCHEMA: unique symbol;
51
+ /**
52
+ * Extend Hono context with Effect runtime and schema.
57
53
  */
58
54
  declare module 'hono' {
59
55
  interface ContextVariableMap {
60
56
  [EFFECT_RUNTIME]?: ManagedRuntime.ManagedRuntime<DatabaseService | AuthService | AuthUserService | HonertiaService | RequestService | ResponseFactoryService, never>;
57
+ [EFFECT_SCHEMA]?: Record<string, unknown>;
61
58
  }
62
59
  }
63
60
  /**
@@ -72,5 +69,9 @@ export declare function getEffectRuntime<E extends Env>(c: HonoContext<E>): Mana
72
69
  * Middleware that sets up the Effect runtime for each request.
73
70
  */
74
71
  export declare function effectBridge<E extends Env, CustomServices = never>(config?: EffectBridgeConfig<E, CustomServices>): MiddlewareHandler<E>;
72
+ /**
73
+ * Get the schema from Hono context (set by effectBridge).
74
+ */
75
+ export declare function getEffectSchema<E extends Env>(c: HonoContext<E>): Record<string, unknown> | undefined;
75
76
  export {};
76
77
  //# sourceMappingURL=bridge.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../../src/effect/bridge.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAA;AAC9C,OAAO,KAAK,EAAE,OAAO,IAAI,WAAW,EAAE,iBAAiB,EAAE,GAAG,EAAE,MAAM,MAAM,CAAA;AAC1E,OAAO,EACL,eAAe,EACf,WAAW,EACX,eAAe,EACf,eAAe,EACf,cAAc,EACd,sBAAsB,EAOvB,MAAM,eAAe,CAAA;AAEtB;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,WAAW,kBAAkB,CAAC,CAAC,SAAS,GAAG,EAAE,cAAc,GAAG,KAAK;IACvE,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,OAAO,CAAA;IACzC;;;OAGG;IACH,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,EAAE,KAAK,CAAC,CAAA;IAC3E;;;;;;;;;;OAUG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACjC;AAED;;GAEG;AACH,QAAA,MAAM,cAAc,eAA0B,CAAA;AAE9C;;GAEG;AACH,OAAO,QAAQ,MAAM,CAAC;IACpB,UAAU,kBAAkB;QAC1B,CAAC,cAAc,CAAC,CAAC,EAAE,cAAc,CAAC,cAAc,CAC5C,eAAe,GACf,WAAW,GACX,eAAe,GACf,eAAe,GACf,cAAc,GACd,sBAAsB,EACxB,KAAK,CACN,CAAA;KACF;CACF;AAqDD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,GAAG,EAAE,cAAc,GAAG,KAAK,EACrE,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,EACjB,MAAM,CAAC,EAAE,kBAAkB,CAAC,CAAC,EAAE,cAAc,CAAC,GAC7C,KAAK,CAAC,KAAK,CACV,cAAc,GACd,sBAAsB,GACtB,eAAe,GACf,eAAe,GACf,WAAW,GACX,eAAe,GACf,cAAc,EAChB,KAAK,EACL,KAAK,CACN,CA0CA;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,GAAG,EAC5C,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,GAChB,cAAc,CAAC,cAAc,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,SAAS,CAEvD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,GAAG,EAAE,cAAc,GAAG,KAAK,EAChE,MAAM,CAAC,EAAE,kBAAkB,CAAC,CAAC,EAAE,cAAc,CAAC,GAC7C,iBAAiB,CAAC,CAAC,CAAC,CAetB"}
1
+ {"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../../src/effect/bridge.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAA;AAC9C,OAAO,KAAK,EAAE,OAAO,IAAI,WAAW,EAAE,iBAAiB,EAAE,GAAG,EAAE,MAAM,MAAM,CAAA;AAC1E,OAAO,EACL,eAAe,EACf,WAAW,EACX,eAAe,EACf,eAAe,EACf,cAAc,EACd,sBAAsB,EAOvB,MAAM,eAAe,CAAA;AAEtB;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,WAAW,kBAAkB,CAAC,CAAC,SAAS,GAAG,EAAE,cAAc,GAAG,KAAK;IACvE;;;OAGG;IACH,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,EAAE,KAAK,CAAC,CAAA;IAC3E;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACjC;AAED;;GAEG;AACH,QAAA,MAAM,cAAc,eAA0B,CAAA;AAE9C;;GAEG;AACH,QAAA,MAAM,aAAa,eAAyB,CAAA;AA6B5C;;GAEG;AACH,OAAO,QAAQ,MAAM,CAAC;IACpB,UAAU,kBAAkB;QAC1B,CAAC,cAAc,CAAC,CAAC,EAAE,cAAc,CAAC,cAAc,CAC5C,eAAe,GACf,WAAW,GACX,eAAe,GACf,eAAe,GACf,cAAc,GACd,sBAAsB,EACxB,KAAK,CACN,CAAA;QACD,CAAC,aAAa,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAC1C;CACF;AAqDD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,GAAG,EAAE,cAAc,GAAG,KAAK,EACrE,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,EACjB,MAAM,CAAC,EAAE,kBAAkB,CAAC,CAAC,EAAE,cAAc,CAAC,GAC7C,KAAK,CAAC,KAAK,CACV,cAAc,GACd,sBAAsB,GACtB,eAAe,GACf,eAAe,GACf,WAAW,GACX,eAAe,GACf,cAAc,EAChB,KAAK,EACL,KAAK,CACN,CA0DA;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,GAAG,EAC5C,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,GAChB,cAAc,CAAC,cAAc,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,SAAS,CAEvD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,GAAG,EAAE,cAAc,GAAG,KAAK,EAChE,MAAM,CAAC,EAAE,kBAAkB,CAAC,CAAC,EAAE,cAAc,CAAC,GAC7C,iBAAiB,CAAC,CAAC,CAAC,CAoBtB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,GAAG,EAC3C,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,GAChB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAErC"}
@@ -9,6 +9,28 @@ import { DatabaseService, AuthService, AuthUserService, HonertiaService, Request
9
9
  * Symbol for storing Effect runtime in Hono context.
10
10
  */
11
11
  const EFFECT_RUNTIME = Symbol('effectRuntime');
12
+ /**
13
+ * Symbol for storing schema in Hono context.
14
+ */
15
+ const EFFECT_SCHEMA = Symbol('effectSchema');
16
+ /**
17
+ * Creates a proxy that throws a helpful error when any property is accessed.
18
+ * Used when a service (database, auth) is not configured but the user tries to use it.
19
+ */
20
+ function createUnconfiguredServiceProxy(serviceName, configPath, example) {
21
+ const message = `${serviceName} is not configured. Add it to setupHonertia: setupHonertia({ honertia: { ${configPath} } })`;
22
+ return new Proxy({}, {
23
+ get(_, prop) {
24
+ // Allow certain properties that might be checked without meaning to "use" the service
25
+ if (prop === 'then' || prop === Symbol.toStringTag || prop === Symbol.iterator) {
26
+ return undefined;
27
+ }
28
+ const error = new Error(message);
29
+ error.hint = `Example: ${example}`;
30
+ throw error;
31
+ },
32
+ });
33
+ }
12
34
  /**
13
35
  * Create a RequestContext from Hono context.
14
36
  */
@@ -64,14 +86,15 @@ export function buildContextLayer(c, config) {
64
86
  const requestLayer = Layer.succeed(RequestService, createRequestContext(c));
65
87
  const responseLayer = Layer.succeed(ResponseFactoryService, createResponseFactory(c));
66
88
  const honertiaLayer = Layer.succeed(HonertiaService, createHonertiaRenderer(c));
67
- // Build optional layers (cast to satisfy type checker - actual type comes from augmentation)
68
- const databaseLayer = config?.database
69
- ? Layer.succeed(DatabaseService, config.database(c))
70
- : Layer.succeed(DatabaseService, c.var?.db);
71
- let baseLayer = Layer.mergeAll(requestLayer, responseLayer, honertiaLayer, databaseLayer);
72
- if (c.var?.auth) {
73
- baseLayer = Layer.merge(baseLayer, Layer.succeed(AuthService, c.var.auth));
74
- }
89
+ // Database layer - provide helpful error proxy if not configured
90
+ const db = c.var?.db;
91
+ const databaseLayer = Layer.succeed(DatabaseService, (db ??
92
+ createUnconfiguredServiceProxy('DatabaseService', 'database: (c) => createDb(...)', 'database: (c) => drizzle(c.env.DB)')));
93
+ // Auth layer - provide helpful error proxy if not configured
94
+ const auth = c.var?.auth;
95
+ const authLayer = Layer.succeed(AuthService, (auth ??
96
+ createUnconfiguredServiceProxy('AuthService', 'auth: (c) => createAuth(...)', 'auth: (c) => betterAuth({ database: c.var.db, ... })')));
97
+ let baseLayer = Layer.mergeAll(requestLayer, responseLayer, honertiaLayer, databaseLayer, authLayer);
75
98
  if (c.var?.authUser) {
76
99
  baseLayer = Layer.merge(baseLayer, Layer.succeed(AuthUserService, c.var.authUser));
77
100
  }
@@ -97,6 +120,10 @@ export function effectBridge(config) {
97
120
  const runtime = ManagedRuntime.make(layer);
98
121
  // Store runtime in context
99
122
  c.set(EFFECT_RUNTIME, runtime);
123
+ // Store schema in context for route model binding
124
+ if (config?.schema) {
125
+ c.set(EFFECT_SCHEMA, config.schema);
126
+ }
100
127
  try {
101
128
  await next();
102
129
  }
@@ -106,3 +133,9 @@ export function effectBridge(config) {
106
133
  }
107
134
  };
108
135
  }
136
+ /**
137
+ * Get the schema from Hono context (set by effectBridge).
138
+ */
139
+ export function getEffectSchema(c) {
140
+ return c.var?.[EFFECT_SCHEMA];
141
+ }
@@ -8,6 +8,11 @@ import type { Context as HonoContext, MiddlewareHandler, Env } from 'hono';
8
8
  import { Redirect, type AppError } from './errors.js';
9
9
  /**
10
10
  * Convert an Effect error to an HTTP response.
11
+ *
12
+ * Most errors are re-thrown so Hono's onError handler can render them
13
+ * via Honertia's error component. Only errors that need special handling
14
+ * (ValidationError for form re-rendering, UnauthorizedError for redirects)
15
+ * return responses directly.
11
16
  */
12
17
  export declare function errorToResponse<E extends Env>(error: AppError, c: HonoContext<E>): Promise<Response>;
13
18
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../src/effect/handler.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,MAAM,EAA+B,MAAM,QAAQ,CAAA;AAC5D,OAAO,KAAK,EAAE,OAAO,IAAI,WAAW,EAAE,iBAAiB,EAAE,GAAG,EAAE,MAAM,MAAM,CAAA;AAE1E,OAAO,EAOL,QAAQ,EACR,KAAK,QAAQ,EACd,MAAM,aAAa,CAAA;AAGpB;;GAEG;AACH,wBAAsB,eAAe,CAAC,CAAC,SAAS,GAAG,EACjD,KAAK,EAAE,QAAQ,EACf,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,GAChB,OAAO,CAAC,QAAQ,CAAC,CAyDnB;AAgBD;;GAEG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,GAAG,EAAE,CAAC,EAAE,GAAG,SAAS,QAAQ,EAClE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,QAAQ,GAAG,QAAQ,EAAE,GAAG,EAAE,CAAC,CAAC,GACjD,iBAAiB,CAAC,CAAC,CAAC,CAoBtB;AAsCD;;GAEG;AACH,wBAAgB,MAAM,CAAC,CAAC,SAAS,GAAG,EAAE,CAAC,EAAE,GAAG,SAAS,QAAQ,EAC3D,EAAE,EAAE,MAAM,MAAM,CAAC,MAAM,CAAC,QAAQ,GAAG,QAAQ,EAAE,GAAG,EAAE,CAAC,CAAC,GACnD,iBAAiB,CAAC,CAAC,CAAC,CAEtB;AAED;;GAEG;AACH,eAAO,MAAM,MAAM,sBAAgB,CAAA"}
1
+ {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../src/effect/handler.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,MAAM,EAA+B,MAAM,QAAQ,CAAA;AAC5D,OAAO,KAAK,EAAE,OAAO,IAAI,WAAW,EAAE,iBAAiB,EAAE,GAAG,EAAE,MAAM,MAAM,CAAA;AAE1E,OAAO,EAML,QAAQ,EACR,KAAK,QAAQ,EACd,MAAM,aAAa,CAAA;AAuBpB;;;;;;;GAOG;AACH,wBAAsB,eAAe,CAAC,CAAC,SAAS,GAAG,EACjD,KAAK,EAAE,QAAQ,EACf,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,GAChB,OAAO,CAAC,QAAQ,CAAC,CAiDnB;AAgBD;;GAEG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,GAAG,EAAE,CAAC,EAAE,GAAG,SAAS,QAAQ,EAClE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,QAAQ,GAAG,QAAQ,EAAE,GAAG,EAAE,CAAC,CAAC,GACjD,iBAAiB,CAAC,CAAC,CAAC,CAoBtB;AAgDD;;GAEG;AACH,wBAAgB,MAAM,CAAC,CAAC,SAAS,GAAG,EAAE,CAAC,EAAE,GAAG,SAAS,QAAQ,EAC3D,EAAE,EAAE,MAAM,MAAM,CAAC,MAAM,CAAC,QAAQ,GAAG,QAAQ,EAAE,GAAG,EAAE,CAAC,CAAC,GACnD,iBAAiB,CAAC,CAAC,CAAC,CAEtB;AAED;;GAEG;AACH,eAAO,MAAM,MAAM,sBAAgB,CAAA"}
@@ -5,11 +5,36 @@
5
5
  */
6
6
  import { Effect, Exit, Cause, ManagedRuntime } from 'effect';
7
7
  import { getEffectRuntime, buildContextLayer } from './bridge.js';
8
- import { ValidationError, UnauthorizedError, NotFoundError, ForbiddenError, HttpError, RouteConfigurationError, Redirect, } from './errors.js';
8
+ import { ValidationError, UnauthorizedError, NotFoundError, HttpError, RouteConfigurationError, Redirect, } from './errors.js';
9
+ /**
10
+ * Convert an AppError to a throwable Error for Hono's onError handler.
11
+ * Preserves error metadata like status codes and hints.
12
+ */
13
+ function toThrowableError(error) {
14
+ const err = new Error(error.message);
15
+ err.name = error._tag;
16
+ // Preserve status for HttpError
17
+ if (error instanceof HttpError) {
18
+ ;
19
+ err.status = error.status;
20
+ }
21
+ // Preserve hint for RouteConfigurationError
22
+ if (error instanceof RouteConfigurationError && error.hint) {
23
+ ;
24
+ err.hint = error.hint;
25
+ }
26
+ return err;
27
+ }
9
28
  /**
10
29
  * Convert an Effect error to an HTTP response.
30
+ *
31
+ * Most errors are re-thrown so Hono's onError handler can render them
32
+ * via Honertia's error component. Only errors that need special handling
33
+ * (ValidationError for form re-rendering, UnauthorizedError for redirects)
34
+ * return responses directly.
11
35
  */
12
36
  export async function errorToResponse(error, c) {
37
+ // ValidationError: re-render form with errors or redirect back
13
38
  if (error instanceof ValidationError) {
14
39
  const isInertia = c.req.header('X-Inertia') === 'true';
15
40
  const prefersJson = c.req.header('Accept')?.includes('application/json') ||
@@ -28,34 +53,26 @@ export async function errorToResponse(error, c) {
28
53
  c.var?.honertia?.setErrors(error.errors);
29
54
  return c.redirect(referer, 303);
30
55
  }
56
+ // UnauthorizedError: redirect to login
31
57
  if (error instanceof UnauthorizedError) {
32
58
  const isInertia = c.req.header('X-Inertia') === 'true';
33
59
  const redirectTo = error.redirectTo ?? '/login';
34
60
  return c.redirect(redirectTo, isInertia ? 303 : 302);
35
61
  }
36
- if (error instanceof ForbiddenError) {
37
- return c.json({ message: error.message }, 403);
38
- }
62
+ // NotFoundError: use Hono's notFound handler (renders via Honertia if configured)
39
63
  if (error instanceof NotFoundError) {
40
64
  return c.notFound();
41
65
  }
66
+ // ForbiddenError: return 403 JSON (useful for API routes)
67
+ if ('_tag' in error && error._tag === 'ForbiddenError') {
68
+ return c.json({ message: error.message }, 403);
69
+ }
70
+ // HttpError: return custom status JSON (gives developers control over HTTP responses)
42
71
  if (error instanceof HttpError) {
43
72
  return c.json({ message: error.message, ...error.body }, error.status);
44
73
  }
45
- if (error instanceof RouteConfigurationError) {
46
- console.error(`[honertia] Route configuration error: ${error.message}`);
47
- if (error.hint) {
48
- console.error(`[honertia] Hint: ${error.hint}`);
49
- }
50
- return c.json({
51
- message: error.message,
52
- hint: error.hint,
53
- type: 'RouteConfigurationError'
54
- }, 500);
55
- }
56
- // Unknown error
57
- console.error('Unhandled error:', error);
58
- return c.json({ message: 'Internal server error' }, 500);
74
+ // All other errors (RouteConfigurationError, etc.): throw to Hono's onError handler
75
+ throw toThrowableError(error);
59
76
  }
60
77
  /**
61
78
  * Handle a Redirect value (which is not an error).
@@ -93,6 +110,9 @@ export function effectHandler(effect) {
93
110
  }
94
111
  /**
95
112
  * Handle an Effect exit value.
113
+ *
114
+ * Failures are converted to responses via errorToResponse.
115
+ * Defects (unexpected errors) are re-thrown for Hono's onError handler.
96
116
  */
97
117
  async function handleExit(exit, c) {
98
118
  if (Exit.isSuccess(exit)) {
@@ -102,7 +122,7 @@ async function handleExit(exit, c) {
102
122
  }
103
123
  return value;
104
124
  }
105
- // Handle failure
125
+ // Handle typed failures
106
126
  const cause = exit.cause;
107
127
  if (Cause.isFailure(cause)) {
108
128
  const error = Cause.failureOption(cause);
@@ -110,14 +130,21 @@ async function handleExit(exit, c) {
110
130
  return await errorToResponse(error.value, c);
111
131
  }
112
132
  }
113
- // Handle defects (unexpected errors)
133
+ // Handle defects (unexpected errors) - throw to Hono's onError handler
114
134
  if (Cause.isDie(cause)) {
115
135
  const defect = Cause.dieOption(cause);
116
136
  if (defect._tag === 'Some') {
117
- console.error('Effect defect:', defect.value);
137
+ const err = defect.value;
138
+ // If it's already an Error, throw it directly
139
+ if (err instanceof Error) {
140
+ throw err;
141
+ }
142
+ // Otherwise wrap it
143
+ throw new Error(String(err));
118
144
  }
119
145
  }
120
- return c.json({ message: 'Internal server error' }, 500);
146
+ // Fallback: throw generic error for Hono's onError handler
147
+ throw new Error('Unknown effect failure');
121
148
  }
122
149
  /**
123
150
  * Create a handler from a function that returns an Effect.
@@ -7,7 +7,7 @@ export { DatabaseService, AuthService, AuthUserService, HonertiaService, Request
7
7
  export { ValidationError, UnauthorizedError, NotFoundError, ForbiddenError, HttpError, RouteConfigurationError, Redirect, type AppError, } from './errors.js';
8
8
  export * from './schema.js';
9
9
  export { getValidationData, formatSchemaErrors, validate, validateRequest, asValidated, asTrusted, type Validated, type Trusted, } from './validation.js';
10
- export { effectBridge, buildContextLayer, getEffectRuntime, type EffectBridgeConfig, } from './bridge.js';
10
+ export { effectBridge, buildContextLayer, getEffectRuntime, getEffectSchema, type EffectBridgeConfig, } from './bridge.js';
11
11
  export { effectHandler, effect, handle, errorToResponse, } from './handler.js';
12
12
  export { action, authorize, dbMutation, dbTransaction, type SafeTx, } from './action.js';
13
13
  export { redirect, render, renderWithErrors, json, text, notFound, forbidden, httpError, prefersJson, jsonOrRender, share, } from './responses.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/effect/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EACL,eAAe,EACf,WAAW,EACX,eAAe,EACf,eAAe,EACf,cAAc,EACd,sBAAsB,EACtB,KAAK,QAAQ,EACb,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,QAAQ,GACd,MAAM,eAAe,CAAA;AAGtB,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,aAAa,EACb,cAAc,EACd,SAAS,EACT,uBAAuB,EACvB,QAAQ,EACR,KAAK,QAAQ,GACd,MAAM,aAAa,CAAA;AAGpB,cAAc,aAAa,CAAA;AAG3B,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,QAAQ,EACR,eAAe,EACf,WAAW,EACX,SAAS,EACT,KAAK,SAAS,EACd,KAAK,OAAO,GACb,MAAM,iBAAiB,CAAA;AAGxB,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,gBAAgB,EAChB,KAAK,kBAAkB,GACxB,MAAM,aAAa,CAAA;AAGpB,OAAO,EACL,aAAa,EACb,MAAM,EACN,MAAM,EACN,eAAe,GAChB,MAAM,cAAc,CAAA;AAGrB,OAAO,EACL,MAAM,EACN,SAAS,EACT,UAAU,EACV,aAAa,EACb,KAAK,MAAM,GACZ,MAAM,aAAa,CAAA;AAGpB,OAAO,EACL,QAAQ,EACR,MAAM,EACN,gBAAgB,EAChB,IAAI,EACJ,IAAI,EACJ,QAAQ,EACR,SAAS,EACT,SAAS,EACT,WAAW,EACX,YAAY,EACZ,KAAK,GACN,MAAM,gBAAgB,CAAA;AAGvB,OAAO,EACL,kBAAkB,EAClB,YAAY,EACZ,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,kBAAkB,GACxB,MAAM,cAAc,CAAA;AAGrB,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,KAAK,EACL,SAAS,EACT,aAAa,EACb,UAAU,EACV,KAAK,aAAa,EAClB,KAAK,UAAU,GAChB,MAAM,cAAc,CAAA;AAGrB,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,WAAW,EACX,WAAW,EACX,YAAY,EACZ,SAAS,EACT,mBAAmB,EACnB,gBAAgB,EAChB,oBAAoB,EACpB,sBAAsB,EACtB,QAAQ,EACR,KAAK,gBAAgB,EACrB,KAAK,0BAA0B,EAC/B,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,GAC5B,MAAM,WAAW,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/effect/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EACL,eAAe,EACf,WAAW,EACX,eAAe,EACf,eAAe,EACf,cAAc,EACd,sBAAsB,EACtB,KAAK,QAAQ,EACb,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,QAAQ,GACd,MAAM,eAAe,CAAA;AAGtB,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,aAAa,EACb,cAAc,EACd,SAAS,EACT,uBAAuB,EACvB,QAAQ,EACR,KAAK,QAAQ,GACd,MAAM,aAAa,CAAA;AAGpB,cAAc,aAAa,CAAA;AAG3B,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,QAAQ,EACR,eAAe,EACf,WAAW,EACX,SAAS,EACT,KAAK,SAAS,EACd,KAAK,OAAO,GACb,MAAM,iBAAiB,CAAA;AAGxB,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EACf,KAAK,kBAAkB,GACxB,MAAM,aAAa,CAAA;AAGpB,OAAO,EACL,aAAa,EACb,MAAM,EACN,MAAM,EACN,eAAe,GAChB,MAAM,cAAc,CAAA;AAGrB,OAAO,EACL,MAAM,EACN,SAAS,EACT,UAAU,EACV,aAAa,EACb,KAAK,MAAM,GACZ,MAAM,aAAa,CAAA;AAGpB,OAAO,EACL,QAAQ,EACR,MAAM,EACN,gBAAgB,EAChB,IAAI,EACJ,IAAI,EACJ,QAAQ,EACR,SAAS,EACT,SAAS,EACT,WAAW,EACX,YAAY,EACZ,KAAK,GACN,MAAM,gBAAgB,CAAA;AAGvB,OAAO,EACL,kBAAkB,EAClB,YAAY,EACZ,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,kBAAkB,GACxB,MAAM,cAAc,CAAA;AAGrB,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,KAAK,EACL,SAAS,EACT,aAAa,EACb,UAAU,EACV,KAAK,aAAa,EAClB,KAAK,UAAU,GAChB,MAAM,cAAc,CAAA;AAGrB,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,WAAW,EACX,WAAW,EACX,YAAY,EACZ,SAAS,EACT,mBAAmB,EACnB,gBAAgB,EAChB,oBAAoB,EACpB,sBAAsB,EACtB,QAAQ,EACR,KAAK,gBAAgB,EACrB,KAAK,0BAA0B,EAC/B,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,GAC5B,MAAM,WAAW,CAAA"}
@@ -12,7 +12,7 @@ export * from './schema.js';
12
12
  // Validation Helpers
13
13
  export { getValidationData, formatSchemaErrors, validate, validateRequest, asValidated, asTrusted, } from './validation.js';
14
14
  // Bridge
15
- export { effectBridge, buildContextLayer, getEffectRuntime, } from './bridge.js';
15
+ export { effectBridge, buildContextLayer, getEffectRuntime, getEffectSchema, } from './bridge.js';
16
16
  // Handler
17
17
  export { effectHandler, effect, handle, errorToResponse, } from './handler.js';
18
18
  // Action Composables
@@ -1 +1 @@
1
- {"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../../src/effect/routing.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../../src/effect/routing.ts"],"names":[],"mappings":"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"}
@@ -5,7 +5,7 @@
5
5
  */
6
6
  import { Effect, Exit, Layer, Schema as S } from 'effect';
7
7
  import { effectHandler } from './handler.js';
8
- import { buildContextLayer } from './bridge.js';
8
+ import { buildContextLayer, getEffectSchema } from './bridge.js';
9
9
  import { parseBindings, toHonoPath, pluralize, findRelation, BoundModels, } from './binding.js';
10
10
  /**
11
11
  * Effect Route Builder with layer composition.
@@ -125,18 +125,20 @@ export class EffectRouteBuilder {
125
125
  const contextLayer = buildContextLayer(c, bridgeConfig);
126
126
  // Resolve route model bindings if we have any and schema is configured
127
127
  let boundModelsLayer;
128
- if (bindings.length > 0 && bridgeConfig?.schema) {
129
- const db = bridgeConfig.database ? bridgeConfig.database(c) : c.var?.db;
128
+ // Get schema from bridgeConfig or from context (set by setupHonertia/effectBridge)
129
+ const schema = bridgeConfig?.schema ?? getEffectSchema(c);
130
+ if (bindings.length > 0 && schema) {
131
+ const db = c.var?.db;
130
132
  if (!db) {
131
133
  return c.notFound();
132
134
  }
133
- const result = await this.resolveBindings(c, bindings, db, bridgeConfig.schema);
135
+ const result = await this.resolveBindings(c, bindings, db, schema);
134
136
  if (result instanceof Response) {
135
137
  return result;
136
138
  }
137
139
  boundModelsLayer = Layer.succeed(BoundModels, result);
138
140
  }
139
- else if (bindings.length > 0 && !bridgeConfig?.schema) {
141
+ else if (bindings.length > 0 && !schema) {
140
142
  // Bindings exist but no schema - provide a map that signals this for better errors
141
143
  const unconfiguredMap = new Map();
142
144
  unconfiguredMap.set('__schema_not_configured__', true);
package/dist/index.d.ts CHANGED
@@ -6,7 +6,7 @@
6
6
  * For schema validators, import from 'honertia/schema'.
7
7
  * For auth helpers, import from 'honertia/auth'.
8
8
  */
9
- export { setupHonertia, createErrorHandlers, registerErrorHandlers, type HonertiaSetupConfig, type ErrorHandlerConfig, } from './setup.js';
9
+ export { setupHonertia, createErrorHandlers, registerErrorHandlers, type HonertiaSetupConfig, type HonertiaFullConfig, type ErrorHandlerConfig, } from './setup.js';
10
10
  export { honertia, HEADERS } from './middleware.js';
11
11
  export type { PageObject, HonertiaConfig, HonertiaInstance, RenderOptions, } from './types.js';
12
12
  export { createTemplate, createVersion, vite, type PageProps, } from './helpers.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,qBAAqB,EACrB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,GACxB,MAAM,YAAY,CAAA;AAGnB,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AAEnD,YAAY,EACV,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,aAAa,GACd,MAAM,YAAY,CAAA;AAGnB,OAAO,EACL,cAAc,EACd,aAAa,EACb,IAAI,EACJ,KAAK,SAAS,GACf,MAAM,cAAc,CAAA;AAOrB,cAAc,mBAAmB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,qBAAqB,EACrB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,GACxB,MAAM,YAAY,CAAA;AAGnB,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AAEnD,YAAY,EACV,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,aAAa,GACd,MAAM,YAAY,CAAA;AAGnB,OAAO,EACL,cAAc,EACd,aAAa,EACb,IAAI,EACJ,KAAK,SAAS,GACf,MAAM,cAAc,CAAA;AAOrB,cAAc,mBAAmB,CAAA"}
package/dist/setup.d.ts CHANGED
@@ -7,20 +7,66 @@
7
7
  import type { MiddlewareHandler, Env, Context } from 'hono';
8
8
  import type { HonertiaConfig } from './types.js';
9
9
  import { type EffectBridgeConfig } from './effect/bridge.js';
10
+ /**
11
+ * Extended Honertia configuration with database, auth, and schema.
12
+ */
13
+ export interface HonertiaFullConfig<E extends Env = Env> extends HonertiaConfig {
14
+ /**
15
+ * Database factory function.
16
+ * Creates the database client for each request.
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * database: (c) => createDb(c.env.DATABASE_URL)
21
+ * ```
22
+ */
23
+ database?: (c: Context<E>) => unknown;
24
+ /**
25
+ * Auth factory function.
26
+ * Creates the auth client for each request.
27
+ * Receives context with `c.var.db` already set (if database is configured).
28
+ *
29
+ * @example
30
+ * ```typescript
31
+ * auth: (c) => createAuth({
32
+ * db: c.var.db,
33
+ * secret: c.env.BETTER_AUTH_SECRET,
34
+ * baseURL: new URL(c.req.url).origin,
35
+ * })
36
+ * ```
37
+ */
38
+ auth?: (c: Context<E>) => unknown;
39
+ /**
40
+ * Drizzle schema for route model binding.
41
+ * Required if using Laravel-style route model binding.
42
+ *
43
+ * @example
44
+ * ```typescript
45
+ * import * as schema from '~/db/schema'
46
+ *
47
+ * setupHonertia({
48
+ * honertia: { version, render, schema }
49
+ * })
50
+ * ```
51
+ */
52
+ schema?: Record<string, unknown>;
53
+ }
10
54
  /**
11
55
  * Configuration for Honertia setup.
12
56
  */
13
57
  export interface HonertiaSetupConfig<E extends Env = Env, CustomServices = never> {
14
58
  /**
15
- * Honertia core configuration.
59
+ * Honertia core configuration including database, auth, and schema.
16
60
  */
17
- honertia: HonertiaConfig;
61
+ honertia: HonertiaFullConfig<E>;
18
62
  /**
19
63
  * Effect bridge configuration (optional).
64
+ * Only needed for custom Effect services.
20
65
  */
21
66
  effect?: EffectBridgeConfig<E, CustomServices>;
22
67
  /**
23
- * Auth configuration (optional).
68
+ * Auth loading configuration (optional).
69
+ * Controls how the authenticated user is loaded from the session.
24
70
  */
25
71
  auth?: {
26
72
  userKey?: string;
@@ -36,6 +82,7 @@ export interface HonertiaSetupConfig<E extends Env = Env, CustomServices = never
36
82
  * Sets up all Honertia middleware in the correct order.
37
83
  *
38
84
  * This bundles:
85
+ * - Database and auth setup (sets `c.var.db` and `c.var.auth`)
39
86
  * - `honertia()` - Core Honertia middleware
40
87
  * - `loadUser()` - Loads authenticated user into context
41
88
  * - `shareAuthMiddleware()` - Shares auth state with pages
@@ -44,11 +91,19 @@ export interface HonertiaSetupConfig<E extends Env = Env, CustomServices = never
44
91
  * @example
45
92
  * ```ts
46
93
  * import { setupHonertia, createTemplate } from 'honertia'
94
+ * import * as schema from '~/db/schema'
47
95
  *
48
96
  * app.use('*', setupHonertia({
49
97
  * honertia: {
50
98
  * version: '1.0.0',
51
99
  * render: createTemplate({ title: 'My App', scripts: [...] }),
100
+ * database: (c) => createDb(c.env.DATABASE_URL),
101
+ * auth: (c) => createAuth({
102
+ * db: c.var.db,
103
+ * secret: c.env.BETTER_AUTH_SECRET,
104
+ * baseURL: new URL(c.req.url).origin,
105
+ * }),
106
+ * schema,
52
107
  * },
53
108
  * }))
54
109
  * ```
@@ -1 +1 @@
1
- {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAE3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAEhD,OAAO,EAAgB,KAAK,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AAE1E;;GAEG;AACH,MAAM,WAAW,mBAAmB,CAAC,CAAC,SAAS,GAAG,GAAG,GAAG,EAAE,cAAc,GAAG,KAAK;IAC9E;;OAEG;IACH,QAAQ,EAAE,cAAc,CAAA;IAExB;;OAEG;IACH,MAAM,CAAC,EAAE,kBAAkB,CAAC,CAAC,EAAE,cAAc,CAAC,CAAA;IAE9C;;OAEG;IACH,IAAI,CAAC,EAAE;QACL,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,aAAa,CAAC,EAAE,MAAM,CAAA;KACvB,CAAA;IAED;;;OAGG;IACH,UAAU,CAAC,EAAE,iBAAiB,CAAC,CAAC,CAAC,EAAE,CAAA;CACpC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,GAAG,EAAE,cAAc,GAAG,KAAK,EACjE,MAAM,EAAE,mBAAmB,CAAC,CAAC,EAAE,cAAc,CAAC,GAC7C,iBAAiB,CAAC,CAAC,CAAC,CAmBtB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAElB;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;IAEvB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IAEf;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,GAAG,EAAE,MAAM,GAAE,kBAAuB;kBAQ3D,OAAO,CAAC,CAAC,CAAC;mBAOT,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC;EAU3C;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,SAAS,GAAG,EACjD,GAAG,EAAE;IAAE,QAAQ,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,IAAI,CAAC;IAAC,OAAO,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,IAAI,CAAA;CAAE,EAC1E,MAAM,GAAE,kBAAuB,GAC9B,IAAI,CAIN"}
1
+ {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAE3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAEhD,OAAO,EAAgB,KAAK,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AAE1E;;GAEG;AACH,MAAM,WAAW,kBAAkB,CAAC,CAAC,SAAS,GAAG,GAAG,GAAG,CAAE,SAAQ,cAAc;IAC7E;;;;;;;;OAQG;IACH,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAA;IAErC;;;;;;;;;;;;;OAaG;IACH,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAA;IAEjC;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACjC;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB,CAAC,CAAC,SAAS,GAAG,GAAG,GAAG,EAAE,cAAc,GAAG,KAAK;IAC9E;;OAEG;IACH,QAAQ,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAAA;IAE/B;;;OAGG;IACH,MAAM,CAAC,EAAE,kBAAkB,CAAC,CAAC,EAAE,cAAc,CAAC,CAAA;IAE9C;;;OAGG;IACH,IAAI,CAAC,EAAE;QACL,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,aAAa,CAAC,EAAE,MAAM,CAAA;KACvB,CAAA;IAED;;;OAGG;IACH,UAAU,CAAC,EAAE,iBAAiB,CAAC,CAAC,CAAC,EAAE,CAAA;CACpC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,GAAG,EAAE,cAAc,GAAG,KAAK,EACjE,MAAM,EAAE,mBAAmB,CAAC,CAAC,EAAE,cAAc,CAAC,GAC7C,iBAAiB,CAAC,CAAC,CAAC,CA2CtB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAElB;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;IAEvB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IAEf;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,GAAG,EAAE,MAAM,GAAE,kBAAuB;kBAQ3D,OAAO,CAAC,CAAC,CAAC;mBAOT,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC;EAa3C;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,SAAS,GAAG,EACjD,GAAG,EAAE;IAAE,QAAQ,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,IAAI,CAAC;IAAC,OAAO,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,IAAI,CAAA;CAAE,EAC1E,MAAM,GAAE,kBAAuB,GAC9B,IAAI,CAIN"}
package/dist/setup.js CHANGED
@@ -12,6 +12,7 @@ import { effectBridge } from './effect/bridge.js';
12
12
  * Sets up all Honertia middleware in the correct order.
13
13
  *
14
14
  * This bundles:
15
+ * - Database and auth setup (sets `c.var.db` and `c.var.auth`)
15
16
  * - `honertia()` - Core Honertia middleware
16
17
  * - `loadUser()` - Loads authenticated user into context
17
18
  * - `shareAuthMiddleware()` - Shares auth state with pages
@@ -20,21 +21,48 @@ import { effectBridge } from './effect/bridge.js';
20
21
  * @example
21
22
  * ```ts
22
23
  * import { setupHonertia, createTemplate } from 'honertia'
24
+ * import * as schema from '~/db/schema'
23
25
  *
24
26
  * app.use('*', setupHonertia({
25
27
  * honertia: {
26
28
  * version: '1.0.0',
27
29
  * render: createTemplate({ title: 'My App', scripts: [...] }),
30
+ * database: (c) => createDb(c.env.DATABASE_URL),
31
+ * auth: (c) => createAuth({
32
+ * db: c.var.db,
33
+ * secret: c.env.BETTER_AUTH_SECRET,
34
+ * baseURL: new URL(c.req.url).origin,
35
+ * }),
36
+ * schema,
28
37
  * },
29
38
  * }))
30
39
  * ```
31
40
  */
32
41
  export function setupHonertia(config) {
42
+ const { database, auth, schema, ...honertiaConfig } = config.honertia;
43
+ // Middleware to set up db and auth on c.var
44
+ const setupServices = createMiddleware(async (c, next) => {
45
+ // Set up database first (auth may depend on it)
46
+ if (database) {
47
+ c.set('db', database(c));
48
+ }
49
+ // Set up auth (can access c.var.db)
50
+ if (auth) {
51
+ c.set('auth', auth(c));
52
+ }
53
+ await next();
54
+ });
55
+ // Build effect bridge config, passing schema from honertia config
56
+ const effectConfig = {
57
+ ...config.effect,
58
+ schema,
59
+ };
33
60
  const middlewares = [
34
- honertia(config.honertia),
61
+ setupServices,
62
+ honertia(honertiaConfig),
35
63
  loadUser(config.auth),
36
64
  shareAuthMiddleware(),
37
- effectBridge(config.effect),
65
+ effectBridge(effectConfig),
38
66
  ...(config.middleware ?? []),
39
67
  ];
40
68
  return createMiddleware(async (c, next) => {
@@ -72,9 +100,12 @@ export function createErrorHandlers(config = {}) {
72
100
  const onError = (err, c) => {
73
101
  console.error(err);
74
102
  const isDev = showDevErrors && c.env?.[envKey] === devValue;
103
+ const status = err.status ?? 500;
104
+ const hint = isDev ? err.hint : undefined;
75
105
  return c.var.honertia.render(component, {
76
- status: 500,
106
+ status,
77
107
  message: isDev ? err.message : 'Something went wrong',
108
+ ...(hint && { hint }),
78
109
  });
79
110
  };
80
111
  return { notFound, onError };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "honertia",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
4
4
  "description": "Inertia.js-style server-driven SPA adapter for Hono",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",