honertia 0.1.11 → 0.1.13

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 CHANGED
@@ -907,6 +907,174 @@ effectRoutes(app).group((route) => {
907
907
  })
908
908
  ```
909
909
 
910
+ #### Route Parameter Validation
911
+
912
+ You can pass a `params` schema to validate route parameters before your handler runs. Invalid values automatically return a 404:
913
+
914
+ ```typescript
915
+ import { Schema as S } from 'effect'
916
+ import { uuid } from 'honertia/effect'
917
+
918
+ effectRoutes(app).get(
919
+ '/projects/:id',
920
+ showProject,
921
+ { params: S.Struct({ id: uuid }) }
922
+ )
923
+ ```
924
+
925
+ This runs validation *before* the handler executes, so invalid UUIDs never hit your database. You can validate multiple params and use any Effect Schema:
926
+
927
+ ```typescript
928
+ effectRoutes(app).get(
929
+ '/api/:version/projects/:id',
930
+ showProject,
931
+ {
932
+ params: S.Struct({
933
+ version: S.Literal('v1', 'v2'),
934
+ id: uuid,
935
+ }),
936
+ }
937
+ )
938
+ ```
939
+
940
+ #### Laravel-Style Route Model Binding
941
+
942
+ Honertia supports Laravel-style route model binding with the `{param}` syntax. This automatically resolves route parameters to database models, returning 404 if the model isn't found.
943
+
944
+ **Setup:**
945
+
946
+ 1. Add your Drizzle schema to the module augmentation:
947
+
948
+ ```typescript
949
+ // src/types.d.ts
950
+ import type { Database } from '~/db/db'
951
+ import type { auth } from '~/lib/auth'
952
+ import * as schema from '~/db/schema'
953
+
954
+ declare module 'honertia/effect' {
955
+ interface HonertiaDatabaseType {
956
+ type: Database
957
+ schema: typeof schema // Add this for route model binding
958
+ }
959
+
960
+ interface HonertiaAuthType {
961
+ type: typeof auth
962
+ }
963
+ }
964
+ ```
965
+
966
+ 2. Pass your schema to `effectRoutes`:
967
+
968
+ ```typescript
969
+ import * as schema from '~/db/schema'
970
+
971
+ effectRoutes(app, { schema })
972
+ .get('/projects/{project}', showProject)
973
+ ```
974
+
975
+ **Basic Usage:**
976
+
977
+ ```typescript
978
+ import { bound } from 'honertia/effect'
979
+
980
+ // Route: /projects/{project}
981
+ // Automatically queries: SELECT * FROM projects WHERE id = :project
982
+
983
+ effectRoutes(app, { schema }).get('/projects/{project}', showProject)
984
+
985
+ const showProject = Effect.gen(function* () {
986
+ const project = yield* bound('project') // Already fetched, guaranteed to exist
987
+ return yield* render('Projects/Show', { project })
988
+ })
989
+ ```
990
+
991
+ **Custom Column Binding:**
992
+
993
+ By default, bindings query the `id` column. Use `{param:column}` syntax to bind by a different column:
994
+
995
+ ```typescript
996
+ // Bind by slug instead of id
997
+ effectRoutes(app, { schema }).get('/projects/{project:slug}', showProject)
998
+ // Queries: SELECT * FROM projects WHERE slug = :project
999
+ ```
1000
+
1001
+ **Nested Route Scoping:**
1002
+
1003
+ For nested routes, Honertia automatically scopes child models to their parents using Drizzle relations:
1004
+
1005
+ ```typescript
1006
+ // Route: /users/{user}/posts/{post}
1007
+ effectRoutes(app, { schema }).get('/users/{user}/posts/{post}', showUserPost)
1008
+
1009
+ // Queries:
1010
+ // 1. SELECT * FROM users WHERE id = :user
1011
+ // 2. SELECT * FROM posts WHERE id = :post AND userId = :user.id
1012
+ ```
1013
+
1014
+ This uses your Drizzle relations to discover the foreign key:
1015
+
1016
+ ```typescript
1017
+ // db/schema.ts
1018
+ export const postsRelations = relations(posts, ({ one }) => ({
1019
+ user: one(users, {
1020
+ fields: [posts.userId],
1021
+ references: [users.id],
1022
+ }),
1023
+ }))
1024
+ ```
1025
+
1026
+ If no relation is found, the child is resolved without scoping (useful for unrelated resources in the same route).
1027
+
1028
+ **How Binding Works:**
1029
+
1030
+ 1. `{project}` is converted to `:project` for Hono's router
1031
+ 2. At request time, the param value is extracted
1032
+ 3. The table name is derived by pluralizing the param (`project` → `projects`)
1033
+ 4. A database query is executed against that table
1034
+ 5. If not found, a 404 is returned before your handler runs
1035
+ 6. If found, the model is available via `bound('project')`
1036
+
1037
+ **Combining with Param Validation:**
1038
+
1039
+ Route model binding and param validation work together. Validation runs first:
1040
+
1041
+ ```typescript
1042
+ effectRoutes(app, { schema }).get(
1043
+ '/projects/{project}',
1044
+ showProject,
1045
+ { params: S.Struct({ project: uuid }) } // Validates UUID format first
1046
+ )
1047
+ ```
1048
+
1049
+ Order of execution:
1050
+ 1. Param validation (returns 404 if schema fails)
1051
+ 2. Model binding (returns 404 if not found in database)
1052
+ 3. Your handler (model guaranteed to exist)
1053
+
1054
+ **Mixed Notation:**
1055
+
1056
+ You can mix Laravel-style `{binding}` with Hono-style `:param` in the same route. Only `{binding}` params are resolved from the database:
1057
+
1058
+ ```typescript
1059
+ // :version is a regular Hono param (not bound)
1060
+ // {project} is resolved from the database
1061
+ effectRoutes(app, { schema }).get(
1062
+ '/api/:version/projects/{project}',
1063
+ showProject
1064
+ )
1065
+
1066
+ const showProject = Effect.gen(function* () {
1067
+ const request = yield* RequestService
1068
+ const version = request.param('version') // 'v1', 'v2', etc.
1069
+ const project = yield* bound('project') // Database model
1070
+ // ...
1071
+ })
1072
+ ```
1073
+
1074
+ **Performance:**
1075
+
1076
+ Routes without `{bindings}` have zero overhead—binding resolution only runs when Laravel-style params are detected. The binding check is a simple regex test at route registration time.
1077
+
910
1078
  ## Validation
911
1079
 
912
1080
  Honertia uses Effect Schema with Laravel-inspired validators:
@@ -1354,10 +1522,12 @@ By default, `DatabaseService` and `AuthService` are typed as `unknown` since Hon
1354
1522
  // src/types.d.ts (or any .d.ts file in your project)
1355
1523
  import type { Database } from '~/db/db'
1356
1524
  import type { auth } from '~/lib/auth'
1525
+ import * as schema from '~/db/schema'
1357
1526
 
1358
1527
  declare module 'honertia/effect' {
1359
1528
  interface HonertiaDatabaseType {
1360
- type: Database // Your Drizzle/Prisma/Kysely type
1529
+ type: Database // Your Drizzle/Prisma/Kysely type
1530
+ schema: typeof schema // Your Drizzle schema (for route model binding)
1361
1531
  }
1362
1532
 
1363
1533
  interface HonertiaAuthType {
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Route Model Binding
3
+ *
4
+ * Laravel-style route model binding for Effect routes.
5
+ * Automatically resolves route parameters to database models.
6
+ */
7
+ import { Context, Effect } from 'effect';
8
+ import type { Table } from 'drizzle-orm';
9
+ import type { HonertiaDatabaseType } from './services.js';
10
+ declare const BoundModelNotFound_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 & {
11
+ readonly _tag: "BoundModelNotFound";
12
+ } & Readonly<A>;
13
+ /**
14
+ * Error thrown when a bound model is not found in the BoundModels context.
15
+ * This indicates a programming error - the binding key doesn't match any resolved model.
16
+ */
17
+ export declare class BoundModelNotFound extends BoundModelNotFound_base<{
18
+ readonly key: string;
19
+ }> {
20
+ get message(): string;
21
+ }
22
+ /**
23
+ * Parsed binding from route path.
24
+ */
25
+ export interface ParsedBinding {
26
+ /** The parameter name (e.g., 'project' from '{project}') */
27
+ param: string;
28
+ /** The column to query (e.g., 'id' or 'slug' from '{project:slug}') */
29
+ column: string;
30
+ }
31
+ /**
32
+ * Parse Laravel-style bindings from a route path.
33
+ *
34
+ * @example
35
+ * parseBindings('/users/{user}/posts/{post:slug}')
36
+ * // => [{ param: 'user', column: 'id' }, { param: 'post', column: 'slug' }]
37
+ */
38
+ export declare function parseBindings(path: string): ParsedBinding[];
39
+ /**
40
+ * Convert Laravel-style route to Hono-style route.
41
+ *
42
+ * @example
43
+ * toHonoPath('/users/{user}/posts/{post:slug}')
44
+ * // => '/users/:user/posts/:post'
45
+ */
46
+ export declare function toHonoPath(path: string): string;
47
+ declare const BoundModels_base: Context.TagClass<BoundModels, "honertia/BoundModels", ReadonlyMap<string, unknown>>;
48
+ /**
49
+ * Service tag for bound models.
50
+ * Provides access to resolved route models in handlers.
51
+ */
52
+ export declare class BoundModels extends BoundModels_base {
53
+ }
54
+ /**
55
+ * Type-safe accessor for bound models.
56
+ *
57
+ * @example
58
+ * const showProject = Effect.gen(function* () {
59
+ * const project = yield* bound('project')
60
+ * return inertia('Projects/Show', { project })
61
+ * })
62
+ */
63
+ export declare const bound: <K extends string>(key: K) => Effect.Effect<K extends keyof HonertiaDatabaseType["schema"] ? HonertiaDatabaseType["schema"][K] extends Table ? HonertiaDatabaseType["schema"][K]["$inferSelect"] : unknown : unknown, BoundModelNotFound, BoundModels>;
64
+ /**
65
+ * Pluralize a singular word.
66
+ * Handles common English pluralization rules.
67
+ *
68
+ * @example
69
+ * pluralize('user') // 'users'
70
+ * pluralize('category') // 'categories'
71
+ * pluralize('box') // 'boxes'
72
+ * pluralize('class') // 'classes'
73
+ */
74
+ export declare function pluralize(word: string): string;
75
+ /**
76
+ * Information about a relation between tables.
77
+ */
78
+ export interface RelationInfo {
79
+ /** Foreign key column on the child table (e.g., 'userId') */
80
+ foreignKey: string;
81
+ /** Referenced column on the parent table (e.g., 'id') */
82
+ references: string;
83
+ }
84
+ /**
85
+ * Find a relation from child table to parent table.
86
+ * Uses Drizzle's relations metadata to discover foreign keys.
87
+ *
88
+ * @param schema - The Drizzle schema object
89
+ * @param childTableName - Name of the child table (e.g., 'posts')
90
+ * @param parentTableName - Name of the parent table (e.g., 'users')
91
+ * @returns Relation info or null if no relation found
92
+ */
93
+ export declare function findRelation(schema: Record<string, unknown>, childTableName: string, parentTableName: string): RelationInfo | null;
94
+ export {};
95
+ //# sourceMappingURL=binding.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"binding.d.ts","sourceRoot":"","sources":["../../src/effect/binding.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,OAAO,EAAQ,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC9C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAA;;;;AAEzD;;;GAGG;AACH,qBAAa,kBAAmB,SAAQ,wBAAuC;IAC7E,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CACrB,CAAC;IACA,IAAI,OAAO,WAEV;CACF;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,4DAA4D;IAC5D,KAAK,EAAE,MAAM,CAAA;IACb,uEAAuE;IACvE,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,EAAE,CAa3D;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE/C;;AAED;;;GAGG;AACH,qBAAa,WAAY,SAAQ,gBAG9B;CAAG;AAEN;;;;;;;;GAQG;AACH,eAAO,MAAM,KAAK,GAAI,CAAC,SAAS,MAAM,EACpC,KAAK,CAAC,KACL,MAAM,CAAC,MAAM,CACd,CAAC,SAAS,MAAM,oBAAoB,CAAC,QAAQ,CAAC,GAC1C,oBAAoB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,KAAK,GAC7C,oBAAoB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,GACjD,OAAO,GACT,OAAO,EACX,kBAAkB,EAClB,WAAW,CAST,CAAA;AAEJ;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAS9C;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,6DAA6D;IAC7D,UAAU,EAAE,MAAM,CAAA;IAClB,yDAAyD;IACzD,UAAU,EAAE,MAAM,CAAA;CACnB;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,cAAc,EAAE,MAAM,EACtB,eAAe,EAAE,MAAM,GACtB,YAAY,GAAG,IAAI,CA2CrB"}
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Route Model Binding
3
+ *
4
+ * Laravel-style route model binding for Effect routes.
5
+ * Automatically resolves route parameters to database models.
6
+ */
7
+ import { Context, Data, Effect } from 'effect';
8
+ /**
9
+ * Error thrown when a bound model is not found in the BoundModels context.
10
+ * This indicates a programming error - the binding key doesn't match any resolved model.
11
+ */
12
+ export class BoundModelNotFound extends Data.TaggedError('BoundModelNotFound') {
13
+ get message() {
14
+ return `No bound model found for key: '${this.key}'. Ensure the route uses {${this.key}} binding syntax.`;
15
+ }
16
+ }
17
+ /**
18
+ * Parse Laravel-style bindings from a route path.
19
+ *
20
+ * @example
21
+ * parseBindings('/users/{user}/posts/{post:slug}')
22
+ * // => [{ param: 'user', column: 'id' }, { param: 'post', column: 'slug' }]
23
+ */
24
+ export function parseBindings(path) {
25
+ const regex = /\{(\w+)(?::(\w+))?\}/g;
26
+ const bindings = [];
27
+ let match;
28
+ while ((match = regex.exec(path)) !== null) {
29
+ bindings.push({
30
+ param: match[1],
31
+ column: match[2] ?? 'id',
32
+ });
33
+ }
34
+ return bindings;
35
+ }
36
+ /**
37
+ * Convert Laravel-style route to Hono-style route.
38
+ *
39
+ * @example
40
+ * toHonoPath('/users/{user}/posts/{post:slug}')
41
+ * // => '/users/:user/posts/:post'
42
+ */
43
+ export function toHonoPath(path) {
44
+ return path.replace(/\{(\w+)(?::\w+)?\}/g, ':$1');
45
+ }
46
+ /**
47
+ * Service tag for bound models.
48
+ * Provides access to resolved route models in handlers.
49
+ */
50
+ export class BoundModels extends Context.Tag('honertia/BoundModels')() {
51
+ }
52
+ /**
53
+ * Type-safe accessor for bound models.
54
+ *
55
+ * @example
56
+ * const showProject = Effect.gen(function* () {
57
+ * const project = yield* bound('project')
58
+ * return inertia('Projects/Show', { project })
59
+ * })
60
+ */
61
+ export const bound = (key) => Effect.gen(function* () {
62
+ const models = yield* BoundModels;
63
+ const model = models.get(key);
64
+ if (!model) {
65
+ return yield* Effect.fail(new BoundModelNotFound({ key }));
66
+ }
67
+ return model;
68
+ });
69
+ /**
70
+ * Pluralize a singular word.
71
+ * Handles common English pluralization rules.
72
+ *
73
+ * @example
74
+ * pluralize('user') // 'users'
75
+ * pluralize('category') // 'categories'
76
+ * pluralize('box') // 'boxes'
77
+ * pluralize('class') // 'classes'
78
+ */
79
+ export function pluralize(word) {
80
+ // Words ending in vowel + y: just add 's' (day -> days)
81
+ if (/[aeiou]y$/i.test(word))
82
+ return word + 's';
83
+ // Words ending in consonant + y: replace y with ies (category -> categories)
84
+ if (/y$/i.test(word))
85
+ return word.slice(0, -1) + 'ies';
86
+ // Words ending in s, x, z, ch, sh: add 'es' (box -> boxes, class -> classes)
87
+ if (/(?:s|x|z|ch|sh)$/i.test(word))
88
+ return word + 'es';
89
+ // Default: add 's'
90
+ return word + 's';
91
+ }
92
+ /**
93
+ * Find a relation from child table to parent table.
94
+ * Uses Drizzle's relations metadata to discover foreign keys.
95
+ *
96
+ * @param schema - The Drizzle schema object
97
+ * @param childTableName - Name of the child table (e.g., 'posts')
98
+ * @param parentTableName - Name of the parent table (e.g., 'users')
99
+ * @returns Relation info or null if no relation found
100
+ */
101
+ export function findRelation(schema, childTableName, parentTableName) {
102
+ // Look for relations definition (e.g., postsRelations)
103
+ const relationsKey = `${childTableName}Relations`;
104
+ const relations = schema[relationsKey];
105
+ if (!relations || typeof relations !== 'object') {
106
+ return null;
107
+ }
108
+ // Drizzle stores relations config - we need to inspect it
109
+ // The relations object has a config property with the relation definitions
110
+ const config = relations.config;
111
+ if (!config || typeof config !== 'function') {
112
+ return null;
113
+ }
114
+ // Try to extract relation info by calling the config
115
+ // This is a bit hacky but necessary to introspect Drizzle relations
116
+ try {
117
+ const relationDefs = config({
118
+ one: (table, opts) => ({ type: 'one', table, ...opts }),
119
+ many: (table, opts) => ({ type: 'many', table, ...opts }),
120
+ });
121
+ for (const [_name, rel] of Object.entries(relationDefs)) {
122
+ const relation = rel;
123
+ if (relation.type !== 'one')
124
+ continue;
125
+ // Check if this relation points to the parent table
126
+ const relatedTableName = getTableName(relation.table);
127
+ if (relatedTableName === parentTableName && relation.fields && relation.references) {
128
+ return {
129
+ foreignKey: getColumnName(relation.fields[0]),
130
+ references: getColumnName(relation.references[0]),
131
+ };
132
+ }
133
+ }
134
+ }
135
+ catch {
136
+ // If introspection fails, fall back to convention
137
+ }
138
+ return null;
139
+ }
140
+ /**
141
+ * Get table name from a Drizzle table object.
142
+ */
143
+ function getTableName(table) {
144
+ if (table && typeof table === 'object') {
145
+ // Drizzle tables have a Symbol for the table name
146
+ const symbols = Object.getOwnPropertySymbols(table);
147
+ for (const sym of symbols) {
148
+ if (sym.description === 'drizzle:Name') {
149
+ return table[sym];
150
+ }
151
+ }
152
+ // Fallback: check for _ property
153
+ if ('_' in table && typeof table._ === 'object') {
154
+ return table._.name;
155
+ }
156
+ }
157
+ return '';
158
+ }
159
+ /**
160
+ * Get column name from a Drizzle column object.
161
+ */
162
+ function getColumnName(column) {
163
+ if (column && typeof column === 'object') {
164
+ if ('name' in column) {
165
+ return column.name;
166
+ }
167
+ }
168
+ return '';
169
+ }
@@ -35,6 +35,18 @@ export interface EffectBridgeConfig<E extends Env, CustomServices = never> {
35
35
  * Return a Layer that provides your custom services.
36
36
  */
37
37
  services?: (c: HonoContext<E>) => Layer.Layer<CustomServices, never, never>;
38
+ /**
39
+ * Drizzle schema for route model binding.
40
+ * Required if using Laravel-style route model binding.
41
+ *
42
+ * @example
43
+ * ```typescript
44
+ * import * as schema from '~/db/schema'
45
+ *
46
+ * effectRoutes(app, { schema })
47
+ * ```
48
+ */
49
+ schema?: Record<string, unknown>;
38
50
  }
39
51
  /**
40
52
  * Symbol for storing Effect runtime in Hono context.
@@ -1 +1 @@
1
- {"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../../src/effect/bridge.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAA;AAC9C,OAAO,KAAK,EAAE,OAAO,IAAI,WAAW,EAAE,iBAAiB,EAAE,GAAG,EAAE,MAAM,MAAM,CAAA;AAC1E,OAAO,EACL,eAAe,EACf,WAAW,EACX,eAAe,EACf,eAAe,EACf,cAAc,EACd,sBAAsB,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"}
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;IAC3E;;;;;;;;;;OAUG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACjC;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"}
@@ -11,6 +11,7 @@ export { effectBridge, buildContextLayer, getEffectRuntime, type EffectBridgeCon
11
11
  export { effectHandler, effect, handle, errorToResponse, } from './handler.js';
12
12
  export { action, authorize, dbMutation, dbTransaction, type SafeTx, } from './action.js';
13
13
  export { redirect, render, renderWithErrors, json, text, notFound, forbidden, httpError, prefersJson, jsonOrRender, share, } from './responses.js';
14
- export { EffectRouteBuilder, effectRoutes, type EffectHandler, type BaseServices, } from './routing.js';
14
+ export { EffectRouteBuilder, effectRoutes, type EffectHandler, type BaseServices, type EffectRouteOptions, } from './routing.js';
15
+ export { BoundModels, BoundModelNotFound, bound, parseBindings, toHonoPath, type ParsedBinding, } from './binding.js';
15
16
  export { RequireAuthLayer, RequireGuestLayer, isAuthenticated, currentUser, requireAuth, requireGuest, shareAuth, shareAuthMiddleware, effectAuthRoutes, betterAuthFormAction, betterAuthLogoutAction, loadUser, type AuthRoutesConfig, type BetterAuthFormActionConfig, type BetterAuthLogoutConfig, type BetterAuthActionResult, } from './auth.js';
16
17
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/effect/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EACL,eAAe,EACf,WAAW,EACX,eAAe,EACf,eAAe,EACf,cAAc,EACd,sBAAsB,EACtB,KAAK,QAAQ,EACb,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,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,EACf,WAAW,EACX,SAAS,EACT,KAAK,SAAS,EACd,KAAK,OAAO,GACb,MAAM,iBAAiB,CAAA;AAGxB,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,gBAAgB,EAChB,KAAK,kBAAkB,GACxB,MAAM,aAAa,CAAA;AAGpB,OAAO,EACL,aAAa,EACb,MAAM,EACN,MAAM,EACN,eAAe,GAChB,MAAM,cAAc,CAAA;AAGrB,OAAO,EACL,MAAM,EACN,SAAS,EACT,UAAU,EACV,aAAa,EACb,KAAK,MAAM,GACZ,MAAM,aAAa,CAAA;AAGpB,OAAO,EACL,QAAQ,EACR,MAAM,EACN,gBAAgB,EAChB,IAAI,EACJ,IAAI,EACJ,QAAQ,EACR,SAAS,EACT,SAAS,EACT,WAAW,EACX,YAAY,EACZ,KAAK,GACN,MAAM,gBAAgB,CAAA;AAGvB,OAAO,EACL,kBAAkB,EAClB,YAAY,EACZ,KAAK,aAAa,EAClB,KAAK,YAAY,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
+ {"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,EACf,WAAW,EACX,SAAS,EACT,KAAK,SAAS,EACd,KAAK,OAAO,GACb,MAAM,iBAAiB,CAAA;AAGxB,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,gBAAgB,EAChB,KAAK,kBAAkB,GACxB,MAAM,aAAa,CAAA;AAGpB,OAAO,EACL,aAAa,EACb,MAAM,EACN,MAAM,EACN,eAAe,GAChB,MAAM,cAAc,CAAA;AAGrB,OAAO,EACL,MAAM,EACN,SAAS,EACT,UAAU,EACV,aAAa,EACb,KAAK,MAAM,GACZ,MAAM,aAAa,CAAA;AAGpB,OAAO,EACL,QAAQ,EACR,MAAM,EACN,gBAAgB,EAChB,IAAI,EACJ,IAAI,EACJ,QAAQ,EACR,SAAS,EACT,SAAS,EACT,WAAW,EACX,YAAY,EACZ,KAAK,GACN,MAAM,gBAAgB,CAAA;AAGvB,OAAO,EACL,kBAAkB,EAClB,YAAY,EACZ,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,kBAAkB,GACxB,MAAM,cAAc,CAAA;AAGrB,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,KAAK,EACL,aAAa,EACb,UAAU,EACV,KAAK,aAAa,GACnB,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"}
@@ -21,5 +21,7 @@ export { action, authorize, dbMutation, dbTransaction, } from './action.js';
21
21
  export { redirect, render, renderWithErrors, json, text, notFound, forbidden, httpError, prefersJson, jsonOrRender, share, } from './responses.js';
22
22
  // Routing
23
23
  export { EffectRouteBuilder, effectRoutes, } from './routing.js';
24
+ // Route Model Binding
25
+ export { BoundModels, BoundModelNotFound, bound, parseBindings, toHonoPath, } from './binding.js';
24
26
  // Auth
25
27
  export { RequireAuthLayer, RequireGuestLayer, isAuthenticated, currentUser, requireAuth, requireGuest, shareAuth, shareAuthMiddleware, effectAuthRoutes, betterAuthFormAction, betterAuthLogoutAction, loadUser, } from './auth.js';
@@ -3,12 +3,12 @@
3
3
  *
4
4
  * Laravel-style routing with Effect handlers.
5
5
  */
6
- import { Effect, Layer } from 'effect';
6
+ import { Effect, Layer, Schema as S } from 'effect';
7
7
  import type { Hono, Env } from 'hono';
8
8
  import { type EffectBridgeConfig } from './bridge.js';
9
- import type { AppError } from './errors.js';
10
- import { Redirect } from './errors.js';
9
+ import { type AppError, Redirect } from './errors.js';
11
10
  import { DatabaseService, AuthService, HonertiaService, RequestService, ResponseFactoryService } from './services.js';
11
+ import { BoundModels } from './binding.js';
12
12
  /**
13
13
  * Type for Effect-based route handlers.
14
14
  * Error type includes Error for compatibility with Effect.tryPromise.
@@ -17,7 +17,26 @@ export type EffectHandler<R = never, E extends AppError | Error = AppError | Err
17
17
  /**
18
18
  * Base services available in every route.
19
19
  */
20
- export type BaseServices = RequestService | ResponseFactoryService | HonertiaService | DatabaseService | AuthService;
20
+ export type BaseServices = RequestService | ResponseFactoryService | HonertiaService | DatabaseService | AuthService | BoundModels;
21
+ /**
22
+ * Route-level configuration options.
23
+ */
24
+ export interface EffectRouteOptions {
25
+ /**
26
+ * Validate route params with the provided schema.
27
+ * Invalid values will return a 404 before the handler runs.
28
+ *
29
+ * @example
30
+ * ```typescript
31
+ * effectRoutes(app).get(
32
+ * '/projects/{project}',
33
+ * showProject,
34
+ * { params: S.Struct({ project: uuid }) }
35
+ * )
36
+ * ```
37
+ */
38
+ params?: S.Schema.Any;
39
+ }
21
40
  /**
22
41
  * Effect Route Builder with layer composition.
23
42
  */
@@ -45,33 +64,32 @@ export declare class EffectRouteBuilder<E extends Env, ProvidedServices = never,
45
64
  */
46
65
  private resolvePath;
47
66
  /**
48
- * Create a Hono handler from an Effect.
49
- */
50
- private createHandler;
51
- /**
52
- * Register a GET route.
67
+ * Validate route params against a schema.
53
68
  */
54
- get<R extends BaseServices | ProvidedServices | CustomServices>(path: string, effect: EffectHandler<R, AppError | Error>): void;
69
+ private ensureParams;
55
70
  /**
56
- * Register a POST route.
71
+ * Resolve route model bindings from the database.
72
+ * Returns a Map of binding names to resolved models, or a 404 Response if any binding fails.
57
73
  */
58
- post<R extends BaseServices | ProvidedServices | CustomServices>(path: string, effect: EffectHandler<R, AppError | Error>): void;
59
- /**
60
- * Register a PUT route.
61
- */
62
- put<R extends BaseServices | ProvidedServices | CustomServices>(path: string, effect: EffectHandler<R, AppError | Error>): void;
63
- /**
64
- * Register a PATCH route.
65
- */
66
- patch<R extends BaseServices | ProvidedServices | CustomServices>(path: string, effect: EffectHandler<R, AppError | Error>): void;
67
- /**
68
- * Register a DELETE route.
69
- */
70
- delete<R extends BaseServices | ProvidedServices | CustomServices>(path: string, effect: EffectHandler<R, AppError | Error>): void;
74
+ private resolveBindings;
75
+ private createHandler;
71
76
  /**
72
- * Register a route for all HTTP methods.
77
+ * Register a route with the given HTTP method.
78
+ * Parses Laravel-style bindings and converts to Hono path format.
73
79
  */
74
- all<R extends BaseServices | ProvidedServices | CustomServices>(path: string, effect: EffectHandler<R, AppError | Error>): void;
80
+ private registerRoute;
81
+ /** Register a GET route. Supports Laravel-style route model binding: /projects/{project} */
82
+ get<R extends BaseServices | ProvidedServices | CustomServices>(path: string, effect: EffectHandler<R, AppError | Error>, options?: EffectRouteOptions): void;
83
+ /** Register a POST route. Supports Laravel-style route model binding: /projects/{project} */
84
+ post<R extends BaseServices | ProvidedServices | CustomServices>(path: string, effect: EffectHandler<R, AppError | Error>, options?: EffectRouteOptions): void;
85
+ /** Register a PUT route. Supports Laravel-style route model binding: /projects/{project} */
86
+ put<R extends BaseServices | ProvidedServices | CustomServices>(path: string, effect: EffectHandler<R, AppError | Error>, options?: EffectRouteOptions): void;
87
+ /** Register a PATCH route. Supports Laravel-style route model binding: /projects/{project} */
88
+ patch<R extends BaseServices | ProvidedServices | CustomServices>(path: string, effect: EffectHandler<R, AppError | Error>, options?: EffectRouteOptions): void;
89
+ /** Register a DELETE route. Supports Laravel-style route model binding: /projects/{project} */
90
+ delete<R extends BaseServices | ProvidedServices | CustomServices>(path: string, effect: EffectHandler<R, AppError | Error>, options?: EffectRouteOptions): void;
91
+ /** Register a route for all HTTP methods. Supports Laravel-style route model binding: /projects/{project} */
92
+ all<R extends BaseServices | ProvidedServices | CustomServices>(path: string, effect: EffectHandler<R, AppError | Error>, options?: EffectRouteOptions): void;
75
93
  }
76
94
  /**
77
95
  * Create an Effect route builder for an app.
@@ -1 +1 @@
1
- {"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../../src/effect/routing.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../../src/effect/routing.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,MAAM,EAAQ,KAAK,EAAE,MAAM,IAAI,CAAC,EAAE,MAAM,QAAQ,CAAA;AACzD,OAAO,KAAK,EAA0B,IAAI,EAAqB,GAAG,EAAE,MAAM,MAAM,CAAA;AAEhF,OAAO,EAAqB,KAAK,kBAAkB,EAAE,MAAM,aAAa,CAAA;AACxE,OAAO,EACL,KAAK,QAAQ,EACb,QAAQ,EACT,MAAM,aAAa,CAAA;AACpB,OAAO,EACL,eAAe,EACf,WAAW,EACX,eAAe,EACf,cAAc,EACd,sBAAsB,EACvB,MAAM,eAAe,CAAA;AACtB,OAAO,EAKL,WAAW,EAEZ,MAAM,cAAc,CAAA;AAErB;;;GAGG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,GAAG,KAAK,EAAE,CAAC,SAAS,QAAQ,GAAG,KAAK,GAAG,QAAQ,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,CACjG,QAAQ,GAAG,QAAQ,EACnB,CAAC,EACD,CAAC,CACF,CAAA;AAED;;GAEG;AACH,MAAM,MAAM,YAAY,GACpB,cAAc,GACd,sBAAsB,GACtB,eAAe,GACf,eAAe,GACf,WAAW,GACX,WAAW,CAAA;AAEf;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAA;CACtB;AAED;;GAEG;AACH,qBAAa,kBAAkB,CAC7B,CAAC,SAAS,GAAG,EACb,gBAAgB,GAAG,KAAK,EACxB,cAAc,GAAG,KAAK;IAGpB,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC;gBAHb,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,EACZ,MAAM,GAAE,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,EAAO,EAC7C,UAAU,GAAE,MAAW,EACvB,YAAY,CAAC,EAAE,kBAAkB,CAAC,CAAC,EAAE,cAAc,CAAC,YAAA;IAGvE;;;OAGG;IACH,OAAO,CAAC,CAAC,EAAE,QAAQ,SAAS,QAAQ,EAClC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,GACrC,kBAAkB,CAAC,CAAC,EAAE,gBAAgB,GAAG,CAAC,EAAE,cAAc,CAAC;IAS9D;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,kBAAkB,CAAC,CAAC,EAAE,gBAAgB,EAAE,cAAc,CAAC;IAU7E;;OAEG;IACH,KAAK,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,kBAAkB,CAAC,CAAC,EAAE,gBAAgB,EAAE,cAAc,CAAC,KAAK,IAAI,GAAG,IAAI;IAI/F;;OAEG;IACH,OAAO,CAAC,WAAW;IAOnB;;OAEG;YACW,YAAY;IAoB1B;;;OAGG;YACW,eAAe;IAgE7B,OAAO,CAAC,aAAa;IAiDrB;;;OAGG;IACH,OAAO,CAAC,aAAa;IAWrB,4FAA4F;IAC5F,GAAG,CAAC,CAAC,SAAS,YAAY,GAAG,gBAAgB,GAAG,cAAc,EAC5D,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,aAAa,CAAC,CAAC,EAAE,QAAQ,GAAG,KAAK,CAAC,EAC1C,OAAO,CAAC,EAAE,kBAAkB,GAC3B,IAAI;IAIP,6FAA6F;IAC7F,IAAI,CAAC,CAAC,SAAS,YAAY,GAAG,gBAAgB,GAAG,cAAc,EAC7D,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,aAAa,CAAC,CAAC,EAAE,QAAQ,GAAG,KAAK,CAAC,EAC1C,OAAO,CAAC,EAAE,kBAAkB,GAC3B,IAAI;IAIP,4FAA4F;IAC5F,GAAG,CAAC,CAAC,SAAS,YAAY,GAAG,gBAAgB,GAAG,cAAc,EAC5D,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,aAAa,CAAC,CAAC,EAAE,QAAQ,GAAG,KAAK,CAAC,EAC1C,OAAO,CAAC,EAAE,kBAAkB,GAC3B,IAAI;IAIP,8FAA8F;IAC9F,KAAK,CAAC,CAAC,SAAS,YAAY,GAAG,gBAAgB,GAAG,cAAc,EAC9D,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,aAAa,CAAC,CAAC,EAAE,QAAQ,GAAG,KAAK,CAAC,EAC1C,OAAO,CAAC,EAAE,kBAAkB,GAC3B,IAAI;IAIP,+FAA+F;IAC/F,MAAM,CAAC,CAAC,SAAS,YAAY,GAAG,gBAAgB,GAAG,cAAc,EAC/D,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,aAAa,CAAC,CAAC,EAAE,QAAQ,GAAG,KAAK,CAAC,EAC1C,OAAO,CAAC,EAAE,kBAAkB,GAC3B,IAAI;IAIP,6GAA6G;IAC7G,GAAG,CAAC,CAAC,SAAS,YAAY,GAAG,gBAAgB,GAAG,cAAc,EAC5D,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,aAAa,CAAC,CAAC,EAAE,QAAQ,GAAG,KAAK,CAAC,EAC1C,OAAO,CAAC,EAAE,kBAAkB,GAC3B,IAAI;CAGR;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,GAAG,EAAE,cAAc,GAAG,KAAK,EAChE,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,EACZ,MAAM,CAAC,EAAE,kBAAkB,CAAC,CAAC,EAAE,cAAc,CAAC,GAC7C,kBAAkB,CAAC,CAAC,EAAE,KAAK,EAAE,cAAc,CAAC,CAE9C"}
@@ -3,9 +3,10 @@
3
3
  *
4
4
  * Laravel-style routing with Effect handlers.
5
5
  */
6
- import { Effect, Layer } from 'effect';
6
+ import { Effect, Exit, Layer, Schema as S } from 'effect';
7
7
  import { effectHandler } from './handler.js';
8
8
  import { buildContextLayer } from './bridge.js';
9
+ import { parseBindings, toHonoPath, pluralize, findRelation, BoundModels, } from './binding.js';
9
10
  /**
10
11
  * Effect Route Builder with layer composition.
11
12
  */
@@ -50,16 +51,97 @@ export class EffectRouteBuilder {
50
51
  return path;
51
52
  }
52
53
  /**
53
- * Create a Hono handler from an Effect.
54
+ * Validate route params against a schema.
54
55
  */
55
- createHandler(effect) {
56
+ async ensureParams(c, schema) {
57
+ if (!schema)
58
+ return null;
59
+ const rawParams = c.req.param();
60
+ const params = typeof rawParams === 'string' ? {} : rawParams;
61
+ const decode = S.decodeUnknown(schema);
62
+ const exit = await Effect.runPromiseExit(decode(params));
63
+ if (Exit.isFailure(exit)) {
64
+ return c.notFound();
65
+ }
66
+ return null;
67
+ }
68
+ /**
69
+ * Resolve route model bindings from the database.
70
+ * Returns a Map of binding names to resolved models, or a 404 Response if any binding fails.
71
+ */
72
+ async resolveBindings(c, bindings, db, schema) {
73
+ if (bindings.length === 0) {
74
+ return new Map();
75
+ }
76
+ // Dynamic import to avoid requiring drizzle-orm for non-binding users
77
+ const { eq } = await import('drizzle-orm');
78
+ const models = new Map();
79
+ let parent = null;
80
+ for (const binding of bindings) {
81
+ const tableName = pluralize(binding.param);
82
+ const table = schema[tableName];
83
+ if (!table) {
84
+ return c.notFound();
85
+ }
86
+ const paramValue = c.req.param(binding.param);
87
+ if (!paramValue) {
88
+ return c.notFound();
89
+ }
90
+ // Build query with primary lookup
91
+ const column = table[binding.column];
92
+ if (!column) {
93
+ return c.notFound();
94
+ }
95
+ const dbClient = db;
96
+ let query = dbClient.select().from(table).where(eq(column, paramValue));
97
+ // If we have a parent, try to scope the query
98
+ if (parent) {
99
+ const relation = findRelation(schema, tableName, parent.tableName);
100
+ if (relation) {
101
+ const foreignKeyColumn = table[relation.foreignKey];
102
+ if (foreignKeyColumn && parent.model[relation.references]) {
103
+ query = query.where(eq(foreignKeyColumn, parent.model[relation.references]));
104
+ }
105
+ }
106
+ }
107
+ // Execute the query
108
+ const result = await query.get();
109
+ if (!result) {
110
+ return c.notFound();
111
+ }
112
+ models.set(binding.param, result);
113
+ parent = { tableName, model: result };
114
+ }
115
+ return models;
116
+ }
117
+ createHandler(effect, bindings, options) {
56
118
  const layers = this.layers;
57
119
  const bridgeConfig = this.bridgeConfig;
58
120
  return async (c) => {
121
+ const validation = await this.ensureParams(c, options?.params);
122
+ if (validation)
123
+ return validation;
59
124
  // Build context layer from Hono context
60
125
  const contextLayer = buildContextLayer(c, bridgeConfig);
126
+ // Resolve route model bindings if we have any and schema is configured
127
+ let boundModelsLayer;
128
+ if (bindings.length > 0 && bridgeConfig?.schema) {
129
+ const db = bridgeConfig.database ? bridgeConfig.database(c) : c.var?.db;
130
+ if (!db) {
131
+ return c.notFound();
132
+ }
133
+ const result = await this.resolveBindings(c, bindings, db, bridgeConfig.schema);
134
+ if (result instanceof Response) {
135
+ return result;
136
+ }
137
+ boundModelsLayer = Layer.succeed(BoundModels, result);
138
+ }
139
+ else {
140
+ // Empty bound models for routes without bindings
141
+ boundModelsLayer = Layer.succeed(BoundModels, new Map());
142
+ }
61
143
  // Combine with provided layers
62
- let fullLayer = contextLayer;
144
+ let fullLayer = Layer.merge(contextLayer, boundModelsLayer);
63
145
  for (const layer of layers) {
64
146
  fullLayer = Layer.merge(fullLayer, layer);
65
147
  }
@@ -70,40 +152,37 @@ export class EffectRouteBuilder {
70
152
  };
71
153
  }
72
154
  /**
73
- * Register a GET route.
155
+ * Register a route with the given HTTP method.
156
+ * Parses Laravel-style bindings and converts to Hono path format.
74
157
  */
75
- get(path, effect) {
76
- this.app.get(this.resolvePath(path), this.createHandler(effect));
158
+ registerRoute(method, path, effect, options) {
159
+ const bindings = parseBindings(path);
160
+ const honoPath = toHonoPath(path);
161
+ this.app[method](this.resolvePath(honoPath), this.createHandler(effect, bindings, options));
77
162
  }
78
- /**
79
- * Register a POST route.
80
- */
81
- post(path, effect) {
82
- this.app.post(this.resolvePath(path), this.createHandler(effect));
163
+ /** Register a GET route. Supports Laravel-style route model binding: /projects/{project} */
164
+ get(path, effect, options) {
165
+ this.registerRoute('get', path, effect, options);
83
166
  }
84
- /**
85
- * Register a PUT route.
86
- */
87
- put(path, effect) {
88
- this.app.put(this.resolvePath(path), this.createHandler(effect));
167
+ /** Register a POST route. Supports Laravel-style route model binding: /projects/{project} */
168
+ post(path, effect, options) {
169
+ this.registerRoute('post', path, effect, options);
89
170
  }
90
- /**
91
- * Register a PATCH route.
92
- */
93
- patch(path, effect) {
94
- this.app.patch(this.resolvePath(path), this.createHandler(effect));
171
+ /** Register a PUT route. Supports Laravel-style route model binding: /projects/{project} */
172
+ put(path, effect, options) {
173
+ this.registerRoute('put', path, effect, options);
95
174
  }
96
- /**
97
- * Register a DELETE route.
98
- */
99
- delete(path, effect) {
100
- this.app.delete(this.resolvePath(path), this.createHandler(effect));
175
+ /** Register a PATCH route. Supports Laravel-style route model binding: /projects/{project} */
176
+ patch(path, effect, options) {
177
+ this.registerRoute('patch', path, effect, options);
101
178
  }
102
- /**
103
- * Register a route for all HTTP methods.
104
- */
105
- all(path, effect) {
106
- this.app.all(this.resolvePath(path), this.createHandler(effect));
179
+ /** Register a DELETE route. Supports Laravel-style route model binding: /projects/{project} */
180
+ delete(path, effect, options) {
181
+ this.registerRoute('delete', path, effect, options);
182
+ }
183
+ /** Register a route for all HTTP methods. Supports Laravel-style route model binding: /projects/{project} */
184
+ all(path, effect, options) {
185
+ this.registerRoute('all', path, effect, options);
107
186
  }
108
187
  }
109
188
  /**
@@ -14,6 +14,7 @@ import { Context } from 'effect';
14
14
  * declare module 'honertia/effect' {
15
15
  * interface HonertiaDatabaseType {
16
16
  * type: Database // Your database type (Drizzle, Prisma, Kysely, etc.)
17
+ * schema: typeof schema // Your Drizzle schema for route model binding
17
18
  * }
18
19
  * }
19
20
  * ```
