@firtoz/router-toolkit 5.2.0 → 5.3.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 (2) hide show
  1. package/package.json +7 -7
  2. package/src/formAction.ts +27 -12
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@firtoz/router-toolkit",
3
- "version": "5.2.0",
3
+ "version": "5.3.1",
4
4
  "description": "Type-safe React Router 7 framework mode helpers with enhanced fetching, form submission, and state management",
5
5
  "main": "./src/index.ts",
6
6
  "module": "./src/index.ts",
@@ -56,9 +56,9 @@
56
56
  },
57
57
  "peerDependencies": {
58
58
  "@firtoz/maybe-error": "^1.5.1",
59
- "react": "^19.2.3",
60
- "react-router": "^7.12.0",
61
- "zod": "^4.3.5"
59
+ "react": "^19.2.4",
60
+ "react-router": "^7.13.0",
61
+ "zod": "^4.3.6"
62
62
  },
63
63
  "engines": {
64
64
  "node": ">=18.0.0"
@@ -72,8 +72,8 @@
72
72
  "devDependencies": {
73
73
  "@testing-library/react": "^16.3.1",
74
74
  "@types/jsdom": "^27.0.0",
75
- "@types/react": "^19.2.8",
76
- "bun-types": "^1.3.6",
77
- "jsdom": "^27.4.0"
75
+ "@types/react": "^19.2.13",
76
+ "bun-types": "^1.3.8",
77
+ "jsdom": "^28.0.0"
78
78
  }
79
79
  }
package/src/formAction.ts CHANGED
@@ -1,8 +1,12 @@
1
1
  /**
2
2
  * @fileoverview Type-safe form action utility for React Router 7
3
3
  *
4
- * This module provides a wrapper for React Router actions that handles form data validation
5
- * using Zod schemas and provides structured error handling with MaybeError.
4
+ * This module provides a wrapper for React Router actions that handles form data and JSON
5
+ * validation using Zod schemas and provides structured error handling with MaybeError.
6
+ *
7
+ * Supports both:
8
+ * - **JSON requests** (`Content-Type: application/json`) - parsed with `request.json()` and validated directly
9
+ * - **FormData requests** (`multipart/form-data` or `application/x-www-form-urlencoded`) - parsed with `request.formData()` and validated with zod-form-data
6
10
  *
7
11
  * ## Overview
8
12
  *
@@ -230,13 +234,18 @@ export interface FormActionConfig<
230
234
  }
231
235
 
232
236
  /**
233
- * Creates a type-safe form action handler that validates form data and provides structured error handling.
237
+ * Creates a type-safe form action handler that validates form data or JSON and provides structured error handling.
234
238
  *
235
239
  * This function wraps a React Router action to:
236
- * 1. Parse and validate form data using a Zod schema
237
- * 2. Call the provided handler with validated data
238
- * 3. Return structured errors for validation failures, handler errors, or unknown errors
239
- * 4. Preserve React Router Response objects (redirects, etc.) by re-throwing them
240
+ * 1. Detect content type (JSON vs FormData) from the request headers
241
+ * 2. Parse and validate the request body using a Zod schema
242
+ * 3. Call the provided handler with validated data
243
+ * 4. Return structured errors for validation failures, handler errors, or unknown errors
244
+ * 5. Preserve React Router Response objects (redirects, etc.) by re-throwing them
245
+ *
246
+ * **Content-Type handling:**
247
+ * - `application/json`: Uses `request.json()` and validates directly with the schema
248
+ * - `multipart/form-data` or `application/x-www-form-urlencoded`: Uses `request.formData()` and validates with zod-form-data
240
249
  *
241
250
  * @template TSchema - The Zod schema type for form validation
242
251
  * @template TResult - The success result type from the handler (defaults to undefined)
@@ -303,19 +312,25 @@ export const formAction = <
303
312
  args: ActionArgs,
304
313
  ): Promise<MaybeError<TResult, FormActionError<TError, TSchema>>> => {
305
314
  try {
306
- const rawFormData = await args.request.formData();
307
- const formData = await zfd.formData(schema).safeParseAsync(rawFormData);
315
+ const contentType = args.request.headers.get("Content-Type");
316
+ const isJson = contentType?.includes("application/json") ?? false;
317
+
318
+ const parseResult = isJson
319
+ ? await schema.safeParseAsync(await args.request.json())
320
+ : await zfd
321
+ .formData(schema)
322
+ .safeParseAsync(await args.request.formData());
308
323
 
309
- if (!formData.success) {
324
+ if (!parseResult.success) {
310
325
  return fail({
311
326
  type: "validation" as const,
312
327
  error: z.treeifyError<z.infer<TSchema>>(
313
- formData.error as z.core.$ZodError<z.infer<TSchema>>,
328
+ parseResult.error as z.core.$ZodError<z.infer<TSchema>>,
314
329
  ),
315
330
  });
316
331
  }
317
332
 
318
- const handlerResult = await handler(args, formData.data);
333
+ const handlerResult = await handler(args, parseResult.data);
319
334
  if (!handlerResult.success) {
320
335
  return fail({
321
336
  type: "handler" as const,