formstand 0.9.0 → 0.11.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/CHANGELOG.md +622 -1
- package/README.md +17 -13
- package/dist/index.cjs +485 -271
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +117 -57
- package/dist/index.d.ts +117 -57
- package/dist/index.js +486 -274
- package/dist/index.js.map +1 -1
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -85,10 +85,10 @@ const form = createForm(schema, {
|
|
|
85
85
|
| `setError(path, errors)` / `setErrors(map)` / `clearErrors(path?)` | the app-owned **server error channel** (`state.serverErrors`) — validation never touches it; `setError` accepts a single string or an array; `clearErrors(path)` clears both channels at the path and its descendants (`clearErrors("")` clears just the root entry; `clearErrors()` clears everything); `setErrors` replaces the whole server channel |
|
|
86
86
|
| `setMode` / `setReValidateMode` | switch modes at runtime |
|
|
87
87
|
| `reset(nextInitial?, options?)` | reset to initial; optional partial overrides (shallow-merged for record roots, replaced wholesale otherwise) and `{ keepErrors, keepTouched, keepSubmitCount }` (no `keepDirty` — dirtiness derives from values vs initial, and reset makes them equal) |
|
|
88
|
-
| `resetField(path)` | reset one field to its initial value, clearing its (and descendants') error/touched state (dirtiness clears by definition — the value now equals initial) |
|
|
88
|
+
| `resetField(path)` | reset one field to its initial value, clearing its (and descendants') error/touched state (dirtiness clears by definition — the value now equals initial); a path with no initial counterpart (an appended array row) keeps its value and only clears field state |
|
|
89
89
|
| `adoptValues(values)` | mid-session rebase: replaces `values` + `initialValues` and clears `errors` and in-flight validation flags (`isValidating`/`isValidatingForm` — the rebase disowns in-flight passes), but **preserves** interaction state (`touched`, `submitCount`, `isSubmitting`, `mode`). Use `reset()` for a full wipe |
|
|
90
90
|
| `updateState(updater)` | atomic multi-field patch; `errors` is derived from `schemaErrors`/`serverErrors`, so patch the channels — the patch type omits `errors` entirely, and a plain-JS `errors` patch is warned about and ignored |
|
|
91
|
-
| `validate()` / `validateField(path)` / `validateFields(paths)` | sync validation; on an async schema
|
|
91
|
+
| `validate()` / `validateField(path)` / `validateFields(paths)` | sync validation; on an async schema all three transparently start the async pass instead and return `{ kind: "pending", promise }` |
|
|
92
92
|
| `validateAsync()` / `validateFieldAsync(path)` / `validateFieldsAsync(paths)` | async; supports `async .refine` |
|
|
93
93
|
| `submit(onValid, onInvalid?, { force? })` → `Promise<SubmitResult>` | full submit flow; resolves `{ kind: "valid", data }`, `{ kind: "invalid", errors }` (errored fields are also marked touched), `{ kind: "skipped" }` when another submit is in flight, or `{ kind: "error", error }` when `onValid` throws/rejects — submit resolves instead of rejecting, so `handleSubmit` never produces an unhandled rejection from an `onValid` throw |
|
|
94
94
|
| `handleSubmit(onValid, onInvalid?)(event?)` | event handler wrapper that calls `preventDefault` |
|
|
@@ -108,7 +108,7 @@ const form = createForm(schema, {
|
|
|
108
108
|
- `mode: "all"`: validate on every change and blur.
|
|
109
109
|
- `reValidateMode` (default `"onChange"`) kicks in once `submitCount > 0`.
|
|
110
110
|
|
|
111
|
-
For schemas with `async .refine`, sync `validate()` / `validateField()` / `validateFields()`
|
|
111
|
+
For schemas with `async .refine`, sync `validate()` / `validateField()` / `validateFields()` don't throw: they detect the async requirement, start the async pass themselves, and hand you the in-flight work as `{ kind: "pending", promise }` (never a bare truthy `Promise` — a boolean gate can't silently pass). The requirement is also remembered per scope, so later sync calls skip straight to the async pass instead of re-paying a doomed sync parse per keystroke. If the *field being validated* is itself sync, `validateField` still settles synchronously even when other fields carry async refines — field validation parses just that field's subschema when it can (see below).
|
|
112
112
|
|
|
113
113
|
## React hooks
|
|
114
114
|
|
|
@@ -165,12 +165,13 @@ users.insert(i, item);
|
|
|
165
165
|
users.error; // array-level error (e.g. min(1))
|
|
166
166
|
```
|
|
167
167
|
|
|
168
|
-
`fields` IDs are
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
(
|
|
173
|
-
|
|
168
|
+
`fields` IDs are shared per `(form, path)` — every hook on the same array sees
|
|
169
|
+
the same ids. Array ops (hook-issued or imperative `form.arrayMove` etc.)
|
|
170
|
+
record their exact index mapping and id derivation replays it, so even rows
|
|
171
|
+
with equal values keep the right ids through ops; whole-array writes
|
|
172
|
+
(`setValue`, `restore`) reconcile by item identity with a positional fallback,
|
|
173
|
+
so editing a field keeps its row's `id` (the row updates instead of
|
|
174
|
+
remounting). A genuinely new item gets a fresh `id`.
|
|
174
175
|
|
|
175
176
|
### `useFormSelector(form, selector)` / `useFormSelectorShallow(form, selector)`
|
|
176
177
|
|
|
@@ -347,9 +348,12 @@ decided by the **existing container**: arrays take numeric segments as indices,
|
|
|
347
348
|
plain objects take any segment as a string key — so a `z.record` keyed `"0"`
|
|
348
349
|
reads and writes the record key instead of silently becoming an array. Only
|
|
349
350
|
when the container doesn't exist yet does the segment type pick what's created
|
|
350
|
-
(numeric → array, otherwise object).
|
|
351
|
-
not addressable
|
|
352
|
-
index must not allocate gigabytes)
|
|
351
|
+
(numeric → array, otherwise object). Three limitations: keys containing `.`
|
|
352
|
+
are not addressable; array writes beyond index 100 000 are refused (a typo'd
|
|
353
|
+
index must not allocate gigabytes); and the typed `FieldPath` union stops at
|
|
354
|
+
9 segments by default — the runtime walks any depth, and `createForm`'s
|
|
355
|
+
type-level `pathDepth` option widens the union per form (a deliberate
|
|
356
|
+
compile-time trade; see the [typed paths guide](https://scrumrot.github.io/formstand/guide/typed-paths)).
|
|
353
357
|
|
|
354
358
|
## Subscriptions (non-React)
|
|
355
359
|
|
|
@@ -408,7 +412,7 @@ Note `submit` still proceeds when the schema is valid even if server errors are
|
|
|
408
412
|
|
|
409
413
|
## Field arrays with nested arrays
|
|
410
414
|
|
|
411
|
-
Both work — `arrayPush("users.0.tags", tag)` mutates the inner array; outer reorders correctly re-key meta for all nested paths. `
|
|
415
|
+
Both work — `arrayPush("users.0.tags", tag)` mutates the inner array; outer reorders correctly re-key meta for all nested paths. Inner row ids live per `(form, path)` and reconcile by value when the outer index re-seats a different item at the path — pass a `key` from the outer `fields` so each row's whole subtree moves with its item.
|
|
412
416
|
|
|
413
417
|
## Optimistic UI
|
|
414
418
|
|