@gnosticdev/hono-actions 1.0.0 → 1.0.2
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 +132 -24
- package/dist/index.d.ts +144 -5
- package/dist/index.js +306 -3
- package/package.json +13 -13
- package/dist/define-action.d.ts +0 -119
- package/dist/define-action.js +0 -57
- package/dist/error.d.ts +0 -14
- package/dist/error.js +0 -10
package/README.md
CHANGED
|
@@ -1,32 +1,69 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Astro Actions with Hono and Valibot
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Define server actions with built-in validation, error handling, and a pre-built hono client for calling the routes.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npm install @gnosticdev/
|
|
8
|
+
npm install @gnosticdev/hono-actions
|
|
9
|
+
# or
|
|
10
|
+
pnpm add @gnosticdev/hono-actions
|
|
11
|
+
# or
|
|
12
|
+
bun add @gnosticdev/hono-actions
|
|
9
13
|
```
|
|
10
14
|
|
|
11
|
-
##
|
|
15
|
+
## Requirements
|
|
12
16
|
|
|
13
|
-
This package requires
|
|
17
|
+
This package requires:
|
|
14
18
|
|
|
15
|
-
-
|
|
16
|
-
|
|
17
|
-
|
|
19
|
+
- `astro`: ^5.13.3
|
|
20
|
+
|
|
21
|
+
All other dependencies (`hono`, `valibot`, `@hono/valibot-validator`, etc.) are bundled with the integration.
|
|
22
|
+
|
|
23
|
+
## Setup
|
|
24
|
+
|
|
25
|
+
### 1. Add the integration to your Astro config
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
// astro.config.ts
|
|
29
|
+
import { defineConfig } from 'astro/config'
|
|
30
|
+
import honoActions from '@gnosticdev/hono-actions'
|
|
31
|
+
|
|
32
|
+
export default defineConfig({
|
|
33
|
+
integrations: [
|
|
34
|
+
honoActions({
|
|
35
|
+
basePath: '/api', // Optional: default is '/api'
|
|
36
|
+
actionsPath: 'src/server/actions.ts' // Optional: custom path to your actions file
|
|
37
|
+
})
|
|
38
|
+
]
|
|
39
|
+
})
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### 2. Create your actions file
|
|
43
|
+
|
|
44
|
+
Create a file at one of these locations (the integration will auto-discover):
|
|
45
|
+
|
|
46
|
+
- `src/server/actions.ts`
|
|
47
|
+
- `src/hono/actions.ts`
|
|
48
|
+
- `src/hono/index.ts`
|
|
49
|
+
- `src/hono.ts`
|
|
18
50
|
|
|
19
51
|
## Usage
|
|
20
52
|
|
|
21
53
|
```typescript
|
|
22
|
-
|
|
54
|
+
// src/server/actions.ts
|
|
55
|
+
import { defineHonoAction, ActionError } from '@gnosticdev/hono-actions'
|
|
23
56
|
import * as v from 'valibot'
|
|
24
57
|
|
|
25
58
|
// Define a simple action
|
|
26
59
|
export const simpleAction = defineHonoAction({
|
|
27
60
|
path: '/simple',
|
|
61
|
+
schema: v.object({
|
|
62
|
+
name: v.string()
|
|
63
|
+
}),
|
|
28
64
|
handler: async (input, ctx) => {
|
|
29
|
-
|
|
65
|
+
// input is automatically typed based on schema
|
|
66
|
+
return { message: `Hello ${input.name}!` }
|
|
30
67
|
}
|
|
31
68
|
})
|
|
32
69
|
|
|
@@ -57,7 +94,7 @@ export const errorAction = defineHonoAction({
|
|
|
57
94
|
}
|
|
58
95
|
})
|
|
59
96
|
|
|
60
|
-
// Export all actions
|
|
97
|
+
// Export all actions in a honoActions object
|
|
61
98
|
export const honoActions = {
|
|
62
99
|
simpleAction,
|
|
63
100
|
validatedAction,
|
|
@@ -65,24 +102,95 @@ export const honoActions = {
|
|
|
65
102
|
}
|
|
66
103
|
```
|
|
67
104
|
|
|
68
|
-
|
|
105
|
+
### 3. Use actions in your Astro components or pages
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
// src/pages/example.astro or any .astro file
|
|
109
|
+
---
|
|
110
|
+
import { honoClient } from '@gnosticdev/hono-actions/client'
|
|
111
|
+
|
|
112
|
+
const response = await honoClient.simpleAction.$post({
|
|
113
|
+
json: { name: 'John' }
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
let result = null
|
|
117
|
+
if (response.ok) {
|
|
118
|
+
result = await response.json() // { message: 'Hello John!' }
|
|
119
|
+
} else {
|
|
120
|
+
console.error(await response.text()) // Error message
|
|
121
|
+
}
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
<div>
|
|
125
|
+
{result && <p>{result.message}</p>}
|
|
126
|
+
</div>
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### 4. Use in client-side JavaScript
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
// In a client-side script or component
|
|
133
|
+
import { honoClient } from '@gnosticdev/hono-actions/client'
|
|
134
|
+
|
|
135
|
+
// Make requests from the browser
|
|
136
|
+
const handleSubmit = async (formData: FormData) => {
|
|
137
|
+
const response = await honoClient.validatedAction.$post({
|
|
138
|
+
json: {
|
|
139
|
+
name: formData.get('name') as string,
|
|
140
|
+
email: formData.get('email') as string
|
|
141
|
+
}
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
if (response.ok) {
|
|
145
|
+
const result = await response.json()
|
|
146
|
+
console.log('Success:', result)
|
|
147
|
+
} else {
|
|
148
|
+
const error = await response.text()
|
|
149
|
+
console.error('Error:', error)
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Configuration Options
|
|
155
|
+
|
|
156
|
+
The integration accepts the following options:
|
|
157
|
+
|
|
158
|
+
- **`basePath`** (optional): The base path for your API routes. Default: `'/api'`
|
|
159
|
+
- **`actionsPath`** (optional): Custom path to your actions file if not using auto-discovery
|
|
160
|
+
|
|
161
|
+
## Features
|
|
162
|
+
|
|
163
|
+
- ✅ **Type-safe**: Full TypeScript support with automatic type inference
|
|
164
|
+
- ✅ **Validation**: Built-in request validation using Valibot schemas
|
|
165
|
+
- ✅ **Error handling**: Custom error types and automatic error responses
|
|
166
|
+
- ✅ **Auto-discovery**: Automatically finds your actions file
|
|
167
|
+
- ✅ **Client generation**: Pre-built client with full type safety
|
|
168
|
+
- ✅ **Development**: Hot reload support during development
|
|
169
|
+
|
|
170
|
+
## Troubleshooting
|
|
171
|
+
|
|
172
|
+
### Actions not found
|
|
173
|
+
|
|
174
|
+
If you get an error that no actions were found, make sure:
|
|
175
|
+
|
|
176
|
+
1. Your actions file is in one of the supported locations
|
|
177
|
+
2. You export a `honoActions` object containing your actions
|
|
178
|
+
3. The file path matches the `actionsPath` option if you specified one
|
|
179
|
+
|
|
180
|
+
### Type errors
|
|
69
181
|
|
|
70
|
-
|
|
182
|
+
If you're getting TypeScript errors:
|
|
71
183
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
184
|
+
1. Make sure all peer dependencies are installed
|
|
185
|
+
2. Run `astro sync` to regenerate types
|
|
186
|
+
3. Restart your TypeScript server in your editor
|
|
75
187
|
|
|
76
|
-
|
|
188
|
+
### Module resolution errors
|
|
77
189
|
|
|
78
|
-
|
|
190
|
+
If you get module resolution errors during development:
|
|
79
191
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
- `INTERNAL_SERVER_ERROR`: Internal server error
|
|
83
|
-
- `UNKNOWN_ERROR`: Unknown error occurred
|
|
84
|
-
- `LOCATION_NOT_FOUND`: Location not found
|
|
85
|
-
- `SESSION_NOT_FOUND`: Session not found
|
|
192
|
+
1. Try clearing your node_modules and reinstalling
|
|
193
|
+
2. Make sure you're using compatible versions of the peer dependencies
|
|
86
194
|
|
|
87
195
|
## License
|
|
88
196
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,144 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import * as hono_hono_base from 'hono/hono-base';
|
|
2
|
+
import * as hono_utils_types from 'hono/utils/types';
|
|
3
|
+
import * as hono_types from 'hono/types';
|
|
4
|
+
import { Context } from 'hono';
|
|
5
|
+
import * as v from 'valibot';
|
|
6
|
+
import * as astro from 'astro';
|
|
7
|
+
|
|
8
|
+
interface Bindings {
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* HonoEnv is passed to the Hono context to provide types on `ctx.env`.
|
|
12
|
+
*
|
|
13
|
+
* We are using `HonoEnv` to avoid confusion with the Cloudflare types on `Env` -> which cooresponds to `Bindings`
|
|
14
|
+
*/
|
|
15
|
+
interface HonoEnv {
|
|
16
|
+
Bindings: Bindings;
|
|
17
|
+
Variables: Record<string, any>;
|
|
18
|
+
}
|
|
19
|
+
type HonoActionSchema = v.ObjectSchema<v.ObjectEntries, v.ErrorMessage<v.ObjectIssue> | undefined> | v.NeverSchema<undefined>;
|
|
20
|
+
interface HonoActionContext<TEnv extends HonoEnv, TPath extends string, TSchema extends HonoActionSchema> extends Context<TEnv, TPath, {
|
|
21
|
+
input: v.InferInput<TSchema>;
|
|
22
|
+
output: v.InferOutput<TSchema>;
|
|
23
|
+
outputFormat: 'json';
|
|
24
|
+
}> {
|
|
25
|
+
env: TEnv['Bindings'];
|
|
26
|
+
}
|
|
27
|
+
type HonoActionParams<TPath extends string, TSchema extends HonoActionSchema, TReturn, TEnv extends HonoEnv = HonoEnv> = {
|
|
28
|
+
path: TPath;
|
|
29
|
+
schema?: TSchema;
|
|
30
|
+
handler: (params: v.InferOutput<TSchema>, context: HonoActionContext<TEnv, TPath, TSchema>) => Promise<TReturn>;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Defines a type-safe Hono action using Valibot for input validation.
|
|
34
|
+
*
|
|
35
|
+
* @param path - The path of the action.
|
|
36
|
+
* @param schema - The object schema for Valibot validation.
|
|
37
|
+
* @default never
|
|
38
|
+
* @param handler - The handler function for the action.
|
|
39
|
+
* @returns A Hono app instance with the defined route
|
|
40
|
+
*/
|
|
41
|
+
declare function defineHonoAction<TPath extends string, TSchema extends HonoActionSchema, TReturn, TEnv extends HonoEnv = HonoEnv>({ path, schema, handler }: HonoActionParams<TPath, TSchema, TReturn, TEnv>): hono_hono_base.HonoBase<TEnv, { [K in hono_types.MergePath<"/", TPath>]: {
|
|
42
|
+
$post: {
|
|
43
|
+
input: hono_types.AddParam<unknown extends ((undefined extends v.InferInput<TSchema | v.UnionSchema<[v.NeverSchema<undefined>, v.ObjectSchema<{}, undefined>], undefined>> ? true : false) extends true ? {
|
|
44
|
+
json?: (v.InferInput<TSchema | v.UnionSchema<[v.NeverSchema<undefined>, v.ObjectSchema<{}, undefined>], undefined>> extends infer T_1 ? T_1 extends v.InferInput<TSchema | v.UnionSchema<[v.NeverSchema<undefined>, v.ObjectSchema<{}, undefined>], undefined>> ? T_1 extends any ? T_1 : { [K2 in keyof T_1]?: any; } : never : never) | undefined;
|
|
45
|
+
} : {
|
|
46
|
+
json: v.InferInput<TSchema | v.UnionSchema<[v.NeverSchema<undefined>, v.ObjectSchema<{}, undefined>], undefined>> extends infer T_2 ? T_2 extends v.InferInput<TSchema | v.UnionSchema<[v.NeverSchema<undefined>, v.ObjectSchema<{}, undefined>], undefined>> ? T_2 extends any ? T_2 : { [K2_1 in keyof T_2]: any; } : never : never;
|
|
47
|
+
}) ? {} : (undefined extends v.InferInput<TSchema | v.UnionSchema<[v.NeverSchema<undefined>, v.ObjectSchema<{}, undefined>], undefined>> ? true : false) extends true ? {
|
|
48
|
+
json?: (v.InferInput<TSchema | v.UnionSchema<[v.NeverSchema<undefined>, v.ObjectSchema<{}, undefined>], undefined>> extends infer T_3 ? T_3 extends v.InferInput<TSchema | v.UnionSchema<[v.NeverSchema<undefined>, v.ObjectSchema<{}, undefined>], undefined>> ? T_3 extends any ? T_3 : { [K2_2 in keyof T_3]?: any; } : never : never) | undefined;
|
|
49
|
+
} : {
|
|
50
|
+
json: v.InferInput<TSchema | v.UnionSchema<[v.NeverSchema<undefined>, v.ObjectSchema<{}, undefined>], undefined>> extends infer T_4 ? T_4 extends v.InferInput<TSchema | v.UnionSchema<[v.NeverSchema<undefined>, v.ObjectSchema<{}, undefined>], undefined>> ? T_4 extends any ? T_4 : { [K2_3 in keyof T_4]: any; } : never : never;
|
|
51
|
+
}, hono_types.MergePath<"/", TPath>>;
|
|
52
|
+
output: unknown extends ({
|
|
53
|
+
data: Awaited<TReturn>;
|
|
54
|
+
error: null;
|
|
55
|
+
} extends hono_utils_types.JSONValue ? { [K_2 in keyof {
|
|
56
|
+
data: Awaited<TReturn>;
|
|
57
|
+
error: null;
|
|
58
|
+
} as ({
|
|
59
|
+
data: Awaited<TReturn>;
|
|
60
|
+
error: null;
|
|
61
|
+
}[K_2] extends infer T_5 ? T_5 extends {
|
|
62
|
+
data: Awaited<TReturn>;
|
|
63
|
+
error: null;
|
|
64
|
+
}[K_2] ? T_5 extends hono_utils_types.InvalidJSONValue ? true : false : never : never) extends true ? never : K_2]: boolean extends ({
|
|
65
|
+
data: Awaited<TReturn>;
|
|
66
|
+
error: null;
|
|
67
|
+
}[K_2] extends infer T_6 ? T_6 extends {
|
|
68
|
+
data: Awaited<TReturn>;
|
|
69
|
+
error: null;
|
|
70
|
+
}[K_2] ? T_6 extends hono_utils_types.InvalidJSONValue ? true : false : never : never) ? hono_utils_types.JSONParsed<{
|
|
71
|
+
data: Awaited<TReturn>;
|
|
72
|
+
error: null;
|
|
73
|
+
}[K_2]> | undefined : hono_utils_types.JSONParsed<{
|
|
74
|
+
data: Awaited<TReturn>;
|
|
75
|
+
error: null;
|
|
76
|
+
}[K_2]>; } : never) ? {} : {
|
|
77
|
+
data: Awaited<TReturn>;
|
|
78
|
+
error: null;
|
|
79
|
+
} extends hono_utils_types.JSONValue ? { [K_2 in keyof {
|
|
80
|
+
data: Awaited<TReturn>;
|
|
81
|
+
error: null;
|
|
82
|
+
} as ({
|
|
83
|
+
data: Awaited<TReturn>;
|
|
84
|
+
error: null;
|
|
85
|
+
}[K_2] extends infer T_5 ? T_5 extends {
|
|
86
|
+
data: Awaited<TReturn>;
|
|
87
|
+
error: null;
|
|
88
|
+
}[K_2] ? T_5 extends hono_utils_types.InvalidJSONValue ? true : false : never : never) extends true ? never : K_2]: boolean extends ({
|
|
89
|
+
data: Awaited<TReturn>;
|
|
90
|
+
error: null;
|
|
91
|
+
}[K_2] extends infer T_6 ? T_6 extends {
|
|
92
|
+
data: Awaited<TReturn>;
|
|
93
|
+
error: null;
|
|
94
|
+
}[K_2] ? T_6 extends hono_utils_types.InvalidJSONValue ? true : false : never : never) ? hono_utils_types.JSONParsed<{
|
|
95
|
+
data: Awaited<TReturn>;
|
|
96
|
+
error: null;
|
|
97
|
+
}[K_2]> | undefined : hono_utils_types.JSONParsed<{
|
|
98
|
+
data: Awaited<TReturn>;
|
|
99
|
+
error: null;
|
|
100
|
+
}[K_2]>; } : never;
|
|
101
|
+
outputFormat: "json";
|
|
102
|
+
status: 200;
|
|
103
|
+
} | {
|
|
104
|
+
input: hono_types.AddParam<unknown extends ((undefined extends v.InferInput<TSchema | v.UnionSchema<[v.NeverSchema<undefined>, v.ObjectSchema<{}, undefined>], undefined>> ? true : false) extends true ? {
|
|
105
|
+
json?: (v.InferInput<TSchema | v.UnionSchema<[v.NeverSchema<undefined>, v.ObjectSchema<{}, undefined>], undefined>> extends infer T_5 ? T_5 extends v.InferInput<TSchema | v.UnionSchema<[v.NeverSchema<undefined>, v.ObjectSchema<{}, undefined>], undefined>> ? T_5 extends any ? T_5 : { [K2_4 in keyof T_5]?: any; } : never : never) | undefined;
|
|
106
|
+
} : {
|
|
107
|
+
json: v.InferInput<TSchema | v.UnionSchema<[v.NeverSchema<undefined>, v.ObjectSchema<{}, undefined>], undefined>> extends infer T_6 ? T_6 extends v.InferInput<TSchema | v.UnionSchema<[v.NeverSchema<undefined>, v.ObjectSchema<{}, undefined>], undefined>> ? T_6 extends any ? T_6 : { [K2_5 in keyof T_6]: any; } : never : never;
|
|
108
|
+
}) ? {} : (undefined extends v.InferInput<TSchema | v.UnionSchema<[v.NeverSchema<undefined>, v.ObjectSchema<{}, undefined>], undefined>> ? true : false) extends true ? {
|
|
109
|
+
json?: (v.InferInput<TSchema | v.UnionSchema<[v.NeverSchema<undefined>, v.ObjectSchema<{}, undefined>], undefined>> extends infer T_7 ? T_7 extends v.InferInput<TSchema | v.UnionSchema<[v.NeverSchema<undefined>, v.ObjectSchema<{}, undefined>], undefined>> ? T_7 extends any ? T_7 : { [K2_6 in keyof T_7]?: any; } : never : never) | undefined;
|
|
110
|
+
} : {
|
|
111
|
+
json: v.InferInput<TSchema | v.UnionSchema<[v.NeverSchema<undefined>, v.ObjectSchema<{}, undefined>], undefined>> extends infer T_8 ? T_8 extends v.InferInput<TSchema | v.UnionSchema<[v.NeverSchema<undefined>, v.ObjectSchema<{}, undefined>], undefined>> ? T_8 extends any ? T_8 : { [K2_7 in keyof T_8]: any; } : never : never;
|
|
112
|
+
}, hono_types.MergePath<"/", TPath>>;
|
|
113
|
+
output: {
|
|
114
|
+
data: null;
|
|
115
|
+
error: {
|
|
116
|
+
message: string;
|
|
117
|
+
code: string;
|
|
118
|
+
};
|
|
119
|
+
};
|
|
120
|
+
outputFormat: "json";
|
|
121
|
+
status: 500;
|
|
122
|
+
};
|
|
123
|
+
}; } extends infer T ? { [KeyType in keyof T]: T[KeyType]; } : never, "/">;
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Standard error codes for actions
|
|
127
|
+
*/
|
|
128
|
+
type ActionErrorCode = 'INPUT_VALIDATION_ERROR' | 'EXTERNAL_API_ERROR' | 'INTERNAL_SERVER_ERROR' | 'UNKNOWN_ERROR' | 'LOCATION_NOT_FOUND' | 'SESSION_NOT_FOUND';
|
|
129
|
+
declare class HonoActionError<TSchema extends v.ObjectSchema<v.ObjectEntries, v.ErrorMessage<v.ObjectIssue> | undefined>, TMessage extends string, TCode extends ActionErrorCode, TIssue extends v.InferIssue<TSchema>> extends Error {
|
|
130
|
+
code: TCode;
|
|
131
|
+
issue?: TIssue;
|
|
132
|
+
constructor({ message, code, issue }: {
|
|
133
|
+
message: TMessage;
|
|
134
|
+
code: TCode;
|
|
135
|
+
issue?: TIssue;
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
declare const _default: (options?: {
|
|
140
|
+
basePath?: string | undefined;
|
|
141
|
+
actionsPath?: string | undefined;
|
|
142
|
+
} | undefined) => astro.AstroIntegration & {};
|
|
143
|
+
|
|
144
|
+
export { type Bindings, HonoActionError, type HonoEnv, _default as default, defineHonoAction };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,306 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
|
|
1
|
+
// src/define-action.ts
|
|
2
|
+
import { vValidator } from "@hono/valibot-validator";
|
|
3
|
+
import { createFactory } from "hono/factory";
|
|
4
|
+
import * as v from "valibot";
|
|
5
|
+
|
|
6
|
+
// src/error.ts
|
|
7
|
+
var HonoActionError = class extends Error {
|
|
8
|
+
code;
|
|
9
|
+
issue;
|
|
10
|
+
constructor({
|
|
11
|
+
message,
|
|
12
|
+
code,
|
|
13
|
+
issue
|
|
14
|
+
}) {
|
|
15
|
+
super(message);
|
|
16
|
+
this.name = "HonoActionError";
|
|
17
|
+
this.code = code;
|
|
18
|
+
this.issue = issue;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// src/define-action.ts
|
|
23
|
+
function defineHonoAction({ path: path2, schema, handler }) {
|
|
24
|
+
const factory = createFactory();
|
|
25
|
+
const app = factory.createApp();
|
|
26
|
+
const route = app.post(
|
|
27
|
+
path2,
|
|
28
|
+
vValidator(
|
|
29
|
+
"json",
|
|
30
|
+
schema ?? v.union([v.never(), v.object({})]),
|
|
31
|
+
async (result, c) => {
|
|
32
|
+
if (!result.success) {
|
|
33
|
+
console.error(result.issues);
|
|
34
|
+
return c.json(
|
|
35
|
+
{
|
|
36
|
+
data: null,
|
|
37
|
+
error: new HonoActionError({
|
|
38
|
+
message: result.issues[0].message,
|
|
39
|
+
code: "INPUT_VALIDATION_ERROR",
|
|
40
|
+
issue: result.issues[0]
|
|
41
|
+
})
|
|
42
|
+
},
|
|
43
|
+
400
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
),
|
|
48
|
+
async (c) => {
|
|
49
|
+
try {
|
|
50
|
+
const json = c.req.valid("json");
|
|
51
|
+
const result = await handler(
|
|
52
|
+
json,
|
|
53
|
+
c
|
|
54
|
+
);
|
|
55
|
+
return c.json(
|
|
56
|
+
{
|
|
57
|
+
data: result,
|
|
58
|
+
error: null
|
|
59
|
+
},
|
|
60
|
+
200
|
|
61
|
+
);
|
|
62
|
+
} catch (error) {
|
|
63
|
+
console.error(error);
|
|
64
|
+
let errorMessage = "Internal server error";
|
|
65
|
+
let errorCode = "INTERNAL_SERVER_ERROR";
|
|
66
|
+
if (error instanceof HonoActionError) {
|
|
67
|
+
errorMessage = error.message;
|
|
68
|
+
errorCode = error.code;
|
|
69
|
+
}
|
|
70
|
+
return c.json(
|
|
71
|
+
{
|
|
72
|
+
data: null,
|
|
73
|
+
error: {
|
|
74
|
+
message: errorMessage,
|
|
75
|
+
code: errorCode
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
500
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
);
|
|
83
|
+
return route;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// src/integration.ts
|
|
87
|
+
import {
|
|
88
|
+
addVirtualImports,
|
|
89
|
+
createResolver,
|
|
90
|
+
defineIntegration
|
|
91
|
+
} from "astro-integration-kit";
|
|
92
|
+
import { z } from "astro/zod";
|
|
93
|
+
import fg from "fast-glob";
|
|
94
|
+
import fs from "fs/promises";
|
|
95
|
+
import path from "path";
|
|
96
|
+
|
|
97
|
+
// src/integration-files.ts
|
|
98
|
+
function generateRouter(opts) {
|
|
99
|
+
const { basePath, relativeActionsPath } = opts;
|
|
100
|
+
return `import type { HonoEnv } from '@gnosticdev/hono-actions'
|
|
101
|
+
import { Hono } from 'hono'
|
|
102
|
+
import { cors } from 'hono/cors'
|
|
103
|
+
import { showRoutes } from 'hono/dev'
|
|
104
|
+
import { logger } from 'hono/logger'
|
|
105
|
+
import { prettyJSON } from 'hono/pretty-json'
|
|
106
|
+
import type { ExtractSchema, MergeSchemaPath } from 'hono/types'
|
|
107
|
+
|
|
108
|
+
export async function buildRouter(){
|
|
109
|
+
type ActionSchema = ExtractSchema<typeof honoActions[keyof typeof honoActions]>
|
|
110
|
+
const { honoActions} = await import('${relativeActionsPath}')
|
|
111
|
+
const app = new Hono<HonoEnv, MergeSchemaPath<ActionSchema, '${basePath}'>>().basePath('${basePath}')
|
|
112
|
+
|
|
113
|
+
app.use('*', cors(), logger(), prettyJSON())
|
|
114
|
+
|
|
115
|
+
for (const [path, action] of Object.entries(honoActions)) {
|
|
116
|
+
app.route(path, action)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
showRoutes(app)
|
|
120
|
+
return app
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export type HonoRouter = Awaited<ReturnType<typeof buildRouter>>
|
|
124
|
+
|
|
125
|
+
const app = await buildRouter()
|
|
126
|
+
export default app`;
|
|
127
|
+
}
|
|
128
|
+
var getAstroHandler = (adapter) => {
|
|
129
|
+
switch (adapter) {
|
|
130
|
+
case "cloudflare":
|
|
131
|
+
return `
|
|
132
|
+
// Generated by Hono Actions Integration
|
|
133
|
+
import router from 'virtual:hono-actions/router'
|
|
134
|
+
import type { APIContext, APIRoute } from 'astro'
|
|
135
|
+
|
|
136
|
+
const handler: APIRoute<APIContext> = async (ctx) => {
|
|
137
|
+
return router.fetch(
|
|
138
|
+
ctx.request,
|
|
139
|
+
ctx.locals.runtime.env,
|
|
140
|
+
ctx.locals.runtime.ctx,
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export { handler as ALL }
|
|
145
|
+
`;
|
|
146
|
+
default:
|
|
147
|
+
throw new Error(`Unsupported adapter: ${adapter}`);
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
var getHonoClient = (port) => `
|
|
151
|
+
// Generated by Hono Actions Integration
|
|
152
|
+
import type { HonoRouter } from './router.js'
|
|
153
|
+
import { hc } from 'hono/client'
|
|
154
|
+
|
|
155
|
+
export function getBaseUrl() {
|
|
156
|
+
// client side can just use the base path
|
|
157
|
+
if (typeof window !== 'undefined') {
|
|
158
|
+
return '/'
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// dev server (dev server) needs to know the port
|
|
162
|
+
if (import.meta.env.DEV) {
|
|
163
|
+
return \`http://localhost:\${${port}}\`
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// server side (production) needs full url
|
|
167
|
+
return import.meta.env.SITE
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export const honoClient = hc<HonoRouter>(getBaseUrl())
|
|
171
|
+
`;
|
|
172
|
+
|
|
173
|
+
// src/lib/utils.ts
|
|
174
|
+
var reservedRoutes = ["_astro", "_actions", "_server_islands"];
|
|
175
|
+
|
|
176
|
+
// src/integration.ts
|
|
177
|
+
var optionsSchema = z.object({
|
|
178
|
+
basePath: z.string().optional(),
|
|
179
|
+
actionsPath: z.string().optional()
|
|
180
|
+
}).optional();
|
|
181
|
+
var VIRTUAL_MODULE_ID_CLIENT = "@gnostic/hono-actions/client";
|
|
182
|
+
var VIRTUAL_MODULE_ID_ROUTER = "virtual:hono-actions/router";
|
|
183
|
+
var ACTION_PATTERNS = [
|
|
184
|
+
"src/**/server/actions.ts",
|
|
185
|
+
"src/**/hono/actions.ts",
|
|
186
|
+
"src/**/hono/index.ts",
|
|
187
|
+
"src/**/hono.ts"
|
|
188
|
+
];
|
|
189
|
+
var integration_default = defineIntegration({
|
|
190
|
+
name: "@gnostic/hono-actions",
|
|
191
|
+
optionsSchema,
|
|
192
|
+
setup: ({ options = {}, name }) => {
|
|
193
|
+
const basePath = options.basePath ?? "/api";
|
|
194
|
+
if (reservedRoutes.includes(basePath)) {
|
|
195
|
+
throw new Error(
|
|
196
|
+
`Base path ${basePath} is reserved by Astro; pick another (e.g. /api2).`
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
const { resolve } = createResolver(import.meta.url);
|
|
200
|
+
return {
|
|
201
|
+
name,
|
|
202
|
+
hooks: {
|
|
203
|
+
"astro:config:setup": async (params) => {
|
|
204
|
+
const { logger, injectRoute, createCodegenDir, config } = params;
|
|
205
|
+
const root = config.root.pathname;
|
|
206
|
+
const files = await fg(ACTION_PATTERNS, {
|
|
207
|
+
cwd: root,
|
|
208
|
+
absolute: true
|
|
209
|
+
});
|
|
210
|
+
const actionsPath = options.actionsPath ?? files[0];
|
|
211
|
+
if (!actionsPath) {
|
|
212
|
+
throw new Error(
|
|
213
|
+
`No actions found. Create one of:
|
|
214
|
+
${ACTION_PATTERNS.map((p) => ` - ${p}`).join("\n")}`
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
const resolvedActionsPath = path.isAbsolute(actionsPath) ? actionsPath : path.join(root, actionsPath);
|
|
218
|
+
params.addWatchFile(resolvedActionsPath);
|
|
219
|
+
logger.info(
|
|
220
|
+
`Found actions: ${path.relative(root, resolvedActionsPath)}`
|
|
221
|
+
);
|
|
222
|
+
const codeGenDir = createCodegenDir();
|
|
223
|
+
const routerPathAbs = path.join(
|
|
224
|
+
codeGenDir.pathname,
|
|
225
|
+
"router.ts"
|
|
226
|
+
);
|
|
227
|
+
const relFromGenToActions = path.relative(codeGenDir.pathname, resolvedActionsPath).split(path.sep).join("/");
|
|
228
|
+
const routerContent = generateRouter({
|
|
229
|
+
basePath,
|
|
230
|
+
relativeActionsPath: relFromGenToActions
|
|
231
|
+
});
|
|
232
|
+
await fs.writeFile(routerPathAbs, routerContent, "utf-8");
|
|
233
|
+
const astroHandlerPathAbs = path.join(
|
|
234
|
+
codeGenDir.pathname,
|
|
235
|
+
"api.ts"
|
|
236
|
+
);
|
|
237
|
+
if (!params.config.adapter?.name)
|
|
238
|
+
throw new Error("No Astro adapter found");
|
|
239
|
+
const astroHandlerContent = getAstroHandler("cloudflare");
|
|
240
|
+
await fs.writeFile(
|
|
241
|
+
astroHandlerPathAbs,
|
|
242
|
+
astroHandlerContent,
|
|
243
|
+
"utf-8"
|
|
244
|
+
);
|
|
245
|
+
const clientPathAbs = path.join(
|
|
246
|
+
codeGenDir.pathname,
|
|
247
|
+
"client.ts"
|
|
248
|
+
);
|
|
249
|
+
const clientContent = getHonoClient(config.server.port);
|
|
250
|
+
await fs.writeFile(clientPathAbs, clientContent, "utf-8");
|
|
251
|
+
addVirtualImports(params, {
|
|
252
|
+
name,
|
|
253
|
+
imports: {
|
|
254
|
+
[VIRTUAL_MODULE_ID_CLIENT]: `export * from '${clientPathAbs}';`,
|
|
255
|
+
[VIRTUAL_MODULE_ID_ROUTER]: `export * from '${routerPathAbs}';`
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
injectRoute({
|
|
259
|
+
pattern: `${basePath}/[...slug]`,
|
|
260
|
+
entrypoint: astroHandlerPathAbs,
|
|
261
|
+
prerender: false
|
|
262
|
+
});
|
|
263
|
+
logger.info(
|
|
264
|
+
`\u2705 Hono Actions route mounted at ${basePath}/[...slug]`
|
|
265
|
+
);
|
|
266
|
+
},
|
|
267
|
+
"astro:config:done": async ({ injectTypes }) => {
|
|
268
|
+
injectTypes({
|
|
269
|
+
filename: "actions.d.ts",
|
|
270
|
+
content: `
|
|
271
|
+
declare module '@gnosticdev/hono-actions' {
|
|
272
|
+
interface Bindings extends Env { ASTRO_LOCALS: App.Locals }
|
|
273
|
+
interface HonoEnv { Bindings: Bindings }
|
|
274
|
+
}
|
|
275
|
+
export {}
|
|
276
|
+
`
|
|
277
|
+
});
|
|
278
|
+
injectTypes({
|
|
279
|
+
filename: "types.d.ts",
|
|
280
|
+
content: `
|
|
281
|
+
// Generated by Hono Actions Integration
|
|
282
|
+
|
|
283
|
+
declare module 'virtual:hono-actions/router' {
|
|
284
|
+
export type HonoRouter = import('./router.ts').HonoRouter
|
|
285
|
+
const app: typeof import('./router.ts').default
|
|
286
|
+
export default app
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
declare module '@gnostic/hono-actions/client' {
|
|
290
|
+
export const honoClient: typeof import('./client').honoClient
|
|
291
|
+
}
|
|
292
|
+
`
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
// src/index.ts
|
|
301
|
+
var index_default = integration_default;
|
|
302
|
+
export {
|
|
303
|
+
HonoActionError,
|
|
304
|
+
index_default as default,
|
|
305
|
+
defineHonoAction
|
|
306
|
+
};
|
package/package.json
CHANGED
|
@@ -6,11 +6,15 @@
|
|
|
6
6
|
"dependencies": {
|
|
7
7
|
"@hono/valibot-validator": "0.5.3",
|
|
8
8
|
"astro-integration-kit": "^0.19.0",
|
|
9
|
-
"
|
|
9
|
+
"fast-glob": "^3.3.3",
|
|
10
|
+
"hono": "^4.9.4",
|
|
11
|
+
"valibot": "^1.1.0"
|
|
10
12
|
},
|
|
11
13
|
"description": "Type-safe Hono server actions with build-in validation",
|
|
12
14
|
"devDependencies": {
|
|
15
|
+
"@biomejs/biome": "^2.2.2",
|
|
13
16
|
"@types/bun": "^1.2.20",
|
|
17
|
+
"tsup": "^8.5.0",
|
|
14
18
|
"typescript": "5.9.2"
|
|
15
19
|
},
|
|
16
20
|
"engineStrict": true,
|
|
@@ -19,16 +23,12 @@
|
|
|
19
23
|
},
|
|
20
24
|
"exports": {
|
|
21
25
|
".": {
|
|
22
|
-
"
|
|
23
|
-
"
|
|
26
|
+
"types": "./dist/index.d.ts",
|
|
27
|
+
"default": "./dist/index.js"
|
|
24
28
|
},
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
},
|
|
29
|
-
"./error": {
|
|
30
|
-
"default": "./dist/error.js",
|
|
31
|
-
"types": "./dist/error.d.ts"
|
|
29
|
+
"./*": {
|
|
30
|
+
"types": "./dist/*.d.ts",
|
|
31
|
+
"default": "./dist/*.js"
|
|
32
32
|
}
|
|
33
33
|
},
|
|
34
34
|
"files": [
|
|
@@ -52,11 +52,11 @@
|
|
|
52
52
|
"access": "public"
|
|
53
53
|
},
|
|
54
54
|
"scripts": {
|
|
55
|
-
"build": "
|
|
56
|
-
"dev": "
|
|
55
|
+
"build": "tsup",
|
|
56
|
+
"dev": "tsup --watch",
|
|
57
57
|
"prepublishOnly": "bun run build"
|
|
58
58
|
},
|
|
59
59
|
"type": "module",
|
|
60
60
|
"types": "dist/index.d.ts",
|
|
61
|
-
"version": "1.0.
|
|
61
|
+
"version": "1.0.2"
|
|
62
62
|
}
|
package/dist/define-action.d.ts
DELETED
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
import type { Context } from 'hono';
|
|
2
|
-
import * as v from 'valibot';
|
|
3
|
-
export interface Bindings {
|
|
4
|
-
}
|
|
5
|
-
/**
|
|
6
|
-
* HonoEnv is passed to the Hono context to provide types on `ctx.env`.
|
|
7
|
-
*
|
|
8
|
-
* We are using `HonoEnv` to avoid confusion with the Cloudflare types on `Env` -> which cooresponds to `Bindings`
|
|
9
|
-
*/
|
|
10
|
-
export interface HonoEnv {
|
|
11
|
-
Bindings: Bindings;
|
|
12
|
-
Variables: Record<string, any>;
|
|
13
|
-
}
|
|
14
|
-
type HonoActionSchema = v.ObjectSchema<v.ObjectEntries, v.ErrorMessage<v.ObjectIssue> | undefined> | v.NeverSchema<undefined>;
|
|
15
|
-
interface HonoActionContext<TEnv extends HonoEnv, TPath extends string, TSchema extends HonoActionSchema> extends Context<TEnv, TPath, {
|
|
16
|
-
input: v.InferInput<TSchema>;
|
|
17
|
-
output: v.InferOutput<TSchema>;
|
|
18
|
-
outputFormat: 'json';
|
|
19
|
-
}> {
|
|
20
|
-
env: TEnv['Bindings'];
|
|
21
|
-
}
|
|
22
|
-
type HonoActionParams<TPath extends string, TSchema extends HonoActionSchema, TReturn, TEnv extends HonoEnv = HonoEnv> = {
|
|
23
|
-
path: TPath;
|
|
24
|
-
schema?: TSchema;
|
|
25
|
-
handler: (params: v.InferOutput<TSchema>, context: HonoActionContext<TEnv, TPath, TSchema>) => Promise<TReturn>;
|
|
26
|
-
};
|
|
27
|
-
/**
|
|
28
|
-
* Defines a type-safe Hono action using Valibot for input validation.
|
|
29
|
-
*
|
|
30
|
-
* @param path - The path of the action.
|
|
31
|
-
* @param schema - The object schema for Valibot validation.
|
|
32
|
-
* @default never
|
|
33
|
-
* @param handler - The handler function for the action.
|
|
34
|
-
* @returns A Hono app instance with the defined route
|
|
35
|
-
*/
|
|
36
|
-
export declare function defineHonoAction<TPath extends string, TSchema extends HonoActionSchema, TReturn, TEnv extends HonoEnv = HonoEnv>({ path, schema, handler }: HonoActionParams<TPath, TSchema, TReturn, TEnv>): import("hono/hono-base").HonoBase<TEnv, { [K in import("hono/types").MergePath<"/", TPath>]: {
|
|
37
|
-
$post: {
|
|
38
|
-
input: import("hono/types").AddParam<unknown extends ((undefined extends v.InferInput<TSchema | v.UnionSchema<[v.NeverSchema<undefined>, v.ObjectSchema<{}, undefined>], undefined>> ? true : false) extends true ? {
|
|
39
|
-
json?: (v.InferInput<TSchema | v.UnionSchema<[v.NeverSchema<undefined>, v.ObjectSchema<{}, undefined>], undefined>> extends infer T_1 ? T_1 extends v.InferInput<TSchema | v.UnionSchema<[v.NeverSchema<undefined>, v.ObjectSchema<{}, undefined>], undefined>> ? T_1 extends any ? T_1 : { [K2 in keyof T_1]?: any; } : never : never) | undefined;
|
|
40
|
-
} : {
|
|
41
|
-
json: v.InferInput<TSchema | v.UnionSchema<[v.NeverSchema<undefined>, v.ObjectSchema<{}, undefined>], undefined>> extends infer T_2 ? T_2 extends v.InferInput<TSchema | v.UnionSchema<[v.NeverSchema<undefined>, v.ObjectSchema<{}, undefined>], undefined>> ? T_2 extends any ? T_2 : { [K2_1 in keyof T_2]: any; } : never : never;
|
|
42
|
-
}) ? {} : (undefined extends v.InferInput<TSchema | v.UnionSchema<[v.NeverSchema<undefined>, v.ObjectSchema<{}, undefined>], undefined>> ? true : false) extends true ? {
|
|
43
|
-
json?: (v.InferInput<TSchema | v.UnionSchema<[v.NeverSchema<undefined>, v.ObjectSchema<{}, undefined>], undefined>> extends infer T_3 ? T_3 extends v.InferInput<TSchema | v.UnionSchema<[v.NeverSchema<undefined>, v.ObjectSchema<{}, undefined>], undefined>> ? T_3 extends any ? T_3 : { [K2_2 in keyof T_3]?: any; } : never : never) | undefined;
|
|
44
|
-
} : {
|
|
45
|
-
json: v.InferInput<TSchema | v.UnionSchema<[v.NeverSchema<undefined>, v.ObjectSchema<{}, undefined>], undefined>> extends infer T_4 ? T_4 extends v.InferInput<TSchema | v.UnionSchema<[v.NeverSchema<undefined>, v.ObjectSchema<{}, undefined>], undefined>> ? T_4 extends any ? T_4 : { [K2_3 in keyof T_4]: any; } : never : never;
|
|
46
|
-
}, import("hono/types").MergePath<"/", TPath>>;
|
|
47
|
-
output: unknown extends ({
|
|
48
|
-
data: Awaited<TReturn>;
|
|
49
|
-
error: null;
|
|
50
|
-
} extends import("hono/utils/types").JSONValue ? { [K_2 in keyof {
|
|
51
|
-
data: Awaited<TReturn>;
|
|
52
|
-
error: null;
|
|
53
|
-
} as ({
|
|
54
|
-
data: Awaited<TReturn>;
|
|
55
|
-
error: null;
|
|
56
|
-
}[K_2] extends infer T_5 ? T_5 extends {
|
|
57
|
-
data: Awaited<TReturn>;
|
|
58
|
-
error: null;
|
|
59
|
-
}[K_2] ? T_5 extends import("hono/utils/types").InvalidJSONValue ? true : false : never : never) extends true ? never : K_2]: boolean extends ({
|
|
60
|
-
data: Awaited<TReturn>;
|
|
61
|
-
error: null;
|
|
62
|
-
}[K_2] extends infer T_6 ? T_6 extends {
|
|
63
|
-
data: Awaited<TReturn>;
|
|
64
|
-
error: null;
|
|
65
|
-
}[K_2] ? T_6 extends import("hono/utils/types").InvalidJSONValue ? true : false : never : never) ? import("hono/utils/types").JSONParsed<{
|
|
66
|
-
data: Awaited<TReturn>;
|
|
67
|
-
error: null;
|
|
68
|
-
}[K_2]> | undefined : import("hono/utils/types").JSONParsed<{
|
|
69
|
-
data: Awaited<TReturn>;
|
|
70
|
-
error: null;
|
|
71
|
-
}[K_2]>; } : never) ? {} : {
|
|
72
|
-
data: Awaited<TReturn>;
|
|
73
|
-
error: null;
|
|
74
|
-
} extends import("hono/utils/types").JSONValue ? { [K_2 in keyof {
|
|
75
|
-
data: Awaited<TReturn>;
|
|
76
|
-
error: null;
|
|
77
|
-
} as ({
|
|
78
|
-
data: Awaited<TReturn>;
|
|
79
|
-
error: null;
|
|
80
|
-
}[K_2] extends infer T_5 ? T_5 extends {
|
|
81
|
-
data: Awaited<TReturn>;
|
|
82
|
-
error: null;
|
|
83
|
-
}[K_2] ? T_5 extends import("hono/utils/types").InvalidJSONValue ? true : false : never : never) extends true ? never : K_2]: boolean extends ({
|
|
84
|
-
data: Awaited<TReturn>;
|
|
85
|
-
error: null;
|
|
86
|
-
}[K_2] extends infer T_6 ? T_6 extends {
|
|
87
|
-
data: Awaited<TReturn>;
|
|
88
|
-
error: null;
|
|
89
|
-
}[K_2] ? T_6 extends import("hono/utils/types").InvalidJSONValue ? true : false : never : never) ? import("hono/utils/types").JSONParsed<{
|
|
90
|
-
data: Awaited<TReturn>;
|
|
91
|
-
error: null;
|
|
92
|
-
}[K_2]> | undefined : import("hono/utils/types").JSONParsed<{
|
|
93
|
-
data: Awaited<TReturn>;
|
|
94
|
-
error: null;
|
|
95
|
-
}[K_2]>; } : never;
|
|
96
|
-
outputFormat: "json";
|
|
97
|
-
status: 200;
|
|
98
|
-
} | {
|
|
99
|
-
input: import("hono/types").AddParam<unknown extends ((undefined extends v.InferInput<TSchema | v.UnionSchema<[v.NeverSchema<undefined>, v.ObjectSchema<{}, undefined>], undefined>> ? true : false) extends true ? {
|
|
100
|
-
json?: (v.InferInput<TSchema | v.UnionSchema<[v.NeverSchema<undefined>, v.ObjectSchema<{}, undefined>], undefined>> extends infer T_5 ? T_5 extends v.InferInput<TSchema | v.UnionSchema<[v.NeverSchema<undefined>, v.ObjectSchema<{}, undefined>], undefined>> ? T_5 extends any ? T_5 : { [K2_4 in keyof T_5]?: any; } : never : never) | undefined;
|
|
101
|
-
} : {
|
|
102
|
-
json: v.InferInput<TSchema | v.UnionSchema<[v.NeverSchema<undefined>, v.ObjectSchema<{}, undefined>], undefined>> extends infer T_6 ? T_6 extends v.InferInput<TSchema | v.UnionSchema<[v.NeverSchema<undefined>, v.ObjectSchema<{}, undefined>], undefined>> ? T_6 extends any ? T_6 : { [K2_5 in keyof T_6]: any; } : never : never;
|
|
103
|
-
}) ? {} : (undefined extends v.InferInput<TSchema | v.UnionSchema<[v.NeverSchema<undefined>, v.ObjectSchema<{}, undefined>], undefined>> ? true : false) extends true ? {
|
|
104
|
-
json?: (v.InferInput<TSchema | v.UnionSchema<[v.NeverSchema<undefined>, v.ObjectSchema<{}, undefined>], undefined>> extends infer T_7 ? T_7 extends v.InferInput<TSchema | v.UnionSchema<[v.NeverSchema<undefined>, v.ObjectSchema<{}, undefined>], undefined>> ? T_7 extends any ? T_7 : { [K2_6 in keyof T_7]?: any; } : never : never) | undefined;
|
|
105
|
-
} : {
|
|
106
|
-
json: v.InferInput<TSchema | v.UnionSchema<[v.NeverSchema<undefined>, v.ObjectSchema<{}, undefined>], undefined>> extends infer T_8 ? T_8 extends v.InferInput<TSchema | v.UnionSchema<[v.NeverSchema<undefined>, v.ObjectSchema<{}, undefined>], undefined>> ? T_8 extends any ? T_8 : { [K2_7 in keyof T_8]: any; } : never : never;
|
|
107
|
-
}, import("hono/types").MergePath<"/", TPath>>;
|
|
108
|
-
output: {
|
|
109
|
-
data: null;
|
|
110
|
-
error: {
|
|
111
|
-
message: string;
|
|
112
|
-
code: string;
|
|
113
|
-
};
|
|
114
|
-
};
|
|
115
|
-
outputFormat: "json";
|
|
116
|
-
status: 500;
|
|
117
|
-
};
|
|
118
|
-
}; } extends infer T ? { [KeyType in keyof T]: T[KeyType]; } : never, "/">;
|
|
119
|
-
export {};
|
package/dist/define-action.js
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { vValidator } from '@hono/valibot-validator';
|
|
2
|
-
import { createFactory } from 'hono/factory';
|
|
3
|
-
import * as v from 'valibot';
|
|
4
|
-
import { HonoActionError } from './error.js';
|
|
5
|
-
/**
|
|
6
|
-
* Defines a type-safe Hono action using Valibot for input validation.
|
|
7
|
-
*
|
|
8
|
-
* @param path - The path of the action.
|
|
9
|
-
* @param schema - The object schema for Valibot validation.
|
|
10
|
-
* @default never
|
|
11
|
-
* @param handler - The handler function for the action.
|
|
12
|
-
* @returns A Hono app instance with the defined route
|
|
13
|
-
*/
|
|
14
|
-
export function defineHonoAction({ path, schema, handler }) {
|
|
15
|
-
const factory = createFactory();
|
|
16
|
-
const app = factory.createApp();
|
|
17
|
-
const route = app.post(path, vValidator('json', schema ?? v.union([v.never(), v.object({})]), async (result, c) => {
|
|
18
|
-
if (!result.success) {
|
|
19
|
-
console.error(result.issues);
|
|
20
|
-
return c.json({
|
|
21
|
-
data: null,
|
|
22
|
-
error: new HonoActionError({
|
|
23
|
-
message: result.issues[0].message,
|
|
24
|
-
code: 'INPUT_VALIDATION_ERROR',
|
|
25
|
-
issue: result.issues[0]
|
|
26
|
-
})
|
|
27
|
-
}, 400);
|
|
28
|
-
}
|
|
29
|
-
}), async (c) => {
|
|
30
|
-
try {
|
|
31
|
-
const json = c.req.valid('json');
|
|
32
|
-
// context is validated after the middleware, but we only need the original definition to be passed back in to the handler here.
|
|
33
|
-
const result = await handler(json, c);
|
|
34
|
-
return c.json({
|
|
35
|
-
data: result,
|
|
36
|
-
error: null
|
|
37
|
-
}, 200);
|
|
38
|
-
}
|
|
39
|
-
catch (error) {
|
|
40
|
-
console.error(error);
|
|
41
|
-
let errorMessage = 'Internal server error';
|
|
42
|
-
let errorCode = 'INTERNAL_SERVER_ERROR';
|
|
43
|
-
if (error instanceof HonoActionError) {
|
|
44
|
-
errorMessage = error.message;
|
|
45
|
-
errorCode = error.code;
|
|
46
|
-
}
|
|
47
|
-
return c.json({
|
|
48
|
-
data: null,
|
|
49
|
-
error: {
|
|
50
|
-
message: errorMessage,
|
|
51
|
-
code: errorCode
|
|
52
|
-
}
|
|
53
|
-
}, 500);
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
return route;
|
|
57
|
-
}
|
package/dist/error.d.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type * as v from 'valibot';
|
|
2
|
-
/**
|
|
3
|
-
* Standard error codes for actions
|
|
4
|
-
*/
|
|
5
|
-
export type ActionErrorCode = 'INPUT_VALIDATION_ERROR' | 'EXTERNAL_API_ERROR' | 'INTERNAL_SERVER_ERROR' | 'UNKNOWN_ERROR' | 'LOCATION_NOT_FOUND' | 'SESSION_NOT_FOUND';
|
|
6
|
-
export declare class HonoActionError<TSchema extends v.ObjectSchema<v.ObjectEntries, v.ErrorMessage<v.ObjectIssue> | undefined>, TMessage extends string, TCode extends ActionErrorCode, TIssue extends v.InferIssue<TSchema>> extends Error {
|
|
7
|
-
code: TCode;
|
|
8
|
-
issue?: TIssue;
|
|
9
|
-
constructor({ message, code, issue }: {
|
|
10
|
-
message: TMessage;
|
|
11
|
-
code: TCode;
|
|
12
|
-
issue?: TIssue;
|
|
13
|
-
});
|
|
14
|
-
}
|