@gnosticdev/hono-actions 1.0.3 → 1.0.5

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
@@ -27,7 +27,7 @@ All other dependencies (`hono`, `valibot`, `@hono/valibot-validator`, etc.) are
27
27
  ```typescript
28
28
  // astro.config.ts
29
29
  import { defineConfig } from 'astro/config'
30
- import honoActions from '@gnosticdev/hono-actions'
30
+ import honoActions from '@gnosticdev/hono-actions/integration'
31
31
 
32
32
  export default defineConfig({
33
33
  integrations: [
@@ -52,7 +52,7 @@ Create a file at one of these locations (the integration will auto-discover):
52
52
 
53
53
  ```typescript
54
54
  // src/server/actions.ts
55
- import { defineHonoAction, ActionError } from '@gnosticdev/hono-actions'
55
+ import { defineHonoAction, HonoActionError } from '@gnosticdev/hono-actions'
56
56
  import * as v from 'valibot'
57
57
 
58
58
  // Define a simple action
@@ -88,7 +88,10 @@ export const errorAction = defineHonoAction({
88
88
  path: '/error',
89
89
  handler: async (input, ctx) => {
90
90
  if (someCondition) {
91
- throw new ActionError('Custom error message', 'EXTERNAL_API_ERROR')
91
+ throw new HonoActionError({
92
+ message: 'Custom error message',
93
+ code: 'EXTERNAL_API_ERROR'
94
+ })
92
95
  }
93
96
  return { success: true }
94
97
  }
@@ -151,6 +154,17 @@ const handleSubmit = async (formData: FormData) => {
151
154
  }
152
155
  ```
153
156
 
157
+ ## Package Structure
158
+
159
+ This package provides two main entry points:
160
+
161
+ - **`@gnosticdev/hono-actions`** (default): Action definition utilities (`defineHonoAction`, `HonoActionError`, types)
162
+ - Safe for browser environments
163
+ - Used in your action files and client-side code
164
+ - **`@gnosticdev/hono-actions/integration`**: Astro integration
165
+ - Uses Node.js built-ins (fs, path)
166
+ - Only used in `astro.config.ts`
167
+
154
168
  ## Configuration Options
155
169
 
156
170
  The integration accepts the following options:
@@ -0,0 +1,138 @@
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
+
7
+ interface Bindings {
8
+ }
9
+ /**
10
+ * HonoEnv is passed to the Hono context to provide types on `ctx.env`.
11
+ *
12
+ * We are using `HonoEnv` to avoid confusion with the Cloudflare types on `Env` -> which cooresponds to `Bindings`
13
+ */
14
+ interface HonoEnv {
15
+ Bindings: Bindings;
16
+ Variables: Record<string, any>;
17
+ }
18
+ type HonoActionSchema = v.ObjectSchema<v.ObjectEntries, v.ErrorMessage<v.ObjectIssue> | undefined> | v.NeverSchema<undefined>;
19
+ interface HonoActionContext<TEnv extends HonoEnv, TPath extends string, TSchema extends HonoActionSchema> extends Context<TEnv, TPath, {
20
+ input: v.InferInput<TSchema>;
21
+ output: v.InferOutput<TSchema>;
22
+ outputFormat: 'json';
23
+ }> {
24
+ env: TEnv['Bindings'];
25
+ }
26
+ type HonoActionParams<TPath extends string, TSchema extends HonoActionSchema, TReturn, TEnv extends HonoEnv = HonoEnv> = {
27
+ path: TPath;
28
+ schema?: TSchema;
29
+ handler: (params: v.InferOutput<TSchema>, context: HonoActionContext<TEnv, TPath, TSchema>) => Promise<TReturn>;
30
+ };
31
+ /**
32
+ * Defines a type-safe Hono action using Valibot for input validation.
33
+ *
34
+ * @param path - The path of the action.
35
+ * @param schema - The object schema for Valibot validation.
36
+ * @default never
37
+ * @param handler - The handler function for the action.
38
+ * @returns A Hono app instance with the defined route
39
+ */
40
+ 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>]: {
41
+ $post: {
42
+ input: hono_types.AddParam<unknown extends ((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_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;
44
+ } : {
45
+ 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;
46
+ }) ? {} : (undefined extends v.InferInput<TSchema | v.UnionSchema<[v.NeverSchema<undefined>, v.ObjectSchema<{}, undefined>], undefined>> ? true : false) extends true ? {
47
+ 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;
48
+ } : {
49
+ 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;
50
+ }, hono_types.MergePath<"/", TPath>>;
51
+ output: unknown extends ({
52
+ data: Awaited<TReturn>;
53
+ error: null;
54
+ } extends hono_utils_types.JSONValue ? { [K_2 in keyof {
55
+ data: Awaited<TReturn>;
56
+ error: null;
57
+ } as ({
58
+ data: Awaited<TReturn>;
59
+ error: null;
60
+ }[K_2] extends infer T_5 ? T_5 extends {
61
+ data: Awaited<TReturn>;
62
+ error: null;
63
+ }[K_2] ? T_5 extends hono_utils_types.InvalidJSONValue ? true : false : never : never) extends true ? never : K_2]: boolean extends ({
64
+ data: Awaited<TReturn>;
65
+ error: null;
66
+ }[K_2] extends infer T_6 ? T_6 extends {
67
+ data: Awaited<TReturn>;
68
+ error: null;
69
+ }[K_2] ? T_6 extends hono_utils_types.InvalidJSONValue ? true : false : never : never) ? hono_utils_types.JSONParsed<{
70
+ data: Awaited<TReturn>;
71
+ error: null;
72
+ }[K_2]> | undefined : hono_utils_types.JSONParsed<{
73
+ data: Awaited<TReturn>;
74
+ error: null;
75
+ }[K_2]>; } : never) ? {} : {
76
+ data: Awaited<TReturn>;
77
+ error: null;
78
+ } extends hono_utils_types.JSONValue ? { [K_2 in keyof {
79
+ data: Awaited<TReturn>;
80
+ error: null;
81
+ } as ({
82
+ data: Awaited<TReturn>;
83
+ error: null;
84
+ }[K_2] extends infer T_5 ? T_5 extends {
85
+ data: Awaited<TReturn>;
86
+ error: null;
87
+ }[K_2] ? T_5 extends hono_utils_types.InvalidJSONValue ? true : false : never : never) extends true ? never : K_2]: boolean extends ({
88
+ data: Awaited<TReturn>;
89
+ error: null;
90
+ }[K_2] extends infer T_6 ? T_6 extends {
91
+ data: Awaited<TReturn>;
92
+ error: null;
93
+ }[K_2] ? T_6 extends hono_utils_types.InvalidJSONValue ? true : false : never : never) ? hono_utils_types.JSONParsed<{
94
+ data: Awaited<TReturn>;
95
+ error: null;
96
+ }[K_2]> | undefined : hono_utils_types.JSONParsed<{
97
+ data: Awaited<TReturn>;
98
+ error: null;
99
+ }[K_2]>; } : never;
100
+ outputFormat: "json";
101
+ status: 200;
102
+ } | {
103
+ input: hono_types.AddParam<unknown extends ((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_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;
105
+ } : {
106
+ 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;
107
+ }) ? {} : (undefined extends v.InferInput<TSchema | v.UnionSchema<[v.NeverSchema<undefined>, v.ObjectSchema<{}, undefined>], undefined>> ? true : false) extends true ? {
108
+ 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;
109
+ } : {
110
+ 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;
111
+ }, hono_types.MergePath<"/", TPath>>;
112
+ output: {
113
+ data: null;
114
+ error: {
115
+ message: string;
116
+ code: string;
117
+ };
118
+ };
119
+ outputFormat: "json";
120
+ status: 500;
121
+ };
122
+ }; } extends infer T ? { [KeyType in keyof T]: T[KeyType]; } : never, "/">;
123
+
124
+ /**
125
+ * Standard error codes for actions
126
+ */
127
+ type ActionErrorCode = 'INPUT_VALIDATION_ERROR' | 'EXTERNAL_API_ERROR' | 'INTERNAL_SERVER_ERROR' | 'UNKNOWN_ERROR' | 'LOCATION_NOT_FOUND' | 'SESSION_NOT_FOUND';
128
+ 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 {
129
+ code: TCode;
130
+ issue?: TIssue;
131
+ constructor({ message, code, issue }: {
132
+ message: TMessage;
133
+ code: TCode;
134
+ issue?: TIssue;
135
+ });
136
+ }
137
+
138
+ export { type ActionErrorCode, type Bindings, HonoActionError, type HonoEnv, defineHonoAction };
@@ -0,0 +1,88 @@
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, schema, handler }) {
24
+ const factory = createFactory();
25
+ const app = factory.createApp();
26
+ const route = app.post(
27
+ path,
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
+ export {
86
+ HonoActionError,
87
+ defineHonoAction
88
+ };
package/dist/index.d.ts CHANGED
@@ -1,144 +1,6 @@
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 };
1
+ export { ActionErrorCode, Bindings, HonoActionError, HonoEnv, defineHonoAction } from './actions.js';
2
+ import 'hono/hono-base';
3
+ import 'hono/utils/types';
4
+ import 'hono/types';
5
+ import 'hono';
6
+ import 'valibot';
package/dist/index.js CHANGED
@@ -20,11 +20,11 @@ var HonoActionError = class extends Error {
20
20
  };
21
21
 
22
22
  // src/define-action.ts
23
- function defineHonoAction({ path: path2, schema, handler }) {
23
+ function defineHonoAction({ path, schema, handler }) {
24
24
  const factory = createFactory();
25
25
  const app = factory.createApp();
26
26
  const route = app.post(
27
- path2,
27
+ path,
28
28
  vValidator(
29
29
  "json",
30
30
  schema ?? v.union([v.never(), v.object({})]),
@@ -82,225 +82,7 @@ function defineHonoAction({ path: path2, schema, handler }) {
82
82
  );
83
83
  return route;
84
84
  }
85
-
86
- // src/integration.ts
87
- import fs from "fs/promises";
88
- import path from "path";
89
- import { z } from "astro/zod";
90
- import {
91
- addVirtualImports,
92
- createResolver,
93
- defineIntegration
94
- } from "astro-integration-kit";
95
- import fg from "fast-glob";
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 './router.js'
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
85
  export {
303
86
  HonoActionError,
304
- index_default as default,
305
87
  defineHonoAction
306
88
  };
@@ -0,0 +1,20 @@
1
+ import * as astro from 'astro';
2
+ import { z } from 'astro/zod';
3
+
4
+ declare const optionsSchema: z.ZodOptional<z.ZodObject<{
5
+ basePath: z.ZodOptional<z.ZodString>;
6
+ actionsPath: z.ZodOptional<z.ZodString>;
7
+ }, "strip", z.ZodTypeAny, {
8
+ basePath?: string | undefined;
9
+ actionsPath?: string | undefined;
10
+ }, {
11
+ basePath?: string | undefined;
12
+ actionsPath?: string | undefined;
13
+ }>>;
14
+ type IntegrationOptions = z.infer<typeof optionsSchema>;
15
+ declare const _default: (options?: {
16
+ basePath?: string | undefined;
17
+ actionsPath?: string | undefined;
18
+ } | undefined) => astro.AstroIntegration & {};
19
+
20
+ export { type IntegrationOptions, _default as default };
@@ -0,0 +1,216 @@
1
+ // src/integration.ts
2
+ import fs from "fs/promises";
3
+ import path from "path";
4
+ import { z } from "astro/zod";
5
+ import {
6
+ addVirtualImports,
7
+ createResolver,
8
+ defineIntegration
9
+ } from "astro-integration-kit";
10
+ import fg from "fast-glob";
11
+
12
+ // src/integration-files.ts
13
+ function generateRouter(opts) {
14
+ const { basePath, relativeActionsPath } = opts;
15
+ return `import type { HonoEnv } from '@gnosticdev/hono-actions'
16
+ import { Hono } from 'hono'
17
+ import { cors } from 'hono/cors'
18
+ import { showRoutes } from 'hono/dev'
19
+ import { logger } from 'hono/logger'
20
+ import { prettyJSON } from 'hono/pretty-json'
21
+ import type { ExtractSchema, MergeSchemaPath } from 'hono/types'
22
+
23
+ export async function buildRouter(){
24
+ type ActionSchema = ExtractSchema<typeof honoActions[keyof typeof honoActions]>
25
+ const { honoActions} = await import('${relativeActionsPath}')
26
+ const app = new Hono<HonoEnv, MergeSchemaPath<ActionSchema, '${basePath}'>>().basePath('${basePath}')
27
+
28
+ app.use('*', cors(), logger(), prettyJSON())
29
+
30
+ for (const [path, action] of Object.entries(honoActions)) {
31
+ app.route(path, action)
32
+ }
33
+
34
+ showRoutes(app)
35
+ return app
36
+ }
37
+
38
+ export type HonoRouter = Awaited<ReturnType<typeof buildRouter>>
39
+
40
+ const app = await buildRouter()
41
+ export default app`;
42
+ }
43
+ var getAstroHandler = (adapter) => {
44
+ switch (adapter) {
45
+ case "cloudflare":
46
+ return `
47
+ // Generated by Hono Actions Integration
48
+ import router from './router.js'
49
+ import type { APIContext, APIRoute } from 'astro'
50
+
51
+ const handler: APIRoute<APIContext> = async (ctx) => {
52
+ return router.fetch(
53
+ ctx.request,
54
+ ctx.locals.runtime.env,
55
+ ctx.locals.runtime.ctx,
56
+ )
57
+ }
58
+
59
+ export { handler as ALL }
60
+ `;
61
+ default:
62
+ throw new Error(`Unsupported adapter: ${adapter}`);
63
+ }
64
+ };
65
+ var getHonoClient = (port) => `
66
+ // Generated by Hono Actions Integration
67
+ import type { HonoRouter } from './router.js'
68
+ import { hc } from 'hono/client'
69
+
70
+ export function getBaseUrl() {
71
+ // client side can just use the base path
72
+ if (typeof window !== 'undefined') {
73
+ return '/'
74
+ }
75
+
76
+ // dev server (dev server) needs to know the port
77
+ if (import.meta.env.DEV) {
78
+ return \`http://localhost:\${${port}}\`
79
+ }
80
+
81
+ // server side (production) needs full url
82
+ return import.meta.env.SITE
83
+ }
84
+
85
+ export const honoClient = hc<HonoRouter>(getBaseUrl())
86
+ `;
87
+
88
+ // src/lib/utils.ts
89
+ var reservedRoutes = ["_astro", "_actions", "_server_islands"];
90
+
91
+ // src/integration.ts
92
+ var optionsSchema = z.object({
93
+ basePath: z.string().optional(),
94
+ actionsPath: z.string().optional()
95
+ }).optional();
96
+ var VIRTUAL_MODULE_ID_CLIENT = "@gnostic/hono-actions/client";
97
+ var VIRTUAL_MODULE_ID_ROUTER = "virtual:hono-actions/router";
98
+ var ACTION_PATTERNS = [
99
+ "src/**/server/actions.ts",
100
+ "src/**/hono/actions.ts",
101
+ "src/**/hono/index.ts",
102
+ "src/**/hono.ts"
103
+ ];
104
+ var integration_default = defineIntegration({
105
+ name: "@gnostic/hono-actions",
106
+ optionsSchema,
107
+ setup: ({ options = {}, name }) => {
108
+ const basePath = options.basePath ?? "/api";
109
+ if (reservedRoutes.includes(basePath)) {
110
+ throw new Error(
111
+ `Base path ${basePath} is reserved by Astro; pick another (e.g. /api2).`
112
+ );
113
+ }
114
+ const { resolve } = createResolver(import.meta.url);
115
+ return {
116
+ name,
117
+ hooks: {
118
+ "astro:config:setup": async (params) => {
119
+ const { logger, injectRoute, createCodegenDir, config } = params;
120
+ const root = config.root.pathname;
121
+ const files = await fg(ACTION_PATTERNS, {
122
+ cwd: root,
123
+ absolute: true
124
+ });
125
+ const actionsPath = options.actionsPath ?? files[0];
126
+ if (!actionsPath) {
127
+ throw new Error(
128
+ `No actions found. Create one of:
129
+ ${ACTION_PATTERNS.map((p) => ` - ${p}`).join("\n")}`
130
+ );
131
+ }
132
+ const resolvedActionsPath = path.isAbsolute(actionsPath) ? actionsPath : path.join(root, actionsPath);
133
+ params.addWatchFile(resolvedActionsPath);
134
+ logger.info(
135
+ `Found actions: ${path.relative(root, resolvedActionsPath)}`
136
+ );
137
+ const codeGenDir = createCodegenDir();
138
+ const routerPathAbs = path.join(
139
+ codeGenDir.pathname,
140
+ "router.ts"
141
+ );
142
+ const relFromGenToActions = path.relative(codeGenDir.pathname, resolvedActionsPath).split(path.sep).join("/");
143
+ const routerContent = generateRouter({
144
+ basePath,
145
+ relativeActionsPath: relFromGenToActions
146
+ });
147
+ await fs.writeFile(routerPathAbs, routerContent, "utf-8");
148
+ const astroHandlerPathAbs = path.join(
149
+ codeGenDir.pathname,
150
+ "api.ts"
151
+ );
152
+ if (!params.config.adapter?.name)
153
+ throw new Error("No Astro adapter found");
154
+ const astroHandlerContent = getAstroHandler("cloudflare");
155
+ await fs.writeFile(
156
+ astroHandlerPathAbs,
157
+ astroHandlerContent,
158
+ "utf-8"
159
+ );
160
+ const clientPathAbs = path.join(
161
+ codeGenDir.pathname,
162
+ "client.ts"
163
+ );
164
+ const clientContent = getHonoClient(config.server.port);
165
+ await fs.writeFile(clientPathAbs, clientContent, "utf-8");
166
+ addVirtualImports(params, {
167
+ name,
168
+ imports: {
169
+ [VIRTUAL_MODULE_ID_CLIENT]: `export * from '${clientPathAbs}';`,
170
+ [VIRTUAL_MODULE_ID_ROUTER]: `export * from '${routerPathAbs}';`
171
+ }
172
+ });
173
+ injectRoute({
174
+ pattern: `${basePath}/[...slug]`,
175
+ entrypoint: astroHandlerPathAbs,
176
+ prerender: false
177
+ });
178
+ logger.info(
179
+ `\u2705 Hono Actions route mounted at ${basePath}/[...slug]`
180
+ );
181
+ },
182
+ "astro:config:done": async ({ injectTypes }) => {
183
+ injectTypes({
184
+ filename: "actions.d.ts",
185
+ content: `
186
+ declare module '@gnosticdev/hono-actions' {
187
+ interface Bindings extends Env { ASTRO_LOCALS: App.Locals }
188
+ interface HonoEnv { Bindings: Bindings }
189
+ }
190
+ export {}
191
+ `
192
+ });
193
+ injectTypes({
194
+ filename: "types.d.ts",
195
+ content: `
196
+ // Generated by Hono Actions Integration
197
+
198
+ declare module 'virtual:hono-actions/router' {
199
+ export type HonoRouter = import('./router.ts').HonoRouter
200
+ const app: typeof import('./router.ts').default
201
+ export default app
202
+ }
203
+
204
+ declare module '@gnostic/hono-actions/client' {
205
+ export const honoClient: typeof import('./client').honoClient
206
+ }
207
+ `
208
+ });
209
+ }
210
+ }
211
+ };
212
+ }
213
+ });
214
+ export {
215
+ integration_default as default
216
+ };
package/package.json CHANGED
@@ -8,24 +8,28 @@
8
8
  "astro-integration-kit": "^0.19.0",
9
9
  "fast-glob": "^3.3.3",
10
10
  "hono": "^4.9.4",
11
- "release-it": "^19.0.4",
12
11
  "valibot": "^1.1.0"
13
12
  },
14
13
  "description": "Type-safe Hono server actions with build-in validation",
15
14
  "devDependencies": {
16
15
  "@biomejs/biome": "^2.2.2",
17
16
  "@types/bun": "^1.2.20",
17
+ "release-it": "^19.0.4",
18
18
  "tsup": "^8.5.0",
19
19
  "typescript": "5.9.2"
20
20
  },
21
- "engineStrict": true,
22
21
  "engines": {
23
22
  "node": ">=22.0.0"
24
23
  },
24
+ "engineStrict": true,
25
25
  "exports": {
26
26
  ".": {
27
- "types": "./dist/index.d.ts",
28
- "default": "./dist/index.js"
27
+ "types": "./dist/actions.d.ts",
28
+ "default": "./dist/actions.js"
29
+ },
30
+ "./integration": {
31
+ "types": "./dist/integration.d.ts",
32
+ "default": "./dist/integration.js"
29
33
  },
30
34
  "./*": {
31
35
  "types": "./dist/*.d.ts",
@@ -43,7 +47,7 @@
43
47
  "server actions"
44
48
  ],
45
49
  "license": "MIT",
46
- "main": "dist/index.js",
50
+ "main": "dist/actions.js",
47
51
  "name": "@gnosticdev/hono-actions",
48
52
  "packageManager": "bun@1.2.21",
49
53
  "peerDependencies": {
@@ -56,13 +60,13 @@
56
60
  "build": "tsup",
57
61
  "dev": "tsup --watch",
58
62
  "prepublishOnly": "bun run build",
63
+ "publish": "bun run release",
59
64
  "release": "release-it",
60
- "release:patch": "release-it patch",
61
- "release:minor": "release-it minor",
62
65
  "release:major": "release-it major",
63
- "publish": "bun run release"
66
+ "release:minor": "release-it minor",
67
+ "release:patch": "release-it patch"
64
68
  },
65
69
  "type": "module",
66
- "types": "dist/index.d.ts",
67
- "version": "1.0.3"
70
+ "types": "dist/actions.d.ts",
71
+ "version": "1.0.5"
68
72
  }