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.
Files changed (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +610 -0
  3. package/dist/auth.d.ts +10 -0
  4. package/dist/auth.d.ts.map +1 -0
  5. package/dist/auth.js +11 -0
  6. package/dist/effect/action.d.ts +107 -0
  7. package/dist/effect/action.d.ts.map +1 -0
  8. package/dist/effect/action.js +150 -0
  9. package/dist/effect/auth.d.ts +94 -0
  10. package/dist/effect/auth.d.ts.map +1 -0
  11. package/dist/effect/auth.js +204 -0
  12. package/dist/effect/bridge.d.ts +40 -0
  13. package/dist/effect/bridge.d.ts.map +1 -0
  14. package/dist/effect/bridge.js +103 -0
  15. package/dist/effect/errors.d.ts +78 -0
  16. package/dist/effect/errors.d.ts.map +1 -0
  17. package/dist/effect/errors.js +37 -0
  18. package/dist/effect/handler.d.ts +25 -0
  19. package/dist/effect/handler.d.ts.map +1 -0
  20. package/dist/effect/handler.js +120 -0
  21. package/dist/effect/index.d.ts +16 -0
  22. package/dist/effect/index.d.ts.map +1 -0
  23. package/dist/effect/index.js +25 -0
  24. package/dist/effect/responses.d.ts +73 -0
  25. package/dist/effect/responses.d.ts.map +1 -0
  26. package/dist/effect/responses.js +104 -0
  27. package/dist/effect/routing.d.ts +90 -0
  28. package/dist/effect/routing.d.ts.map +1 -0
  29. package/dist/effect/routing.js +124 -0
  30. package/dist/effect/schema.d.ts +263 -0
  31. package/dist/effect/schema.d.ts.map +1 -0
  32. package/dist/effect/schema.js +586 -0
  33. package/dist/effect/services.d.ts +85 -0
  34. package/dist/effect/services.d.ts.map +1 -0
  35. package/dist/effect/services.js +24 -0
  36. package/dist/effect/validation.d.ts +38 -0
  37. package/dist/effect/validation.d.ts.map +1 -0
  38. package/dist/effect/validation.js +69 -0
  39. package/dist/helpers.d.ts +65 -0
  40. package/dist/helpers.d.ts.map +1 -0
  41. package/dist/helpers.js +116 -0
  42. package/dist/index.d.ts +14 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +26 -0
  45. package/dist/middleware.d.ts +14 -0
  46. package/dist/middleware.d.ts.map +1 -0
  47. package/dist/middleware.js +113 -0
  48. package/dist/react.d.ts +17 -0
  49. package/dist/react.d.ts.map +1 -0
  50. package/dist/react.js +4 -0
  51. package/dist/schema.d.ts +9 -0
  52. package/dist/schema.d.ts.map +1 -0
  53. package/dist/schema.js +34 -0
  54. package/dist/setup.d.ts +113 -0
  55. package/dist/setup.d.ts.map +1 -0
  56. package/dist/setup.js +96 -0
  57. package/dist/test-utils.d.ts +105 -0
  58. package/dist/test-utils.d.ts.map +1 -0
  59. package/dist/test-utils.js +210 -0
  60. package/dist/types.d.ts +37 -0
  61. package/dist/types.d.ts.map +1 -0
  62. package/dist/types.js +11 -0
  63. package/package.json +71 -0
@@ -0,0 +1,103 @@
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 { DatabaseService, AuthService, AuthUserService, HonertiaService, RequestService, ResponseFactoryService, } from './services.js';
8
+ /**
9
+ * Symbol for storing Effect runtime in Hono context.
10
+ */
11
+ const EFFECT_RUNTIME = Symbol('effectRuntime');
12
+ /**
13
+ * Create a RequestContext from Hono context.
14
+ */
15
+ function createRequestContext(c) {
16
+ return {
17
+ method: c.req.method,
18
+ url: c.req.url,
19
+ headers: c.req.raw.headers,
20
+ param: (name) => c.req.param(name),
21
+ params: () => {
22
+ const params = c.req.param();
23
+ return typeof params === 'string' ? {} : params;
24
+ },
25
+ query: () => c.req.query(),
26
+ json: () => c.req.json(),
27
+ parseBody: () => c.req.parseBody(),
28
+ header: (name) => c.req.header(name),
29
+ };
30
+ }
31
+ /**
32
+ * Create a ResponseFactory from Hono context.
33
+ */
34
+ function createResponseFactory(c) {
35
+ return {
36
+ redirect: (url, status = 302) => c.redirect(url, status),
37
+ json: (data, status = 200) => c.json(data, status),
38
+ text: (data, status = 200) => c.text(data, status),
39
+ notFound: () => c.notFound(),
40
+ };
41
+ }
42
+ /**
43
+ * Create a HonertiaRenderer from Hono context.
44
+ */
45
+ function createHonertiaRenderer(c) {
46
+ const honertia = c.var?.honertia;
47
+ if (!honertia) {
48
+ return {
49
+ render: async () => c.text('Honertia not configured', 500),
50
+ share: () => { },
51
+ setErrors: () => { },
52
+ };
53
+ }
54
+ return {
55
+ render: (component, props) => honertia.render(component, props),
56
+ share: (key, value) => honertia.share(key, value),
57
+ setErrors: (errors) => honertia.setErrors(errors),
58
+ };
59
+ }
60
+ /**
61
+ * Build the Effect layer from Hono context.
62
+ */
63
+ export function buildContextLayer(c, config) {
64
+ const requestLayer = Layer.succeed(RequestService, createRequestContext(c));
65
+ const responseLayer = Layer.succeed(ResponseFactoryService, createResponseFactory(c));
66
+ const honertiaLayer = Layer.succeed(HonertiaService, createHonertiaRenderer(c));
67
+ // Build optional layers
68
+ const databaseLayer = config?.database
69
+ ? Layer.succeed(DatabaseService, config.database(c))
70
+ : Layer.succeed(DatabaseService, c.var?.db);
71
+ let baseLayer = Layer.mergeAll(requestLayer, responseLayer, honertiaLayer, databaseLayer);
72
+ if (c.var?.auth) {
73
+ baseLayer = Layer.merge(baseLayer, Layer.succeed(AuthService, c.var.auth));
74
+ }
75
+ if (c.var?.authUser) {
76
+ baseLayer = Layer.merge(baseLayer, Layer.succeed(AuthUserService, c.var.authUser));
77
+ }
78
+ return baseLayer;
79
+ }
80
+ /**
81
+ * Get the Effect runtime from Hono context.
82
+ */
83
+ export function getEffectRuntime(c) {
84
+ return c.var?.[EFFECT_RUNTIME];
85
+ }
86
+ /**
87
+ * Middleware that sets up the Effect runtime for each request.
88
+ */
89
+ export function effectBridge(config) {
90
+ return async (c, next) => {
91
+ const layer = buildContextLayer(c, config);
92
+ const runtime = ManagedRuntime.make(layer);
93
+ // Store runtime in context
94
+ c.set(EFFECT_RUNTIME, runtime);
95
+ try {
96
+ await next();
97
+ }
98
+ finally {
99
+ // Cleanup runtime after request
100
+ await runtime.dispose();
101
+ }
102
+ };
103
+ }
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Typed Errors for Honertia Effect
3
+ *
4
+ * Using Effect's Data.TaggedError for type-safe error handling.
5
+ */
6
+ declare const ValidationError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => import("effect/Cause").YieldableError & {
7
+ readonly _tag: "ValidationError";
8
+ } & Readonly<A>;
9
+ /**
10
+ * Validation Error - Field-level validation failures
11
+ */
12
+ export declare class ValidationError extends ValidationError_base<{
13
+ readonly errors: Record<string, string>;
14
+ readonly component?: string;
15
+ }> {
16
+ }
17
+ declare const UnauthorizedError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => import("effect/Cause").YieldableError & {
18
+ readonly _tag: "UnauthorizedError";
19
+ } & Readonly<A>;
20
+ /**
21
+ * Unauthorized Error - Authentication/authorization failures
22
+ */
23
+ export declare class UnauthorizedError extends UnauthorizedError_base<{
24
+ readonly message: string;
25
+ readonly redirectTo?: string;
26
+ }> {
27
+ }
28
+ declare const NotFoundError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => import("effect/Cause").YieldableError & {
29
+ readonly _tag: "NotFoundError";
30
+ } & Readonly<A>;
31
+ /**
32
+ * Not Found Error - Resource not found
33
+ */
34
+ export declare class NotFoundError extends NotFoundError_base<{
35
+ readonly resource: string;
36
+ readonly id?: string | number;
37
+ }> {
38
+ }
39
+ declare const ForbiddenError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => import("effect/Cause").YieldableError & {
40
+ readonly _tag: "ForbiddenError";
41
+ } & Readonly<A>;
42
+ /**
43
+ * Forbidden Error - Authenticated but not authorized
44
+ */
45
+ export declare class ForbiddenError extends ForbiddenError_base<{
46
+ readonly message: string;
47
+ }> {
48
+ }
49
+ declare const HttpError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => import("effect/Cause").YieldableError & {
50
+ readonly _tag: "HttpError";
51
+ } & Readonly<A>;
52
+ /**
53
+ * HTTP Error - Generic HTTP error
54
+ */
55
+ export declare class HttpError extends HttpError_base<{
56
+ readonly status: number;
57
+ readonly message: string;
58
+ readonly body?: unknown;
59
+ }> {
60
+ }
61
+ declare const Redirect_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => Readonly<A> & {
62
+ readonly _tag: "Redirect";
63
+ };
64
+ /**
65
+ * Redirect - Not an error, but uses same control flow
66
+ * This is a tagged class (not error) for redirects
67
+ */
68
+ export declare class Redirect extends Redirect_base<{
69
+ readonly url: string;
70
+ readonly status: 302 | 303;
71
+ }> {
72
+ }
73
+ /**
74
+ * Union of all application errors
75
+ */
76
+ export type AppError = ValidationError | UnauthorizedError | NotFoundError | ForbiddenError | HttpError;
77
+ export {};
78
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/effect/errors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;;;;AAIH;;GAEG;AACH,qBAAa,eAAgB,SAAQ,qBAAoC;IACvE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACvC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAC5B,CAAC;CAAG;;;;AAEL;;GAEG;AACH,qBAAa,iBAAkB,SAAQ,uBAAsC;IAC3E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAC7B,CAAC;CAAG;;;;AAEL;;GAEG;AACH,qBAAa,aAAc,SAAQ,mBAAkC;IACnE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CAC9B,CAAC;CAAG;;;;AAEL;;GAEG;AACH,qBAAa,cAAe,SAAQ,oBAAmC;IACrE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CACzB,CAAC;CAAG;;;;AAEL;;GAEG;AACH,qBAAa,SAAU,SAAQ,eAA8B;IAC3D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAA;CACxB,CAAC;CAAG;;;;AAEL;;;GAGG;AACH,qBAAa,QAAS,SAAQ,cAA6B;IACzD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,MAAM,EAAE,GAAG,GAAG,GAAG,CAAA;CAC3B,CAAC;CAAG;AAEL;;GAEG;AACH,MAAM,MAAM,QAAQ,GAChB,eAAe,GACf,iBAAiB,GACjB,aAAa,GACb,cAAc,GACd,SAAS,CAAA"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Typed Errors for Honertia Effect
3
+ *
4
+ * Using Effect's Data.TaggedError for type-safe error handling.
5
+ */
6
+ import { Data } from 'effect';
7
+ /**
8
+ * Validation Error - Field-level validation failures
9
+ */
10
+ export class ValidationError extends Data.TaggedError('ValidationError') {
11
+ }
12
+ /**
13
+ * Unauthorized Error - Authentication/authorization failures
14
+ */
15
+ export class UnauthorizedError extends Data.TaggedError('UnauthorizedError') {
16
+ }
17
+ /**
18
+ * Not Found Error - Resource not found
19
+ */
20
+ export class NotFoundError extends Data.TaggedError('NotFoundError') {
21
+ }
22
+ /**
23
+ * Forbidden Error - Authenticated but not authorized
24
+ */
25
+ export class ForbiddenError extends Data.TaggedError('ForbiddenError') {
26
+ }
27
+ /**
28
+ * HTTP Error - Generic HTTP error
29
+ */
30
+ export class HttpError extends Data.TaggedError('HttpError') {
31
+ }
32
+ /**
33
+ * Redirect - Not an error, but uses same control flow
34
+ * This is a tagged class (not error) for redirects
35
+ */
36
+ export class Redirect extends Data.TaggedClass('Redirect') {
37
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Effect Handler
3
+ *
4
+ * Wraps Effect computations into Hono handlers.
5
+ */
6
+ import { Effect } from 'effect';
7
+ import type { Context as HonoContext, MiddlewareHandler, Env } from 'hono';
8
+ import { Redirect, type AppError } from './errors.js';
9
+ /**
10
+ * Convert an Effect error to an HTTP response.
11
+ */
12
+ export declare function errorToResponse<E extends Env>(error: AppError, c: HonoContext<E>): Promise<Response>;
13
+ /**
14
+ * Wrap an Effect into a Hono handler.
15
+ */
16
+ export declare function effectHandler<E extends Env, R, Err extends AppError>(effect: Effect.Effect<Response | Redirect, Err, R>): MiddlewareHandler<E>;
17
+ /**
18
+ * Create a handler from a function that returns an Effect.
19
+ */
20
+ export declare function effect<E extends Env, R, Err extends AppError>(fn: () => Effect.Effect<Response | Redirect, Err, R>): MiddlewareHandler<E>;
21
+ /**
22
+ * Create a handler from an Effect directly.
23
+ */
24
+ export declare const handle: typeof effectHandler;
25
+ //# sourceMappingURL=handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../src/effect/handler.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,MAAM,EAA+B,MAAM,QAAQ,CAAA;AAC5D,OAAO,KAAK,EAAE,OAAO,IAAI,WAAW,EAAE,iBAAiB,EAAE,GAAG,EAAE,MAAM,MAAM,CAAA;AAE1E,OAAO,EAML,QAAQ,EACR,KAAK,QAAQ,EACd,MAAM,aAAa,CAAA;AAGpB;;GAEG;AACH,wBAAsB,eAAe,CAAC,CAAC,SAAS,GAAG,EACjD,KAAK,EAAE,QAAQ,EACf,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,GAChB,OAAO,CAAC,QAAQ,CAAC,CA6CnB;AAgBD;;GAEG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,GAAG,EAAE,CAAC,EAAE,GAAG,SAAS,QAAQ,EAClE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,QAAQ,GAAG,QAAQ,EAAE,GAAG,EAAE,CAAC,CAAC,GACjD,iBAAiB,CAAC,CAAC,CAAC,CAoBtB;AAsCD;;GAEG;AACH,wBAAgB,MAAM,CAAC,CAAC,SAAS,GAAG,EAAE,CAAC,EAAE,GAAG,SAAS,QAAQ,EAC3D,EAAE,EAAE,MAAM,MAAM,CAAC,MAAM,CAAC,QAAQ,GAAG,QAAQ,EAAE,GAAG,EAAE,CAAC,CAAC,GACnD,iBAAiB,CAAC,CAAC,CAAC,CAEtB;AAED;;GAEG;AACH,eAAO,MAAM,MAAM,sBAAgB,CAAA"}
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Effect Handler
3
+ *
4
+ * Wraps Effect computations into Hono handlers.
5
+ */
6
+ import { Effect, Exit, Cause, ManagedRuntime } from 'effect';
7
+ import { getEffectRuntime, buildContextLayer } from './bridge.js';
8
+ import { ValidationError, UnauthorizedError, NotFoundError, ForbiddenError, HttpError, Redirect, } from './errors.js';
9
+ /**
10
+ * Convert an Effect error to an HTTP response.
11
+ */
12
+ export async function errorToResponse(error, c) {
13
+ if (error instanceof ValidationError) {
14
+ const isInertia = c.req.header('X-Inertia') === 'true';
15
+ const prefersJson = c.req.header('Accept')?.includes('application/json') ||
16
+ c.req.header('Content-Type')?.includes('application/json');
17
+ if (prefersJson && !isInertia) {
18
+ return c.json({ errors: error.errors }, 422);
19
+ }
20
+ // For Inertia requests with a component, render the component with errors
21
+ if (error.component && c.var?.honertia) {
22
+ const honertia = c.var.honertia;
23
+ honertia.setErrors(error.errors);
24
+ return await honertia.render(error.component);
25
+ }
26
+ // Redirect back with errors
27
+ const referer = c.req.header('Referer') || '/';
28
+ c.var?.honertia?.setErrors(error.errors);
29
+ return c.redirect(referer, 303);
30
+ }
31
+ if (error instanceof UnauthorizedError) {
32
+ const isInertia = c.req.header('X-Inertia') === 'true';
33
+ const redirectTo = error.redirectTo ?? '/login';
34
+ return c.redirect(redirectTo, isInertia ? 303 : 302);
35
+ }
36
+ if (error instanceof ForbiddenError) {
37
+ return c.json({ message: error.message }, 403);
38
+ }
39
+ if (error instanceof NotFoundError) {
40
+ return c.notFound();
41
+ }
42
+ if (error instanceof HttpError) {
43
+ return c.json({ message: error.message, ...error.body }, error.status);
44
+ }
45
+ // Unknown error
46
+ console.error('Unhandled error:', error);
47
+ return c.json({ message: 'Internal server error' }, 500);
48
+ }
49
+ /**
50
+ * Handle a Redirect value (which is not an error).
51
+ */
52
+ function handleRedirect(redirect, c) {
53
+ return c.redirect(redirect.url, redirect.status);
54
+ }
55
+ /**
56
+ * Check if a value is a Redirect.
57
+ */
58
+ function isRedirect(value) {
59
+ return value instanceof Redirect;
60
+ }
61
+ /**
62
+ * Wrap an Effect into a Hono handler.
63
+ */
64
+ export function effectHandler(effect) {
65
+ return async (c) => {
66
+ const runtime = getEffectRuntime(c);
67
+ if (!runtime) {
68
+ // No runtime set up, create one for this request
69
+ const layer = buildContextLayer(c);
70
+ const tempRuntime = ManagedRuntime.make(layer);
71
+ try {
72
+ const exit = await tempRuntime.runPromiseExit(effect);
73
+ return await handleExit(exit, c);
74
+ }
75
+ finally {
76
+ await tempRuntime.dispose();
77
+ }
78
+ }
79
+ const exit = await runtime.runPromiseExit(effect);
80
+ return await handleExit(exit, c);
81
+ };
82
+ }
83
+ /**
84
+ * Handle an Effect exit value.
85
+ */
86
+ async function handleExit(exit, c) {
87
+ if (Exit.isSuccess(exit)) {
88
+ const value = exit.value;
89
+ if (isRedirect(value)) {
90
+ return handleRedirect(value, c);
91
+ }
92
+ return value;
93
+ }
94
+ // Handle failure
95
+ const cause = exit.cause;
96
+ if (Cause.isFailure(cause)) {
97
+ const error = Cause.failureOption(cause);
98
+ if (error._tag === 'Some') {
99
+ return await errorToResponse(error.value, c);
100
+ }
101
+ }
102
+ // Handle defects (unexpected errors)
103
+ if (Cause.isDie(cause)) {
104
+ const defect = Cause.dieOption(cause);
105
+ if (defect._tag === 'Some') {
106
+ console.error('Effect defect:', defect.value);
107
+ }
108
+ }
109
+ return c.json({ message: 'Internal server error' }, 500);
110
+ }
111
+ /**
112
+ * Create a handler from a function that returns an Effect.
113
+ */
114
+ export function effect(fn) {
115
+ return effectHandler(Effect.suspend(fn));
116
+ }
117
+ /**
118
+ * Create a handler from an Effect directly.
119
+ */
120
+ export const handle = effectHandler;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Effect Module Barrel Export
3
+ *
4
+ * Re-exports all Effect-related functionality.
5
+ */
6
+ export { DatabaseService, AuthService, AuthUserService, HonertiaService, RequestService, ResponseFactoryService, type AuthUser, type HonertiaRenderer, type RequestContext, type ResponseFactory, } from './services.js';
7
+ export { ValidationError, UnauthorizedError, NotFoundError, ForbiddenError, HttpError, Redirect, type AppError, } from './errors.js';
8
+ export * from './schema.js';
9
+ export { getValidationData, formatSchemaErrors, validate, validateRequest, } from './validation.js';
10
+ export { effectBridge, buildContextLayer, getEffectRuntime, type EffectBridgeConfig, } from './bridge.js';
11
+ export { effectHandler, effect, handle, errorToResponse, } from './handler.js';
12
+ export { effectAction, dbAction, authAction, simpleAction, injectUser, dbOperation, prepareData, preparedAction, } from './action.js';
13
+ export { redirect, render, renderWithErrors, json, text, notFound, forbidden, httpError, prefersJson, jsonOrRender, share, } from './responses.js';
14
+ export { EffectRouteBuilder, effectRoutes, type EffectHandler, type BaseServices, } from './routing.js';
15
+ export { RequireAuthLayer, RequireGuestLayer, isAuthenticated, currentUser, requireAuth, requireGuest, shareAuth, shareAuthMiddleware, effectAuthRoutes, loadUser, type AuthRoutesConfig, } from './auth.js';
16
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +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,GACrB,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,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,YAAY,EACZ,UAAU,EACV,WAAW,EACX,WAAW,EACX,cAAc,GACf,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,QAAQ,EACR,KAAK,gBAAgB,GACtB,MAAM,WAAW,CAAA"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Effect Module Barrel Export
3
+ *
4
+ * Re-exports all Effect-related functionality.
5
+ */
6
+ // Services
7
+ export { DatabaseService, AuthService, AuthUserService, HonertiaService, RequestService, ResponseFactoryService, } from './services.js';
8
+ // Errors
9
+ export { ValidationError, UnauthorizedError, NotFoundError, ForbiddenError, HttpError, Redirect, } from './errors.js';
10
+ // Schema Validators
11
+ export * from './schema.js';
12
+ // Validation Helpers
13
+ export { getValidationData, formatSchemaErrors, validate, validateRequest, } from './validation.js';
14
+ // Bridge
15
+ export { effectBridge, buildContextLayer, getEffectRuntime, } from './bridge.js';
16
+ // Handler
17
+ export { effectHandler, effect, handle, errorToResponse, } from './handler.js';
18
+ // Action Factories
19
+ export { effectAction, dbAction, authAction, simpleAction, injectUser, dbOperation, prepareData, preparedAction, } from './action.js';
20
+ // Response Helpers
21
+ export { redirect, render, renderWithErrors, json, text, notFound, forbidden, httpError, prefersJson, jsonOrRender, share, } from './responses.js';
22
+ // Routing
23
+ export { EffectRouteBuilder, effectRoutes, } from './routing.js';
24
+ // Auth
25
+ export { RequireAuthLayer, RequireGuestLayer, isAuthenticated, currentUser, requireAuth, requireGuest, shareAuth, shareAuthMiddleware, effectAuthRoutes, loadUser, } from './auth.js';
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Effect Response Helpers
3
+ *
4
+ * Utility functions for creating responses within Effect computations.
5
+ */
6
+ import { Effect } from 'effect';
7
+ import { HonertiaService, ResponseFactoryService, RequestService } from './services.js';
8
+ import { Redirect, NotFoundError, ForbiddenError, HttpError } from './errors.js';
9
+ /**
10
+ * Create a redirect response.
11
+ *
12
+ * @example
13
+ * return yield* redirect('/projects')
14
+ * return yield* redirect('/login', 302)
15
+ */
16
+ export declare const redirect: (url: string, status?: 302 | 303) => Effect.Effect<Redirect, never, never>;
17
+ /**
18
+ * Render a Honertia component.
19
+ *
20
+ * @example
21
+ * return yield* render('Dashboard/Index', { projects })
22
+ */
23
+ export declare const render: <T extends object>(component: string, props?: T) => Effect.Effect<Response, never, HonertiaService>;
24
+ /**
25
+ * Render a Honertia component with validation errors pre-set.
26
+ *
27
+ * @example
28
+ * return yield* renderWithErrors('Auth/Login', { email: 'Invalid' })
29
+ */
30
+ export declare const renderWithErrors: <T extends object>(component: string, errors: Record<string, string>, props?: T) => Effect.Effect<Response, never, HonertiaService>;
31
+ /**
32
+ * Return a JSON response.
33
+ *
34
+ * @example
35
+ * return yield* json({ success: true })
36
+ * return yield* json({ error: 'Not found' }, 404)
37
+ */
38
+ export declare const json: <T>(data: T, status?: number) => Effect.Effect<Response, never, ResponseFactoryService>;
39
+ /**
40
+ * Return a text response.
41
+ */
42
+ export declare const text: (data: string, status?: number) => Effect.Effect<Response, never, ResponseFactoryService>;
43
+ /**
44
+ * Fail with a not found error.
45
+ *
46
+ * @example
47
+ * if (!project) return yield* notFound('Project')
48
+ */
49
+ export declare const notFound: (resource: string, id?: string | number) => Effect.Effect<never, NotFoundError, never>;
50
+ /**
51
+ * Fail with a forbidden error.
52
+ *
53
+ * @example
54
+ * if (!canEdit) return yield* forbidden('You cannot edit this resource')
55
+ */
56
+ export declare const forbidden: (message?: string) => Effect.Effect<never, ForbiddenError, never>;
57
+ /**
58
+ * Fail with a custom HTTP error.
59
+ */
60
+ export declare const httpError: (status: number, message: string, body?: unknown) => Effect.Effect<never, HttpError, never>;
61
+ /**
62
+ * Check if the request prefers JSON response.
63
+ */
64
+ export declare const prefersJson: Effect.Effect<boolean, never, RequestService>;
65
+ /**
66
+ * Return JSON if client prefers it, otherwise render Honertia component.
67
+ */
68
+ export declare const jsonOrRender: <T extends object>(component: string, data: T) => Effect.Effect<Response, never, RequestService | HonertiaService | ResponseFactoryService>;
69
+ /**
70
+ * Share data with all Honertia responses.
71
+ */
72
+ export declare const share: (key: string, value: unknown) => Effect.Effect<void, never, HonertiaService>;
73
+ //# sourceMappingURL=responses.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"responses.d.ts","sourceRoot":"","sources":["../../src/effect/responses.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC/B,OAAO,EACL,eAAe,EACf,sBAAsB,EACtB,cAAc,EACf,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAEhF;;;;;;GAMG;AACH,eAAO,MAAM,QAAQ,GAAI,KAAK,MAAM,EAAE,SAAQ,GAAG,GAAG,GAAS,KAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,KAAK,EAAE,KAAK,CACrD,CAAA;AAE/C;;;;;GAKG;AACH,eAAO,MAAM,MAAM,GAAI,CAAC,SAAS,MAAM,EACrC,WAAW,MAAM,EACjB,QAAQ,CAAC,KACR,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,KAAK,EAAE,eAAe,CAI7C,CAAA;AAEJ;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GAAI,CAAC,SAAS,MAAM,EAC/C,WAAW,MAAM,EACjB,QAAQ,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC9B,QAAQ,CAAC,KACR,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,KAAK,EAAE,eAAe,CAK7C,CAAA;AAEJ;;;;;;GAMG;AACH,eAAO,MAAM,IAAI,GAAI,CAAC,EAAE,MAAM,CAAC,EAAE,eAAY,KAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,KAAK,EAAE,sBAAsB,CAIjG,CAAA;AAEJ;;GAEG;AACH,eAAO,MAAM,IAAI,GAAI,MAAM,MAAM,EAAE,eAAY,KAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,KAAK,EAAE,sBAAsB,CAInG,CAAA;AAEJ;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,GAAI,UAAU,MAAM,EAAE,KAAK,MAAM,GAAG,MAAM,KAAG,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,aAAa,EAAE,KAAK,CACzD,CAAA;AAElD;;;;;GAKG;AACH,eAAO,MAAM,SAAS,GAAI,gBAAqB,KAAG,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,cAAc,EAAE,KAAK,CAC9C,CAAA;AAE9C;;GAEG;AACH,eAAO,MAAM,SAAS,GACpB,QAAQ,MAAM,EACd,SAAS,MAAM,EACf,OAAO,OAAO,KACb,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,CACe,CAAA;AAEvD;;GAEG;AACH,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,EAAE,cAAc,CAWlE,CAAA;AAEJ;;GAEG;AACH,eAAO,MAAM,YAAY,GAAI,CAAC,SAAS,MAAM,EAC3C,WAAW,MAAM,EACjB,MAAM,CAAC,KACN,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,KAAK,EAAE,cAAc,GAAG,eAAe,GAAG,sBAAsB,CAOvF,CAAA;AAEJ;;GAEG;AACH,eAAO,MAAM,KAAK,GAAI,KAAK,MAAM,EAAE,OAAO,OAAO,KAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,eAAe,CAI1F,CAAA"}
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Effect Response Helpers
3
+ *
4
+ * Utility functions for creating responses within Effect computations.
5
+ */
6
+ import { Effect } from 'effect';
7
+ import { HonertiaService, ResponseFactoryService, RequestService, } from './services.js';
8
+ import { Redirect, NotFoundError, ForbiddenError, HttpError } from './errors.js';
9
+ /**
10
+ * Create a redirect response.
11
+ *
12
+ * @example
13
+ * return yield* redirect('/projects')
14
+ * return yield* redirect('/login', 302)
15
+ */
16
+ export const redirect = (url, status = 303) => Effect.succeed(new Redirect({ url, status }));
17
+ /**
18
+ * Render a Honertia component.
19
+ *
20
+ * @example
21
+ * return yield* render('Dashboard/Index', { projects })
22
+ */
23
+ export const render = (component, props) => Effect.gen(function* () {
24
+ const honertia = yield* HonertiaService;
25
+ return yield* Effect.promise(() => honertia.render(component, props));
26
+ });
27
+ /**
28
+ * Render a Honertia component with validation errors pre-set.
29
+ *
30
+ * @example
31
+ * return yield* renderWithErrors('Auth/Login', { email: 'Invalid' })
32
+ */
33
+ export const renderWithErrors = (component, errors, props) => Effect.gen(function* () {
34
+ const honertia = yield* HonertiaService;
35
+ honertia.setErrors(errors);
36
+ return yield* Effect.promise(() => honertia.render(component, props));
37
+ });
38
+ /**
39
+ * Return a JSON response.
40
+ *
41
+ * @example
42
+ * return yield* json({ success: true })
43
+ * return yield* json({ error: 'Not found' }, 404)
44
+ */
45
+ export const json = (data, status = 200) => Effect.gen(function* () {
46
+ const factory = yield* ResponseFactoryService;
47
+ return factory.json(data, status);
48
+ });
49
+ /**
50
+ * Return a text response.
51
+ */
52
+ export const text = (data, status = 200) => Effect.gen(function* () {
53
+ const factory = yield* ResponseFactoryService;
54
+ return factory.text(data, status);
55
+ });
56
+ /**
57
+ * Fail with a not found error.
58
+ *
59
+ * @example
60
+ * if (!project) return yield* notFound('Project')
61
+ */
62
+ export const notFound = (resource, id) => Effect.fail(new NotFoundError({ resource, id }));
63
+ /**
64
+ * Fail with a forbidden error.
65
+ *
66
+ * @example
67
+ * if (!canEdit) return yield* forbidden('You cannot edit this resource')
68
+ */
69
+ export const forbidden = (message = 'Forbidden') => Effect.fail(new ForbiddenError({ message }));
70
+ /**
71
+ * Fail with a custom HTTP error.
72
+ */
73
+ export const httpError = (status, message, body) => Effect.fail(new HttpError({ status, message, body }));
74
+ /**
75
+ * Check if the request prefers JSON response.
76
+ */
77
+ export const prefersJson = Effect.gen(function* () {
78
+ const request = yield* RequestService;
79
+ const isInertia = request.header('X-Inertia') === 'true';
80
+ if (isInertia)
81
+ return false;
82
+ const accept = request.header('Accept') || '';
83
+ if (accept.includes('application/json'))
84
+ return true;
85
+ const contentType = request.header('Content-Type') || '';
86
+ return contentType.includes('application/json');
87
+ });
88
+ /**
89
+ * Return JSON if client prefers it, otherwise render Honertia component.
90
+ */
91
+ export const jsonOrRender = (component, data) => Effect.gen(function* () {
92
+ const wantsJson = yield* prefersJson;
93
+ if (wantsJson) {
94
+ return yield* json(data);
95
+ }
96
+ return yield* render(component, data);
97
+ });
98
+ /**
99
+ * Share data with all Honertia responses.
100
+ */
101
+ export const share = (key, value) => Effect.gen(function* () {
102
+ const honertia = yield* HonertiaService;
103
+ honertia.share(key, value);
104
+ });