@gnosticdev/hono-actions 1.2.5 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +94 -10
  2. package/dist/index.js +79 -36
  3. package/package.json +7 -2
package/README.md CHANGED
@@ -18,16 +18,94 @@ This package requires:
18
18
 
19
19
  - `astro`: ^5.13.0
20
20
 
21
+ ## Supported Adapters
22
+
23
+ This integration works with all supported Astro adapters:
24
+
25
+ - `@astrojs/cloudflare`
26
+ - `@astrojs/node`
27
+ - `@astrojs/vercel`
28
+ - `@astrojs/netlify`
29
+
21
30
  ## Setup
22
31
 
23
32
  ### 1. Add the integration to your Astro config
24
33
 
34
+ The integration works with all Astro adapters. Here are examples for each:
35
+
36
+ #### Cloudflare
37
+
38
+ ```typescript
39
+ // astro.config.ts
40
+ import { defineConfig } from 'astro/config'
41
+ import cloudflare from '@astrojs/cloudflare'
42
+ import honoActions from '@gnosticdev/hono-actions'
43
+
44
+ export default defineConfig({
45
+ output: 'server',
46
+ adapter: cloudflare(),
47
+ integrations: [
48
+ honoActions({
49
+ basePath: '/api', // Optional: default is '/api'
50
+ actionsPath: 'src/server/actions.ts' // Optional: custom path to your actions file
51
+ })
52
+ ]
53
+ })
54
+ ```
55
+
56
+ #### Node.js
57
+
58
+ ```typescript
59
+ // astro.config.ts
60
+ import { defineConfig } from 'astro/config'
61
+ import node from '@astrojs/node'
62
+ import honoActions from '@gnosticdev/hono-actions'
63
+
64
+ export default defineConfig({
65
+ output: 'server',
66
+ adapter: node({
67
+ mode: 'standalone' // or 'middleware'
68
+ }),
69
+ integrations: [
70
+ honoActions({
71
+ basePath: '/api', // Optional: default is '/api'
72
+ actionsPath: 'src/server/actions.ts' // Optional: custom path to your actions file
73
+ })
74
+ ]
75
+ })
76
+ ```
77
+
78
+ #### Vercel
79
+
25
80
  ```typescript
26
81
  // astro.config.ts
27
82
  import { defineConfig } from 'astro/config'
28
- import honoActions from '@gnosticdev/hono-actions/integration'
83
+ import vercel from '@astrojs/vercel/serverless'
84
+ import honoActions from '@gnosticdev/hono-actions'
29
85
 
30
86
  export default defineConfig({
87
+ output: 'server',
88
+ adapter: vercel(),
89
+ integrations: [
90
+ honoActions({
91
+ basePath: '/api', // Optional: default is '/api'
92
+ actionsPath: 'src/server/actions.ts' // Optional: custom path to your actions file
93
+ })
94
+ ]
95
+ })
96
+ ```
97
+
98
+ #### Netlify
99
+
100
+ ```typescript
101
+ // astro.config.ts
102
+ import { defineConfig } from 'astro/config'
103
+ import netlify from '@astrojs/netlify'
104
+ import honoActions from '@gnosticdev/hono-actions'
105
+
106
+ export default defineConfig({
107
+ output: 'server',
108
+ adapter: netlify(),
31
109
  integrations: [
32
110
  honoActions({
33
111
  basePath: '/api', // Optional: default is '/api'
@@ -136,22 +214,28 @@ const { message } = await parseResponse(
136
214
  ```typescript
137
215
  // In a client-side script or component
138
216
  import { honoClient } from '@gnosticdev/hono-actions/client'
217
+ import type { DetailedError } from '@gnosticdev/hono-actions/client'
139
218
 
140
219
  // Make requests from the browser
