nuxt-safe-action 0.2.1 → 0.4.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/dist/module.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "compatibility": {
5
5
  "nuxt": ">=3.0.0"
6
6
  },
7
- "version": "0.2.1",
7
+ "version": "0.4.0",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "1.0.2",
10
10
  "unbuild": "unknown"
package/dist/module.mjs CHANGED
@@ -40,7 +40,7 @@ const module$1 = defineNuxtModule({
40
40
  return;
41
41
  }
42
42
  logger.info(
43
- `Found ${actionFiles.length} action file(s): ${actionFiles.map((a) => a.name).join(", ")}`
43
+ `Found ${actionFiles.length} action file(s): ${actionFiles.map((a) => `${a.method.toUpperCase()} ${a.name}`).join(", ")}`
44
44
  );
45
45
  nitroConfig.virtual = nitroConfig.virtual || {};
46
46
  nitroConfig.handlers = nitroConfig.handlers || [];
@@ -49,7 +49,7 @@ const module$1 = defineNuxtModule({
49
49
  nitroConfig.virtual[virtualKey] = generateHandlerCode(action);
50
50
  nitroConfig.handlers.push({
51
51
  route: `/api/_actions/${action.name}`,
52
- method: "post",
52
+ method: action.method,
53
53
  handler: virtualKey
54
54
  });
55
55
  }
@@ -73,14 +73,15 @@ const module$1 = defineNuxtModule({
73
73
  ];
74
74
  for (const action of actionFiles) {
75
75
  const exportName = toCamelCase(action.name);
76
- const relativePath = posix.join("../..", "server", actionsDir, action.name);
76
+ const fileBasename = action.method === "post" ? action.name : `${action.name}.${action.method}`;
77
+ const relativePath = posix.join("../..", "server", actionsDir, fileBasename);
77
78
  lines.push(`import type _action_${exportName} from '${relativePath}'`);
78
79
  }
79
80
  lines.push("");
80
81
  for (const action of actionFiles) {
81
82
  const exportName = toCamelCase(action.name);
82
83
  lines.push(
83
- `export const ${exportName}: SafeActionReference<(typeof _action_${exportName})['_types']['input'], (typeof _action_${exportName})['_types']['output'], (typeof _action_${exportName})['_types']['serverError']> = Object.freeze({ __safeActionPath: '${action.name}' }) as any`
84
+ `export const ${exportName}: SafeActionReference<(typeof _action_${exportName})['_types']['input'], (typeof _action_${exportName})['_types']['output'], (typeof _action_${exportName})['_types']['serverError']> = Object.freeze({ __safeActionPath: '${action.name}', __safeActionMethod: '${action.method.toUpperCase()}' }) as any`
84
85
  );
85
86
  }
86
87
  return lines.join("\n") + "\n";
@@ -97,6 +98,17 @@ const module$1 = defineNuxtModule({
97
98
  nuxt.options.alias["#safe-action/actions"] = join(nuxt.options.buildDir, "safe-action/actions");
98
99
  }
99
100
  });
101
+ const HTTP_METHODS = ["get", "post", "put", "patch", "delete"];
102
+ function parseMethodSuffix(filename) {
103
+ const dotIndex = filename.lastIndexOf(".");
104
+ if (dotIndex > 0) {
105
+ const suffix = filename.slice(dotIndex + 1).toLowerCase();
106
+ if (HTTP_METHODS.includes(suffix)) {
107
+ return { name: filename.slice(0, dotIndex), method: suffix };
108
+ }
109
+ }
110
+ return { name: filename, method: "post" };
111
+ }
100
112
  function scanActionFiles(dir, prefix = "") {
101
113
  const results = [];
102
114
  if (!existsSync(dir)) return results;
@@ -106,13 +118,27 @@ function scanActionFiles(dir, prefix = "") {
106
118
  if (entry.isDirectory()) {
107
119
  results.push(...scanActionFiles(fullPath, prefix ? `${prefix}/${entry.name}` : entry.name));
108
120
  } else if (entry.isFile() && entry.name.endsWith(".ts") && !entry.name.endsWith(".d.ts") && entry.name !== "index.ts") {
109
- const name = prefix ? `${prefix}/${basename(entry.name, ".ts")}` : basename(entry.name, ".ts");
110
- results.push({ name, filePath: fullPath });
121
+ const raw = basename(entry.name, ".ts");
122
+ const { name: parsedName, method } = parseMethodSuffix(raw);
123
+ const name = prefix ? `${prefix}/${parsedName}` : parsedName;
124
+ results.push({ name, filePath: fullPath, method });
111
125
  }
112
126
  }
113
127
  return results;
114
128
  }
115
129
  function generateHandlerCode(action) {
130
+ if (action.method === "get") {
131
+ return `
132
+ import { defineEventHandler, getQuery } from 'h3'
133
+ import action from '${action.filePath}'
134
+
135
+ export default defineEventHandler(async (event) => {
136
+ const query = getQuery(event)
137
+ const rawInput = query.input ? JSON.parse(query.input) : undefined
138
+ return action._execute(rawInput, event)
139
+ })
140
+ `;
141
+ }
116
142
  return `
117
143
  import { defineEventHandler, readBody } from 'h3'
118
144
  import action from '${action.filePath}'
@@ -1,6 +1,7 @@
1
1
  import { ref, computed, readonly } from "vue";
2
2
  export function useAction(action, callbacks) {
3
3
  const actionPath = action.__safeActionPath;
4
+ const actionMethod = action.__safeActionMethod || "POST";
4
5
  const data = ref();
5
6
  const serverError = ref();
6
7
  const validationErrors = ref();
@@ -11,13 +12,9 @@ export function useAction(action, callbacks) {
11
12
  status.value = "executing";
12
13
  callbacks?.onExecute?.({ input });
13
14
  try {
14
- const result = await $fetch(
15
- `/api/_actions/${actionPath}`,
16
- {
17
- method: "POST",
18
- body: input
19
- }
20
- );
15
+ const fetchOptions = actionMethod === "GET" ? { method: "GET" } : { method: actionMethod, body: input };
16
+ const url = actionMethod === "GET" && input != null ? `/api/_actions/${actionPath}?input=${encodeURIComponent(JSON.stringify(input))}` : `/api/_actions/${actionPath}`;
17
+ const result = await $fetch(url, fetchOptions);
21
18
  if (result.data !== void 0) {
22
19
  data.value = result.data;
23
20
  status.value = "hasSucceeded";
@@ -21,7 +21,6 @@ export interface SafeAction<TInput = unknown, TOutput = unknown, TServerError =
21
21
  output: TOutput;
22
22
  serverError: TServerError;
23
23
  };
24
- /** used by the generated Nitro handler */
25
24
  _execute: (rawInput: unknown, event: H3Event) => Promise<ActionResult<TOutput, TServerError>>;
26
25
  }
27
26
  export interface SafeActionReference<TInput = unknown, TOutput = unknown, TServerError = string> {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nuxt-safe-action",
3
- "version": "0.2.1",
3
+ "version": "0.4.0",
4
4
  "description": "Type-safe and validated server actions for Nuxt",
5
5
  "repository": {
6
6
  "type": "git",
@@ -80,7 +80,7 @@
80
80
  "zod": "^3.24.0"
81
81
  },
82
82
  "peerDependencies": {
83
- "zod": "^3.20.0"
83
+ "zod": "^3.20.0 || ^4.0.0"
84
84
  },
85
85
  "peerDependenciesMeta": {
86
86
  "zod": {