@louise-toolkit/astro 0.4.0 → 0.5.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.
@@ -1,5 +1,5 @@
1
1
  import { z } from "astro/zod";
2
- import type { FormConfig } from "louise-toolkit/forms";
2
+ import { type FormConfig } from "louise-toolkit/forms";
3
3
  /**
4
4
  * Build a Zod schema from a `defineForm` definition's fields—the form is the
5
5
  * single source of truth for its Astro Action `input`, so the handler receives a
@@ -19,18 +19,30 @@
19
19
  // Zod builder from `astro/zod` (an optional peer), so the framework-agnostic
20
20
  // core never takes a Zod dependency.
21
21
  import { z } from "astro/zod";
22
+ import { coerceFormValue } from "louise-toolkit/forms";
23
+ /** Coerce the way `formRoute` does, first, so an Action accepts what the route
24
+ * accepts: a URL without a scheme, a number with the locale's separators. A
25
+ * blank value becomes `undefined`, which an optional field allows. */
26
+ function forgiving(field, locale, schema) {
27
+ return z.preprocess((raw) => coerceFormValue(field, raw, { locale }) ?? undefined, schema);
28
+ }
22
29
  /** Map one form field to its Zod type, including the type's built-in format
23
30
  * check (email/url) and coercion (number/date/checkbox). */
24
- function formFieldToZod(field) {
31
+ function formFieldToZod(field, locale) {
25
32
  const required = field.required ?? false;
26
33
  switch (field.type) {
27
34
  case "email":
28
35
  return required ? z.email() : z.email().optional();
29
- case "url":
30
- return required ? z.url() : z.url().optional();
31
- case "number":
32
- // Form values arrive as strings; `coerce` turns "5" into 5.
33
- return required ? z.coerce.number() : z.coerce.number().optional();
36
+ case "url": {
37
+ const url = z.url({ error: "Enter a web address, like example.com." });
38
+ return forgiving(field, locale, required ? url : url.optional());
39
+ }
40
+ case "number": {
41
+ // Form values arrive as strings; the shared coercion turns "1,200" into
42
+ // 1200 under the form's locale, and leaves anything else a string.
43
+ const number = z.number({ error: "Enter a number, like 1200." });
44
+ return forgiving(field, locale, required ? number : number.optional());
45
+ }
34
46
  case "date":
35
47
  // Accepts an ISO string, an epoch number, or a Date.
36
48
  return required ? z.coerce.date() : z.coerce.date().optional();
@@ -69,7 +81,7 @@ function formFieldToZod(field) {
69
81
  export function formToAstroSchema(form) {
70
82
  const shape = {};
71
83
  for (const [key, field] of Object.entries(form.fields)) {
72
- shape[key] = formFieldToZod(field);
84
+ shape[key] = formFieldToZod(field, form.locale);
73
85
  }
74
86
  return z.object(shape);
75
87
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@louise-toolkit/astro",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Astro adapter for louise-toolkit: middleware, Actions, content-layer loaders, and the forms schema bridge.",
5
5
  "keywords": [
6
6
  "astro",
@@ -35,7 +35,7 @@
35
35
  "access": "public"
36
36
  },
37
37
  "dependencies": {
38
- "louise-toolkit": "0.35.0"
38
+ "louise-toolkit": "0.36.0"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@cloudflare/workers-types": "^5.20260829.1",