@@ -21,7 +22,8 @@ import { Context } from 'effect';
21
22
  * Then use the `DatabaseService` tag to get your typed database.
22
23
  */
23
24
  export interface HonertiaDatabaseType {
24
- [key: string]: unknown;
25
+ type: unknown;
26
+ schema: Record<string, unknown>;
25
27
  }
26
28
  /**
27
29
  * Augmentable interface for auth type.
@@ -1 +1 @@
1
- {"version":3,"file":"services.d.ts","sourceRoot":"","sources":["../../src/effect/services.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AAEhC;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,oBAAoB;IACnC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,gBAAgB;IAC/B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAED;;GAEG;AACH,QAAA,MAAM,oBAAoB,EAAE,OAAO,CAAC,QAAQ,CAC1C,eAAe,EACf,mBAAmB,EACnB,oBAAoB,CAAC,MAAM,CAAC,CACuD,CAAA;AAErF,qBAAa,eAAgB,SAAQ,oBAAoB;CAAG;AAE5D;;GAEG;AACH,QAAA,MAAM,gBAAgB,EAAE,OAAO,CAAC,QAAQ,CACtC,WAAW,EACX,eAAe,EACf,gBAAgB,CAAC,MAAM,CAAC,CAC+C,CAAA;AAEzE,qBAAa,WAAY,SAAQ,gBAAgB;CAAG;AAEpD;;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"}
1
+ {"version":3,"file":"services.d.ts","sourceRoot":"","sources":["../../src/effect/services.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "honertia",
3
- "version": "0.1.11",
3
+ "version": "0.1.13",
4
4
  "description": "Inertia.js-style server-driven SPA adapter for Hono",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -60,11 +60,21 @@
60
60
  },
61
61
  "peerDependencies": {
62
62
  "hono": ">=4.0.0",
63
- "better-auth": ">=1.0.0"
63
+ "better-auth": ">=1.0.0",
64
+ "drizzle-orm": ">=0.30.0"
65
+ },
66
+ "peerDependenciesMeta": {
67
+ "better-auth": {
68
+ "optional": true
69
+ },
70
+ "drizzle-orm": {
71
+ "optional": true
72
+ }
64
73
  },
65
74
  "devDependencies": {
66
75
  "@types/bun": "^1.1.0",
67
76
  "better-auth": "^1.0.0",
77
+ "drizzle-orm": "^0.38.0",
68
78
  "hono": "^4.6.0",
69
79
  "typescript": "^5.3.0"
70
80
  }