honertia 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +610 -0
- package/dist/auth.d.ts +10 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +11 -0
- package/dist/effect/action.d.ts +107 -0
- package/dist/effect/action.d.ts.map +1 -0
- package/dist/effect/action.js +150 -0
- package/dist/effect/auth.d.ts +94 -0
- package/dist/effect/auth.d.ts.map +1 -0
- package/dist/effect/auth.js +204 -0
- package/dist/effect/bridge.d.ts +40 -0
- package/dist/effect/bridge.d.ts.map +1 -0
- package/dist/effect/bridge.js +103 -0
- package/dist/effect/errors.d.ts +78 -0
- package/dist/effect/errors.d.ts.map +1 -0
- package/dist/effect/errors.js +37 -0
- package/dist/effect/handler.d.ts +25 -0
- package/dist/effect/handler.d.ts.map +1 -0
- package/dist/effect/handler.js +120 -0
- package/dist/effect/index.d.ts +16 -0
- package/dist/effect/index.d.ts.map +1 -0
- package/dist/effect/index.js +25 -0
- package/dist/effect/responses.d.ts +73 -0
- package/dist/effect/responses.d.ts.map +1 -0
- package/dist/effect/responses.js +104 -0
- package/dist/effect/routing.d.ts +90 -0
- package/dist/effect/routing.d.ts.map +1 -0
- package/dist/effect/routing.js +124 -0
- package/dist/effect/schema.d.ts +263 -0
- package/dist/effect/schema.d.ts.map +1 -0
- package/dist/effect/schema.js +586 -0
- package/dist/effect/services.d.ts +85 -0
- package/dist/effect/services.d.ts.map +1 -0
- package/dist/effect/services.js +24 -0
- package/dist/effect/validation.d.ts +38 -0
- package/dist/effect/validation.d.ts.map +1 -0
- package/dist/effect/validation.js +69 -0
- package/dist/helpers.d.ts +65 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/helpers.js +116 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/middleware.d.ts +14 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +113 -0
- package/dist/react.d.ts +17 -0
- package/dist/react.d.ts.map +1 -0
- package/dist/react.js +4 -0
- package/dist/schema.d.ts +9 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +34 -0
- package/dist/setup.d.ts +113 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +96 -0
- package/dist/test-utils.d.ts +105 -0
- package/dist/test-utils.d.ts.map +1 -0
- package/dist/test-utils.js +210 -0
- package/dist/types.d.ts +37 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +11 -0
- package/package.json +71 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Effect Action Factories
|
|
3
|
+
*
|
|
4
|
+
* Pure function factories for creating Effect-based request handlers.
|
|
5
|
+
*/
|
|
6
|
+
import { Effect, Schema as S } from 'effect';
|
|
7
|
+
import { DatabaseService, AuthUserService, RequestService, type AuthUser } from './services.js';
|
|
8
|
+
import { ValidationError, UnauthorizedError } from './errors.js';
|
|
9
|
+
import { Redirect } from './errors.js';
|
|
10
|
+
/**
|
|
11
|
+
* Create an Effect action with schema validation.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* const createProject = effectAction(
|
|
15
|
+
* S.Struct({ name: requiredString }),
|
|
16
|
+
* (input) => Effect.gen(function* () {
|
|
17
|
+
* 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
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* const createProject = dbAction(
|
|
33
|
+
* S.Struct({ name: requiredString }),
|
|
34
|
+
* (input, { db, user }) => Effect.gen(function* () {
|
|
35
|
+
* yield* Effect.tryPromise(() =>
|
|
36
|
+
* db.insert(projects).values({ ...input, userId: user.user.id })
|
|
37
|
+
* )
|
|
38
|
+
* return new Redirect({ url: '/projects', status: 303 })
|
|
39
|
+
* })
|
|
40
|
+
* )
|
|
41
|
+
*/
|
|
42
|
+
export declare function dbAction<A, I, E>(schema: S.Schema<A, I>, handler: (input: A, deps: {
|
|
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>;
|
|
50
|
+
/**
|
|
51
|
+
* Create an Effect action that requires authentication.
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* const showDashboard = authAction(() => Effect.gen(function* () {
|
|
55
|
+
* const user = yield* AuthUserService
|
|
56
|
+
* const honertia = yield* HonertiaService
|
|
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.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* const listProjects = simpleAction(() => Effect.gen(function* () {
|
|
66
|
+
* const db = yield* DatabaseService
|
|
67
|
+
* const user = yield* AuthUserService
|
|
68
|
+
* const projects = yield* Effect.tryPromise(() => db.query.projects.findMany())
|
|
69
|
+
* const honertia = yield* HonertiaService
|
|
70
|
+
* return yield* Effect.tryPromise(() => honertia.render('Projects', { projects }))
|
|
71
|
+
* }))
|
|
72
|
+
*/
|
|
73
|
+
export declare function simpleAction<R, E>(handler: () => Effect.Effect<Response | Redirect, E, R>): Effect.Effect<Response | Redirect, E, R>;
|
|
74
|
+
/**
|
|
75
|
+
* Inject additional data into the validated input.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* const createProject = effectAction(
|
|
79
|
+
* S.Struct({ name: requiredString }),
|
|
80
|
+
* (input) => pipe(
|
|
81
|
+
* injectUser(input),
|
|
82
|
+
* Effect.flatMap(({ name, userId }) =>
|
|
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.
|
|
101
|
+
*/
|
|
102
|
+
export declare function preparedAction<A, I, R, E>(schema: S.Schema<A, I>, prepare: (data: Record<string, unknown>) => Record<string, unknown> | Promise<Record<string, unknown>>, handler: (input: A) => Effect.Effect<Response | Redirect, E, R>, options?: {
|
|
103
|
+
errorComponent?: string;
|
|
104
|
+
messages?: Record<string, string>;
|
|
105
|
+
attributes?: Record<string, string>;
|
|
106
|
+
}): Effect.Effect<Response | Redirect, E | ValidationError, R | RequestService>;
|
|
107
|
+
//# sourceMappingURL=action.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action.d.ts","sourceRoot":"","sources":["../../src/effect/action.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Effect Action Factories
|
|
3
|
+
*
|
|
4
|
+
* Pure function factories for creating Effect-based request handlers.
|
|
5
|
+
*/
|
|
6
|
+
import { Effect } from 'effect';
|
|
7
|
+
import { DatabaseService, AuthUserService, } from './services.js';
|
|
8
|
+
import { validateRequest, getValidationData, validate } from './validation.js';
|
|
9
|
+
/**
|
|
10
|
+
* Create an Effect action with schema validation.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* const createProject = effectAction(
|
|
14
|
+
* S.Struct({ name: requiredString }),
|
|
15
|
+
* (input) => Effect.gen(function* () {
|
|
16
|
+
* 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
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* const createProject = dbAction(
|
|
37
|
+
* S.Struct({ name: requiredString }),
|
|
38
|
+
* (input, { db, user }) => Effect.gen(function* () {
|
|
39
|
+
* yield* Effect.tryPromise(() =>
|
|
40
|
+
* db.insert(projects).values({ ...input, userId: user.user.id })
|
|
41
|
+
* )
|
|
42
|
+
* return new Redirect({ url: '/projects', status: 303 })
|
|
43
|
+
* })
|
|
44
|
+
* )
|
|
45
|
+
*/
|
|
46
|
+
export function dbAction(schema, handler, options) {
|
|
47
|
+
return Effect.gen(function* () {
|
|
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
|
+
});
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Create an Effect action that requires authentication.
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* const showDashboard = authAction(() => Effect.gen(function* () {
|
|
63
|
+
* const user = yield* AuthUserService
|
|
64
|
+
* const honertia = yield* HonertiaService
|
|
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.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* const listProjects = simpleAction(() => Effect.gen(function* () {
|
|
79
|
+
* const db = yield* DatabaseService
|
|
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.
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* const createProject = effectAction(
|
|
94
|
+
* S.Struct({ name: requiredString }),
|
|
95
|
+
* (input) => pipe(
|
|
96
|
+
* injectUser(input),
|
|
97
|
+
* Effect.flatMap(({ name, userId }) =>
|
|
98
|
+
* Effect.tryPromise(() => db.insert(projects).values({ name, userId }))
|
|
99
|
+
* )
|
|
100
|
+
* )
|
|
101
|
+
* )
|
|
102
|
+
*/
|
|
103
|
+
export function injectUser(input) {
|
|
104
|
+
return Effect.gen(function* () {
|
|
105
|
+
const authUser = yield* AuthUserService;
|
|
106
|
+
return { ...input, userId: authUser.user.id };
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Run a database operation wrapped in Effect.
|
|
111
|
+
*/
|
|
112
|
+
export function dbOperation(operation) {
|
|
113
|
+
return Effect.gen(function* () {
|
|
114
|
+
const db = yield* DatabaseService;
|
|
115
|
+
return yield* Effect.tryPromise({
|
|
116
|
+
try: () => operation(db),
|
|
117
|
+
catch: (error) => error instanceof Error ? error : new Error(String(error)),
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
}
|
|
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
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Effect Auth Layers and Helpers
|
|
3
|
+
*
|
|
4
|
+
* Authentication and authorization via Effect Layers.
|
|
5
|
+
*/
|
|
6
|
+
import { Effect, Layer } from 'effect';
|
|
7
|
+
import type { Hono, MiddlewareHandler, Env } from 'hono';
|
|
8
|
+
import { AuthUserService, HonertiaService, type AuthUser } from './services.js';
|
|
9
|
+
import { UnauthorizedError } from './errors.js';
|
|
10
|
+
/**
|
|
11
|
+
* Layer that requires an authenticated user.
|
|
12
|
+
* Fails with UnauthorizedError if no user is present.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* effectRoutes(app)
|
|
16
|
+
* .provide(RequireAuthLayer)
|
|
17
|
+
* .get('/dashboard', showDashboard)
|
|
18
|
+
*/
|
|
19
|
+
export declare const RequireAuthLayer: Layer.Layer<AuthUserService, UnauthorizedError, never>;
|
|
20
|
+
/**
|
|
21
|
+
* Layer that requires no authenticated user (guest only).
|
|
22
|
+
* Fails if a user is present, succeeds (as a no-op) if no user.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* effectRoutes(app)
|
|
26
|
+
* .provide(RequireGuestLayer)
|
|
27
|
+
* .get('/login', showLogin)
|
|
28
|
+
*/
|
|
29
|
+
export declare const RequireGuestLayer: Layer.Layer<never, UnauthorizedError, never>;
|
|
30
|
+
/**
|
|
31
|
+
* Check if user is authenticated without failing.
|
|
32
|
+
*/
|
|
33
|
+
export declare const isAuthenticated: Effect.Effect<boolean, never, never>;
|
|
34
|
+
/**
|
|
35
|
+
* Get the current user if authenticated.
|
|
36
|
+
*/
|
|
37
|
+
export declare const currentUser: Effect.Effect<AuthUser | null, never, never>;
|
|
38
|
+
/**
|
|
39
|
+
* Require authentication or redirect.
|
|
40
|
+
*/
|
|
41
|
+
export declare const requireAuth: (redirectTo?: string) => Effect.Effect<AuthUser, UnauthorizedError, never>;
|
|
42
|
+
/**
|
|
43
|
+
* Require guest status or redirect.
|
|
44
|
+
*/
|
|
45
|
+
export declare const requireGuest: (redirectTo?: string) => Effect.Effect<void, UnauthorizedError, never>;
|
|
46
|
+
/**
|
|
47
|
+
* Share auth state with Honertia.
|
|
48
|
+
*/
|
|
49
|
+
export declare const shareAuth: Effect.Effect<void, never, HonertiaService>;
|
|
50
|
+
/**
|
|
51
|
+
* Middleware version of shareAuth for use with app.use().
|
|
52
|
+
*/
|
|
53
|
+
export declare function shareAuthMiddleware<E extends Env>(): MiddlewareHandler<E>;
|
|
54
|
+
/**
|
|
55
|
+
* Configuration for auth routes.
|
|
56
|
+
*/
|
|
57
|
+
export interface AuthRoutesConfig<E extends Env> {
|
|
58
|
+
loginPath?: string;
|
|
59
|
+
registerPath?: string;
|
|
60
|
+
logoutPath?: string;
|
|
61
|
+
apiPath?: string;
|
|
62
|
+
logoutRedirect?: string;
|
|
63
|
+
loginRedirect?: string;
|
|
64
|
+
loginComponent?: string;
|
|
65
|
+
registerComponent?: string;
|
|
66
|
+
sessionCookie?: string;
|
|
67
|
+
/**
|
|
68
|
+
* CORS configuration for auth API routes.
|
|
69
|
+
* If provided, adds CORS headers to `/api/auth/*` routes.
|
|
70
|
+
*/
|
|
71
|
+
cors?: {
|
|
72
|
+
origin: string | string[] | ((origin: string) => string | undefined | null);
|
|
73
|
+
credentials?: boolean;
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Register standard auth routes.
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* effectAuthRoutes(app, {
|
|
81
|
+
* loginComponent: 'Auth/Login',
|
|
82
|
+
* registerComponent: 'Auth/Register',
|
|
83
|
+
* })
|
|
84
|
+
*/
|
|
85
|
+
export declare function effectAuthRoutes<E extends Env>(app: Hono<E>, config?: AuthRoutesConfig<E>): void;
|
|
86
|
+
/**
|
|
87
|
+
* Middleware to load the authenticated user.
|
|
88
|
+
* This should be used early in the middleware chain.
|
|
89
|
+
*/
|
|
90
|
+
export declare function loadUser<E extends Env>(config?: {
|
|
91
|
+
userKey?: string;
|
|
92
|
+
sessionCookie?: string;
|
|
93
|
+
}): MiddlewareHandler<E>;
|
|
94
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +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;AAC9C,OAAO,KAAK,EAAE,IAAI,EAAE,iBAAiB,EAAE,GAAG,EAAE,MAAM,MAAM,CAAA;AACxD,OAAO,EAAE,eAAe,EAAe,eAAe,EAAkB,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAA;AAC5G,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAI/C;;;;;;;;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;;GAEG;AACH,MAAM,WAAW,gBAAgB,CAAC,CAAC,SAAS,GAAG;IAC7C,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;;;OAGG;IACH,IAAI,CAAC,EAAE;QACL,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC,CAAA;QAC3E,WAAW,CAAC,EAAE,OAAO,CAAA;KACtB,CAAA;CACF;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,GAAG,EAC5C,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,EACZ,MAAM,GAAE,gBAAgB,CAAC,CAAC,CAAM,GAC/B,IAAI,CAoGN;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,GAAG,EACpC,MAAM,GAAE;IACN,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,aAAa,CAAC,EAAE,MAAM,CAAA;CAClB,GACL,iBAAiB,CAAC,CAAC,CAAC,CAwBtB"}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Effect Auth Layers and Helpers
|
|
3
|
+
*
|
|
4
|
+
* Authentication and authorization via Effect Layers.
|
|
5
|
+
*/
|
|
6
|
+
import { Effect, Layer, Option } from 'effect';
|
|
7
|
+
import { AuthUserService, AuthService, HonertiaService, RequestService } from './services.js';
|
|
8
|
+
import { UnauthorizedError } from './errors.js';
|
|
9
|
+
import { effectRoutes } from './routing.js';
|
|
10
|
+
import { render } from './responses.js';
|
|
11
|
+
/**
|
|
12
|
+
* Layer that requires an authenticated user.
|
|
13
|
+
* Fails with UnauthorizedError if no user is present.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* effectRoutes(app)
|
|
17
|
+
* .provide(RequireAuthLayer)
|
|
18
|
+
* .get('/dashboard', showDashboard)
|
|
19
|
+
*/
|
|
20
|
+
export const RequireAuthLayer = Layer.effect(AuthUserService, Effect.gen(function* () {
|
|
21
|
+
// Try to get existing AuthUserService
|
|
22
|
+
const maybeUser = yield* Effect.serviceOption(AuthUserService);
|
|
23
|
+
if (Option.isNone(maybeUser)) {
|
|
24
|
+
return yield* Effect.fail(new UnauthorizedError({
|
|
25
|
+
message: 'Authentication required',
|
|
26
|
+
redirectTo: '/login',
|
|
27
|
+
}));
|
|
28
|
+
}
|
|
29
|
+
return maybeUser.value;
|
|
30
|
+
}));
|
|
31
|
+
/**
|
|
32
|
+
* Layer that requires no authenticated user (guest only).
|
|
33
|
+
* Fails if a user is present, succeeds (as a no-op) if no user.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* effectRoutes(app)
|
|
37
|
+
* .provide(RequireGuestLayer)
|
|
38
|
+
* .get('/login', showLogin)
|
|
39
|
+
*/
|
|
40
|
+
export const RequireGuestLayer = Layer.effectDiscard(Effect.gen(function* () {
|
|
41
|
+
const maybeUser = yield* Effect.serviceOption(AuthUserService);
|
|
42
|
+
if (Option.isSome(maybeUser)) {
|
|
43
|
+
return yield* Effect.fail(new UnauthorizedError({
|
|
44
|
+
message: 'Already authenticated',
|
|
45
|
+
redirectTo: '/',
|
|
46
|
+
}));
|
|
47
|
+
}
|
|
48
|
+
// Guest confirmed - no user present, succeed silently
|
|
49
|
+
}));
|
|
50
|
+
/**
|
|
51
|
+
* Check if user is authenticated without failing.
|
|
52
|
+
*/
|
|
53
|
+
export const isAuthenticated = Effect.serviceOption(AuthUserService).pipe(Effect.map(Option.isSome));
|
|
54
|
+
/**
|
|
55
|
+
* Get the current user if authenticated.
|
|
56
|
+
*/
|
|
57
|
+
export const currentUser = Effect.serviceOption(AuthUserService).pipe(Effect.map((option) => (Option.isSome(option) ? option.value : null)));
|
|
58
|
+
/**
|
|
59
|
+
* Require authentication or redirect.
|
|
60
|
+
*/
|
|
61
|
+
export const requireAuth = (redirectTo = '/login') => Effect.serviceOption(AuthUserService).pipe(Effect.flatMap((option) => {
|
|
62
|
+
if (Option.isNone(option)) {
|
|
63
|
+
return Effect.fail(new UnauthorizedError({ message: 'Unauthenticated', redirectTo }));
|
|
64
|
+
}
|
|
65
|
+
return Effect.succeed(option.value);
|
|
66
|
+
}));
|
|
67
|
+
/**
|
|
68
|
+
* Require guest status or redirect.
|
|
69
|
+
*/
|
|
70
|
+
export const requireGuest = (redirectTo = '/') => Effect.serviceOption(AuthUserService).pipe(Effect.flatMap((option) => {
|
|
71
|
+
if (Option.isSome(option)) {
|
|
72
|
+
return Effect.fail(new UnauthorizedError({ message: 'Already authenticated', redirectTo }));
|
|
73
|
+
}
|
|
74
|
+
return Effect.void;
|
|
75
|
+
}));
|
|
76
|
+
/**
|
|
77
|
+
* Share auth state with Honertia.
|
|
78
|
+
*/
|
|
79
|
+
export const shareAuth = Effect.gen(function* () {
|
|
80
|
+
const honertia = yield* HonertiaService;
|
|
81
|
+
const user = yield* currentUser;
|
|
82
|
+
honertia.share('auth', { user: user?.user ?? null });
|
|
83
|
+
});
|
|
84
|
+
/**
|
|
85
|
+
* Middleware version of shareAuth for use with app.use().
|
|
86
|
+
*/
|
|
87
|
+
export function shareAuthMiddleware() {
|
|
88
|
+
return async (c, next) => {
|
|
89
|
+
const honertia = c.var?.honertia;
|
|
90
|
+
const authUser = c.var?.authUser;
|
|
91
|
+
if (honertia) {
|
|
92
|
+
honertia.share('auth', { user: authUser?.user ?? null });
|
|
93
|
+
}
|
|
94
|
+
await next();
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Register standard auth routes.
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* effectAuthRoutes(app, {
|
|
102
|
+
* loginComponent: 'Auth/Login',
|
|
103
|
+
* registerComponent: 'Auth/Register',
|
|
104
|
+
* })
|
|
105
|
+
*/
|
|
106
|
+
export function effectAuthRoutes(app, config = {}) {
|
|
107
|
+
const { loginPath = '/login', registerPath = '/register', logoutPath = '/logout', apiPath = '/api/auth', logoutRedirect = '/login', loginComponent = 'Auth/Login', registerComponent = 'Auth/Register', } = config;
|
|
108
|
+
const routes = effectRoutes(app);
|
|
109
|
+
// Login page (guest only)
|
|
110
|
+
routes.get(loginPath, Effect.gen(function* () {
|
|
111
|
+
yield* requireGuest(loginPath === '/login' ? '/' : loginPath);
|
|
112
|
+
return yield* render(loginComponent);
|
|
113
|
+
}));
|
|
114
|
+
// Register page (guest only)
|
|
115
|
+
routes.get(registerPath, Effect.gen(function* () {
|
|
116
|
+
yield* requireGuest(registerPath === '/register' ? '/' : registerPath);
|
|
117
|
+
return yield* render(registerComponent);
|
|
118
|
+
}));
|
|
119
|
+
// Logout (POST)
|
|
120
|
+
routes.post(logoutPath, Effect.gen(function* () {
|
|
121
|
+
const auth = yield* AuthService;
|
|
122
|
+
const request = yield* RequestService;
|
|
123
|
+
// Revoke session server-side
|
|
124
|
+
yield* Effect.tryPromise(() => auth.api.signOut({
|
|
125
|
+
headers: request.headers,
|
|
126
|
+
}));
|
|
127
|
+
// Clear cookie and redirect
|
|
128
|
+
const sessionCookie = config.sessionCookie ?? 'better-auth.session_token';
|
|
129
|
+
return new Response(null, {
|
|
130
|
+
status: 303,
|
|
131
|
+
headers: {
|
|
132
|
+
'Location': logoutRedirect,
|
|
133
|
+
'Set-Cookie': `${sessionCookie}=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly; SameSite=Lax`,
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
}));
|
|
137
|
+
// Better-auth API handler (handles sign-in, sign-up, etc.)
|
|
138
|
+
// Apply CORS if configured
|
|
139
|
+
if (config.cors) {
|
|
140
|
+
const corsConfig = config.cors;
|
|
141
|
+
app.use(`${apiPath}/*`, async (c, next) => {
|
|
142
|
+
const origin = c.req.header('Origin');
|
|
143
|
+
// Determine allowed origin
|
|
144
|
+
let allowedOrigin = null;
|
|
145
|
+
if (typeof corsConfig.origin === 'function') {
|
|
146
|
+
allowedOrigin = origin ? corsConfig.origin(origin) ?? null : null;
|
|
147
|
+
}
|
|
148
|
+
else if (Array.isArray(corsConfig.origin)) {
|
|
149
|
+
allowedOrigin = origin && corsConfig.origin.includes(origin) ? origin : null;
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
allowedOrigin = corsConfig.origin;
|
|
153
|
+
}
|
|
154
|
+
if (allowedOrigin) {
|
|
155
|
+
c.header('Access-Control-Allow-Origin', allowedOrigin);
|
|
156
|
+
if (corsConfig.credentials) {
|
|
157
|
+
c.header('Access-Control-Allow-Credentials', 'true');
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
// Handle preflight
|
|
161
|
+
if (c.req.method === 'OPTIONS') {
|
|
162
|
+
c.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
|
|
163
|
+
c.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
|
|
164
|
+
c.header('Access-Control-Max-Age', '86400');
|
|
165
|
+
return c.body(null, 204);
|
|
166
|
+
}
|
|
167
|
+
await next();
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
app.all(`${apiPath}/*`, async (c) => {
|
|
171
|
+
const auth = c.var?.auth;
|
|
172
|
+
if (!auth) {
|
|
173
|
+
return c.json({ error: 'Auth not configured' }, 500);
|
|
174
|
+
}
|
|
175
|
+
return auth.handler(c.req.raw);
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Middleware to load the authenticated user.
|
|
180
|
+
* This should be used early in the middleware chain.
|
|
181
|
+
*/
|
|
182
|
+
export function loadUser(config = {}) {
|
|
183
|
+
const { userKey = 'authUser' } = config;
|
|
184
|
+
return async (c, next) => {
|
|
185
|
+
const auth = c.var?.auth;
|
|
186
|
+
if (!auth) {
|
|
187
|
+
await next();
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
try {
|
|
191
|
+
const session = await auth.api.getSession({ headers: c.req.raw.headers });
|
|
192
|
+
if (session) {
|
|
193
|
+
c.set(userKey, {
|
|
194
|
+
user: session.user,
|
|
195
|
+
session: session.session,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
// Session fetch failed, continue without user
|
|
201
|
+
}
|
|
202
|
+
await next();
|
|
203
|
+
};
|
|
204
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hono-Effect Bridge
|
|
3
|
+
*
|
|
4
|
+
* Middleware that connects Hono's request handling to Effect's runtime.
|
|
5
|
+
*/
|
|
6
|
+
import { Layer, ManagedRuntime } from 'effect';
|
|
7
|
+
import type { Context as HonoContext, MiddlewareHandler, Env } from 'hono';
|
|
8
|
+
import { DatabaseService, AuthService, AuthUserService, HonertiaService, RequestService, ResponseFactoryService } from './services.js';
|
|
9
|
+
/**
|
|
10
|
+
* Configuration for the Effect bridge.
|
|
11
|
+
*/
|
|
12
|
+
export interface EffectBridgeConfig<E extends Env> {
|
|
13
|
+
database?: (c: HonoContext<E>) => unknown;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Symbol for storing Effect runtime in Hono context.
|
|
17
|
+
*/
|
|
18
|
+
declare const EFFECT_RUNTIME: unique symbol;
|
|
19
|
+
/**
|
|
20
|
+
* Extend Hono context with Effect runtime.
|
|
21
|
+
*/
|
|
22
|
+
declare module 'hono' {
|
|
23
|
+
interface ContextVariableMap {
|
|
24
|
+
[EFFECT_RUNTIME]?: ManagedRuntime.ManagedRuntime<DatabaseService | AuthService | AuthUserService | HonertiaService | RequestService | ResponseFactoryService, never>;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Build the Effect layer from Hono context.
|
|
29
|
+
*/
|
|
30
|
+
export declare function buildContextLayer<E extends Env>(c: HonoContext<E>, config?: EffectBridgeConfig<E>): Layer.Layer<RequestService | ResponseFactoryService | HonertiaService | DatabaseService | AuthService | AuthUserService, never, never>;
|
|
31
|
+
/**
|
|
32
|
+
* Get the Effect runtime from Hono context.
|
|
33
|
+
*/
|
|
34
|
+
export declare function getEffectRuntime<E extends Env>(c: HonoContext<E>): ManagedRuntime.ManagedRuntime<any, never> | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* Middleware that sets up the Effect runtime for each request.
|
|
37
|
+
*/
|
|
38
|
+
export declare function effectBridge<E extends Env>(config?: EffectBridgeConfig<E>): MiddlewareHandler<E>;
|
|
39
|
+
export {};
|
|
40
|
+
//# sourceMappingURL=bridge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../../src/effect/bridge.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAU,KAAK,EAAE,cAAc,EAAW,MAAM,QAAQ,CAAA;AAC/D,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;;GAEG;AACH,MAAM,WAAW,kBAAkB,CAAC,CAAC,SAAS,GAAG;IAC/C,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,OAAO,CAAA;CAC1C;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,EAC7C,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,EACjB,MAAM,CAAC,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAC7B,KAAK,CAAC,KAAK,CACV,cAAc,GACd,sBAAsB,GACtB,eAAe,GACf,eAAe,GACf,WAAW,GACX,eAAe,EACjB,KAAK,EACL,KAAK,CACN,CAmCA;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,EACxC,MAAM,CAAC,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAC7B,iBAAiB,CAAC,CAAC,CAAC,CAetB"}
|