@gnosticdev/hono-actions 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,89 @@
1
+ # @gnosticdev/define-hono-action
2
+
3
+ Type-safe Hono action definitions with Valibot validation.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @gnosticdev/define-hono-action
9
+ ```
10
+
11
+ ## Peer Dependencies
12
+
13
+ This package requires the following peer dependencies:
14
+
15
+ - `@hono/valibot-validator`: ^0.2.0
16
+ - `hono`: ^4.0.0
17
+ - `valibot`: ^0.30.0
18
+
19
+ ## Usage
20
+
21
+ ```typescript
22
+ import { defineHonoAction, ActionError } from '@gnosticdev/define-hono-action'
23
+ import * as v from 'valibot'
24
+
25
+ // Define a simple action
26
+ export const simpleAction = defineHonoAction({
27
+ path: '/simple',
28
+ handler: async (input, ctx) => {
29
+ return { message: 'Hello World!' }
30
+ }
31
+ })
32
+
33
+ // Define an action with validation
34
+ export const validatedAction = defineHonoAction({
35
+ path: '/validated',
36
+ schema: v.object({
37
+ name: v.string(),
38
+ email: v.pipe(v.string(), v.email())
39
+ }),
40
+ handler: async (input, ctx) => {
41
+ // input is automatically typed based on schema
42
+ return {
43
+ message: `Hello ${input.name}!`,
44
+ email: input.email
45
+ }
46
+ }
47
+ })
48
+
49
+ // Use custom error handling
50
+ export const errorAction = defineHonoAction({
51
+ path: '/error',
52
+ handler: async (input, ctx) => {
53
+ if (someCondition) {
54
+ throw new ActionError('Custom error message', 'EXTERNAL_API_ERROR')
55
+ }
56
+ return { success: true }
57
+ }
58
+ })
59
+
60
+ // Export all actions
61
+ export const honoActions = {
62
+ simpleAction,
63
+ validatedAction,
64
+ errorAction
65
+ }
66
+ ```
67
+
68
+ ## Types
69
+
70
+ The package exports the following types:
71
+
72
+ - `ActionErrorCode`: Union type of possible error codes
73
+ - `ActionError`: Custom error class for action errors
74
+ - `HonoActions`: Type for collections of actions
75
+
76
+ ## Error Codes
77
+
78
+ Available error codes:
79
+
80
+ - `INPUT_VALIDATION_ERROR`: Validation failed for input data
81
+ - `EXTERNAL_API_ERROR`: Error calling external API
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
86
+
87
+ ## License
88
+
89
+ MIT
@@ -0,0 +1,119 @@
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 {};
@@ -0,0 +1,57 @@
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
+ }
@@ -0,0 +1,14 @@
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
+ }
package/dist/error.js ADDED
@@ -0,0 +1,10 @@
1
+ export class HonoActionError extends Error {
2
+ code;
3
+ issue;
4
+ constructor({ message, code, issue }) {
5
+ super(message);
6
+ this.name = 'HonoActionError';
7
+ this.code = code;
8
+ this.issue = issue;
9
+ }
10
+ }
@@ -0,0 +1,5 @@
1
+ import type { Bindings, HonoEnv } from './define-action.js';
2
+ import { defineHonoAction } from './define-action.js';
3
+ import { HonoActionError } from './error.js';
4
+ export type { Bindings, HonoEnv };
5
+ export { defineHonoAction, HonoActionError };
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ import { defineHonoAction } from './define-action.js';
2
+ import { HonoActionError } from './error.js';
3
+ export { defineHonoAction, HonoActionError };
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "author": {
3
+ "name": "gnosticdev",
4
+ "url": "https://github.com/gnosticdev"
5
+ },
6
+ "dependencies": {
7
+ "@hono/valibot-validator": "0.5.3",
8
+ "astro-integration-kit": "^0.19.0",
9
+ "hono": "^4.9.4"
10
+ },
11
+ "description": "Type-safe Hono server actions with build-in validation",
12
+ "devDependencies": {
13
+ "@types/bun": "^1.2.20",
14
+ "typescript": "5.9.2"
15
+ },
16
+ "engineStrict": true,
17
+ "engines": {
18
+ "node": ">=22.0.0"
19
+ },
20
+ "exports": {
21
+ ".": {
22
+ "default": "./dist/index.js",
23
+ "types": "./dist/index.d.ts"
24
+ },
25
+ "./define-action": {
26
+ "default": "./dist/define-action.js",
27
+ "types": "./dist/define-action.d.ts"
28
+ },
29
+ "./error": {
30
+ "default": "./dist/error.js",
31
+ "types": "./dist/error.d.ts"
32
+ }
33
+ },
34
+ "files": [
35
+ "dist"
36
+ ],
37
+ "keywords": [
38
+ "hono",
39
+ "actions",
40
+ "astro",
41
+ "validation",
42
+ "server actions"
43
+ ],
44
+ "license": "MIT",
45
+ "main": "dist/index.js",
46
+ "name": "@gnosticdev/hono-actions",
47
+ "packageManager": "bun@1.2.21",
48
+ "peerDependencies": {
49
+ "astro": "^5.13.3"
50
+ },
51
+ "publishConfig": {
52
+ "access": "public"
53
+ },
54
+ "scripts": {
55
+ "build": "rm -rf dist && tsc",
56
+ "dev": "rm -rf dist && tsc --watch",
57
+ "prepublishOnly": "bun run build"
58
+ },
59
+ "type": "module",
60
+ "types": "dist/index.d.ts",
61
+ "version": "1.0.0"
62
+ }