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 +225 -24
- package/dist/effect/binding.js +1 -1
- package/dist/effect/bridge.d.ts +12 -11
- package/dist/effect/bridge.d.ts.map +1 -1
- package/dist/effect/bridge.js +41 -8
- package/dist/effect/handler.d.ts +5 -0
- package/dist/effect/handler.d.ts.map +1 -1
- package/dist/effect/handler.js +49 -22
- package/dist/effect/index.d.ts +1 -1
- package/dist/effect/index.d.ts.map +1 -1
- package/dist/effect/index.js +1 -1
- package/dist/effect/routing.d.ts.map +1 -1
- package/dist/effect/routing.js +7 -5
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/setup.d.ts +58 -3
- package/dist/setup.d.ts.map +1 -1
- package/dist/setup.js +34 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -84,18 +84,7 @@ class BindingsService extends Context.Tag('app/Bindings')<
|
|
|
84
84
|
{ KV: KVNamespace }
|
|
85
85
|
>() {}
|
|
86
86
|
|
|
87
|
-
//
|
|
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
|
-
//
|
|
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 `
|
|
964
|
+
2. Pass your schema to `setupHonertia`:
|
|
967
965
|
|
|
968
966
|
```typescript
|
|
969
967
|
import * as schema from '~/db/schema'
|
|
970
968
|
|
|
971
|
-
|
|
972
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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, {
|
package/dist/effect/binding.js
CHANGED
|
@@ -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
|
|
68
|
+
hint: `Pass your schema to setupHonertia: setupHonertia({ honertia: { schema } })`
|
|
69
69
|
});
|
|
70
70
|
}
|
|
71
71
|
const model = models.get(key);
|
package/dist/effect/bridge.d.ts
CHANGED
|
@@ -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
|
-
*
|
|
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
|
-
*
|
|
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
|
|
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"}
|
package/dist/effect/bridge.js
CHANGED
|
@@ -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
|
-
//
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
:
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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
|
+
}
|
package/dist/effect/handler.d.ts
CHANGED
|
@@ -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,
|
|
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"}
|
package/dist/effect/handler.js
CHANGED
|
@@ -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,
|
|
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
|
-
|
|
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
|
-
|
|
46
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
package/dist/effect/index.d.ts
CHANGED
|
@@ -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"}
|
package/dist/effect/index.js
CHANGED
|
@@ -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":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,MAAM,EAAQ,KAAK,EAAE,MAAM,IAAI,CAAC,EAAE,MAAM,QAAQ,CAAA;AACzD,OAAO,KAAK,EAA0B,IAAI,EAAqB,GAAG,EAAE,MAAM,MAAM,CAAA;AAEhF,OAAO,
|
|
1
|
+
{"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../../src/effect/routing.ts"],"names":[],"mappings":"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"}
|
package/dist/effect/routing.js
CHANGED
|
@@ -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
|
-
|
|
129
|
-
|
|
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,
|
|
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 && !
|
|
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';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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:
|
|
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
|
* ```
|
package/dist/setup.d.ts.map
CHANGED
|
@@ -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,
|
|
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
|
-
|
|
61
|
+
setupServices,
|
|
62
|
+
honertia(honertiaConfig),
|
|
35
63
|
loadUser(config.auth),
|
|
36
64
|
shareAuthMiddleware(),
|
|
37
|
-
effectBridge(
|
|
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
|
|
106
|
+
status,
|
|
77
107
|
message: isDev ? err.message : 'Something went wrong',
|
|
108
|
+
...(hint && { hint }),
|
|
78
109
|
});
|
|
79
110
|
};
|
|
80
111
|
return { notFound, onError };
|