honertia 0.1.2 → 0.1.4
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 +526 -205
- package/dist/auth.d.ts +1 -1
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +1 -1
- package/dist/effect/action.d.ts +43 -83
- package/dist/effect/action.d.ts.map +1 -1
- package/dist/effect/action.js +57 -116
- package/dist/effect/auth.d.ts +79 -3
- package/dist/effect/auth.d.ts.map +1 -1
- package/dist/effect/auth.js +191 -21
- package/dist/effect/index.d.ts +2 -2
- package/dist/effect/index.d.ts.map +1 -1
- package/dist/effect/index.js +3 -3
- package/package.json +1 -1
package/dist/auth.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Re-exports all authentication and authorization functionality.
|
|
5
5
|
* Import from 'honertia/auth' for auth-related functionality.
|
|
6
6
|
*/
|
|
7
|
-
export { RequireAuthLayer, RequireGuestLayer, isAuthenticated, currentUser, requireAuth, requireGuest, shareAuth, shareAuthMiddleware, effectAuthRoutes, loadUser, type AuthRoutesConfig, } from './effect/auth.js';
|
|
7
|
+
export { RequireAuthLayer, RequireGuestLayer, isAuthenticated, currentUser, requireAuth, requireGuest, shareAuth, shareAuthMiddleware, effectAuthRoutes, betterAuthFormAction, betterAuthLogoutAction, loadUser, type AuthRoutesConfig, type AuthActionEffect, type BetterAuthFormActionConfig, type BetterAuthLogoutConfig, type BetterAuthActionResult, } from './effect/auth.js';
|
|
8
8
|
export { AuthService, AuthUserService, type AuthUser, } from './effect/services.js';
|
|
9
9
|
export { UnauthorizedError, ForbiddenError, } from './effect/errors.js';
|
|
10
10
|
//# sourceMappingURL=auth.d.ts.map
|
package/dist/auth.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,WAAW,EACX,WAAW,EACX,YAAY,EACZ,SAAS,EACT,mBAAmB,EACnB,gBAAgB,EAChB,QAAQ,EACR,KAAK,gBAAgB,
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,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,gBAAgB,EACrB,KAAK,0BAA0B,EAC/B,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,GAC5B,MAAM,kBAAkB,CAAA;AAGzB,OAAO,EACL,WAAW,EACX,eAAe,EACf,KAAK,QAAQ,GACd,MAAM,sBAAsB,CAAA;AAG7B,OAAO,EACL,iBAAiB,EACjB,cAAc,GACf,MAAM,oBAAoB,CAAA"}
|
package/dist/auth.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Re-exports all authentication and authorization functionality.
|
|
5
5
|
* Import from 'honertia/auth' for auth-related functionality.
|
|
6
6
|
*/
|
|
7
|
-
export { RequireAuthLayer, RequireGuestLayer, isAuthenticated, currentUser, requireAuth, requireGuest, shareAuth, shareAuthMiddleware, effectAuthRoutes, loadUser, } from './effect/auth.js';
|
|
7
|
+
export { RequireAuthLayer, RequireGuestLayer, isAuthenticated, currentUser, requireAuth, requireGuest, shareAuth, shareAuthMiddleware, effectAuthRoutes, betterAuthFormAction, betterAuthLogoutAction, loadUser, } from './effect/auth.js';
|
|
8
8
|
// Re-export auth-related services
|
|
9
9
|
export { AuthService, AuthUserService, } from './effect/services.js';
|
|
10
10
|
// Re-export auth-related errors
|
package/dist/effect/action.d.ts
CHANGED
|
@@ -1,107 +1,67 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Effect Action
|
|
2
|
+
* Effect Action Composables
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Composable helpers for building Effect-based request handlers.
|
|
5
|
+
* Actions are fully opt-in - yield* only what you need.
|
|
5
6
|
*/
|
|
6
|
-
import { Effect
|
|
7
|
-
import { DatabaseService,
|
|
8
|
-
import {
|
|
9
|
-
import { Redirect } from './errors.js';
|
|
7
|
+
import { Effect } from 'effect';
|
|
8
|
+
import { DatabaseService, type AuthUser } from './services.js';
|
|
9
|
+
import { UnauthorizedError, ForbiddenError, Redirect } from './errors.js';
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
11
|
+
* Semantic wrapper for Effect actions.
|
|
12
|
+
*
|
|
13
|
+
* This is a minimal wrapper that marks an Effect as an action.
|
|
14
|
+
* All capabilities are opt-in via yield* inside your handler.
|
|
12
15
|
*
|
|
13
16
|
* @example
|
|
14
|
-
* const createProject =
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
+
* const createProject = action(
|
|
18
|
+
* Effect.gen(function* () {
|
|
19
|
+
* // Opt-in to authorization
|
|
20
|
+
* const auth = yield* authorize()
|
|
21
|
+
*
|
|
22
|
+
* // Opt-in to validation
|
|
23
|
+
* const input = yield* validateRequest(S.Struct({ name: requiredString }))
|
|
24
|
+
*
|
|
25
|
+
* // Opt-in to database
|
|
17
26
|
* const db = yield* DatabaseService
|
|
18
|
-
* yield* Effect.tryPromise(() => db.insert(projects).values(input))
|
|
19
|
-
* return new Redirect({ url: '/projects', status: 303 })
|
|
20
|
-
* })
|
|
21
|
-
* )
|
|
22
|
-
*/
|
|
23
|
-
export declare function effectAction<A, I, R, E>(schema: S.Schema<A, I>, handler: (input: A) => Effect.Effect<Response | Redirect, E, R>, options?: {
|
|
24
|
-
errorComponent?: string;
|
|
25
|
-
messages?: Record<string, string>;
|
|
26
|
-
attributes?: Record<string, string>;
|
|
27
|
-
}): Effect.Effect<Response | Redirect, E | ValidationError, R | RequestService>;
|
|
28
|
-
/**
|
|
29
|
-
* Create an Effect action that requires authentication and database access.
|
|
30
27
|
*
|
|
31
|
-
* @example
|
|
32
|
-
* const createProject = dbAction(
|
|
33
|
-
* S.Struct({ name: requiredString }),
|
|
34
|
-
* (input, { db, user }) => Effect.gen(function* () {
|
|
35
28
|
* yield* Effect.tryPromise(() =>
|
|
36
|
-
* db.insert(projects).values({ ...input, userId:
|
|
29
|
+
* db.insert(projects).values({ ...input, userId: auth.user.id })
|
|
37
30
|
* )
|
|
38
31
|
* return new Redirect({ url: '/projects', status: 303 })
|
|
39
32
|
* })
|
|
40
33
|
* )
|
|
41
34
|
*/
|
|
42
|
-
export declare function
|
|
43
|
-
db: unknown;
|
|
44
|
-
user: AuthUser;
|
|
45
|
-
}) => Effect.Effect<Response | Redirect, E, never>, options?: {
|
|
46
|
-
errorComponent?: string;
|
|
47
|
-
messages?: Record<string, string>;
|
|
48
|
-
attributes?: Record<string, string>;
|
|
49
|
-
}): Effect.Effect<Response | Redirect, E | ValidationError | UnauthorizedError, RequestService | DatabaseService | AuthUserService>;
|
|
35
|
+
export declare function action<R, E>(handler: Effect.Effect<Response | Redirect, E, R>): Effect.Effect<Response | Redirect, E, R>;
|
|
50
36
|
/**
|
|
51
|
-
*
|
|
37
|
+
* Authorization helper - opt-in to auth check.
|
|
52
38
|
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* return yield* Effect.tryPromise(() => honertia.render('Dashboard', { user: user.user }))
|
|
58
|
-
* }))
|
|
59
|
-
*/
|
|
60
|
-
export declare function authAction<R, E>(handler: (user: AuthUser) => Effect.Effect<Response | Redirect, E, R>): Effect.Effect<Response | Redirect, E | UnauthorizedError, R | AuthUserService>;
|
|
61
|
-
/**
|
|
62
|
-
* Create a simple Effect action without validation.
|
|
39
|
+
* Returns the authenticated user if authorized.
|
|
40
|
+
* Fails with UnauthorizedError if no user is present.
|
|
41
|
+
* Fails with ForbiddenError if the check returns false.
|
|
42
|
+
* The check function is optional - if not provided, just requires authentication.
|
|
63
43
|
*
|
|
64
44
|
* @example
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
45
|
+
* // Just require authentication
|
|
46
|
+
* const auth = yield* authorize()
|
|
47
|
+
*
|
|
48
|
+
* // Require specific role (if your user type has a role field)
|
|
49
|
+
* const auth = yield* authorize((a) => a.user.role === 'admin')
|
|
50
|
+
*
|
|
51
|
+
* // Require resource ownership
|
|
52
|
+
* const auth = yield* authorize((a) => a.user.id === project.userId)
|
|
72
53
|
*/
|
|
73
|
-
export declare function
|
|
54
|
+
export declare function authorize(check?: (user: AuthUser) => boolean): Effect.Effect<AuthUser, UnauthorizedError | ForbiddenError, never>;
|
|
74
55
|
/**
|
|
75
|
-
*
|
|
56
|
+
* Run multiple database operations in a transaction.
|
|
57
|
+
* Automatically rolls back on any failure.
|
|
76
58
|
*
|
|
77
59
|
* @example
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
* (
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
* Effect.tryPromise(() => db.insert(projects).values({ name, userId }))
|
|
84
|
-
* )
|
|
85
|
-
* )
|
|
86
|
-
* )
|
|
87
|
-
*/
|
|
88
|
-
export declare function injectUser<T extends Record<string, unknown>>(input: T): Effect.Effect<T & {
|
|
89
|
-
userId: string;
|
|
90
|
-
}, UnauthorizedError, AuthUserService>;
|
|
91
|
-
/**
|
|
92
|
-
* Run a database operation wrapped in Effect.
|
|
93
|
-
*/
|
|
94
|
-
export declare function dbOperation<T>(operation: (db: unknown) => Promise<T>): Effect.Effect<T, Error, DatabaseService>;
|
|
95
|
-
/**
|
|
96
|
-
* Prepare validation data by transforming it before validation.
|
|
97
|
-
*/
|
|
98
|
-
export declare function prepareData<T extends Record<string, unknown>>(transform: (data: Record<string, unknown>) => T | Promise<T>): Effect.Effect<T, never, RequestService>;
|
|
99
|
-
/**
|
|
100
|
-
* Create an action with custom data preparation.
|
|
60
|
+
* yield* dbTransaction(async (tx) => {
|
|
61
|
+
* await tx.insert(users).values({ name: 'Alice' })
|
|
62
|
+
* await tx.update(accounts).set({ balance: 100 }).where(eq(accounts.userId, id))
|
|
63
|
+
* return { success: true }
|
|
64
|
+
* })
|
|
101
65
|
*/
|
|
102
|
-
export declare function
|
|
103
|
-
errorComponent?: string;
|
|
104
|
-
messages?: Record<string, string>;
|
|
105
|
-
attributes?: Record<string, string>;
|
|
106
|
-
}): Effect.Effect<Response | Redirect, E | ValidationError, R | RequestService>;
|
|
66
|
+
export declare function dbTransaction<T>(operations: (tx: unknown) => Promise<T>): Effect.Effect<T, Error, DatabaseService>;
|
|
107
67
|
//# sourceMappingURL=action.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"action.d.ts","sourceRoot":"","sources":["../../src/effect/action.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"action.d.ts","sourceRoot":"","sources":["../../src/effect/action.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAA;AACvC,OAAO,EACL,eAAe,EAEf,KAAK,QAAQ,EACd,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAEzE;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,CAAC,EACzB,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,QAAQ,GAAG,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC,GAChD,MAAM,CAAC,MAAM,CAAC,QAAQ,GAAG,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC,CAE1C;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,SAAS,CACvB,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,OAAO,GAClC,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,iBAAiB,GAAG,cAAc,EAAE,KAAK,CAAC,CAoBpE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAC7B,UAAU,EAAE,CAAC,EAAE,EAAE,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,GACtC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,eAAe,CAAC,CAQ1C"}
|
package/dist/effect/action.js
CHANGED
|
@@ -1,150 +1,91 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Effect Action
|
|
2
|
+
* Effect Action Composables
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Composable helpers for building Effect-based request handlers.
|
|
5
|
+
* Actions are fully opt-in - yield* only what you need.
|
|
5
6
|
*/
|
|
6
|
-
import { Effect } from 'effect';
|
|
7
|
+
import { Effect, Option } from 'effect';
|
|
7
8
|
import { DatabaseService, AuthUserService, } from './services.js';
|
|
8
|
-
import {
|
|
9
|
+
import { UnauthorizedError, ForbiddenError } from './errors.js';
|
|
9
10
|
/**
|
|
10
|
-
*
|
|
11
|
+
* Semantic wrapper for Effect actions.
|
|
12
|
+
*
|
|
13
|
+
* This is a minimal wrapper that marks an Effect as an action.
|
|
14
|
+
* All capabilities are opt-in via yield* inside your handler.
|
|
11
15
|
*
|
|
12
16
|
* @example
|
|
13
|
-
* const createProject =
|
|
14
|
-
*
|
|
15
|
-
*
|
|
17
|
+
* const createProject = action(
|
|
18
|
+
* Effect.gen(function* () {
|
|
19
|
+
* // Opt-in to authorization
|
|
20
|
+
* const auth = yield* authorize()
|
|
21
|
+
*
|
|
22
|
+
* // Opt-in to validation
|
|
23
|
+
* const input = yield* validateRequest(S.Struct({ name: requiredString }))
|
|
24
|
+
*
|
|
25
|
+
* // Opt-in to database
|
|
16
26
|
* const db = yield* DatabaseService
|
|
17
|
-
* yield* Effect.tryPromise(() => db.insert(projects).values(input))
|
|
18
|
-
* return new Redirect({ url: '/projects', status: 303 })
|
|
19
|
-
* })
|
|
20
|
-
* )
|
|
21
|
-
*/
|
|
22
|
-
export function effectAction(schema, handler, options) {
|
|
23
|
-
return Effect.gen(function* () {
|
|
24
|
-
const input = yield* validateRequest(schema, {
|
|
25
|
-
errorComponent: options?.errorComponent,
|
|
26
|
-
messages: options?.messages,
|
|
27
|
-
attributes: options?.attributes,
|
|
28
|
-
});
|
|
29
|
-
return yield* handler(input);
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Create an Effect action that requires authentication and database access.
|
|
34
27
|
*
|
|
35
|
-
* @example
|
|
36
|
-
* const createProject = dbAction(
|
|
37
|
-
* S.Struct({ name: requiredString }),
|
|
38
|
-
* (input, { db, user }) => Effect.gen(function* () {
|
|
39
28
|
* yield* Effect.tryPromise(() =>
|
|
40
|
-
* db.insert(projects).values({ ...input, userId:
|
|
29
|
+
* db.insert(projects).values({ ...input, userId: auth.user.id })
|
|
41
30
|
* )
|
|
42
31
|
* return new Redirect({ url: '/projects', status: 303 })
|
|
43
32
|
* })
|
|
44
33
|
* )
|
|
45
34
|
*/
|
|
46
|
-
export function
|
|
47
|
-
return
|
|
48
|
-
const db = yield* DatabaseService;
|
|
49
|
-
const user = yield* AuthUserService;
|
|
50
|
-
const input = yield* validateRequest(schema, {
|
|
51
|
-
errorComponent: options?.errorComponent,
|
|
52
|
-
messages: options?.messages,
|
|
53
|
-
attributes: options?.attributes,
|
|
54
|
-
});
|
|
55
|
-
return yield* handler(input, { db, user });
|
|
56
|
-
});
|
|
35
|
+
export function action(handler) {
|
|
36
|
+
return handler;
|
|
57
37
|
}
|
|
58
38
|
/**
|
|
59
|
-
*
|
|
39
|
+
* Authorization helper - opt-in to auth check.
|
|
60
40
|
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
* return yield* Effect.tryPromise(() => honertia.render('Dashboard', { user: user.user }))
|
|
66
|
-
* }))
|
|
67
|
-
*/
|
|
68
|
-
export function authAction(handler) {
|
|
69
|
-
return Effect.gen(function* () {
|
|
70
|
-
const user = yield* AuthUserService;
|
|
71
|
-
return yield* handler(user);
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Create a simple Effect action without validation.
|
|
41
|
+
* Returns the authenticated user if authorized.
|
|
42
|
+
* Fails with UnauthorizedError if no user is present.
|
|
43
|
+
* Fails with ForbiddenError if the check returns false.
|
|
44
|
+
* The check function is optional - if not provided, just requires authentication.
|
|
76
45
|
*
|
|
77
46
|
* @example
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
* const user = yield* AuthUserService
|
|
81
|
-
* const projects = yield* Effect.tryPromise(() => db.query.projects.findMany())
|
|
82
|
-
* const honertia = yield* HonertiaService
|
|
83
|
-
* return yield* Effect.tryPromise(() => honertia.render('Projects', { projects }))
|
|
84
|
-
* }))
|
|
85
|
-
*/
|
|
86
|
-
export function simpleAction(handler) {
|
|
87
|
-
return handler();
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* Inject additional data into the validated input.
|
|
47
|
+
* // Just require authentication
|
|
48
|
+
* const auth = yield* authorize()
|
|
91
49
|
*
|
|
92
|
-
*
|
|
93
|
-
* const
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
* Effect.flatMap(({ name, userId }) =>
|
|
98
|
-
* Effect.tryPromise(() => db.insert(projects).values({ name, userId }))
|
|
99
|
-
* )
|
|
100
|
-
* )
|
|
101
|
-
* )
|
|
50
|
+
* // Require specific role (if your user type has a role field)
|
|
51
|
+
* const auth = yield* authorize((a) => a.user.role === 'admin')
|
|
52
|
+
*
|
|
53
|
+
* // Require resource ownership
|
|
54
|
+
* const auth = yield* authorize((a) => a.user.id === project.userId)
|
|
102
55
|
*/
|
|
103
|
-
export function
|
|
56
|
+
export function authorize(check) {
|
|
104
57
|
return Effect.gen(function* () {
|
|
105
|
-
const
|
|
106
|
-
|
|
58
|
+
const maybeUser = yield* Effect.serviceOption(AuthUserService);
|
|
59
|
+
if (Option.isNone(maybeUser)) {
|
|
60
|
+
return yield* Effect.fail(new UnauthorizedError({
|
|
61
|
+
message: 'Authentication required',
|
|
62
|
+
redirectTo: '/login',
|
|
63
|
+
}));
|
|
64
|
+
}
|
|
65
|
+
const user = maybeUser.value;
|
|
66
|
+
if (check && !check(user)) {
|
|
67
|
+
return yield* Effect.fail(new ForbiddenError({ message: 'Not authorized' }));
|
|
68
|
+
}
|
|
69
|
+
return user;
|
|
107
70
|
});
|
|
108
71
|
}
|
|
109
72
|
/**
|
|
110
|
-
* Run
|
|
73
|
+
* Run multiple database operations in a transaction.
|
|
74
|
+
* Automatically rolls back on any failure.
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* yield* dbTransaction(async (tx) => {
|
|
78
|
+
* await tx.insert(users).values({ name: 'Alice' })
|
|
79
|
+
* await tx.update(accounts).set({ balance: 100 }).where(eq(accounts.userId, id))
|
|
80
|
+
* return { success: true }
|
|
81
|
+
* })
|
|
111
82
|
*/
|
|
112
|
-
export function
|
|
83
|
+
export function dbTransaction(operations) {
|
|
113
84
|
return Effect.gen(function* () {
|
|
114
85
|
const db = yield* DatabaseService;
|
|
115
86
|
return yield* Effect.tryPromise({
|
|
116
|
-
try: () =>
|
|
87
|
+
try: () => db.transaction(operations),
|
|
117
88
|
catch: (error) => error instanceof Error ? error : new Error(String(error)),
|
|
118
89
|
});
|
|
119
90
|
});
|
|
120
91
|
}
|
|
121
|
-
/**
|
|
122
|
-
* Prepare validation data by transforming it before validation.
|
|
123
|
-
*/
|
|
124
|
-
export function prepareData(transform) {
|
|
125
|
-
return Effect.gen(function* () {
|
|
126
|
-
const data = yield* getValidationData;
|
|
127
|
-
return yield* Effect.tryPromise({
|
|
128
|
-
try: () => Promise.resolve(transform(data)),
|
|
129
|
-
catch: () => new Error('Transform failed'),
|
|
130
|
-
}).pipe(Effect.catchAll(() => Effect.succeed(data)));
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
/**
|
|
134
|
-
* Create an action with custom data preparation.
|
|
135
|
-
*/
|
|
136
|
-
export function preparedAction(schema, prepare, handler, options) {
|
|
137
|
-
return Effect.gen(function* () {
|
|
138
|
-
const rawData = yield* getValidationData;
|
|
139
|
-
const preparedData = yield* Effect.tryPromise({
|
|
140
|
-
try: () => Promise.resolve(prepare(rawData)),
|
|
141
|
-
catch: () => new Error('Prepare failed'),
|
|
142
|
-
}).pipe(Effect.catchAll(() => Effect.succeed(rawData)));
|
|
143
|
-
const input = yield* validate(schema, {
|
|
144
|
-
errorComponent: options?.errorComponent,
|
|
145
|
-
messages: options?.messages,
|
|
146
|
-
attributes: options?.attributes,
|
|
147
|
-
})(preparedData);
|
|
148
|
-
return yield* handler(input);
|
|
149
|
-
});
|
|
150
|
-
}
|
package/dist/effect/auth.d.ts
CHANGED
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Authentication and authorization via Effect Layers.
|
|
5
5
|
*/
|
|
6
|
-
import { Effect, Layer } from 'effect';
|
|
6
|
+
import { Effect, Layer, Schema as S } from 'effect';
|
|
7
7
|
import type { Hono, MiddlewareHandler, Env } from 'hono';
|
|
8
|
-
import { AuthUserService, HonertiaService, type AuthUser } from './services.js';
|
|
9
|
-
import { UnauthorizedError } from './errors.js';
|
|
8
|
+
import { AuthUserService, AuthService, HonertiaService, RequestService, type AuthUser } from './services.js';
|
|
9
|
+
import { UnauthorizedError, ValidationError } from './errors.js';
|
|
10
|
+
import { type EffectHandler } from './routing.js';
|
|
10
11
|
/**
|
|
11
12
|
* Layer that requires an authenticated user.
|
|
12
13
|
* Fails with UnauthorizedError if no user is present.
|
|
@@ -51,6 +52,11 @@ export declare const shareAuth: Effect.Effect<void, never, HonertiaService>;
|
|
|
51
52
|
* Middleware version of shareAuth for use with app.use().
|
|
52
53
|
*/
|
|
53
54
|
export declare function shareAuthMiddleware<E extends Env>(): MiddlewareHandler<E>;
|
|
55
|
+
/**
|
|
56
|
+
* An auth action effect that returns a Response.
|
|
57
|
+
* Used for loginAction, registerAction, logoutAction, and guestActions.
|
|
58
|
+
*/
|
|
59
|
+
export type AuthActionEffect<R = never, E extends Error = Error> = EffectHandler<R, E>;
|
|
54
60
|
/**
|
|
55
61
|
* Configuration for auth routes.
|
|
56
62
|
*/
|
|
@@ -60,6 +66,9 @@ export interface AuthRoutesConfig<E extends Env> {
|
|
|
60
66
|
logoutPath?: string;
|
|
61
67
|
apiPath?: string;
|
|
62
68
|
logoutRedirect?: string;
|
|
69
|
+
/**
|
|
70
|
+
* Redirect path for authenticated users hitting login/register pages.
|
|
71
|
+
*/
|
|
63
72
|
loginRedirect?: string;
|
|
64
73
|
loginComponent?: string;
|
|
65
74
|
registerComponent?: string;
|
|
@@ -72,6 +81,37 @@ export interface AuthRoutesConfig<E extends Env> {
|
|
|
72
81
|
origin: string | string[] | ((origin: string) => string | undefined | null);
|
|
73
82
|
credentials?: boolean;
|
|
74
83
|
};
|
|
84
|
+
/**
|
|
85
|
+
* POST handler for login form submission.
|
|
86
|
+
* Automatically wrapped with RequireGuestLayer.
|
|
87
|
+
* Use betterAuthFormAction to create this.
|
|
88
|
+
*/
|
|
89
|
+
loginAction?: AuthActionEffect;
|
|
90
|
+
/**
|
|
91
|
+
* POST handler for registration form submission.
|
|
92
|
+
* Automatically wrapped with RequireGuestLayer.
|
|
93
|
+
* Use betterAuthFormAction to create this.
|
|
94
|
+
*/
|
|
95
|
+
registerAction?: AuthActionEffect;
|
|
96
|
+
/**
|
|
97
|
+
* POST handler for logout.
|
|
98
|
+
* If not provided, uses a default handler that calls auth.api.signOut.
|
|
99
|
+
* Use betterAuthLogoutAction to create this.
|
|
100
|
+
*/
|
|
101
|
+
logoutAction?: AuthActionEffect;
|
|
102
|
+
/**
|
|
103
|
+
* Additional guest-only POST routes for extended auth flows.
|
|
104
|
+
* Keys are paths (e.g., '/forgot-password'), values are Effect handlers.
|
|
105
|
+
* All routes are wrapped with RequireGuestLayer.
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* guestActions: {
|
|
109
|
+
* '/forgot-password': forgotPasswordAction,
|
|
110
|
+
* '/reset-password': resetPasswordAction,
|
|
111
|
+
* '/login/2fa': verify2FAAction,
|
|
112
|
+
* }
|
|
113
|
+
*/
|
|
114
|
+
guestActions?: Record<string, AuthActionEffect>;
|
|
75
115
|
}
|
|
76
116
|
/**
|
|
77
117
|
* Register standard auth routes.
|
|
@@ -80,6 +120,8 @@ export interface AuthRoutesConfig<E extends Env> {
|
|
|
80
120
|
* effectAuthRoutes(app, {
|
|
81
121
|
* loginComponent: 'Auth/Login',
|
|
82
122
|
* registerComponent: 'Auth/Register',
|
|
123
|
+
* loginAction: loginUser,
|
|
124
|
+
* registerAction: registerUser,
|
|
83
125
|
* })
|
|
84
126
|
*/
|
|
85
127
|
export declare function effectAuthRoutes<E extends Env>(app: Hono<E>, config?: AuthRoutesConfig<E>): void;
|
|
@@ -91,4 +133,38 @@ export declare function loadUser<E extends Env>(config?: {
|
|
|
91
133
|
userKey?: string;
|
|
92
134
|
sessionCookie?: string;
|
|
93
135
|
}): MiddlewareHandler<E>;
|
|
136
|
+
/**
|
|
137
|
+
* Result types from better-auth calls that expose headers.
|
|
138
|
+
*/
|
|
139
|
+
export type BetterAuthActionResult = Response | Headers | {
|
|
140
|
+
headers?: Headers | HeadersInit;
|
|
141
|
+
};
|
|
142
|
+
/**
|
|
143
|
+
* Config for better-auth form actions (login/register).
|
|
144
|
+
*/
|
|
145
|
+
export interface BetterAuthFormActionConfig<A, I, AuthClient = unknown> {
|
|
146
|
+
schema: S.Schema<A, I>;
|
|
147
|
+
errorComponent: string;
|
|
148
|
+
call: (auth: AuthClient, input: A, request: Request) => Promise<BetterAuthActionResult>;
|
|
149
|
+
errorMapper?: (error: unknown) => Record<string, string>;
|
|
150
|
+
redirectTo?: string | ((input: A, result: BetterAuthActionResult) => string);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Create a better-auth form action with Honertia-friendly responses.
|
|
154
|
+
*
|
|
155
|
+
* Copies Set-Cookie headers from better-auth and redirects with 303.
|
|
156
|
+
* Maps errors into ValidationError so the standard error handler can render.
|
|
157
|
+
*/
|
|
158
|
+
export declare function betterAuthFormAction<A, I, AuthClient = unknown>(config: BetterAuthFormActionConfig<A, I, AuthClient>): Effect.Effect<Response, ValidationError, RequestService | AuthService>;
|
|
159
|
+
/**
|
|
160
|
+
* Config for better-auth logout actions.
|
|
161
|
+
*/
|
|
162
|
+
export interface BetterAuthLogoutConfig {
|
|
163
|
+
redirectTo?: string;
|
|
164
|
+
cookieNames?: string[];
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Create a better-auth logout action that clears cookies and redirects.
|
|
168
|
+
*/
|
|
169
|
+
export declare function betterAuthLogoutAction(config?: BetterAuthLogoutConfig): Effect.Effect<Response, never, RequestService | AuthService>;
|
|
94
170
|
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -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,QAAQ,CAAA;
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/effect/auth.ts"],"names":[],"mappings":"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"}
|