honertia 0.1.5 → 0.1.7
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 +319 -218
- package/dist/effect/auth.d.ts +5 -1
- package/dist/effect/auth.d.ts.map +1 -1
- package/dist/effect/bridge.d.ts.map +1 -1
- package/dist/effect/index.d.ts +1 -1
- package/dist/effect/index.d.ts.map +1 -1
- package/dist/effect/routing.d.ts.map +1 -1
- package/dist/effect/schema.d.ts.map +1 -1
- package/dist/effect/services.d.ts +41 -0
- package/dist/effect/services.d.ts.map +1 -1
- package/dist/effect/services.js +6 -0
- package/dist/effect/validation.d.ts +35 -13
- package/dist/effect/validation.d.ts.map +1 -1
- package/dist/effect/validation.js +29 -35
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -23,10 +23,15 @@ I wanted to build on Cloudflare Workers whilst retaining the ergonomics of the L
|
|
|
23
23
|
bun add honertia
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
### Demo
|
|
27
|
+
|
|
28
|
+
Deploy the honertia-worker-demo repo to Cloudflare
|
|
27
29
|
|
|
28
30
|
[](https://deploy.workers.cloudflare.com/?url=https://github.com/PatrickOgilvie/honertia-worker-demo)
|
|
29
31
|
|
|
32
|
+
## Quick Start
|
|
33
|
+
|
|
34
|
+
|
|
30
35
|
### Recommended File Structure
|
|
31
36
|
|
|
32
37
|
```
|
|
@@ -429,6 +434,245 @@ vite.hmrHead() // HMR preamble script tags for React Fast Refresh
|
|
|
429
434
|
- **Dependencies**:
|
|
430
435
|
- `effect` >= 3.12.0
|
|
431
436
|
|
|
437
|
+
## Anatomy of an Action
|
|
438
|
+
|
|
439
|
+
Actions in Honertia are fully composable Effect computations. Instead of using different action factories for different combinations of features, you opt-in to exactly what you need by yielding services and helpers inside your action.
|
|
440
|
+
|
|
441
|
+
This design is inspired by Laravel's [laravel-actions](https://laravelactions.com/) package, where you opt-in to capabilities by adding methods to your action class. In Honertia, you opt-in by yielding services - the order of your `yield*` statements determines the execution order.
|
|
442
|
+
|
|
443
|
+
### The `action` Wrapper
|
|
444
|
+
|
|
445
|
+
The `action` function is a semantic wrapper that marks an Effect as an action:
|
|
446
|
+
|
|
447
|
+
```typescript
|
|
448
|
+
import { Effect } from 'effect'
|
|
449
|
+
import { action } from 'honertia/effect'
|
|
450
|
+
|
|
451
|
+
export const myAction = action(
|
|
452
|
+
Effect.gen(function* () {
|
|
453
|
+
// Your action logic here
|
|
454
|
+
return new Response('OK')
|
|
455
|
+
})
|
|
456
|
+
)
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
It's intentionally minimal - all the power comes from what you yield inside.
|
|
460
|
+
|
|
461
|
+
### Composable Helpers
|
|
462
|
+
|
|
463
|
+
#### `authorize` - Authentication & Authorization
|
|
464
|
+
|
|
465
|
+
Opt-in to authentication and authorization checks. Returns the authenticated user, fails with `UnauthorizedError` if no user is present, and fails with `ForbiddenError` if the check returns `false`.
|
|
466
|
+
|
|
467
|
+
```typescript
|
|
468
|
+
import { authorize } from 'honertia/effect'
|
|
469
|
+
|
|
470
|
+
// Just require authentication (any logged-in user)
|
|
471
|
+
const auth = yield* authorize()
|
|
472
|
+
|
|
473
|
+
// Require a specific role
|
|
474
|
+
const auth = yield* authorize((a) => a.user.role === 'admin')
|
|
475
|
+
|
|
476
|
+
// Require resource ownership (see caveat below)
|
|
477
|
+
const auth = yield* authorize((a) => a.user.id === project.userId)
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
If the check function returns `false`, the action fails immediately with a `ForbiddenError`.
|
|
481
|
+
|
|
482
|
+
> **Query-level vs authorize() checks**
|
|
483
|
+
>
|
|
484
|
+
> Use `authorize()` for:
|
|
485
|
+
> - Role/permission checks before any DB work: `authorize((a) => a.user.role === 'admin')`
|
|
486
|
+
> - Checks that can't be expressed in SQL
|
|
487
|
+
>
|
|
488
|
+
> For resource ownership, prefer query-level filtering:
|
|
489
|
+
> ```typescript
|
|
490
|
+
> // Better: single query, no information leakage
|
|
491
|
+
> const auth = yield* authorize()
|
|
492
|
+
> const project = yield* Effect.tryPromise(() =>
|
|
493
|
+
> db.query.projects.findFirst({
|
|
494
|
+
> where: and(eq(projects.id, id), eq(projects.userId, auth.user.id)),
|
|
495
|
+
> })
|
|
496
|
+
> )
|
|
497
|
+
> if (!project) return yield* notFound('Project')
|
|
498
|
+
> ```
|
|
499
|
+
>
|
|
500
|
+
> This approach is more secure (no difference between "not found" and "not yours") and more efficient (single query).
|
|
501
|
+
|
|
502
|
+
#### `validateRequest` - Schema Validation
|
|
503
|
+
|
|
504
|
+
Opt-in to request validation using Effect Schema:
|
|
505
|
+
|
|
506
|
+
```typescript
|
|
507
|
+
import { Schema as S } from 'effect'
|
|
508
|
+
import { validateRequest, requiredString } from 'honertia/effect'
|
|
509
|
+
|
|
510
|
+
const input = yield* validateRequest(
|
|
511
|
+
S.Struct({ name: requiredString, description: S.optional(S.String) }),
|
|
512
|
+
{ errorComponent: 'Projects/Create' }
|
|
513
|
+
)
|
|
514
|
+
// input is fully typed: { name: string, description?: string }
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
On validation failure, re-renders `errorComponent` with field-level errors.
|
|
518
|
+
|
|
519
|
+
#### `DatabaseService` - Database Access
|
|
520
|
+
|
|
521
|
+
Opt-in to database access:
|
|
522
|
+
|
|
523
|
+
```typescript
|
|
524
|
+
import { DatabaseService } from 'honertia/effect'
|
|
525
|
+
|
|
526
|
+
const db = yield* DatabaseService
|
|
527
|
+
const projects = yield* Effect.tryPromise(() =>
|
|
528
|
+
db.query.projects.findMany()
|
|
529
|
+
)
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
#### `render` / `redirect` - Responses
|
|
533
|
+
|
|
534
|
+
Return responses from your action:
|
|
535
|
+
|
|
536
|
+
```typescript
|
|
537
|
+
import { render, redirect } from 'honertia/effect'
|
|
538
|
+
|
|
539
|
+
// Render a page
|
|
540
|
+
return yield* render('Projects/Index', { projects })
|
|
541
|
+
|
|
542
|
+
// Redirect after mutation
|
|
543
|
+
return yield* redirect('/projects')
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
### Building an Action
|
|
547
|
+
|
|
548
|
+
Here's how these composables work together:
|
|
549
|
+
|
|
550
|
+
```typescript
|
|
551
|
+
import { Effect, Schema as S } from 'effect'
|
|
552
|
+
import {
|
|
553
|
+
action,
|
|
554
|
+
authorize,
|
|
555
|
+
validateRequest,
|
|
556
|
+
DatabaseService,
|
|
557
|
+
redirect,
|
|
558
|
+
requiredString,
|
|
559
|
+
} from 'honertia/effect'
|
|
560
|
+
|
|
561
|
+
const CreateProjectSchema = S.Struct({
|
|
562
|
+
name: requiredString,
|
|
563
|
+
description: S.optional(S.String),
|
|
564
|
+
})
|
|
565
|
+
|
|
566
|
+
export const createProject = action(
|
|
567
|
+
Effect.gen(function* () {
|
|
568
|
+
// 1. Authorization - fail fast if not allowed
|
|
569
|
+
const auth = yield* authorize((a) => a.user.role === 'author')
|
|
570
|
+
|
|
571
|
+
// 2. Validation - parse and validate request body
|
|
572
|
+
const input = yield* validateRequest(CreateProjectSchema, {
|
|
573
|
+
errorComponent: 'Projects/Create',
|
|
574
|
+
})
|
|
575
|
+
|
|
576
|
+
// 3. Database - perform the mutation
|
|
577
|
+
const db = yield* DatabaseService
|
|
578
|
+
yield* Effect.tryPromise(() =>
|
|
579
|
+
db.insert(projects).values({
|
|
580
|
+
...input,
|
|
581
|
+
userId: auth.user.id,
|
|
582
|
+
})
|
|
583
|
+
)
|
|
584
|
+
|
|
585
|
+
// 4. Response - redirect on success
|
|
586
|
+
return yield* redirect('/projects')
|
|
587
|
+
})
|
|
588
|
+
)
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
### Execution Order Matters
|
|
592
|
+
|
|
593
|
+
The order you yield services determines when they execute:
|
|
594
|
+
|
|
595
|
+
```typescript
|
|
596
|
+
// Authorization BEFORE validation (recommended for most actions)
|
|
597
|
+
// Don't waste cycles validating if user can't perform the action
|
|
598
|
+
const auth = yield* authorize((a) => a.user.role === 'admin')
|
|
599
|
+
const input = yield* validateRequest(schema)
|
|
600
|
+
|
|
601
|
+
// Validation BEFORE authorization (when you need to fetch the resource first)
|
|
602
|
+
// Validate the ID format, fetch from DB, then check ownership against the DB record
|
|
603
|
+
const { id } = yield* validateRequest(Schema.Struct({ id: Schema.UUID }))
|
|
604
|
+
const project = yield* db.findProjectById(id)
|
|
605
|
+
const auth = yield* authorize((a) => a.user.id === project.ownerId)
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
### Type Safety
|
|
609
|
+
|
|
610
|
+
Effect tracks all service requirements at the type level. Your action's type signature shows exactly what it needs:
|
|
611
|
+
|
|
612
|
+
```typescript
|
|
613
|
+
// This action requires: RequestService, DatabaseService
|
|
614
|
+
export const createProject: Effect.Effect<
|
|
615
|
+
Response | Redirect,
|
|
616
|
+
ValidationError | UnauthorizedError | ForbiddenError | Error,
|
|
617
|
+
RequestService | DatabaseService
|
|
618
|
+
>
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
The compiler ensures all required services are provided when the action runs.
|
|
622
|
+
Note: `authorize` uses an optional `AuthUserService`, so it won't appear in the required service list unless you `yield* AuthUserService` directly or provide `RequireAuthLayer` explicitly.
|
|
623
|
+
|
|
624
|
+
### Minimal Actions
|
|
625
|
+
|
|
626
|
+
Not every action needs all features. Use only what you need:
|
|
627
|
+
|
|
628
|
+
```typescript
|
|
629
|
+
// Public page - no auth, no validation
|
|
630
|
+
export const showAbout = action(
|
|
631
|
+
Effect.gen(function* () {
|
|
632
|
+
return yield* render('About', {})
|
|
633
|
+
})
|
|
634
|
+
)
|
|
635
|
+
|
|
636
|
+
// Read-only authenticated page
|
|
637
|
+
export const showDashboard = action(
|
|
638
|
+
Effect.gen(function* () {
|
|
639
|
+
const auth = yield* authorize()
|
|
640
|
+
const db = yield* DatabaseService
|
|
641
|
+
const stats = yield* fetchStats(db, auth)
|
|
642
|
+
return yield* render('Dashboard', { stats })
|
|
643
|
+
})
|
|
644
|
+
)
|
|
645
|
+
|
|
646
|
+
// API endpoint with just validation
|
|
647
|
+
export const searchProjects = action(
|
|
648
|
+
Effect.gen(function* () {
|
|
649
|
+
const { query } = yield* validateRequest(S.Struct({ query: S.String }))
|
|
650
|
+
const db = yield* DatabaseService
|
|
651
|
+
const results = yield* search(db, query)
|
|
652
|
+
return yield* json({ results })
|
|
653
|
+
})
|
|
654
|
+
)
|
|
655
|
+
```
|
|
656
|
+
|
|
657
|
+
### Helper Utilities
|
|
658
|
+
|
|
659
|
+
#### `dbTransaction` - Database Transactions
|
|
660
|
+
|
|
661
|
+
Run multiple database operations in a transaction with automatic rollback on failure. The database instance is passed explicitly to keep the dependency visible and consistent with other service patterns:
|
|
662
|
+
|
|
663
|
+
```typescript
|
|
664
|
+
import { DatabaseService, dbTransaction } from 'honertia/effect'
|
|
665
|
+
|
|
666
|
+
const db = yield* DatabaseService
|
|
667
|
+
|
|
668
|
+
yield* dbTransaction(db, async (tx) => {
|
|
669
|
+
await tx.insert(users).values({ name: 'Alice', email: 'alice@example.com' })
|
|
670
|
+
await tx.update(accounts).set({ balance: 100 }).where(eq(accounts.userId, id))
|
|
671
|
+
// If any operation fails, the entire transaction rolls back
|
|
672
|
+
return { success: true }
|
|
673
|
+
})
|
|
674
|
+
```
|
|
675
|
+
|
|
432
676
|
## Core Concepts
|
|
433
677
|
|
|
434
678
|
### Effect-Based Handlers
|
|
@@ -655,6 +899,45 @@ export const createProject = Effect.gen(function* () {
|
|
|
655
899
|
})
|
|
656
900
|
```
|
|
657
901
|
|
|
902
|
+
### Validation Options
|
|
903
|
+
|
|
904
|
+
`validateRequest` accepts an options object with:
|
|
905
|
+
|
|
906
|
+
```typescript
|
|
907
|
+
const input = yield* validateRequest(schema, {
|
|
908
|
+
// Re-render this component with errors on validation failure
|
|
909
|
+
// If not set, redirects back to the previous page
|
|
910
|
+
errorComponent: 'Projects/Create',
|
|
911
|
+
|
|
912
|
+
// Override default error messages per field
|
|
913
|
+
messages: {
|
|
914
|
+
name: 'Please enter a project name',
|
|
915
|
+
email: 'That email address is not valid',
|
|
916
|
+
},
|
|
917
|
+
|
|
918
|
+
// Human-readable field names for the :attribute placeholder
|
|
919
|
+
// Use with messages like 'The :attribute field is required'
|
|
920
|
+
attributes: {
|
|
921
|
+
name: 'project name',
|
|
922
|
+
email: 'email address',
|
|
923
|
+
},
|
|
924
|
+
})
|
|
925
|
+
```
|
|
926
|
+
|
|
927
|
+
**Example with `:attribute` placeholder:**
|
|
928
|
+
|
|
929
|
+
```typescript
|
|
930
|
+
const schema = S.Struct({
|
|
931
|
+
email: S.String.pipe(S.minLength(1, { message: () => 'The :attribute field is required' })),
|
|
932
|
+
})
|
|
933
|
+
|
|
934
|
+
const input = yield* validateRequest(schema, {
|
|
935
|
+
attributes: { email: 'email address' },
|
|
936
|
+
errorComponent: 'Auth/Register',
|
|
937
|
+
})
|
|
938
|
+
// Error: "The email address field is required"
|
|
939
|
+
```
|
|
940
|
+
|
|
658
941
|
### Available Validators
|
|
659
942
|
|
|
660
943
|
#### Strings
|
|
@@ -972,223 +1255,6 @@ export const logoutUser = betterAuthLogoutAction({
|
|
|
972
1255
|
2. **better-auth returns an error** → Calls your `errorMapper`, then re-renders `errorComponent` with those errors
|
|
973
1256
|
3. **Success** → Sets session cookies from better-auth's response headers, then 303 redirects to `redirectTo`
|
|
974
1257
|
|
|
975
|
-
## Anatomy of an Action
|
|
976
|
-
|
|
977
|
-
Actions in Honertia are fully composable Effect computations. Instead of using different action factories for different combinations of features, you opt-in to exactly what you need by yielding services and helpers inside your action.
|
|
978
|
-
|
|
979
|
-
This design is inspired by Laravel's [laravel-actions](https://laravelactions.com/) package, where you opt-in to capabilities by adding methods to your action class. In Honertia, you opt-in by yielding services - the order of your `yield*` statements determines the execution order.
|
|
980
|
-
|
|
981
|
-
### The `action` Wrapper
|
|
982
|
-
|
|
983
|
-
The `action` function is a semantic wrapper that marks an Effect as an action:
|
|
984
|
-
|
|
985
|
-
```typescript
|
|
986
|
-
import { Effect } from 'effect'
|
|
987
|
-
import { action } from 'honertia/effect'
|
|
988
|
-
|
|
989
|
-
export const myAction = action(
|
|
990
|
-
Effect.gen(function* () {
|
|
991
|
-
// Your action logic here
|
|
992
|
-
return new Response('OK')
|
|
993
|
-
})
|
|
994
|
-
)
|
|
995
|
-
```
|
|
996
|
-
|
|
997
|
-
It's intentionally minimal - all the power comes from what you yield inside.
|
|
998
|
-
|
|
999
|
-
### Composable Helpers
|
|
1000
|
-
|
|
1001
|
-
#### `authorize` - Authentication & Authorization
|
|
1002
|
-
|
|
1003
|
-
Opt-in to authentication and authorization checks. Returns the authenticated user, fails with `UnauthorizedError` if no user is present, and fails with `ForbiddenError` if the check returns `false`.
|
|
1004
|
-
|
|
1005
|
-
```typescript
|
|
1006
|
-
import { authorize } from 'honertia/effect'
|
|
1007
|
-
|
|
1008
|
-
// Just require authentication (any logged-in user)
|
|
1009
|
-
const auth = yield* authorize()
|
|
1010
|
-
|
|
1011
|
-
// Require a specific role
|
|
1012
|
-
const auth = yield* authorize((a) => a.user.role === 'admin')
|
|
1013
|
-
|
|
1014
|
-
// Require resource ownership
|
|
1015
|
-
const auth = yield* authorize((a) => a.user.id === project.userId)
|
|
1016
|
-
```
|
|
1017
|
-
|
|
1018
|
-
If the check function returns `false`, the action fails immediately with a `ForbiddenError`.
|
|
1019
|
-
|
|
1020
|
-
#### `validateRequest` - Schema Validation
|
|
1021
|
-
|
|
1022
|
-
Opt-in to request validation using Effect Schema:
|
|
1023
|
-
|
|
1024
|
-
```typescript
|
|
1025
|
-
import { Schema as S } from 'effect'
|
|
1026
|
-
import { validateRequest, requiredString } from 'honertia/effect'
|
|
1027
|
-
|
|
1028
|
-
const input = yield* validateRequest(
|
|
1029
|
-
S.Struct({ name: requiredString, description: S.optional(S.String) }),
|
|
1030
|
-
{ errorComponent: 'Projects/Create' }
|
|
1031
|
-
)
|
|
1032
|
-
// input is fully typed: { name: string, description?: string }
|
|
1033
|
-
```
|
|
1034
|
-
|
|
1035
|
-
On validation failure, re-renders `errorComponent` with field-level errors.
|
|
1036
|
-
|
|
1037
|
-
#### `DatabaseService` - Database Access
|
|
1038
|
-
|
|
1039
|
-
Opt-in to database access:
|
|
1040
|
-
|
|
1041
|
-
```typescript
|
|
1042
|
-
import { DatabaseService } from 'honertia/effect'
|
|
1043
|
-
|
|
1044
|
-
const db = yield* DatabaseService
|
|
1045
|
-
const projects = yield* Effect.tryPromise(() =>
|
|
1046
|
-
db.query.projects.findMany()
|
|
1047
|
-
)
|
|
1048
|
-
```
|
|
1049
|
-
|
|
1050
|
-
#### `render` / `redirect` - Responses
|
|
1051
|
-
|
|
1052
|
-
Return responses from your action:
|
|
1053
|
-
|
|
1054
|
-
```typescript
|
|
1055
|
-
import { render, redirect } from 'honertia/effect'
|
|
1056
|
-
|
|
1057
|
-
// Render a page
|
|
1058
|
-
return yield* render('Projects/Index', { projects })
|
|
1059
|
-
|
|
1060
|
-
// Redirect after mutation
|
|
1061
|
-
return yield* redirect('/projects')
|
|
1062
|
-
```
|
|
1063
|
-
|
|
1064
|
-
### Building an Action
|
|
1065
|
-
|
|
1066
|
-
Here's how these composables work together:
|
|
1067
|
-
|
|
1068
|
-
```typescript
|
|
1069
|
-
import { Effect, Schema as S } from 'effect'
|
|
1070
|
-
import {
|
|
1071
|
-
action,
|
|
1072
|
-
authorize,
|
|
1073
|
-
validateRequest,
|
|
1074
|
-
DatabaseService,
|
|
1075
|
-
redirect,
|
|
1076
|
-
requiredString,
|
|
1077
|
-
} from 'honertia/effect'
|
|
1078
|
-
|
|
1079
|
-
const CreateProjectSchema = S.Struct({
|
|
1080
|
-
name: requiredString,
|
|
1081
|
-
description: S.optional(S.String),
|
|
1082
|
-
})
|
|
1083
|
-
|
|
1084
|
-
export const createProject = action(
|
|
1085
|
-
Effect.gen(function* () {
|
|
1086
|
-
// 1. Authorization - fail fast if not allowed
|
|
1087
|
-
const auth = yield* authorize((a) => a.user.role === 'author')
|
|
1088
|
-
|
|
1089
|
-
// 2. Validation - parse and validate request body
|
|
1090
|
-
const input = yield* validateRequest(CreateProjectSchema, {
|
|
1091
|
-
errorComponent: 'Projects/Create',
|
|
1092
|
-
})
|
|
1093
|
-
|
|
1094
|
-
// 3. Database - perform the mutation
|
|
1095
|
-
const db = yield* DatabaseService
|
|
1096
|
-
yield* Effect.tryPromise(() =>
|
|
1097
|
-
db.insert(projects).values({
|
|
1098
|
-
...input,
|
|
1099
|
-
userId: auth.user.id,
|
|
1100
|
-
})
|
|
1101
|
-
)
|
|
1102
|
-
|
|
1103
|
-
// 4. Response - redirect on success
|
|
1104
|
-
return yield* redirect('/projects')
|
|
1105
|
-
})
|
|
1106
|
-
)
|
|
1107
|
-
```
|
|
1108
|
-
|
|
1109
|
-
### Execution Order Matters
|
|
1110
|
-
|
|
1111
|
-
The order you yield services determines when they execute:
|
|
1112
|
-
|
|
1113
|
-
```typescript
|
|
1114
|
-
// Authorization BEFORE validation (recommended for mutations)
|
|
1115
|
-
// Don't waste cycles validating if user can't perform the action
|
|
1116
|
-
const auth = yield* authorize((a) => a.user.role === 'admin')
|
|
1117
|
-
const input = yield* validateRequest(schema)
|
|
1118
|
-
|
|
1119
|
-
// Validation BEFORE authorization (when you need input for auth check)
|
|
1120
|
-
const input = yield* validateRequest(schema)
|
|
1121
|
-
const auth = yield* authorize((a) => a.user.id === input.ownerId)
|
|
1122
|
-
```
|
|
1123
|
-
|
|
1124
|
-
### Type Safety
|
|
1125
|
-
|
|
1126
|
-
Effect tracks all service requirements at the type level. Your action's type signature shows exactly what it needs:
|
|
1127
|
-
|
|
1128
|
-
```typescript
|
|
1129
|
-
// This action requires: RequestService, DatabaseService
|
|
1130
|
-
export const createProject: Effect.Effect<
|
|
1131
|
-
Response | Redirect,
|
|
1132
|
-
ValidationError | UnauthorizedError | ForbiddenError | Error,
|
|
1133
|
-
RequestService | DatabaseService
|
|
1134
|
-
>
|
|
1135
|
-
```
|
|
1136
|
-
|
|
1137
|
-
The compiler ensures all required services are provided when the action runs.
|
|
1138
|
-
Note: `authorize` uses an optional `AuthUserService`, so it won't appear in the required service list unless you `yield* AuthUserService` directly or provide `RequireAuthLayer` explicitly.
|
|
1139
|
-
|
|
1140
|
-
### Minimal Actions
|
|
1141
|
-
|
|
1142
|
-
Not every action needs all features. Use only what you need:
|
|
1143
|
-
|
|
1144
|
-
```typescript
|
|
1145
|
-
// Public page - no auth, no validation
|
|
1146
|
-
export const showAbout = action(
|
|
1147
|
-
Effect.gen(function* () {
|
|
1148
|
-
return yield* render('About', {})
|
|
1149
|
-
})
|
|
1150
|
-
)
|
|
1151
|
-
|
|
1152
|
-
// Read-only authenticated page
|
|
1153
|
-
export const showDashboard = action(
|
|
1154
|
-
Effect.gen(function* () {
|
|
1155
|
-
const auth = yield* authorize()
|
|
1156
|
-
const db = yield* DatabaseService
|
|
1157
|
-
const stats = yield* fetchStats(db, auth)
|
|
1158
|
-
return yield* render('Dashboard', { stats })
|
|
1159
|
-
})
|
|
1160
|
-
)
|
|
1161
|
-
|
|
1162
|
-
// API endpoint with just validation
|
|
1163
|
-
export const searchProjects = action(
|
|
1164
|
-
Effect.gen(function* () {
|
|
1165
|
-
const { query } = yield* validateRequest(S.Struct({ query: S.String }))
|
|
1166
|
-
const db = yield* DatabaseService
|
|
1167
|
-
const results = yield* search(db, query)
|
|
1168
|
-
return yield* json({ results })
|
|
1169
|
-
})
|
|
1170
|
-
)
|
|
1171
|
-
```
|
|
1172
|
-
|
|
1173
|
-
### Helper Utilities
|
|
1174
|
-
|
|
1175
|
-
#### `dbTransaction` - Database Transactions
|
|
1176
|
-
|
|
1177
|
-
Run multiple database operations in a transaction with automatic rollback on failure. The database instance is passed explicitly to keep the dependency visible and consistent with other service patterns:
|
|
1178
|
-
|
|
1179
|
-
```typescript
|
|
1180
|
-
import { DatabaseService, dbTransaction } from 'honertia/effect'
|
|
1181
|
-
|
|
1182
|
-
const db = yield* DatabaseService
|
|
1183
|
-
|
|
1184
|
-
yield* dbTransaction(db, async (tx) => {
|
|
1185
|
-
await tx.insert(users).values({ name: 'Alice', email: 'alice@example.com' })
|
|
1186
|
-
await tx.update(accounts).set({ balance: 100 }).where(eq(accounts.userId, id))
|
|
1187
|
-
// If any operation fails, the entire transaction rolls back
|
|
1188
|
-
return { success: true }
|
|
1189
|
-
})
|
|
1190
|
-
```
|
|
1191
|
-
|
|
1192
1258
|
## React Integration
|
|
1193
1259
|
|
|
1194
1260
|
### Page Component Type
|
|
@@ -1233,6 +1299,41 @@ const Layout: HonertiaPage<Props> = ({ auth, children }) => {
|
|
|
1233
1299
|
}
|
|
1234
1300
|
```
|
|
1235
1301
|
|
|
1302
|
+
## TypeScript
|
|
1303
|
+
|
|
1304
|
+
### Typed Services via Module Augmentation
|
|
1305
|
+
|
|
1306
|
+
By default, `DatabaseService` and `AuthService` are typed as `unknown` since Honertia is database and auth agnostic. You can provide your specific types via module augmentation:
|
|
1307
|
+
|
|
1308
|
+
```typescript
|
|
1309
|
+
// src/types.d.ts (or any .d.ts file in your project)
|
|
1310
|
+
import type { Database } from '~/db/db'
|
|
1311
|
+
import type { auth } from '~/lib/auth'
|
|
1312
|
+
|
|
1313
|
+
declare module 'honertia/effect' {
|
|
1314
|
+
interface HonertiaDatabaseType {
|
|
1315
|
+
db: Database // Your Drizzle/Prisma/Kysely type
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
interface HonertiaAuthType {
|
|
1319
|
+
auth: typeof auth // Your better-auth instance type
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
```
|
|
1323
|
+
|
|
1324
|
+
Once augmented, `DatabaseService` and `AuthService` will be properly typed:
|
|
1325
|
+
|
|
1326
|
+
```typescript
|
|
1327
|
+
// Before augmentation: db is `unknown`, requires casting
|
|
1328
|
+
const db = (yield* DatabaseService) as Database
|
|
1329
|
+
|
|
1330
|
+
// After augmentation: db is typed as `Database`
|
|
1331
|
+
const db = yield* DatabaseService
|
|
1332
|
+
const projects = yield* Effect.tryPromise(() =>
|
|
1333
|
+
db.query.projects.findMany() // ✅ Full type safety
|
|
1334
|
+
)
|
|
1335
|
+
```
|
|
1336
|
+
|
|
1236
1337
|
## Architecture Notes
|
|
1237
1338
|
|
|
1238
1339
|
### Request-Scoped Services
|
package/dist/effect/auth.d.ts
CHANGED
|
@@ -55,8 +55,12 @@ export declare function shareAuthMiddleware<E extends Env>(): MiddlewareHandler<
|
|
|
55
55
|
/**
|
|
56
56
|
* An auth action effect that returns a Response.
|
|
57
57
|
* Used for loginAction, registerAction, logoutAction, and guestActions.
|
|
58
|
+
*
|
|
59
|
+
* The default service requirement is `RequestService | AuthService` because
|
|
60
|
+
* that's what the factory functions (betterAuthFormAction, betterAuthLogoutAction)
|
|
61
|
+
* return, and effectAuthRoutes provides these services automatically.
|
|
58
62
|
*/
|
|
59
|
-
export type AuthActionEffect<R =
|
|
63
|
+
export type AuthActionEffect<R = RequestService | AuthService, E extends Error = Error> = EffectHandler<R, E>;
|
|
60
64
|
/**
|
|
61
65
|
* Configuration for auth routes.
|
|
62
66
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/effect/auth.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,MAAM,EAAE,KAAK,EAAU,MAAM,IAAI,CAAC,EAAE,MAAM,QAAQ,CAAA;AAC3D,OAAO,KAAK,EAAE,IAAI,EAAE,iBAAiB,EAAE,GAAG,EAAE,MAAM,MAAM,CAAA;AACxD,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,eAAe,EAAE,cAAc,EAAE,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAA;AAC5G,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAChE,OAAO,EAAgB,KAAK,aAAa,EAAE,MAAM,cAAc,CAAA;AAI/D;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,wDAiB5B,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,iBAAiB,8CAe7B,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,CACM,CAAA;AAEvE;;GAEG;AACH,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,CAAC,QAAQ,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,CAGlE,CAAA;AAEH;;GAEG;AACH,eAAO,MAAM,WAAW,GACtB,mBAAqB,KACpB,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,iBAAiB,EAAE,KAAK,CAQhD,CAAA;AAEH;;GAEG;AACH,eAAO,MAAM,YAAY,GACvB,mBAAgB,KACf,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,iBAAiB,EAAE,KAAK,CAQ5C,CAAA;AAEH;;GAEG;AACH,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,eAAe,CAK9D,CAAA;AAEJ;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,GAAG,KAAK,iBAAiB,CAAC,CAAC,CAAC,CASzE;AAED
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/effect/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../../src/effect/bridge.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,
|
|
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,EAKvB,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;CAC5E;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"}
|
package/dist/effect/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Re-exports all Effect-related functionality.
|
|
5
5
|
*/
|
|
6
|
-
export { DatabaseService, AuthService, AuthUserService, HonertiaService, RequestService, ResponseFactoryService, type AuthUser, type HonertiaRenderer, type RequestContext, type ResponseFactory, } from './services.js';
|
|
6
|
+
export { DatabaseService, AuthService, AuthUserService, HonertiaService, RequestService, ResponseFactoryService, type AuthUser, type HonertiaRenderer, type RequestContext, type ResponseFactory, type HonertiaDatabaseType, type HonertiaAuthType, } from './services.js';
|
|
7
7
|
export { ValidationError, UnauthorizedError, NotFoundError, ForbiddenError, HttpError, Redirect, type AppError, } from './errors.js';
|
|
8
8
|
export * from './schema.js';
|
|
9
9
|
export { getValidationData, formatSchemaErrors, validate, validateRequest, } from './validation.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,
|
|
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,GACtB,MAAM,eAAe,CAAA;AAGtB,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,aAAa,EACb,cAAc,EACd,SAAS,EACT,QAAQ,EACR,KAAK,QAAQ,GACd,MAAM,aAAa,CAAA;AAGpB,cAAc,aAAa,CAAA;AAG3B,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,QAAQ,EACR,eAAe,GAChB,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,aAAa,GACd,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,GAClB,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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../../src/effect/routing.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAA;AACtC,OAAO,KAAK,EAAE,IAAI,EAAqB,GAAG,EAAE,MAAM,MAAM,CAAA;AAExD,OAAO,EAAqB,KAAK,kBAAkB,EAAE,MAAM,aAAa,CAAA;AACxE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AACtC,OAAO,EACL,eAAe,EACf,WAAW,
|
|
1
|
+
{"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../../src/effect/routing.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/effect/schema.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,MAAM,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/effect/schema.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,15 +4,56 @@
|
|
|
4
4
|
* Service tags for dependency injection via Effect.
|
|
5
5
|
*/
|
|
6
6
|
import { Context } from 'effect';
|
|
7
|
+
/**
|
|
8
|
+
* Augmentable interface for database type.
|
|
9
|
+
* Users can extend this via module augmentation:
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* // In your project's types.d.ts or similar
|
|
14
|
+
* declare module 'honertia/effect' {
|
|
15
|
+
* interface HonertiaDatabaseType {
|
|
16
|
+
* db: Database // Your database type (Drizzle, Prisma, Kysely, etc.)
|
|
17
|
+
* }
|
|
18
|
+
* }
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* Then `DatabaseService` will be typed as your `Database` type.
|
|
22
|
+
*/
|
|
23
|
+
export interface HonertiaDatabaseType {
|
|
24
|
+
db?: unknown;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Augmentable interface for auth type.
|
|
28
|
+
* Users can extend this via module augmentation:
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* declare module 'honertia/effect' {
|
|
33
|
+
* interface HonertiaAuthType {
|
|
34
|
+
* auth: ReturnType<typeof betterAuth> // Your auth instance type
|
|
35
|
+
* }
|
|
36
|
+
* }
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export interface HonertiaAuthType {
|
|
40
|
+
auth?: unknown;
|
|
41
|
+
}
|
|
7
42
|
declare const DatabaseService_base: Context.TagClass<DatabaseService, "honertia/Database", unknown>;
|
|
8
43
|
/**
|
|
9
44
|
* Database Service - Generic database client
|
|
45
|
+
*
|
|
46
|
+
* By default typed as `unknown`. Use module augmentation on
|
|
47
|
+
* `HonertiaDatabaseType` to provide your database type.
|
|
10
48
|
*/
|
|
11
49
|
export declare class DatabaseService extends DatabaseService_base {
|
|
12
50
|
}
|
|
13
51
|
declare const AuthService_base: Context.TagClass<AuthService, "honertia/Auth", unknown>;
|
|
14
52
|
/**
|
|
15
53
|
* Auth Service - Better-auth instance
|
|
54
|
+
*
|
|
55
|
+
* By default typed as `unknown`. Use module augmentation on
|
|
56
|
+
* `HonertiaAuthType` to provide your auth type.
|
|
16
57
|
*/
|
|
17
58
|
export declare class AuthService extends AuthService_base {
|
|
18
59
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"services.d.ts","sourceRoot":"","sources":["../../src/effect/services.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,OAAO,EAAU,MAAM,QAAQ,CAAA
|
|
1
|
+
{"version":3,"file":"services.d.ts","sourceRoot":"","sources":["../../src/effect/services.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,OAAO,EAAU,MAAM,QAAQ,CAAA;AAExC;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,oBAAoB;IACnC,EAAE,CAAC,EAAE,OAAO,CAAA;CACb;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,OAAO,CAAA;CACf;;AAMD;;;;;GAKG;AACH,qBAAa,eAAgB,SAAQ,oBAGlC;CAAG;;AAEN;;;;;GAKG;AACH,qBAAa,WAAY,SAAQ,gBAG9B;CAAG;AAEN;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE;QACJ,EAAE,EAAE,MAAM,CAAA;QACV,KAAK,EAAE,MAAM,CAAA;QACb,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;QACnB,aAAa,EAAE,OAAO,CAAA;QACtB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;QACpB,SAAS,EAAE,IAAI,CAAA;QACf,SAAS,EAAE,IAAI,CAAA;KAChB,CAAA;IACD,OAAO,EAAE;QACP,EAAE,EAAE,MAAM,CAAA;QACV,MAAM,EAAE,MAAM,CAAA;QACd,SAAS,EAAE,IAAI,CAAA;QACf,KAAK,EAAE,MAAM,CAAA;QACb,SAAS,EAAE,IAAI,CAAA;QACf,SAAS,EAAE,IAAI,CAAA;KAChB,CAAA;CACF;;AAED,qBAAa,eAAgB,SAAQ,oBAGlC;CAAG;AAEN;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACtC,SAAS,EAAE,MAAM,EACjB,KAAK,CAAC,EAAE,CAAC,GACR,OAAO,CAAC,QAAQ,CAAC,CAAA;IACpB,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAA;IACxC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAA;CAChD;;AAED,qBAAa,eAAgB,SAAQ,oBAGlC;CAAG;AAEN;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;IACvC,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC/B,IAAI,CAAC,CAAC,GAAG,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,CAAA;IAC/B,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;IAC7C,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;CACzC;;AAED,qBAAa,cAAe,SAAQ,mBAGjC;CAAG;AAEN;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAA;IAChD,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAA;IAC3C,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAA;IAC7C,QAAQ,IAAI,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;CACzC;;AAED,qBAAa,sBAAuB,SAAQ,2BAGzC;CAAG"}
|
package/dist/effect/services.js
CHANGED
|
@@ -6,11 +6,17 @@
|
|
|
6
6
|
import { Context } from 'effect';
|
|
7
7
|
/**
|
|
8
8
|
* Database Service - Generic database client
|
|
9
|
+
*
|
|
10
|
+
* By default typed as `unknown`. Use module augmentation on
|
|
11
|
+
* `HonertiaDatabaseType` to provide your database type.
|
|
9
12
|
*/
|
|
10
13
|
export class DatabaseService extends Context.Tag('honertia/Database')() {
|
|
11
14
|
}
|
|
12
15
|
/**
|
|
13
16
|
* Auth Service - Better-auth instance
|
|
17
|
+
*
|
|
18
|
+
* By default typed as `unknown`. Use module augmentation on
|
|
19
|
+
* `HonertiaAuthType` to provide your auth type.
|
|
14
20
|
*/
|
|
15
21
|
export class AuthService extends Context.Tag('honertia/Auth')() {
|
|
16
22
|
}
|
|
@@ -8,31 +8,53 @@ import { RequestService } from './services.js';
|
|
|
8
8
|
import { ValidationError } from './errors.js';
|
|
9
9
|
/**
|
|
10
10
|
* Extract validation data from the request.
|
|
11
|
-
* Merges route params, query params, and body.
|
|
11
|
+
* Merges route params, query params, and body (body takes precedence).
|
|
12
12
|
*/
|
|
13
|
-
export declare const getValidationData: Effect.Effect<
|
|
14
|
-
[x: string]: unknown;
|
|
15
|
-
}, never, RequestService>;
|
|
13
|
+
export declare const getValidationData: Effect.Effect<Record<string, unknown>, ValidationError, RequestService>;
|
|
16
14
|
/**
|
|
17
15
|
* Format Effect Schema parse errors into field-level validation errors.
|
|
18
16
|
*/
|
|
19
17
|
export declare function formatSchemaErrors(error: ParseResult.ParseError, messages?: Record<string, string>, attributes?: Record<string, string>): Record<string, string>;
|
|
20
18
|
/**
|
|
21
|
-
*
|
|
22
|
-
* Returns validated data or fails with ValidationError.
|
|
19
|
+
* Options for validation functions.
|
|
23
20
|
*/
|
|
24
|
-
export
|
|
21
|
+
export interface ValidateOptions {
|
|
22
|
+
/**
|
|
23
|
+
* Custom error messages keyed by field name.
|
|
24
|
+
* Overrides the default messages from Effect Schema.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* { email: 'Please enter a valid email address' }
|
|
28
|
+
*/
|
|
25
29
|
messages?: Record<string, string>;
|
|
30
|
+
/**
|
|
31
|
+
* Human-readable names for fields, used with the `:attribute` placeholder.
|
|
32
|
+
* If a message contains `:attribute`, it will be replaced with the value here.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* // With attributes: { email: 'email address' }
|
|
36
|
+
* // And message: 'The :attribute field is required'
|
|
37
|
+
* // Produces: 'The email address field is required'
|
|
38
|
+
*/
|
|
26
39
|
attributes?: Record<string, string>;
|
|
40
|
+
/**
|
|
41
|
+
* The Inertia component to re-render when validation fails.
|
|
42
|
+
* If set, a ValidationError will trigger a re-render of this component
|
|
43
|
+
* with the errors passed as props. If not set, redirects back.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* 'Projects/Create'
|
|
47
|
+
*/
|
|
27
48
|
errorComponent?: string;
|
|
28
|
-
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Validate data against a schema.
|
|
52
|
+
* Returns validated data or fails with ValidationError.
|
|
53
|
+
*/
|
|
54
|
+
export declare function validate<A, I>(schema: S.Schema<A, I>, data: unknown, options?: ValidateOptions): Effect.Effect<A, ValidationError, never>;
|
|
29
55
|
/**
|
|
30
56
|
* Validate request data against a schema.
|
|
31
57
|
* Extracts data from request and validates in one step.
|
|
32
58
|
*/
|
|
33
|
-
export declare
|
|
34
|
-
messages?: Record<string, string>;
|
|
35
|
-
attributes?: Record<string, string>;
|
|
36
|
-
errorComponent?: string;
|
|
37
|
-
}) => Effect.Effect<A, ValidationError, RequestService>;
|
|
59
|
+
export declare function validateRequest<A, I>(schema: S.Schema<A, I>, options?: ValidateOptions): Effect.Effect<A, ValidationError, RequestService>;
|
|
38
60
|
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/effect/validation.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAA;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAE7C;;;GAGG;AACH,eAAO,MAAM,iBAAiB
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/effect/validation.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAA;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAE7C;;;GAGG;AACH,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,MAAM,CAC3C,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACvB,eAAe,EACf,cAAc,CA0Bd,CAAA;AAEF;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,WAAW,CAAC,UAAU,EAC7B,QAAQ,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,EACrC,UAAU,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GACtC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAgBxB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAEjC;;;;;;;;OAQG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAEnC;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,CAAC,EAC3B,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,EACtB,IAAI,EAAE,OAAO,EACb,OAAO,GAAE,eAAoB,GAC5B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,eAAe,EAAE,KAAK,CAAC,CAS1C;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,CAAC,EAClC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,EACtB,OAAO,GAAE,eAAoB,GAC5B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,eAAe,EAAE,cAAc,CAAC,CAKnD"}
|
|
@@ -8,46 +8,36 @@ import { RequestService } from './services.js';
|
|
|
8
8
|
import { ValidationError } from './errors.js';
|
|
9
9
|
/**
|
|
10
10
|
* Extract validation data from the request.
|
|
11
|
-
* Merges route params, query params, and body.
|
|
11
|
+
* Merges route params, query params, and body (body takes precedence).
|
|
12
12
|
*/
|
|
13
13
|
export const getValidationData = Effect.gen(function* () {
|
|
14
14
|
const request = yield* RequestService;
|
|
15
15
|
const routeParams = request.params();
|
|
16
16
|
const queryParams = request.query();
|
|
17
|
-
|
|
18
|
-
if (
|
|
19
|
-
|
|
20
|
-
const bodyResult = yield* Effect.tryPromise({
|
|
21
|
-
try: () => contentType.includes('application/json')
|
|
22
|
-
? request.json()
|
|
23
|
-
: request.parseBody(),
|
|
24
|
-
catch: () => ({}),
|
|
25
|
-
}).pipe(Effect.catchAll(() => Effect.succeed({})));
|
|
26
|
-
body = bodyResult;
|
|
17
|
+
// Only parse body for methods that typically have one
|
|
18
|
+
if (['GET', 'HEAD'].includes(request.method.toUpperCase())) {
|
|
19
|
+
return { ...routeParams, ...queryParams };
|
|
27
20
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
};
|
|
21
|
+
const contentType = request.header('Content-Type') ?? '';
|
|
22
|
+
const isJson = contentType.includes('application/json');
|
|
23
|
+
const body = yield* Effect.tryPromise(() => isJson ? request.json() : request.parseBody()).pipe(Effect.mapError(() => new ValidationError({
|
|
24
|
+
errors: { form: isJson ? 'Invalid JSON body' : 'Could not parse request body' },
|
|
25
|
+
})));
|
|
26
|
+
return { ...routeParams, ...queryParams, ...body };
|
|
33
27
|
});
|
|
34
28
|
/**
|
|
35
29
|
* Format Effect Schema parse errors into field-level validation errors.
|
|
36
30
|
*/
|
|
37
31
|
export function formatSchemaErrors(error, messages = {}, attributes = {}) {
|
|
38
32
|
const errors = {};
|
|
39
|
-
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
? issue.path.map(p => typeof p === 'object' && p !== null && 'key' in p ? p.key : String(p)).join('.')
|
|
44
|
-
: 'form';
|
|
45
|
-
if (errors[pathStr])
|
|
33
|
+
const issues = ParseResult.ArrayFormatter.formatErrorSync(error);
|
|
34
|
+
for (const issue of issues) {
|
|
35
|
+
const field = issue.path.length > 0 ? issue.path.map(String).join('.') : 'form';
|
|
36
|
+
if (errors[field])
|
|
46
37
|
continue; // First error wins
|
|
47
|
-
const attribute = attributes[
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
errors[pathStr] = message.replace(/:attribute/g, attribute);
|
|
38
|
+
const attribute = attributes[field] ?? field;
|
|
39
|
+
const message = messages[field] ?? issue.message;
|
|
40
|
+
errors[field] = message.replace(/:attribute/g, attribute);
|
|
51
41
|
}
|
|
52
42
|
return errors;
|
|
53
43
|
}
|
|
@@ -55,15 +45,19 @@ export function formatSchemaErrors(error, messages = {}, attributes = {}) {
|
|
|
55
45
|
* Validate data against a schema.
|
|
56
46
|
* Returns validated data or fails with ValidationError.
|
|
57
47
|
*/
|
|
58
|
-
export
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
48
|
+
export function validate(schema, data, options = {}) {
|
|
49
|
+
return S.decodeUnknown(schema)(data).pipe(Effect.mapError((error) => new ValidationError({
|
|
50
|
+
errors: formatSchemaErrors(error, options.messages, options.attributes),
|
|
51
|
+
component: options.errorComponent,
|
|
52
|
+
})));
|
|
53
|
+
}
|
|
62
54
|
/**
|
|
63
55
|
* Validate request data against a schema.
|
|
64
56
|
* Extracts data from request and validates in one step.
|
|
65
57
|
*/
|
|
66
|
-
export
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
58
|
+
export function validateRequest(schema, options = {}) {
|
|
59
|
+
return Effect.gen(function* () {
|
|
60
|
+
const data = yield* getValidationData;
|
|
61
|
+
return yield* validate(schema, data, options);
|
|
62
|
+
});
|
|
63
|
+
}
|