141
220
  const handleSubmit = async (formData: FormData) => {
142
- const response = await honoClient.api.anotherAction.$post({
221
+ const { data, error } = await parseResponse(await honoClient.api.anotherAction.$post({
143
222
  json: {
144
223
  name2: formData.get('name') as string
145
224
  }
225
+ }).catch((err: DetailedError) => {
226
+ console.error('Error:', err)
227
+ return {
228
+ data: null,
229
+ error: err
230
+ }
146
231
  })
147
232
 
148
- if (response.ok) {
149
- const result = await response.json()
150
- console.log('Success:', result)
151
- } else {
152
- const error = await response.text()
153
- console.error('Error:', error)
233
+ if (error){
234
+ throw new Error(`Action failed: ${error.message}`)
154
235
  }
236
+
237
+ // type safe access to the data
238
+ console.log('my name is ', data.name2)
155
239
  }
156
240
  ```
157
241
 
@@ -159,11 +243,11 @@ const handleSubmit = async (formData: FormData) => {
159
243
 
160
244
  This package provides these entry points:
161
245
 
162
- - **`@gnosticdev/hono-actions/actions`**: Action definition utilities (`defineHonoAction`, `HonoActionError`, `HonoEnv`)
246
+ - **`@gnosticdev/hono-actions/actions`**: Action definition utilities (`defineHonoAction`, `HonoActionError`, `HonoEnv` (for cloudflare usage))
163
247
  - Used in your actions file(s)
164
248
  - **`@gnosticdev/hono-actions/client`**: Pre-built Hono client and helpers (`honoClient`, `parseResponse`)
165
249
  - Safe for browser and server environments
166
- - **`@gnosticdev/hono-actions/integration`**: Astro integration
250
+ - **`@gnosticdev/hono-actions`**: Astro integration
167
251
  - Uses Node.js built-ins (fs, path)
168
252
  - Only used in `astro.config.ts`
169
253
 
package/dist/index.js CHANGED
@@ -11,7 +11,11 @@ import { glob } from "tinyglobby";
11
11
 
12
12
  // src/integration-files.ts
13
13
  function generateRouter(opts) {
14
- const { basePath, relativeActionsPath } = opts;
14
+ const { basePath, relativeActionsPath, adapter } = opts;
15
+ let exportedApp = `export default app`;
16
+ if (adapter === "@astrojs/netlify") {
17
+ exportedApp = `export default handle(app)`;
18
+ }
15
19
  return `import type { HonoEnv, MergeActionKeyIntoPath } from '@gnosticdev/hono-actions/actions'
16
20
  import { Hono } from 'hono'
17
21
  import { cors } from 'hono/cors'
@@ -19,12 +23,13 @@ import { showRoutes } from 'hono/dev'
19
23
  import { logger } from 'hono/logger'
20
24
  import { prettyJSON } from 'hono/pretty-json'
21
25
  import type { ExtractSchema, MergeSchemaPath } from 'hono/types'
26
+ ${adapter === "@astrojs/netlify" ? "import { handle } from 'hono/netlify'" : ""}
22
27
 
23
28
  async function buildRouter(){
24
29
  type ActionsWithKeyedPaths = MergeActionKeyIntoPath<typeof honoActions>
25
30
  type ActionSchema = ExtractSchema<ActionsWithKeyedPaths[keyof ActionsWithKeyedPaths]>
26
31
  const { honoActions} = await import('${relativeActionsPath}')
27
- const app = new Hono<HonoEnv, MergeSchemaPath<ActionSchema, \`${basePath}\`>>().basePath('${basePath}')
32
+ const app = new Hono<HonoEnv, MergeSchemaPath<ActionSchema, '${basePath}'>>().basePath('${basePath}')
28
33
 
29
34
  app.use('*', cors(), logger(), prettyJSON())
30
35
 
@@ -41,7 +46,7 @@ const app = await buildRouter()
41
46
  console.log('------- Hono Routes -------')
42
47
  showRoutes(app)
43
48
  console.log('---------------------------')
44
- export default app`;
49
+ ${exportedApp}`;
45
50
  }
46
51
  var generateAstroHandler = (adapter) => {
47
52
  switch (adapter) {
@@ -49,17 +54,49 @@ var generateAstroHandler = (adapter) => {
49
54
  return `
50
55
  /// <reference types="./types.d.ts" />
51
56
  // Generated by Hono Actions Integration
57
+ // adapter: ${adapter}
58
+ import type { APIContext, APIRoute } from 'astro'
59
+ import router from './router.js'
60
+
61
+ const handler: APIRoute<APIContext> = async (ctx) => {
62
+ return router.fetch(
63
+ ctx.request,
64
+ ctx.locals.runtime.env, // required for cloudflare adapter
65
+ ctx.locals.runtime.ctx, // required for cloudflare adapter
66
+ )
67
+ }
68
+
69
+ export { handler as ALL }
70
+ `;
71
+ case "@astrojs/node":
72
+ case "@astrojs/vercel":
73
+ return `
74
+ /// <reference types="./types.d.ts" />
75
+ // Generated by Hono Actions Integration
76
+ // adapter: ${adapter}
52
77
  import type { APIContext, APIRoute } from 'astro'
53
78
  import router from './router.js'
54
79
 
55
80
  const handler: APIRoute<APIContext> = async (ctx) => {
56
81
  return router.fetch(
57
82
  ctx.request,
58
- ctx.locals.runtime.env,
59
- ctx.locals.runtime.ctx,
60
83
  )
61
84
  }
62
85
 
86
+ export { handler as ALL }
87
+ `;
88
+ case "@astrojs/netlify":
89
+ return `
90
+ /// <reference types="./types.d.ts" />
91
+ // Generated by Hono Actions Integration
92
+ // adapter: ${adapter}
93
+ import type { APIContext, APIRoute } from 'astro'
94
+ import netlifyHandler from './router.js'
95
+
96
+ const handler: APIRoute<APIContext> = async (ctx) => {
97
+ return netlifyHandler(ctx.request, ctx)
98
+ }
99
+
63
100
  export { handler as ALL }
64
101
  `;
65
102
  default:
@@ -70,6 +107,7 @@ var generateHonoClient = (port) => `
70
107
  // Generated by Hono Actions Integration
71
108
  import type { HonoRouter } from './router.js'
72
109
  import { hc, parseResponse } from 'hono/client'
110
+ import type { DetailedError } from 'hono/client'
73
111
 
74
112
  function getBaseUrl() {
75
113
  // client side can just use the base path
@@ -86,11 +124,16 @@ function getBaseUrl() {
86
124
  return import.meta.env.SITE ?? ''
87
125
  }
88
126
  export { parseResponse, hc }
127
+ export type { DetailedError }
89
128
  export const honoClient = hc<HonoRouter>(getBaseUrl())
90
129
  `;
91
130
 
92
131
  // src/lib/utils.ts
93
132
  var reservedRoutes = ["_astro", "_actions", "_server_islands"];
133
+ var SUPPORTED_ADAPTERS = ["@astrojs/cloudflare", "@astrojs/node", "@astrojs/netlify", "@astrojs/vercel"];
134
+ function isSupportedAdapter(adapter) {
135
+ return SUPPORTED_ADAPTERS.includes(adapter);
136
+ }
94
137
 
95
138
  // src/integration.ts
96
139
  var optionsSchema = z.object({
@@ -119,10 +162,6 @@ var ACTION_PATTERNS = [
119
162
  "src/hono/index.ts",
120
163
  "src/hono.ts"
121
164
  ];
122
- var SUPPORTED_ADAPTERS = ["@astrojs/cloudflare"];
123
- function isSupportedAdapter(adapter) {
124
- return SUPPORTED_ADAPTERS.includes(adapter);
125
- }
126
165
  var integration_default = defineIntegration({
127
166
  name: "@gnosticdev/hono-actions",
128
167
  optionsSchema,
@@ -164,33 +203,31 @@ ${ACTION_PATTERNS.map((p) => ` - ${p}`).join("\n")}`
164
203
  "router.ts"
165
204
  );
166
205
  const relFromGenToActions = path.relative(codeGenDir.pathname, resolvedActionsPath).split(path.sep).join("/");
167
- const routerContent = generateRouter({
168
- basePath,
169
- relativeActionsPath: relFromGenToActions
170
- });
171
- await fs.writeFile(routerPathAbs, routerContent, "utf-8");
172
- const astroHandlerPathAbs = path.join(
173
- codeGenDir.pathname,
174
- "api.ts"
175
- );
176
206
  const adapter = params.config.adapter?.name;
177
207
  if (!adapter) {
178
208
  logger.error(
179
209
  `No Astro adapter found. Add one of:
180
- - ${SUPPORTED_ADAPTERS.join("\n - ")} to your astro.config.mjs`
210
+ - ${SUPPORTED_ADAPTERS.join("\n - ")} to your astro.config.mjs`
181
211
  );
182
212
  return;
183
213
  }
184
- let astroHandlerContent;
185
- if (isSupportedAdapter(adapter)) {
186
- astroHandlerContent = generateAstroHandler(adapter);
187
- } else {
188
- throw new Error(`Unsupported adapter: ${adapter}`, {
189
- cause: `Only ${SUPPORTED_ADAPTERS.join(
190
- ", "
191
- )} are supported for now`
192
- });
214
+ if (!isSupportedAdapter(adapter)) {
215
+ logger.error(
216
+ `Unsupported adapter: ${adapter}. Only ${SUPPORTED_ADAPTERS.join("\n - ")} are supported`
217
+ );
218
+ return;
193
219
  }
220
+ const routerContent = generateRouter({
221
+ basePath,
222
+ relativeActionsPath: relFromGenToActions,
223
+ adapter
224
+ });
225
+ await fs.writeFile(routerPathAbs, routerContent, "utf-8");
226
+ const astroHandlerPathAbs = path.join(
227
+ codeGenDir.pathname,
228
+ "api.ts"
229
+ );
230
+ const astroHandlerContent = generateAstroHandler(adapter);
194
231
  await fs.writeFile(
195
232
  astroHandlerPathAbs,
196
233
  astroHandlerContent,
@@ -241,22 +278,28 @@ export {}
241
278
  declare module '@gnosticdev/hono-actions/client' {
242
279
  export const honoClient: typeof import('./client').honoClient
243
280
  export const parseResponse: typeof import('./client').parseResponse
281
+ exoprt type DetailedError = import('./client').DetailedError
244
282
  }
245
283
  `;
246
- if (!config.adapter?.name) {
284
+ const adapter = config.adapter?.name;
285
+ if (!adapter) {
247
286
  logger.warn("No adapter found...");
248
287
  return;
249
288
  }
250
- if (config.adapter.name !== "@astrojs/cloudflare") {
251
- logger.warn("Unsupported adapter...");
289
+ if (!isSupportedAdapter(adapter)) {
290
+ logger.warn(
291
+ `Unsupported adapter: ${adapter}. Only ${SUPPORTED_ADAPTERS.join("\n - ")} are supported`
292
+ );
252
293
  return;
253
294
  }
254
- clientTypes += `
295
+ if (adapter === "@astrojs/cloudflare") {
296
+ clientTypes += `
255
297
  type Runtime = import('@astrojs/cloudflare').Runtime<Env>
256
- declare namespace App {
257
- interface Locals extends Runtime {}
258
- }
259
- `;
298
+ declare namespace App {
299
+ interface Locals extends Runtime {}
300
+ }
301
+ `;
302
+ }
260
303
  injectTypes({
261
304
  filename: "types.d.ts",
262
305
  content: clientTypes
package/package.json CHANGED
@@ -14,7 +14,12 @@
14
14
  "devDependencies": {
15
15
  "tsup": "^8.5.0",
16
16
  "typescript": "catalog:",
17
- "vitest": "catalog:"
17
+ "vitest": "catalog:",
18
+ "@astrojs/cloudflare": "catalog:",
19
+ "@astrojs/netlify": "catalog:",
20
+ "@astrojs/node": "catalog:",
21
+ "@astrojs/vercel": "catalog:",
22
+ "astro": "catalog:"
18
23
  },
19
24
  "exports": {
20
25
  ".": {
@@ -56,5 +61,5 @@
56
61
  },
57
62
  "type": "module",
58
63
  "types": "./dist/index.d.ts",
59
- "version": "1.2.5"
64
+ "version": "2.0.1"
60
65
  }