@k8ordo/form 0.1.0 → 1.0.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/README.md +31 -17
- package/dist/async-check.d.mts +3 -4
- package/dist/derive/attributes.d.mts +43 -8
- package/dist/derive/attributes.mjs +74 -19
- package/dist/derive/form-fields.d.mts +6 -5
- package/dist/derive/form-fields.mjs +80 -23
- package/dist/derive/messages.d.mts +2 -3
- package/dist/derive/messages.mjs +8 -11
- package/dist/hidden-value.d.mts +4 -8
- package/dist/hidden-value.mjs +8 -9
- package/dist/index.d.mts +4 -4
- package/dist/parse/parse-form.d.mts +3 -4
- package/dist/parse/parse-form.mjs +25 -4
- package/dist/parse/paths.d.mts +4 -5
- package/dist/paths.d.mts +34 -5
- package/dist/rules/define-form.d.mts +4 -5
- package/dist/rules/rules.d.mts +26 -11
- package/dist/rules/rules.mjs +14 -7
- package/dist/schema/object-schema.d.mts +7 -6
- package/dist/schema/walk.d.mts +16 -8
- package/dist/schema/walk.mjs +77 -10
- package/dist/server.d.mts +3 -3
- package/dist/types.d.mts +38 -17
- package/dist/use-form.d.mts +32 -12
- package/dist/use-form.mjs +136 -22
- package/docs/GUIDE.md +469 -70
- package/docs/llms.txt +62 -11
- package/package.json +23 -16
package/docs/llms.txt
CHANGED
|
@@ -16,17 +16,68 @@ When installed via npm, these docs live under `node_modules/@k8ordo/form/docs/`.
|
|
|
16
16
|
what the package guarantees, what it does not do yet, and how it pairs with
|
|
17
17
|
`@k8ordo/ui` without depending on it.
|
|
18
18
|
|
|
19
|
+
## Not supported
|
|
20
|
+
|
|
21
|
+
`z.array(z.file())` derives repeated single-file rows, not one `multiple` input.
|
|
22
|
+
Constraints behind a `.transform()` (or a `.pipe()` whose output JSON Schema
|
|
23
|
+
cannot describe) are not derived: the field becomes a bare text input and is
|
|
24
|
+
listed in `dropped`. There is no input masking. A schema no control could
|
|
25
|
+
satisfy is refused at derive time rather than left to fail on every
|
|
26
|
+
submission: `z.number()` and `z.literal(1)` (use `z.coerce.number()`),
|
|
27
|
+
`z.date()`, and `z.bigint()`; so is a `z.stringbool().default(true)` checkbox,
|
|
28
|
+
which could never submit `false`. A `z.custom()` that rejects strings cannot be refused at derive
|
|
29
|
+
time: it derives as a text input, is listed in `dropped`, and fails on the
|
|
30
|
+
server. Of `.refine()` / `.superRefine()` checks, `dropped` counts only the ones
|
|
31
|
+
on the schema as a whole, not those on a single field, a nested object, or a
|
|
32
|
+
row. Rules cannot target fields inside repeated rows.
|
|
33
|
+
|
|
19
34
|
## Entry points
|
|
20
35
|
|
|
21
|
-
- `@k8ordo/form` — client. `useForm(fields, state)` takes the whole
|
|
22
|
-
`formFields` result and
|
|
36
|
+
- `@k8ordo/form` — client. `useForm(fields, state?)` takes the whole
|
|
37
|
+
`formFields` result (and the action's state, when there is one) and
|
|
38
|
+
returns props for the `<form>` element — which also hear a reset, React's
|
|
39
|
+
own after every form action included — a
|
|
23
40
|
`field(path)` accessor giving input attributes, the current error, and
|
|
24
|
-
`invalid` / `required`, an `array(path)` accessor for repeated rows
|
|
25
|
-
`
|
|
26
|
-
`
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
41
|
+
`invalid` / `required`, an `array(path)` accessor for repeated rows (its
|
|
42
|
+
`error` is the array's own message, with `errorProps` to let focus land on
|
|
43
|
+
it), `formError` (the message no field owns, with the props that let focus
|
|
44
|
+
land on it), and `isDirty`. On submit it checks every field — untouched ones
|
|
45
|
+
and the rules included — and stops a failing submission, a GET form with no
|
|
46
|
+
action as much as one with (a `formNoValidate` button skips the check);
|
|
47
|
+
after a submission stopped here or failed on the server, focus moves to the
|
|
48
|
+
first failure in document order, and a first failure the hook has no
|
|
49
|
+
message for (a control outside the schema) is reported by the browser
|
|
50
|
+
itself through `reportValidity()`. Also `useAsyncCheck` for server-answered
|
|
51
|
+
per-field checks and `HiddenValue` for components that render no input of
|
|
52
|
+
their own. The types
|
|
53
|
+
of the `formFields` result (`FormFields`, `DerivedField`, `DerivedArray`,
|
|
54
|
+
`DroppedCheck`, `FieldInput`, `StringCheckboxInput`, `ValidityFlag`, `Rule`,
|
|
55
|
+
`DerivedRule`, `RuleMessage`) are exported here as
|
|
56
|
+
well, since the result crosses to the client as a prop, and so are
|
|
57
|
+
`FormState` and the return types (`UseFormReturn`, `FieldView`, `ArrayView`,
|
|
58
|
+
`RowView`, `FormErrorView`, `AsyncCheck`).
|
|
59
|
+
- `@k8ordo/form/server` — server. `formFields(schemaOrDefinition)` derives
|
|
60
|
+
serializable fields plus a `dropped` list of checks HTML cannot express
|
|
61
|
+
(outside production the list is also logged once per schema with
|
|
62
|
+
`console.warn`), and throws on a schema no form can represent (including a
|
|
63
|
+
leaf that could never accept a submitted string, such as `z.number()`);
|
|
64
|
+
`parseForm(schemaOrDefinition, formData)` validates a submission and returns
|
|
65
|
+
typed data or per-field errors with the submitted values. An untouched
|
|
66
|
+
control reaches the schema as what it actually submits: `''` for text,
|
|
67
|
+
`false` for a checkbox, and nothing at all for a number, a
|
|
68
|
+
`z.coerce.bigint()`, a file, a choice (a radio group with none selected, a
|
|
69
|
+
`<select>` on a `value=""` placeholder), or a `z.stringbool()` checkbox —
|
|
70
|
+
which, checked, submits the `value` derived from the schema's own spelling of
|
|
71
|
+
`true`, the same string `@k8ordo/state` writes into a URL — so `z.coerce.number()` cannot read a
|
|
72
|
+
blank field as 0 and `z.enum([…]).optional()` can be left empty. A checkbox
|
|
73
|
+
group always echoes as an array.
|
|
74
|
+
`defineForm(schema, rules)` attaches cross-field rules (`sameAs`,
|
|
75
|
+
`minChecked`, `requiredWhen`) typed against the schema's paths; a rule's
|
|
76
|
+
message is a string or a function called when the rule is reported
|
|
77
|
+
(`formFields` calls it while deriving, so a rule crosses to the client as a
|
|
78
|
+
`DerivedRule` with the text in it; `parseForm` calls it when the rule
|
|
79
|
+
breaks), which keeps a localized definition at module scope. Types:
|
|
80
|
+
`ParseResult`, `FormDefinition`, `Rule`, `RuleMessage`, `FormState`, and the
|
|
81
|
+
`formFields` result's `FormFields`, `DerivedField`, `DerivedArray`,
|
|
82
|
+
`DerivedRule`, `DroppedCheck`, `FieldInput`, `StringCheckboxInput`,
|
|
83
|
+
`ValidityFlag`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@k8ordo/form",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Derive HTML constraint attributes and per-field errors from one zod schema. Works without JavaScript.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"constraint-validation",
|
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
"zod"
|
|
12
12
|
],
|
|
13
13
|
"homepage": "https://ordo.k8o.me",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/k35o/k8ordo/issues"
|
|
16
|
+
},
|
|
14
17
|
"license": "MIT",
|
|
15
18
|
"author": "k8o <kosakanoki@gmail.com>",
|
|
16
19
|
"repository": {
|
|
@@ -39,23 +42,26 @@
|
|
|
39
42
|
"registry": "https://registry.npmjs.org"
|
|
40
43
|
},
|
|
41
44
|
"devDependencies": {
|
|
42
|
-
"@
|
|
43
|
-
"@
|
|
44
|
-
"@
|
|
45
|
-
"react": "
|
|
46
|
-
"
|
|
45
|
+
"@arethetypeswrong/cli": "0.18.5",
|
|
46
|
+
"@k8ordo/state": "^1.0.0",
|
|
47
|
+
"@types/react": "19.3.0",
|
|
48
|
+
"@vitejs/plugin-react": "6.1.2",
|
|
49
|
+
"@vitest/browser-playwright": "5.0.3",
|
|
50
|
+
"publint": "0.3.25",
|
|
51
|
+
"react": "19.3.0",
|
|
52
|
+
"react-dom": "19.3.0",
|
|
47
53
|
"typescript": "7.0.2",
|
|
48
|
-
"vite": "
|
|
49
|
-
"vite-plus": "
|
|
50
|
-
"vitest": "
|
|
51
|
-
"vitest-browser-react": "2.
|
|
52
|
-
"zod": "4.
|
|
54
|
+
"vite": "npm:@voidzero-dev/vite-plus-core@1.1.0",
|
|
55
|
+
"vite-plus": "1.1.0",
|
|
56
|
+
"vitest": "5.0.3",
|
|
57
|
+
"vitest-browser-react": "2.3.0",
|
|
58
|
+
"zod": "4.6.5"
|
|
53
59
|
},
|
|
54
60
|
"peerDependencies": {
|
|
55
|
-
"@types/react": ">=19.
|
|
56
|
-
"react": ">=19.
|
|
57
|
-
"react-dom": ">=19.
|
|
58
|
-
"typescript": ">=7.0.
|
|
61
|
+
"@types/react": ">=19.3.0",
|
|
62
|
+
"react": ">=19.3.0",
|
|
63
|
+
"react-dom": ">=19.3.0",
|
|
64
|
+
"typescript": ">=7.0.0",
|
|
59
65
|
"zod": "^4.4.3"
|
|
60
66
|
},
|
|
61
67
|
"peerDependenciesMeta": {
|
|
@@ -71,6 +77,7 @@
|
|
|
71
77
|
"test": "vp test",
|
|
72
78
|
"typecheck": "tsc --noEmit",
|
|
73
79
|
"check": "vp check",
|
|
74
|
-
"check:write": "vp check --fix"
|
|
80
|
+
"check:write": "vp check --fix",
|
|
81
|
+
"check:package": "publint && attw --pack . --profile esm-only"
|
|
75
82
|
}
|
|
76
83
|
}
|