@thomasfosterau/effect-forms 0.3.1 → 0.3.2

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/index.d.ts CHANGED
@@ -945,6 +945,5 @@ interface ResolvedFieldTrigger<A, R = never> {
945
945
  * field-trigger dispatch runs.
946
946
  */
947
947
  declare function resolveFieldTrigger<A, R = never>(validators: FieldValidators<A, R> | undefined, trigger: FieldTrigger): ResolvedFieldTrigger<A, R>;
948
- //#endregion
949
948
  export { Coerce_d_exports as Coerce, Command_d_exports as Command, Constraints_d_exports as Constraints, Errors_d_exports as Errors, Field_d_exports as Field, Form_d_exports as Form, FormBuilder_d_exports as FormBuilder, FormData_d_exports as FormData, Issues_d_exports as Issues, Mode_d_exports as Mode, Path_d_exports as Path, Result_d_exports as Result, State_d_exports as State, Validate_d_exports as Validate };
950
949
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
1
+ import { t as __exportAll } from "./rolldown-runtime-D8jQpDG7.js";
2
2
  import * as Arr from "effect/Array";
3
3
  import * as Exit from "effect/Exit";
4
4
  import * as Match from "effect/Match";
@@ -11,7 +11,7 @@ import * as Option from "effect/Option";
11
11
  import * as Effect from "effect/Effect";
12
12
  import * as SchemaIssue from "effect/SchemaIssue";
13
13
  import * as Duration from "effect/Duration";
14
- //#region src/Coerce.ts
14
+ // src/Coerce.ts
15
15
  var Coerce_exports = /* @__PURE__ */ __exportAll({
16
16
  booleanFromCheckbox: () => booleanFromCheckbox,
17
17
  emptyToNull: () => emptyToNull,
@@ -75,8 +75,7 @@ function booleanFromCheckbox(raw) {
75
75
  const normalised = raw.trim().toLowerCase();
76
76
  return !(normalised === "" || normalised === "false" || normalised === "0" || normalised === "off" || normalised === "no");
77
77
  }
78
- //#endregion
79
- //#region src/Command.ts
78
+ // src/Command.ts
80
79
  /**
81
80
  * Command-struct introspection and form derivation.
82
81
  *
@@ -381,8 +380,7 @@ const createAdapter = (commandPlan, overrides = {}) => (input) => Rec.fromEntrie
381
380
  * decode brands it.
382
381
  */
383
382
  const updateAdapter = (commandPlan, overrides = {}) => (input) => Rec.fromEntries([["id", input.id], ...adaptFields(commandPlan, overrides, "update", input)]);
384
- //#endregion
385
- //#region src/Constraints.ts
383
+ // src/Constraints.ts
386
384
  /**
387
385
  * HTML input constraints derived from an Effect Schema.
388
386
  *
@@ -481,6 +479,8 @@ function applyPayloadPatch(representation) {
481
479
  const patch = payloadPatches[id];
482
480
  return patch(representation.payload);
483
481
  }
482
+ /** Whether a check is `Schema.isInt` (it implies `step="1"`, not an attribute payload). */
483
+ const isIntCheck = (representation) => representation.id === "effect/schema/isInt";
484
484
  /**
485
485
  * Derive an {@link InputConstraint}'s refinement attributes from the checks
486
486
  * attached to `outer` (the property's own type, e.g. the union wrapping an
@@ -495,7 +495,6 @@ function constraintFromChecks(outer, inner) {
495
495
  const representation = filter.annotations?.representation;
496
496
  return representation === void 0 ? [] : [representation];
497
497
  });
498
- const isIntCheck = (representation) => representation.id === "effect/schema/isInt";
499
498
  const patched = Arr.reduce(representations, {}, (out, representation) => isIntCheck(representation) ? out : {
500
499
  ...out,
501
500
  ...applyPayloadPatch(representation)
@@ -610,8 +609,7 @@ function constraintAt(constraints, path) {
610
609
  });
611
610
  return node !== void 0 && isLeafConstraint(node) ? node : {};
612
611
  }
613
- //#endregion
614
- //#region src/FormData.ts
612
+ // src/FormData.ts
615
613
  /**
616
614
  * Structured parsing of an HTML form body into a plain object the schema can
617
615
  * decode.
@@ -636,6 +634,8 @@ var FormData_exports = /* @__PURE__ */ __exportAll({
636
634
  parseFieldPath: () => parseFieldPath,
637
635
  requestToObject: () => requestToObject
638
636
  });
637
+ /** A pending path token, dropped when empty. */
638
+ const nonEmpty = (buffer) => buffer === "" ? [] : [buffer];
639
639
  /**
640
640
  * Split a flat field name into its path tokens. Supports dot notation
641
641
  * (`a.b`), bracket notation (`a[b]`, `a[0]`), and the trailing append form
@@ -643,7 +643,6 @@ var FormData_exports = /* @__PURE__ */ __exportAll({
643
643
  * `"a.b[]"` → `["a","b",""]`.
644
644
  */
645
645
  function parseFieldPath(name) {
646
- const nonEmpty = (buffer) => buffer === "" ? [] : [buffer];
647
646
  const scanned = Arr.reduce(Arr.fromIterable(name), {
648
647
  tokens: Arr.empty(),
649
648
  buffer: "",
@@ -739,8 +738,7 @@ function formDataToObject(source) {
739
738
  async function requestToObject(request) {
740
739
  return formDataToObject(await request.formData());
741
740
  }
742
- //#endregion
743
- //#region src/Path.ts
741
+ // src/Path.ts
744
742
  /**
745
743
  * Nested get / set along a {@link FormPath}.
746
744
  *
@@ -890,8 +888,7 @@ function remapKeyedRecord(record, arrayPath, map) {
890
888
  return remapped === null ? [] : [[toString(remapped), value]];
891
889
  }));
892
890
  }
893
- //#endregion
894
- //#region src/Errors.ts
891
+ // src/Errors.ts
895
892
  /**
896
893
  * The nested form-error tree and its operations.
897
894
  *
@@ -1128,8 +1125,7 @@ function setSourceAt(sourced, source, path, messages) {
1128
1125
  function clearSourceAt(sourced, source, path) {
1129
1126
  return setSourceAt(sourced, source, path, []);
1130
1127
  }
1131
- //#endregion
1132
- //#region src/Field.ts
1128
+ // src/Field.ts
1133
1129
  /**
1134
1130
  * Field definitions for the {@link module:effect-forms/FormBuilder}.
1135
1131
  *
@@ -1244,8 +1240,7 @@ function defaultItemAt(schema, path) {
1244
1240
  function defaultEncodedValues(fields) {
1245
1241
  return Rec.map(fields, (def) => isArrayFieldDef(def) ? [] : defaultValue(def.schema));
1246
1242
  }
1247
- //#endregion
1248
- //#region src/Issues.ts
1243
+ // src/Issues.ts
1249
1244
  /**
1250
1245
  * Form issues — the flat, path-addressed representation of a single
1251
1246
  * validation failure, plus the walker that turns an Effect Schema issue
@@ -1339,8 +1334,7 @@ function filterIssueToFormIssues(issue) {
1339
1334
  }
1340
1335
  return schemaIssuesToFormIssues(issue);
1341
1336
  }
1342
- //#endregion
1343
- //#region src/FormBuilder.ts
1337
+ // src/FormBuilder.ts
1344
1338
  /**
1345
1339
  * Compose a form schema one field at a time, with cross-field refinements.
1346
1340
  *
@@ -1459,8 +1453,7 @@ function runAsyncRefinements(self, values) {
1459
1453
  onNonEmpty: (refinements) => Effect.forEach(refinements, (fn) => fn(values), { concurrency: "unbounded" }).pipe(Effect.map((outputs) => Arr.flatMap(outputs, filterOutputToFormIssues$1)))
1460
1454
  });
1461
1455
  }
1462
- //#endregion
1463
- //#region src/State.ts
1456
+ // src/State.ts
1464
1457
  /**
1465
1458
  * {@link FormState} — the serialisable snapshot of a form.
1466
1459
  *
@@ -1527,8 +1520,7 @@ function FormStateSchema(data) {
1527
1520
  constraints: Schema.optional(Schema.Record(Schema.String, Schema.Unknown))
1528
1521
  });
1529
1522
  }
1530
- //#endregion
1531
- //#region src/Form.ts
1523
+ // src/Form.ts
1532
1524
  /**
1533
1525
  * {@link make} — the core entry point: bind an Effect Schema to a form and
1534
1526
  * get back a validator and an empty-state factory.
@@ -1557,6 +1549,8 @@ var Form_exports = /* @__PURE__ */ __exportAll({
1557
1549
  fromBuilder: () => fromBuilder,
1558
1550
  make: () => make
1559
1551
  });
1552
+ /** {@link Form.parse}: flatten a posted body, pass a plain record through. */
1553
+ const parseInput = (input) => input instanceof FormData || input instanceof URLSearchParams ? formDataToObject(input) : input;
1560
1554
  const toStructured = (input) => {
1561
1555
  if (input === null || input === void 0) return Effect.succeed(null);
1562
1556
  if (input instanceof Request) return Effect.tryPromise({
@@ -1601,7 +1595,7 @@ function make(schema, options) {
1601
1595
  constraints,
1602
1596
  ...overrides
1603
1597
  });
1604
- const parse = (input) => input instanceof FormData || input instanceof URLSearchParams ? formDataToObject(input) : input;
1598
+ const parse = parseInput;
1605
1599
  const validate = (input) => toStructured(input).pipe(Effect.flatMap((object) => {
1606
1600
  if (object === null) return Effect.succeed(empty());
1607
1601
  return decode(object).pipe(Effect.matchEffect({
@@ -1686,8 +1680,7 @@ function fromBuilder(builder, options = {}) {
1686
1680
  validate
1687
1681
  };
1688
1682
  }
1689
- //#endregion
1690
- //#region src/Mode.ts
1683
+ // src/Mode.ts
1691
1684
  /**
1692
1685
  * When a form validates (and, optionally, auto-submits).
1693
1686
  *
@@ -1746,8 +1739,7 @@ function parse(mode) {
1746
1739
  revalidate
1747
1740
  };
1748
1741
  }
1749
- //#endregion
1750
- //#region src/Result.ts
1742
+ // src/Result.ts
1751
1743
  var Result_exports = /* @__PURE__ */ __exportAll({
1752
1744
  errorResult: () => errorResult,
1753
1745
  failure: () => failure,
@@ -1778,8 +1770,7 @@ const errorResult = (message, status = 500) => ({
1778
1770
  status,
1779
1771
  message
1780
1772
  });
1781
- //#endregion
1782
- //#region src/Validate.ts
1773
+ // src/Validate.ts
1783
1774
  /**
1784
1775
  * Low-level validation helpers — decode a value through a schema and get back
1785
1776
  * the {@link FormErrors} tree (or the list of {@link FormIssue}s) instead of a
@@ -1880,7 +1871,6 @@ function resolveFieldTrigger(validators, trigger) {
1880
1871
  asyncDebounceMs: validators?.asyncDebounceMs ?? 0
1881
1872
  };
1882
1873
  }
1883
- //#endregion
1884
1874
  export { Coerce_exports as Coerce, Command_exports as Command, Constraints_exports as Constraints, Errors_exports as Errors, Field_exports as Field, Form_exports as Form, FormBuilder_exports as FormBuilder, FormData_exports as FormData, Issues_exports as Issues, Mode_exports as Mode, Path_exports as Path, Result_exports as Result, State_exports as State, Validate_exports as Validate };
1885
1875
 
1886
1876
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["replaceAt","filterOutputToFormIssues","Field.makeField","make","Field.isArrayFieldDef","Field.isFieldDef","issuesFromFilterOutput","make","makeFormState","filterOutputToFormIssues"],"sources":["../src/Coerce.ts","../src/Command.ts","../src/Constraints.ts","../src/FormData.ts","../src/Path.ts","../src/Errors.ts","../src/Field.ts","../src/Issues.ts","../src/FormBuilder.ts","../src/State.ts","../src/Form.ts","../src/Mode.ts","../src/Result.ts","../src/Validate.ts"],"sourcesContent":["/**\n * Tri-state coercers for the HTML-form boundary.\n *\n * An HTML form submits every value as a string, and an absent optional field\n * arrives as `undefined` (or an empty string). Mapping that onto a\n * value / `null` / `undefined` update contract — where a value **sets**, `null`\n * **clears** a nullable field, and `undefined` **skips** (carry-forward) — is\n * the same boundary discipline the host app applies, lifted here so any\n * consumer of the core gets it without a framework dependency.\n *\n * These are plain, total functions: they never throw and never reach for a\n * framework's `error()` helper (validation failures belong on the schema, not\n * in a coercer).\n *\n * @copyright Thomas Foster\n * @module effect-forms/Coerce\n */\n\n/** Empty string / `undefined` → `undefined`; otherwise the value (skip semantics). */\nexport function emptyToUndefined<T>(value: T | \"\" | undefined): T | undefined {\n return value === \"\" || value === undefined ? undefined : value;\n}\n\n/**\n * `undefined` stays `undefined` (skip); empty string / `null` → `null`\n * (clear a nullable field); otherwise the value (set). The three-state coercer\n * for nullable update fields.\n */\nexport function emptyToNull<T>(value: T | \"\" | null | undefined): T | null | undefined {\n if (value === undefined) return undefined;\n if (value === \"\" || value === null) return null;\n return value;\n}\n\n/** Trim, then {@link emptyToUndefined}: whitespace-only collapses to `undefined` (skip). */\nexport function trimToUndefined(value: string | undefined): string | undefined {\n return emptyToUndefined(value?.trim());\n}\n\n/** Trim, then {@link emptyToNull}: whitespace-only / blank clears (`null`). */\nexport function trimToNull(value: string | undefined): string | null | undefined {\n return emptyToNull(value?.trim());\n}\n\n/**\n * Map a numeric text field onto the value / `null` / `undefined` contract.\n * Blank clears on the nullable path and skips otherwise; a present value\n * parses with `Number` (`NaN` for non-numeric input — validate downstream).\n */\nexport function numberFromField(\n raw: string | undefined,\n options: { nullable: false },\n): number | undefined;\nexport function numberFromField(\n raw: string | undefined,\n options: { nullable: true },\n): number | null | undefined;\nexport function numberFromField(\n raw: string | undefined,\n { nullable }: { nullable: boolean },\n): number | null | undefined {\n const coerced = nullable ? emptyToNull(raw) : emptyToUndefined(raw);\n return coerced === undefined || coerced === null ? coerced : Number(coerced);\n}\n\n/**\n * Interpret a checkbox field. An HTML checkbox submits its `value` (default\n * `\"on\"`) only when checked and omits the field entirely when unchecked, so a\n * present field is `true` and an absent one is `false`. The truthy strings\n * `\"on\"` / `\"true\"` / `\"1\"` / `\"yes\"` all read as `true`; an explicit\n * `\"false\"` / `\"0\"` / `\"off\"` / `\"no\"` reads as `false`.\n */\nexport function booleanFromCheckbox(raw: string | undefined): boolean {\n if (raw === undefined) return false;\n const normalised = raw.trim().toLowerCase();\n return !(\n normalised === \"\" ||\n normalised === \"false\" ||\n normalised === \"0\" ||\n normalised === \"off\" ||\n normalised === \"no\"\n );\n}\n","/**\n * Command-struct introspection and form derivation.\n *\n * A host app's write surface is a pair of flat command `Schema.Struct`s per\n * entity — a create struct (required fields bare, the rest `optional`) and an\n * update struct (keyed by a required `id`, every field `optional`, clearable\n * fields `optional(NullOr(...))` carrying the value / `null` / `undefined`\n * tri-state). This module reads that pair into a plain-data {@link CommandPlan}\n * and derives the three per-entity form artefacts that were previously\n * hand-written per entity: the all-string Form-Input schema fields, the blank\n * defaults, and the tri-state adapters that map a validated multipart input\n * onto the encoded command payload.\n *\n * Classification leads with behavioural decode probes — date codecs are\n * transformations and branded ids erase to `String`, so \"what does this\n * schema accept\" is the only robust signal for those (the same approach the\n * host app's model walkers use). Two kinds fall back to AST matching instead,\n * because no decode probe can name what they are: `enum`'s literal option set\n * (needed anyway for the renderer, not just the classification) and\n * `structured`'s \"this is a nested object, not a scalar\" shape. Neither\n * shadows a probe-classified kind — see {@link classify}.\n *\n * The derived adapters emit the **encoded** command payload (dates stay\n * `YYYY-MM-DD` strings, ids stay plain strings) — the command's canonical\n * decode downstream owns branding and validation. Genuinely irregular fields\n * (a create-time default, an asymmetric coercion) stay expressible via the\n * per-field {@link AdaptOverrides} escape hatch instead of forcing ejection.\n *\n * @copyright Thomas Foster\n * @module effect-forms/Command\n */\n\nimport * as Arr from \"effect/Array\";\nimport * as Exit from \"effect/Exit\";\nimport * as Match from \"effect/Match\";\nimport * as Predicate from \"effect/Predicate\";\nimport * as Rec from \"effect/Record\";\nimport * as Schema from \"effect/Schema\";\nimport * as SchemaAST from \"effect/SchemaAST\";\nimport * as SchemaParser from \"effect/SchemaParser\";\nimport {\n emptyToNull,\n emptyToUndefined,\n numberFromField,\n trimToNull,\n trimToUndefined,\n} from \"./Coerce.ts\";\n\n/** How a command field renders and coerces at the form boundary. */\nexport type FieldKind =\n | \"text\"\n | \"id\"\n | \"number\"\n | \"date\"\n | \"jsonArray\"\n | \"boolean\"\n | \"enum\"\n | \"structured\";\n\n/** One command field's derived plan, as plain data. */\nexport interface FieldPlan {\n readonly key: string;\n readonly kind: FieldKind;\n /** Present on the create struct. */\n readonly inCreate: boolean;\n /** Bare (non-optional) on the create struct. */\n readonly createRequired: boolean;\n /** Present on the update struct. */\n readonly inUpdate: boolean;\n /** `optional(NullOr(...))` on the update struct — blank clears (`null`). */\n readonly updateClearable: boolean;\n /**\n * The field's literal option values, read off the AST — present only when\n * `kind` is `\"enum\"`. In declaration order, stringified (a `<select>`'s\n * option values are always strings at the form boundary, whatever the\n * decoded literal's own type).\n */\n readonly options?: readonly string[];\n}\n\n/** The derived plan for one entity's create/update command pair. */\nexport interface CommandPlan {\n /** Every field except `id`, in update-struct declaration order (create-only fields appended). */\n readonly fields: readonly FieldPlan[];\n /** The create struct carries an optional client-generated `id` (idempotent retries). */\n readonly clientId: boolean;\n}\n\n/** Per-field adapter overrides — the escape hatch for genuinely irregular coercion. */\nexport type AdaptOverrides = Readonly<Record<string, (raw: string | undefined) => unknown>>;\n\n/** A validated all-string multipart input, as the Form-Input schemas produce it. */\nexport type FormInputRecord = Readonly<Record<string, string | undefined>>;\n\n// ── errors ────────────────────────────────────────────────────────────\n\n/** A command field {@link plan} cannot classify into any {@link FieldKind}. */\nclass UnclassifiableFieldError extends Schema.TaggedError<UnclassifiableFieldError>()(\n \"UnclassifiableFieldError\",\n { message: Schema.String },\n) {}\n\n/** A malformed `jsonArray` field payload, as {@link jsonRowsFromField} rejects it. */\nclass InvalidJsonRowsError extends Schema.TaggedError<InvalidJsonRowsError>()(\n \"InvalidJsonRowsError\",\n { message: Schema.String },\n) {}\n\n// ── probes ────────────────────────────────────────────────────────────\n\n/**\n * Run one decode probe: whether the schema accepts the input, plus the decoded\n * value for instance checks (the date probe needs to see a `Date` come out,\n * not just a successful decode).\n */\nconst probe = (schema: Schema.Top, input: unknown): Exit.Exit<unknown, unknown> => {\n // SAFETY: the cast only drops `schema`'s decoding-service requirement. The\n // decode runs to an `Exit`, so a schema that does need a service (or decodes\n // asynchronously, or throws) yields a failed `Exit` — which a probe reads as\n // \"does not decode\", exactly as it reads an ordinary schema issue.\n const decoder = schema as Schema.Codec<unknown>;\n return SchemaParser.decodeUnknownExit(decoder)(input);\n};\n\n/** True when a schema decodes the given input without failing. */\nconst decodes = (schema: Schema.Top, input: unknown): boolean =>\n Exit.isSuccess(probe(schema, input));\n\n/** A syntactically valid UUID no other field kind accepts as non-text. */\nconst PROBE_UUID = \"9f8c1c2e-4b3a-4c5d-8e6f-0a1b2c3d4e5f\";\n\n/**\n * Peel exactly the `optional` / `NullOr` wrapper layers off a property's type\n * AST, stopping at the first union that isn't itself a pure wrapper. A wrapper\n * union carries exactly one non-`null`/`undefined` member (the wrapped type);\n * a union with zero or several such members is a real value-level union (an\n * `enum` field's own literal set, say) rather than another layer to peel, so\n * the loop stops there. Unlike {@link module:effect-forms/Constraints}'s\n * `unwrapOptional` (which only ever needs the *first* real member), this\n * walks every wrapper layer and preserves a multi-member union intact —\n * `enumOptions` below needs the full literal set, not just one branch of it.\n */\nfunction unwrapWrapperUnion(ast: SchemaAST.AST): SchemaAST.AST {\n if (!SchemaAST.isUnion(ast)) return ast;\n const real = Arr.filter(ast.types, (t) => !SchemaAST.isUndefined(t) && !SchemaAST.isNull(t));\n if (real.length !== 1 || real.length === ast.types.length) return ast;\n return unwrapWrapperUnion(real[0]);\n}\n\n/**\n * The literal option values of a field shaped like `Schema.Literal(...)` or\n * `Schema.Literals([...])` (optionally `optional` / `NullOr`-wrapped) —\n * `undefined` when the field isn't literal-shaped. Stringified: a `<select>`'s\n * option values are strings at the form boundary regardless of the literal's\n * own type.\n */\nfunction enumOptions(ast: SchemaAST.AST): readonly string[] | undefined {\n const inner = unwrapWrapperUnion(ast);\n if (SchemaAST.isLiteral(inner)) return [String(inner.literal)];\n if (SchemaAST.isUnion(inner)) {\n const literals = Arr.filter(inner.types, SchemaAST.isLiteral);\n if (Arr.isReadonlyArrayNonEmpty(literals) && literals.length === inner.types.length) {\n return Arr.map(literals, (literal) => String(literal.literal));\n }\n }\n return undefined;\n}\n\n/**\n * Whether a field is itself an object/struct schema (optionally `optional` /\n * `NullOr`-wrapped) — the `structured` kind's shape (inline / block ProseKit\n * JSON: `{ type, content }`). Mirrors {@link module:effect-forms/Field}'s and\n * {@link module:effect-forms/Constraints}'s own `SchemaAST.isObjects` struct\n * check. A `Schema.declare`d type (`UploadedDocument`, `Image`) has no\n * `Objects` AST node, so it is never caught here — the default-throw the\n * derivation relies on stays intact.\n */\nfunction isStructured(ast: SchemaAST.AST): boolean {\n return SchemaAST.isObjects(unwrapWrapperUnion(ast));\n}\n\n/** One field's classification: its {@link FieldKind}, plus `options` for `\"enum\"`. */\ninterface Classification {\n readonly kind: FieldKind;\n readonly options?: readonly string[];\n}\n\n/**\n * The scalar kinds classified purely by what a schema decodes, with no AST\n * inspection: array → boolean → number → date → id → text. `undefined` when\n * the schema accepts none of them, so {@link classify} can fall through to\n * its AST-based checks (`enum`, `structured`).\n */\nconst probeKind = (schema: Schema.Top): FieldKind | undefined => {\n if (decodes(schema, [])) return \"jsonArray\";\n if (decodes(schema, true)) return \"boolean\";\n if (decodes(schema, 42)) return \"number\";\n const date = probe(schema, \"2024-01-02T03:04:05.000Z\");\n if (Exit.isSuccess(date) && date.value instanceof Date) return \"date\";\n if (decodes(schema, PROBE_UUID) && !decodes(schema, \"not-a-uuid\")) return \"id\";\n if (decodes(schema, \"plain text\")) return \"text\";\n return undefined;\n};\n\n/**\n * Classify one field schema: by its AST shape first, then by what it accepts.\n * `enum` is checked before {@link probeKind} rather than after, since a\n * decode probe can't tell \"the literal set an enum accepts\" from any other\n * shape (worse, a numeric-literal enum whose set happens to include the `42`\n * probe value would otherwise misclassify as `number`) — reading the literal\n * set off the AST is unambiguous, and it's needed anyway for the renderer.\n * `structured` is checked last, after every probe has failed: an object\n * schema never decodes any of `probeKind`'s scalar probes, so there is no\n * real ordering hazard, but putting the broadest, AST-only check last keeps\n * every more specific kind first in line. A field that matches nothing\n * (`Schema.declare`d — `UploadedDocument`, `Image`) throws at module load so\n * the gap surfaces when the plan is declared, not on a live submission.\n */\nconst classify = (key: string, schema: Schema.Top): Classification => {\n const options = enumOptions(schema.ast);\n if (options !== undefined) return { kind: \"enum\", options };\n const probed = probeKind(schema);\n if (probed !== undefined) return { kind: probed };\n if (isStructured(schema.ast)) return { kind: \"structured\" };\n throw new UnclassifiableFieldError({\n message: `Command.plan: field \"${key}\" matches no known kind (text/id/number/date/jsonArray/boolean/enum/structured) — a Schema.declare'd type needs an explicit form-level treatment before it can derive`,\n });\n};\n\n// ── plan ──────────────────────────────────────────────────────────────\n\n/** Minimal runtime shape of a command `Schema.Struct`. */\ninterface StructLike {\n readonly fields: Record<string, Schema.Top>;\n}\n\n/**\n * Read a create/update command struct pair into a {@link CommandPlan}.\n * Runs once at module load — probe cost is a handful of tiny decodes per\n * field, never per render or per request.\n */\nexport const plan = (create: StructLike, update: StructLike): CommandPlan => {\n const createFields = create.fields;\n const updateFields = update.fields;\n const keys = [\n ...Arr.filter(Rec.keys(updateFields), (key) => key !== \"id\"),\n ...Arr.filter(Rec.keys(createFields), (key) => key !== \"id\" && !(key in updateFields)),\n ];\n\n const fields = Arr.map(keys, (key): FieldPlan => {\n const createField = createFields[key];\n const updateField = updateFields[key];\n const { kind, options } = classify(key, createField ?? updateField);\n const fieldPlan: FieldPlan = {\n key,\n kind,\n inCreate: createField !== undefined,\n createRequired: createField !== undefined && !decodes(createField, undefined),\n inUpdate: updateField !== undefined,\n updateClearable: updateField !== undefined && decodes(updateField, null),\n };\n return options === undefined ? fieldPlan : { ...fieldPlan, options };\n });\n\n return { fields, clientId: \"id\" in createFields };\n};\n\n// ── Form-Input schema fields ──────────────────────────────────────────\n\n/**\n * Derive the all-string Form-Input schema fields for one mode — every value is\n * a string at the multipart boundary. Create-required fields get a min-length\n * check so blankness surfaces as an inline field error; the update `id` gets\n * the UUID check. Spread into `Schema.Struct({ ...Command.formInputFields(plan,\n * \"create\") })`, with `overrides` spread last for irregular fields.\n */\nexport const formInputFields = (\n commandPlan: CommandPlan,\n mode: \"create\" | \"update\",\n overrides: Readonly<Record<string, Schema.Top>> = {},\n): Record<string, Schema.Top> => {\n const idField: Array<readonly [string, Schema.Top]> =\n mode === \"update\"\n ? [[\"id\", Schema.String.check(Schema.isUUID())]]\n : commandPlan.clientId\n ? [[\"id\", Schema.optional(Schema.String)]]\n : [];\n const planFields = Arr.flatMap(\n commandPlan.fields,\n (field): Array<readonly [string, Schema.Top]> => {\n if (mode === \"create\" ? !field.inCreate : !field.inUpdate) return [];\n return [\n [\n field.key,\n mode === \"create\" && field.createRequired\n ? Schema.String.check(Schema.isMinLength(1))\n : Schema.optional(Schema.String),\n ],\n ];\n },\n );\n // Overrides come last, so an override replaces a derived field in place.\n return Rec.fromEntries([...idField, ...planFields, ...Rec.toEntries(overrides)]);\n};\n\n/**\n * Blank string defaults for one mode's Form-Input shape. Client-id entities\n * override `id` at the call site (`{ ...blankDefaults(plan, \"create\"), id:\n * crypto.randomUUID() }`) so retries stay idempotent.\n */\nexport const blankDefaults = (\n commandPlan: CommandPlan,\n mode: \"create\" | \"update\",\n): Record<string, string> => {\n const keys = [\n ...(mode === \"update\" || commandPlan.clientId ? [\"id\"] : []),\n ...Arr.flatMap(commandPlan.fields, (field) =>\n (mode === \"create\" ? field.inCreate : field.inUpdate) ? [field.key] : [],\n ),\n ];\n return Rec.fromEntries(Arr.map(keys, (key) => [key, \"\"] as const));\n};\n\n// ── adapters ──────────────────────────────────────────────────────────\n\n/**\n * Parse a JSON-encoded row list (a contributor list) from a multipart form\n * field. A blank or absent field yields `undefined` (skip — leave the existing\n * list untouched); an empty JSON array (`\"[]\"`) clears the list. Rows keep\n * `id` / `creditedName` when present; `orderingPosition` falls back to the\n * row's array index. Throws a plain `Error` on a malformed payload — mapping\n * that onto a framework response is the caller's concern.\n */\nexport const jsonRowsFromField = (\n raw: string | undefined,\n label: string,\n):\n | {\n id?: string | undefined;\n agentId: string;\n creditedName?: string | undefined;\n orderingPosition: number;\n }[]\n | undefined => {\n if (raw === undefined || raw.trim() === \"\") return undefined;\n const decoded = decodeJson(raw);\n if (Exit.isFailure(decoded)) {\n throw new InvalidJsonRowsError({ message: `Invalid ${label} payload — expected JSON array` });\n }\n const parsed = decoded.value;\n if (!Arr.isArray(parsed)) {\n throw new InvalidJsonRowsError({ message: `Invalid ${label} payload — expected array` });\n }\n return Arr.map(parsed, (row, index) => {\n if (!Predicate.isObjectOrArray(row)) {\n throw new InvalidJsonRowsError({ message: `Invalid ${label} entry at index ${index}` });\n }\n // `row[key]`, `undefined` when absent — what a plain property read gives.\n const propertyOf = (key: string): unknown =>\n Predicate.hasProperty(row, key) ? row[key] : undefined;\n const agentId = propertyOf(\"agentId\");\n if (!Predicate.isString(agentId)) {\n throw new InvalidJsonRowsError({\n message: `${label} entry at index ${index} missing agentId`,\n });\n }\n const id = propertyOf(\"id\");\n const creditedName = propertyOf(\"creditedName\");\n const orderingPosition = propertyOf(\"orderingPosition\");\n return {\n id: Predicate.isString(id) ? id : undefined,\n agentId,\n creditedName: Predicate.isString(creditedName) ? creditedName : undefined,\n orderingPosition: Predicate.isNumber(orderingPosition) ? orderingPosition : index,\n };\n });\n};\n\n/** Parse a JSON text (`JSON.parse` semantics) to a success or a failed `Exit`. */\nconst decodeJson = SchemaParser.decodeUnknownExit(Schema.fromJsonString(Schema.Unknown));\n\n/** Coercion context for one field in one mode. */\ninterface CoerceContext {\n /** Required at create: emit the raw value's canonical form, never skip. */\n readonly required: boolean;\n /** Blank clears (`null`) rather than skips (`undefined`). */\n readonly clearable: boolean;\n /** Trim before the create-mode blank check (dates trim only at create). */\n readonly trimCreate: boolean;\n}\n\n/** Text fields: trim always; required create fields never skip. */\nconst coerceText = (ctx: CoerceContext, raw: string | undefined): unknown =>\n ctx.required ? (raw ?? \"\").trim() : ctx.clearable ? trimToNull(raw) : trimToUndefined(raw);\n\n/** Id fields: no trimming (pickers emit `\"\"` or a UUID); required create FKs pass through raw. */\nconst coerceId = (ctx: CoerceContext, raw: string | undefined): unknown =>\n ctx.required ? raw : ctx.clearable ? emptyToNull(raw) : emptyToUndefined(raw);\n\n/** Numeric text fields: blank clears on the clearable path, skips otherwise. */\nconst coerceNumber = (ctx: CoerceContext, raw: string | undefined): unknown =>\n ctx.clearable\n ? numberFromField(raw, { nullable: true })\n : numberFromField(raw, { nullable: false });\n\n/** Date fields stay encoded (`YYYY-MM-DD` strings); create trims, update passes through. */\nconst coerceDate = (ctx: CoerceContext, raw: string | undefined): unknown => {\n const value = ctx.trimCreate ? raw?.trim() : raw;\n return ctx.clearable ? emptyToNull(value) : emptyToUndefined(value);\n};\n\n/**\n * Boolean fields: a `<select>`'s value set, not a checkbox — the only truthy\n * raw value is the literal string `\"true\"`, everything else (`\"false\"`,\n * blank, absent) reads as `false`. Create defaults an absent/blank field to\n * `false` (never skips); update skips absent/blank unless clearable, in which\n * case it clears (`null`) instead — the create-defaults / update-skips\n * asymmetry the Content family already hand-writes.\n */\nconst coerceBoolean = (ctx: CoerceContext, raw: string | undefined): unknown => {\n const trimmed = raw?.trim();\n if (trimmed === undefined || trimmed === \"\") {\n return ctx.required ? false : ctx.clearable ? null : undefined;\n }\n return trimmed === \"true\";\n};\n\n/**\n * Parse a JSON-encoded structured document (inline / block ProseKit JSON)\n * from a multipart field. Blank or absent skips (`undefined`) or, on the\n * clearable path, clears (`null`); a present value is `JSON.parse`d and\n * passed through as-is — shape validation is the command's own decode's job,\n * exactly as {@link jsonRowsFromField} leaves row-shape validation to itself\n * rather than duplicating it here. `JSON.parse` throws a `SyntaxError` on\n * malformed JSON, same as {@link jsonRowsFromField} does on a malformed row\n * list — mapping that onto a framework response is the caller's concern.\n */\nconst structuredFromField = (raw: string | undefined, clearable: boolean): unknown => {\n const trimmed = raw?.trim();\n if (trimmed === undefined || trimmed === \"\") return clearable ? null : undefined;\n return JSON.parse(trimmed);\n};\n\n/** Build one field's {@link CoerceContext} for one mode. */\nconst coerceContextFor = (field: FieldPlan, mode: \"create\" | \"update\"): CoerceContext => ({\n required: mode === \"create\" && field.createRequired,\n clearable: mode === \"update\" && field.updateClearable,\n trimCreate: mode === \"create\",\n});\n\n/** The default coercion for one field in one mode. */\nconst coerce = (field: FieldPlan, mode: \"create\" | \"update\", raw: string | undefined): unknown => {\n const ctx = coerceContextFor(field, mode);\n return Match.value(field.kind).pipe(\n Match.when(Match.is(\"text\", \"enum\"), () => coerceText(ctx, raw)),\n Match.when(\"id\", () => coerceId(ctx, raw)),\n Match.when(\"number\", () => coerceNumber(ctx, raw)),\n Match.when(\"date\", () => coerceDate(ctx, raw)),\n Match.when(\"jsonArray\", () => jsonRowsFromField(raw, field.key)),\n Match.when(\"boolean\", () => coerceBoolean(ctx, raw)),\n Match.when(\"structured\", () => structuredFromField(raw, ctx.clearable)),\n Match.exhaustive,\n );\n};\n\n/**\n * Adapt every field of `commandPlan` present in `mode`, in plan order, to a\n * `[key, value]` entry: through the field's override when it has one, else\n * the default {@link coerce}.\n */\nconst adaptFields = (\n commandPlan: CommandPlan,\n overrides: AdaptOverrides,\n mode: \"create\" | \"update\",\n input: FormInputRecord,\n): Array<readonly [string, unknown]> =>\n Arr.flatMap(commandPlan.fields, (field): Array<readonly [string, unknown]> => {\n if (mode === \"create\" ? !field.inCreate : !field.inUpdate) return [];\n const override = overrides[field.key];\n const raw = input[field.key];\n return [[field.key, override ? override(raw) : coerce(field, mode, raw)]];\n });\n\n/**\n * Derive the create adapter: validated all-string input → encoded create\n * command payload. `overrides` replace the default per-field coercion for\n * genuinely irregular fields (e.g. a create-time fallback default).\n */\nexport const createAdapter =\n (commandPlan: CommandPlan, overrides: AdaptOverrides = {}) =>\n (input: FormInputRecord): Record<string, unknown> =>\n Rec.fromEntries([\n ...(commandPlan.clientId ? [[\"id\", trimToUndefined(input.id)] as const] : []),\n ...adaptFields(commandPlan, overrides, \"create\", input),\n ]);\n\n/**\n * Derive the update adapter: validated all-string input → encoded update\n * command payload (value sets, `null` clears a clearable field, `undefined`\n * skips). The required `id` passes through as-is — the command's canonical\n * decode brands it.\n */\nexport const updateAdapter =\n (commandPlan: CommandPlan, overrides: AdaptOverrides = {}) =>\n (input: FormInputRecord): Record<string, unknown> =>\n Rec.fromEntries([\n [\"id\", input.id] as const,\n ...adaptFields(commandPlan, overrides, \"update\", input),\n ]);\n","/**\n * HTML input constraints derived from an Effect Schema.\n *\n * Native validation attributes can be read (`required`, `minlength`, …)\n * off the schema so a form can apply them to its inputs for free, layering\n * browser-native validation under the schema-driven validation. This module\n * does the framework-neutral half: walk the schema AST and emit an\n * {@link InputConstraints} tree a UI can spread onto its `<input>`s.\n *\n * `required` comes from a struct field's optionality; `minlength` /\n * `maxlength` / `min` / `max` / `pattern` / `step` are derived from the\n * `representation` annotation Effect Schema's built-in checks carry\n * (`isMinLength`, `isBetween`, `isPattern`, …), keyed by its stable\n * `effect/schema/<check>` id. A check with no recognised representation id\n * (a bare `Schema.filter` predicate, a custom `.check()`) contributes\n * nothing; there is no attribute to guess at,\n * so the derivation stays silent rather than wrong. The walk descends into\n * nested structs and into a homogeneous array's item type (folding the\n * array's own optionality into the item's `required`); a fixed-shape tuple\n * with differently-typed elements has no single item schema to represent as\n * one constraint, so it derives only `required`. Like\n * {@link module:effect-forms/Field}'s `fromAst`, an `optional` / `NullOr`\n * union is unwrapped by taking its first non-`null`/`undefined` member —\n * scoped to that wrapping pattern, not general discriminated unions, whose\n * branches can't be flattened into one constraint either. The `overrides`\n * argument to {@link constraintsFromSchema} remains the seam for anything the\n * walk cannot infer (or should not — a check that's semantically stronger\n * than any HTML attribute).\n *\n * @copyright Thomas Foster\n * @module effect-forms/Constraints\n */\n\nimport * as Arr from \"effect/Array\";\nimport * as Option from \"effect/Option\";\nimport * as Predicate from \"effect/Predicate\";\nimport * as Rec from \"effect/Record\";\nimport type * as Schema from \"effect/Schema\";\nimport * as SchemaAST from \"effect/SchemaAST\";\nimport type * as SchemaRepresentation from \"effect/SchemaRepresentation\";\nimport type { FormPath } from \"./Issues.ts\";\n\n/** Native validation attributes for a single input. */\nexport interface InputConstraint {\n readonly required?: boolean;\n readonly min?: number | string;\n readonly max?: number | string;\n readonly minlength?: number;\n readonly maxlength?: number;\n readonly pattern?: string;\n readonly step?: number;\n}\n\n/** A nested tree of {@link InputConstraint}s mirroring the schema's shape. */\nexport interface InputConstraints {\n readonly [key: string]: InputConstraint | InputConstraints | undefined;\n}\n\n/** Whether a node in an {@link InputConstraints} tree is a leaf ({@link InputConstraint}) rather than a branch. */\nexport const isLeafConstraint = (\n value: InputConstraint | InputConstraints,\n): value is InputConstraint =>\n \"required\" in value ||\n \"min\" in value ||\n \"max\" in value ||\n \"minlength\" in value ||\n \"maxlength\" in value ||\n \"pattern\" in value ||\n \"step\" in value;\n\n/** Whether a property's type AST is optional (so the field is not required). */\nconst isOptional = (type: SchemaAST.AST): boolean => type.context?.isOptional === true;\n\n/**\n * Unwrap an `optional` / `NullOr` union the way {@link module:effect-forms/Field}'s\n * `fromAst` does: recurse into the first member that isn't a bare `undefined`\n * / `null` literal, so a wrapped struct / array / refined leaf still yields\n * its own shape instead of falling through to an empty constraint.\n */\nfunction unwrapOptional(type: SchemaAST.AST): SchemaAST.AST {\n if (!SchemaAST.isUnion(type)) return type;\n return Arr.findFirst(\n type.types,\n (member) => !SchemaAST.isUndefined(member) && !SchemaAST.isNull(member),\n ).pipe(Option.match({ onNone: () => type, onSome: unwrapOptional }));\n}\n\n/** Flatten a `Checks` array (recursing into `FilterGroup`s) into individual filters. */\nfunction flattenChecks(\n checks: ReadonlyArray<SchemaAST.Check<never>> | undefined,\n): Array<SchemaAST.Filter<never>> {\n if (checks === undefined) return [];\n return Arr.flatMap(checks, (check) =>\n Predicate.isTagged(check, \"FilterGroup\") ? flattenChecks(check.checks) : [check],\n );\n}\n\n/** A same-shape, mutable patch of {@link InputConstraint} fields. */\ntype ConstraintPatch = { -readonly [K in keyof InputConstraint]?: InputConstraint[K] };\n\n/**\n * The `representation.payload` shapes of the built-in checks that map onto a\n * native HTML attribute, keyed by the check's stable representation id.\n * Mirrors each check constructor in `effect/Schema` (e.g. `isMinLength`\n * writes `{ id: \"effect/schema/isMinLength\", payload: { minLength } }`).\n */\ninterface CheckPayloads {\n readonly \"effect/schema/isMinLength\": { readonly minLength: number };\n readonly \"effect/schema/isMaxLength\": { readonly maxLength: number };\n readonly \"effect/schema/isBetweenLength\": { readonly minimum: number; readonly maximum: number };\n readonly \"effect/schema/isPattern\": { readonly source: string; readonly flags: string };\n readonly \"effect/schema/isGreaterThan\": { readonly exclusiveMinimum: number };\n readonly \"effect/schema/isGreaterThanOrEqualTo\": { readonly minimum: number };\n readonly \"effect/schema/isLessThan\": { readonly exclusiveMaximum: number };\n readonly \"effect/schema/isLessThanOrEqualTo\": { readonly maximum: number };\n readonly \"effect/schema/isBetween\": { readonly minimum: number; readonly maximum: number };\n readonly \"effect/schema/isMultipleOf\": { readonly divisor: number };\n}\n\n/**\n * One constraint patch per recognised representation id — the built-in\n * checks that map onto a native HTML attribute. An id with no entry here\n * (an unannotated custom filter, or a built-in check with no HTML\n * equivalent) derives nothing. `isInt` isn't a plain patch: it only sets\n * `step` as a fallback when nothing more specific already did, so it's\n * handled separately in {@link constraintFromChecks}.\n */\nconst payloadPatches = {\n \"effect/schema/isMinLength\": (p) => ({ minlength: p.minLength }),\n \"effect/schema/isMaxLength\": (p) => ({ maxlength: p.maxLength }),\n \"effect/schema/isBetweenLength\": (p) => ({ minlength: p.minimum, maxlength: p.maximum }),\n \"effect/schema/isPattern\": (p) => ({ pattern: p.source }),\n \"effect/schema/isGreaterThan\": (p) => ({ min: p.exclusiveMinimum }),\n \"effect/schema/isGreaterThanOrEqualTo\": (p) => ({ min: p.minimum }),\n \"effect/schema/isLessThan\": (p) => ({ max: p.exclusiveMaximum }),\n \"effect/schema/isLessThanOrEqualTo\": (p) => ({ max: p.maximum }),\n \"effect/schema/isBetween\": (p) => ({ min: p.minimum, max: p.maximum }),\n \"effect/schema/isMultipleOf\": (p) => ({ step: p.divisor }),\n} satisfies {\n readonly [K in keyof CheckPayloads]: (payload: CheckPayloads[K]) => ConstraintPatch;\n};\n\n/** Whether `id` names a check {@link payloadPatches} has a patch for (an own key, never an inherited one). */\nconst isPatchedCheckId = (id: string): id is keyof CheckPayloads =>\n Rec.has<string, unknown>(payloadPatches, id);\n\n/**\n * Look up and apply `payloadPatches[representation.id]`. The cast is sound:\n * each payload is authored by the same built-in check constructor its id\n * names, so an entry is never called with a mismatched payload shape.\n */\nfunction applyPayloadPatch(\n representation: SchemaRepresentation.RepresentationAnnotation,\n): ConstraintPatch | undefined {\n const id = representation.id;\n if (!isPatchedCheckId(id)) return undefined;\n // SAFETY: every `effect/schema/is*` id in `CheckPayloads` is the\n // representation id of one of Effect's built-in checks, which publishes its\n // `payload` in exactly the shape `CheckPayloads` records for that id — so\n // the patch for `id` accepts this representation's payload.\n const patch = payloadPatches[id] as (payload: Schema.Json) => ConstraintPatch;\n return patch(representation.payload);\n}\n\n/**\n * Derive an {@link InputConstraint}'s refinement attributes from the checks\n * attached to `outer` (the property's own type, e.g. the union wrapping an\n * optional field) and `inner` (the unwrapped leaf) — `inner`'s checks win on\n * conflict since they're the more specific, unwrapped node. A check whose\n * `representation` annotation has no recognised id (an unannotated custom\n * filter) is skipped: there is no attribute to derive from it.\n */\nfunction constraintFromChecks(outer: SchemaAST.AST, inner: SchemaAST.AST): InputConstraint {\n const filters =\n inner === outer\n ? flattenChecks(outer.checks)\n : [...flattenChecks(outer.checks), ...flattenChecks(inner.checks)];\n const representations = Arr.flatMap(filters, (filter) => {\n const representation = filter.annotations?.representation;\n return representation === undefined ? [] : [representation];\n });\n\n const isIntCheck = (representation: SchemaRepresentation.RepresentationAnnotation): boolean =>\n representation.id === \"effect/schema/isInt\";\n const empty: ConstraintPatch = {};\n const patched = Arr.reduce(representations, empty, (out, representation) =>\n isIntCheck(representation) ? out : { ...out, ...applyPayloadPatch(representation) },\n );\n\n return Arr.some(representations, isIntCheck) && patched.step === undefined\n ? { ...patched, step: 1 }\n : patched;\n}\n\n/**\n * Whether `ast` is a homogeneous array/list — `Schema.Array(item)` or\n * `Schema.NonEmptyArray(item)` — as opposed to a fixed-shape tuple with\n * differently-typed positional elements. Only a homogeneous array has a\n * single item schema to descend into; a tuple's elements each need their own\n * constraint, which this leaf-per-key tree can't represent, so it's left to\n * `overrides`.\n */\nfunction homogeneousItem(ast: SchemaAST.Arrays): SchemaAST.AST | undefined {\n if (ast.rest.length !== 1) return undefined;\n const [item] = ast.rest;\n return Arr.match(ast.elements, {\n onEmpty: () => item,\n onNonEmpty: (elements) => (elements.length === 1 && elements[0] === item ? item : undefined),\n });\n}\n\n/**\n * Derive the constraint (or nested tree) for one property/item type: a\n * struct descends into its properties, a homogeneous array descends into its\n * item type (the array's own length checks have no native equivalent for a\n * repeated field, so they're not derived) while folding the array's own\n * optionality into the item's `required`, and anything else is a leaf whose\n * `required` comes from optionality and whose refinement attributes come\n * from {@link constraintFromChecks}.\n */\nfunction constraintsForType(type: SchemaAST.AST): InputConstraint | InputConstraints {\n const required = !isOptional(type);\n const inner = unwrapOptional(type);\n if (SchemaAST.isObjects(inner)) {\n return constraintsForObjects(inner);\n }\n if (SchemaAST.isArrays(inner)) {\n const item = homogeneousItem(inner);\n if (!item) return { required };\n const itemConstraints = constraintsForType(item);\n return isLeafConstraint(itemConstraints)\n ? { ...itemConstraints, required: required && itemConstraints.required !== false }\n : itemConstraints;\n }\n return { required, ...constraintFromChecks(type, inner) };\n}\n\n/** Build the constraint tree for one object AST node. */\nfunction constraintsForObjects(ast: SchemaAST.Objects): InputConstraints {\n return Rec.fromEntries(\n Arr.map(\n ast.propertySignatures,\n (ps) => [String(ps.name), constraintsForType(ps.type)] as const,\n ),\n );\n}\n\n/** Deep-merge constraint overrides over a derived tree. */\nfunction mergeConstraints(base: InputConstraints, overrides: InputConstraints): InputConstraints {\n const merged = Arr.flatMap(\n Rec.keys<string, InputConstraints[string]>(overrides),\n (key): Array<readonly [string, InputConstraint | InputConstraints]> => {\n const override = overrides[key];\n if (override === undefined) return [];\n const existing = base[key];\n if (existing !== undefined && !isLeafConstraint(existing) && !isLeafConstraint(override)) {\n return [[key, mergeConstraints(existing, override)]];\n }\n if (existing !== undefined && isLeafConstraint(existing) && isLeafConstraint(override)) {\n return [[key, { ...existing, ...override }]];\n }\n return [[key, override]];\n },\n );\n return { ...base, ...Rec.fromEntries(merged) };\n}\n\n/**\n * Derive the {@link InputConstraints} for a struct schema. Non-struct schemas\n * yield an empty tree. `overrides` (deep-merged over the derived tree, and\n * winning on conflict) is the seam for anything the AST walk does not infer.\n *\n * @example\n * ```ts\n * import * as Schema from \"effect/Schema\"\n * import { Constraints } from \"@thomasfosterau/effect-forms\"\n *\n * const Signup = Schema.Struct({\n * name: Schema.String.check(Schema.isMinLength(2)),\n * nickname: Schema.optional(Schema.String),\n * })\n * console.log(Constraints.constraintsFromSchema(Signup))\n * // { name: { required: true, minlength: 2 }, nickname: { required: false } }\n * ```\n */\nexport function constraintsFromSchema(\n schema: Schema.Top,\n overrides: InputConstraints = {},\n): InputConstraints {\n const ast = schema.ast;\n const base = SchemaAST.isObjects(ast) ? constraintsForObjects(ast) : {};\n return mergeConstraints(base, overrides);\n}\n\n/**\n * Read the {@link InputConstraint} leaf at `path` in an {@link InputConstraints}\n * tree, defaulting to `{}` when `path` has no derived constraints. Array\n * indices in `path` are skipped rather than descended into — the tree has no\n * per-item nodes to index into, since {@link constraintsForType} derives one\n * constraint (or branch) per *field*, shared by every item at that field. A\n * bare item path (`tags[0]`) resolves to that shared leaf; a path that\n * continues past the index into a struct item's own field (`items[0].label`)\n * skips the index and resolves straight to `label`'s derived constraint.\n */\nexport function constraintAt(constraints: InputConstraints, path: FormPath): InputConstraint {\n // Index segments are skipped (a list's items share one constraint). Once a\n // string segment runs past a leaf or off the tree the accumulator is\n // `undefined`, and stays so for every later segment.\n const node = Arr.reduce<string | number, InputConstraint | InputConstraints | undefined>(\n path,\n constraints,\n (current, segment) => {\n if (Predicate.isNumber(segment)) return current;\n if (current === undefined || isLeafConstraint(current)) return undefined;\n return current[segment];\n },\n );\n return node !== undefined && isLeafConstraint(node) ? node : {};\n}\n","/**\n * Structured parsing of an HTML form body into a plain object the schema can\n * decode.\n *\n * Browsers flatten structured data into `name=value` pairs using bracket /\n * dot path notation (`author.name`, `author[name]`, `tags[0]`, `tags[]`).\n * {@link formDataToObject} reverses that into a nested object / array tree —\n * the framework-neutral step that turns a form body into structured data, before handing it\n * to a validator. Coercion (strings → numbers / booleans / dates) is then the\n * schema's job: decode the result with a schema built from coercing leaves\n * (`Schema.FiniteFromString`, `Schema.BooleanFromString`, the {@link\n * module:effect-forms/Coerce} helpers, …).\n *\n * `File` values pass through untouched; repeated bare keys collapse into an\n * array. The parser is total and never throws.\n *\n * @copyright Thomas Foster\n * @module effect-forms/FormData\n */\n\nimport * as Arr from \"effect/Array\";\nimport * as Predicate from \"effect/Predicate\";\nimport * as Rec from \"effect/Record\";\n\n/** Anything that can be parsed into a structured form object. */\nexport type FormBodySource =\n | FormData\n | URLSearchParams\n | Iterable<readonly [string, FormDataEntryValue | string]>;\n\ntype Container = Record<string, unknown> | unknown[];\n\n/**\n * Split a flat field name into its path tokens. Supports dot notation\n * (`a.b`), bracket notation (`a[b]`, `a[0]`), and the trailing append form\n * (`a[]`, represented by an empty-string token). `\"a[b][0]\"` → `[\"a\",\"b\",\"0\"]`;\n * `\"a.b[]\"` → `[\"a\",\"b\",\"\"]`.\n */\nexport function parseFieldPath(name: string): string[] {\n const nonEmpty = (buffer: string): ReadonlyArray<string> => (buffer === \"\" ? [] : [buffer]);\n const scanned = Arr.reduce(\n Arr.fromIterable(name),\n { tokens: Arr.empty<string>(), buffer: \"\", inBracket: false },\n ({ tokens, buffer, inBracket }, char) => {\n if (char === \"[\") {\n return { tokens: [...tokens, ...nonEmpty(buffer)], buffer: \"\", inBracket: true };\n }\n if (char === \"]\") return { tokens: [...tokens, buffer], buffer: \"\", inBracket: false };\n if (char === \".\" && !inBracket) {\n return { tokens: [...tokens, ...nonEmpty(buffer)], buffer: \"\", inBracket };\n }\n return { tokens, buffer: buffer + char, inBracket };\n },\n );\n const tokens = [...scanned.tokens, ...nonEmpty(scanned.buffer)];\n // A leading empty token can appear for names like `[0]`; drop it.\n return tokens.length > 1 && tokens[0] === \"\" ? tokens.slice(1) : tokens;\n}\n\nconst isIndexToken = (token: string): boolean => token === \"\" || /^\\d+$/.test(token);\n\n/** Create the container a token should live in: array for index/append tokens. */\nconst makeContainer = (nextToken: string): Container => (isIndexToken(nextToken) ? [] : {});\n\nconst setOnContainer = (container: Container, token: string, value: unknown): void => {\n if (Arr.isArray(container)) {\n if (token === \"\") container.push(value);\n else container[Number(token)] = value;\n } else {\n container[token] = value;\n }\n};\n\nconst getFromContainer = (container: Container, token: string): unknown =>\n Arr.isArray(container) ? container[Number(token)] : container[token];\n\n/** Assign `value` deep into `root` along `tokens`, creating containers as needed. */\nfunction assign(root: Container, tokens: Arr.NonEmptyReadonlyArray<string>, value: unknown): void {\n const last = Arr.lastNonEmpty(tokens);\n // Walk every token but the last, descending into (or creating) the\n // container each one names; `tokens[i + 1]` is the token after `token`.\n const node = Arr.reduce(Arr.initNonEmpty(tokens), root, (parent, token, i): Container => {\n const child = token === \"\" ? undefined : getFromContainer(parent, token);\n if (Predicate.isObjectOrArray(child)) return child;\n const created = makeContainer(tokens[i + 1]);\n setOnContainer(parent, token, created);\n return created;\n });\n // A repeated bare key (no brackets) collapses into an array.\n if (last !== \"\" && !Array.isArray(node)) {\n const existing = getFromContainer(node, last);\n if (existing !== undefined) {\n const arr = Array.isArray(existing) ? existing : [existing];\n arr.push(value);\n setOnContainer(node, last, arr);\n return;\n }\n }\n setOnContainer(node, last, value);\n}\n\n/** Normalise a source into `[name, value]` entries. */\nfunction entriesOf(source: FormBodySource): Iterable<readonly [string, unknown]> {\n if (source instanceof FormData || source instanceof URLSearchParams) return source.entries();\n return source;\n}\n\n/**\n * Parse a form body into a nested object / array tree. Field names use dot or\n * bracket path notation (see {@link parseFieldPath}); repeated bare keys\n * become arrays; `File` values pass through. The result is ready to hand to\n * `Schema.decodeUnknown` with a schema whose leaves coerce the string inputs.\n *\n * @example\n * ```ts\n * import { FormData } from \"@thomasfosterau/effect-forms\"\n *\n * const body = new URLSearchParams([\n * [\"author.name\", \"Ada\"],\n * [\"tags[]\", \"math\"],\n * [\"tags[]\", \"engines\"],\n * ])\n * console.log(FormData.formDataToObject(body))\n * // { author: { name: \"Ada\" }, tags: [\"math\", \"engines\"] }\n * ```\n */\nexport function formDataToObject(source: FormBodySource): Record<string, unknown> {\n return Arr.reduce(entriesOf(source), Rec.empty<string, unknown>(), (root, [name, value]) => {\n const tokens = parseFieldPath(name);\n if (Arr.isReadonlyArrayNonEmpty(tokens)) assign(root, tokens, value);\n return root;\n });\n}\n\n/** Read and parse a `Request`'s form body. Errors (e.g. wrong content-type) reject. */\nexport async function requestToObject(request: Request): Promise<Record<string, unknown>> {\n const formData = await request.formData();\n return formDataToObject(formData);\n}\n","/**\n * Nested get / set along a {@link FormPath}.\n *\n * A form's data is a tree, and the reactive bindings address it by path:\n * `[\"author\", \"name\"]`, `[\"tags\", 0]`. This module reads and writes a value\n * at such a path — {@link getAt} descends, {@link setAt} returns a new tree\n * with the value replaced (structural sharing, never a mutation) so a rune /\n * atom store can diff cheaply. It is the framework-neutral half of the\n * reactive field bindings (the Svelte store leans on it for\n * `setValue` and the array operations).\n *\n * Paths are {@link FormPath}s — the same array-of-segments the {@link\n * module:effect-forms/Errors} tree and {@link module:effect-forms/Issues}\n * walker already speak — so string keys address object properties and number\n * segments address array indices. {@link toString} / {@link fromString}\n * convert to and from the flat `author.name` / `tags[0]` notation the HTML\n * body parser ({@link module:effect-forms/FormData}) uses.\n *\n * @copyright Thomas Foster\n * @module effect-forms/Path\n */\n\nimport * as Arr from \"effect/Array\";\nimport * as Predicate from \"effect/Predicate\";\nimport * as Rec from \"effect/Record\";\nimport { parseFieldPath } from \"./FormData.ts\";\nimport type { FormPath } from \"./Issues.ts\";\n\n/**\n * Read the value at `path`. Returns `undefined` if any segment is missing or\n * traverses a non-object. The empty path returns the object itself.\n */\nexport function getAt(obj: unknown, path: FormPath): unknown {\n // Once a segment lands on a non-object the accumulator is `undefined`, which\n // is itself a non-object, so every later segment keeps it `undefined`.\n // A missing key reads as `undefined` either way, so guarding on `in` first\n // matches a plain property read on any object or array.\n return Arr.reduce(path, obj, (current, segment) =>\n Predicate.isObjectOrArray(current) && Predicate.hasProperty(current, segment)\n ? current[segment]\n : undefined,\n );\n}\n\n/**\n * Return a new tree with the value at `path` replaced by `value`. Objects and\n * arrays along the path are cloned (structural sharing elsewhere); missing\n * containers are created as arrays for numeric segments and objects otherwise.\n * The empty path returns `value`. Pure — `obj` is never mutated.\n *\n * @example\n * ```ts\n * import { Path } from \"@thomasfosterau/effect-forms\"\n *\n * const before = { authors: [{ name: \"Ada\" }] }\n * const after = Path.setAt(before, [\"authors\", 0, \"name\"], \"Grace\")\n * console.log(Path.getAt(after, [\"authors\", 0, \"name\"])) // \"Grace\"\n * console.log(before.authors[0]?.name) // \"Ada\" — the input is untouched\n * ```\n */\nexport function setAt<T>(obj: T, path: FormPath, value: unknown): T {\n return Arr.match(path, {\n // SAFETY: the empty path addresses the whole tree, so `value` is the new\n // root; `setAt`'s documented contract is that the caller writes a value of\n // the shape `T` expects at `path`, which here is `T` itself.\n onEmpty: () => value as T,\n onNonEmpty: ([head, ...rest]) => {\n // `Object(obj)` boxes a primitive root so the spread type-checks; spreading\n // the box copies exactly what spreading `obj` itself would.\n // SAFETY: `clone` is a fresh shallow copy — an array or a plain object —\n // and both accept a read or write under any string or number key.\n const clone = (Arr.isArray(obj) ? [...obj] : { ...Object(obj) }) as Record<\n PropertyKey,\n unknown\n >;\n const child = clone[head];\n clone[head] = Arr.match(rest, {\n onEmpty: () => value,\n onNonEmpty: (tail) =>\n setAt(\n Predicate.isObjectOrArray(child) ? child : Predicate.isNumber(tail[0]) ? [] : {},\n tail,\n value,\n ),\n });\n // SAFETY: `clone` is a shallow copy of `obj` (an array when `obj` is\n // one, a plain object otherwise) differing only at `head`, where the\n // caller's `value` (by `setAt`'s contract, of the type `T` holds at\n // `path`) was written.\n return clone as T;\n },\n });\n}\n\n/** Whether `path` is `root` or nested beneath it. */\nexport function isUnder(path: FormPath, root: FormPath): boolean {\n return path.length >= root.length && Arr.every(root, (segment, i) => path[i] === segment);\n}\n\n/**\n * Render a {@link FormPath} as flat field-name notation: string segments join\n * with `.` and number segments become `[n]` (`[\"tags\", 0]` → `\"tags[0]\"`).\n * The inverse of {@link fromString}.\n */\nexport function toString(path: FormPath): string {\n return Arr.reduce(path, \"\", (out, segment) =>\n Predicate.isNumber(segment) ? `${out}[${segment}]` : out === \"\" ? segment : `${out}.${segment}`,\n );\n}\n\n/**\n * Parse flat field-name notation into a {@link FormPath}: integer tokens become\n * number segments (array indices), everything else stays a string. The inverse\n * of {@link toString}.\n */\nexport function fromString(name: string): FormPath {\n return parseFieldPath(name).map((token) => (/^\\d+$/.test(token) ? Number(token) : token));\n}\n\n/**\n * The deep-key type: every valid {@link toString} path into `T`, as a union of\n * string literals (`\"author.name\"`, `\"tags[0]\"`, `\"tags[0].label\"`, …). This is\n * the type-checked counterpart of a hand-written {@link FormPath} — a typo'd\n * segment or a path into a field that doesn't exist is a compile error. Bounded\n * to a fixed recursion depth ({@link Prev}'s length) so a struct with cycles\n * (there shouldn't be any — form data is JSON-shaped) can't blow up inference.\n *\n * Used by {@link module:effect-forms-svelte/FormStore}'s `field(path)` — see\n * the trade-off recorded in `docs/decisions/` for why this was chosen over the\n * core's (now-removed) opaque `FieldRef`.\n */\nexport type FieldPath<T, D extends number = 8> = T extends object\n ? [D] extends [never]\n ? never\n : {\n [K in Extract<keyof T, string>]: `${K}${PathSuffix<T[K], Prev[D]>}`;\n }[Extract<keyof T, string>]\n : never;\n\n/** Recursion-depth countdown used by {@link FieldPath} / {@link PathValue}. */\ntype Prev = [never, 0, 1, 2, 3, 4, 5, 6, 7];\n\n/**\n * The continuation after a key: nothing, an array index (the bracket closes\n * immediately — `[0]`, not `[0.label]` — then {@link PathSuffix} recurses on\n * the element type for whatever comes after), or `.key`.\n */\ntype PathSuffix<T, D extends number> = [D] extends [never]\n ? \"\"\n : T extends ReadonlyArray<infer E>\n ? \"\" | `[${number}]${PathSuffix<E, Prev[D]>}`\n : T extends object\n ?\n | \"\"\n | { [K in Extract<keyof T, string>]: `.${K}${PathSuffix<T[K], Prev[D]>}` }[Extract<\n keyof T,\n string\n >]\n : \"\";\n\n/** The base key of a single dot-segment (`\"tags[0]\"` → `\"tags\"`). */\ntype SegmentBase<S extends string> = S extends `${infer Base}[${string}` ? Base : S;\n\n/** The bracket indices of a single dot-segment (`\"tags[0][1]\"` → `[\"0\", \"1\"]`), untyped past presence. */\ntype SegmentIndices<S extends string> = S extends `${string}[${infer Idx}]${infer Rest}`\n ? [Idx, ...SegmentIndicesRest<Rest>]\n : [];\n\n/** Continuation of {@link SegmentIndices} once the leading (non-bracket) prefix has been consumed. */\ntype SegmentIndicesRest<S extends string> = S extends `[${infer Idx}]${infer Rest}`\n ? [Idx, ...SegmentIndicesRest<Rest>]\n : [];\n\n/** Step through an array type once per entry in `Idxs`. */\ntype ApplyIndices<T, Idxs extends readonly unknown[]> = Idxs extends [unknown, ...infer Rest]\n ? T extends ReadonlyArray<infer E>\n ? ApplyIndices<E, Rest>\n : never\n : T;\n\n/** Resolve a single dot-segment (base key plus any bracket indices) against `T`. */\ntype ResolveSegment<T, S extends string> =\n SegmentBase<S> extends keyof T ? ApplyIndices<T[SegmentBase<S>], SegmentIndices<S>> : never;\n\n/**\n * The value type at a {@link FieldPath} `P` into `T` — the leaf type `field(path)`\n * resolves to. `PathValue<{ tags: string[] }, \"tags[0]\">` is `string`.\n */\nexport type PathValue<T, P extends string> = P extends `${infer Head}.${infer Rest}`\n ? PathValue<ResolveSegment<T, Head>, Rest>\n : ResolveSegment<T, P>;\n\n/**\n * Maps an array index that existed before an index-shifting mutation to where\n * it lives afterwards, or to `null` when the item at that index no longer\n * exists (its per-index state should be dropped). Built by {@link removeAt} /\n * {@link insertAt} / {@link swapAt} / {@link moveAt} / {@link replaceAt}, and\n * consumed by {@link remapIndex} / {@link remapKeyedRecord} and\n * `Errors.remapIndices` to keep per-index state (touched flags, error trees)\n * in sync with the row it was recorded against.\n */\nexport type IndexMap = (index: number) => number | null;\n\n/** The {@link IndexMap} for removing the item at `index`: shifts later indices down by one. */\nexport function removeAt(index: number): IndexMap {\n return (i) => (i === index ? null : i > index ? i - 1 : i);\n}\n\n/** The {@link IndexMap} for inserting an item at `index`: shifts `index` and later up by one. */\nexport function insertAt(index: number): IndexMap {\n return (i) => (i >= index ? i + 1 : i);\n}\n\n/** The {@link IndexMap} for swapping the items at `a` and `b`. */\nexport function swapAt(a: number, b: number): IndexMap {\n return (i) => (i === a ? b : i === b ? a : i);\n}\n\n/** The {@link IndexMap} for moving the item at `from` to `to`, shifting the items between. */\nexport function moveAt(from: number, to: number): IndexMap {\n return (i) => {\n if (i === from) return to;\n if (from < to) return i > from && i <= to ? i - 1 : i;\n return i >= to && i < from ? i + 1 : i;\n };\n}\n\n/** The {@link IndexMap} for replacing the item at `index` in place: drops only that index's state. */\nexport function replaceAt(index: number): IndexMap {\n return (i) => (i === index ? null : i);\n}\n\n/** The {@link IndexMap} for clearing every item in the array: drops all per-index state. */\nexport const clearAll: IndexMap = () => null;\n\n/**\n * Remap `path`'s index segment immediately under `arrayPath` through `map`.\n * Paths outside `arrayPath`, and `arrayPath` itself (a whole-array message),\n * pass through unchanged; a path whose index segment maps to `null` is\n * dropped (returns `null`) — its item no longer exists. Pure.\n */\nexport function remapIndex(path: FormPath, arrayPath: FormPath, map: IndexMap): FormPath | null {\n if (!isUnder(path, arrayPath) || path.length === arrayPath.length) return path;\n const segment = path[arrayPath.length];\n if (!Predicate.isNumber(segment)) return path;\n const mapped = map(segment);\n if (mapped === null) return null;\n if (mapped === segment) return path;\n return [...path.slice(0, arrayPath.length), mapped, ...path.slice(arrayPath.length + 1)];\n}\n\n/**\n * Remap every entry of a flat, path-keyed record (a {@link toString} key per\n * entry — e.g. touched flags) through {@link remapIndex}. Entries whose path\n * maps to `null` are dropped. Pure.\n */\nexport function remapKeyedRecord<T>(\n record: Record<string, T>,\n arrayPath: FormPath,\n map: IndexMap,\n): Record<string, T> {\n return Rec.fromEntries(\n Arr.flatMap(Rec.toEntries(record), ([key, value]) => {\n const remapped = remapIndex(fromString(key), arrayPath, map);\n return remapped === null ? [] : [[toString(remapped), value] as const];\n }),\n );\n}\n","/**\n * The nested form-error tree and its operations.\n *\n * {@link FormErrors} is a nested error tree so a UI can read\n * `errors.author.name` or `errors.tags[0]` directly. The representation is:\n *\n * - a **leaf** field's messages are a `string[]` keyed by the field name\n * (`{ title: [\"Required\"] }`);\n * - a **branch** (nested object / array) is a nested `FormErrors`\n * (`{ author: { name: [\"Required\"] } }`); array elements are keyed by their\n * stringified index (`{ tags: { \"0\": [\"Too short\"] } }`);\n * - a message attached to a **branch itself** (a cross-field or\n * whole-array message) lives under that branch's `_errors` key.\n *\n * The tree is plain JSON — it serialises across the SSR boundary unchanged —\n * and the operations here ({@link issuesToErrors}, {@link flattenErrors},\n * {@link setError}, {@link mergeErrors}, …) are pure and return new trees.\n *\n * @copyright Thomas Foster\n * @module effect-forms/Errors\n */\n\nimport * as Arr from \"effect/Array\";\nimport * as Predicate from \"effect/Predicate\";\nimport * as Rec from \"effect/Record\";\nimport type { FormIssue, FormPath } from \"./Issues.ts\";\nimport { type IndexMap, remapIndex } from \"./Path.ts\";\n\n/** The root key under which a branch's own (non-field) messages are stored. */\nexport const ROOT_ERROR_KEY = \"_errors\";\n\n/**\n * A nested tree of validation messages. Leaves are `string[]`; branches are\n * nested {@link FormErrors}; a branch's own messages live under\n * {@link ROOT_ERROR_KEY}. Always JSON-serialisable.\n */\nexport interface FormErrors {\n readonly [ROOT_ERROR_KEY]?: readonly string[];\n readonly [key: string]: FormErrors | readonly string[] | undefined;\n}\n\n/**\n * A {@link FormErrors} tree typed to the shape of `A`, so fields are accessed\n * by name with type-safety: for `A = { name: string; author: { bio: string } }`,\n * `errors.name` is `readonly string[] | undefined` and `errors.author?.bio`\n * likewise. A branch's own (cross-field) messages live under `_errors`. The\n * runtime value is an ordinary {@link FormErrors} — this only refines the type.\n */\nexport type TypedFormErrors<A> = [NonNullable<A>] extends [ReadonlyArray<infer E>]\n ? {\n readonly [index: number]: TypedFormErrors<E> | undefined;\n readonly _errors?: readonly string[];\n }\n : [NonNullable<A>] extends [object]\n ? { readonly [K in keyof A]?: TypedFormErrors<A[K]> } & { readonly _errors?: readonly string[] }\n : readonly string[];\n\n/** Mutable mirror of {@link FormErrors} used while building a tree. */\ninterface MutableNode {\n [key: string]: string[] | MutableNode | undefined;\n}\n\nconst isMessageList = <T>(value: T): value is Extract<T, readonly string[]> =>\n Arr.isArray(value) && Arr.every(value, Predicate.isString);\n\nconst ownErrors = (node: MutableNode): string[] => {\n const existing = node[ROOT_ERROR_KEY];\n if (isMessageList(existing)) return existing;\n const created: string[] = [];\n node[ROOT_ERROR_KEY] = created;\n return created;\n};\n\n/**\n * Return the branch at `node[key]`, making one in place if needed: an absent\n * key gets a new empty branch, and a leaf's message list is converted into a\n * branch carrying those same messages as its own {@link ROOT_ERROR_KEY} (a\n * path used as a leaf is now needed as a branch too). Shared by {@link insert}\n * and {@link setLeaf}'s path-descent, the one piece of their branch-walking\n * that's identical.\n */\nconst branchAt = (node: MutableNode, key: string): MutableNode => {\n const existing = node[key];\n if (existing === undefined) {\n const created: MutableNode = {};\n node[key] = created;\n return created;\n }\n if (isMessageList(existing)) {\n const promoted: MutableNode = { [ROOT_ERROR_KEY]: existing };\n node[key] = promoted;\n return promoted;\n }\n return existing;\n};\n\n/**\n * Insert one message at `path` into a mutable error tree. A leaf becomes a\n * `string[]`; if the same path is later used as a branch the existing list is\n * preserved under {@link ROOT_ERROR_KEY}; if a branch later receives its own\n * message it lands under {@link ROOT_ERROR_KEY}. The empty path targets the\n * tree root's {@link ROOT_ERROR_KEY}.\n */\nconst insert = (root: MutableNode, path: FormPath, message: string): void => {\n if (!Arr.isReadonlyArrayNonEmpty(path)) {\n ownErrors(root).push(message);\n return;\n }\n\n const node = Arr.reduce(Arr.initNonEmpty(path), root, (parent, segment) =>\n branchAt(parent, String(segment)),\n );\n\n const last = String(Arr.lastNonEmpty(path));\n const existing = node[last];\n if (existing === undefined) {\n node[last] = [message];\n } else if (isMessageList(existing)) {\n existing.push(message);\n } else {\n // The leaf key is already a branch — attach to its own `_errors`.\n ownErrors(existing).push(message);\n }\n};\n\n/** Build a nested {@link FormErrors} tree from a flat list of {@link FormIssue}s. */\nexport function issuesToErrors(issues: ReadonlyArray<FormIssue>): FormErrors {\n const root: MutableNode = {};\n Arr.forEach(issues, (issue) => insert(root, issue.path, issue.message));\n return root;\n}\n\n/**\n * Flatten a {@link FormErrors} tree back into a list of {@link FormIssue}s, in\n * depth-first order. The inverse of {@link issuesToErrors} (modulo message\n * ordering within a single field).\n */\nexport function flattenErrors(errors: FormErrors): FormIssue[] {\n const walk = (node: FormErrors, path: Array<string | number>): Array<FormIssue> =>\n Arr.flatMap(Rec.keys<string, FormErrors[string]>(node), (key): Array<FormIssue> => {\n if (key === ROOT_ERROR_KEY) {\n return Arr.map(node[ROOT_ERROR_KEY] ?? [], (message) => ({ path, message }));\n }\n const value = node[key];\n if (value === undefined) return [];\n const segment: string | number = /^\\d+$/.test(key) ? Number(key) : key;\n if (isMessageList(value)) {\n return Arr.map(value, (message) => ({ path: [...path, segment], message }));\n }\n return walk(value, [...path, segment]);\n });\n\n return walk(errors, []);\n}\n\n/** Whether the tree carries any message anywhere. */\nexport function hasErrors(errors: FormErrors): boolean {\n return Arr.isReadonlyArrayNonEmpty(flattenErrors(errors));\n}\n\n/**\n * Read the messages attached directly to `path` (a leaf's list, or a branch's\n * own {@link ROOT_ERROR_KEY}). Returns `undefined` when the path has no direct\n * messages.\n */\nexport function getError(errors: FormErrors, path: FormPath): readonly string[] | undefined {\n // Once the walk leaves the tree (an absent key, or a leaf with path left\n // over) the accumulator is `undefined`, and stays so for every later segment.\n const node = Arr.reduce<string | number, FormErrors | readonly string[] | undefined>(\n path,\n errors,\n (current, segment) =>\n current === undefined || isMessageList(current) ? undefined : current[String(segment)],\n );\n if (node === undefined) return undefined;\n if (isMessageList(node)) return node;\n return node[ROOT_ERROR_KEY];\n}\n\n/** Return a new tree with `message` added at `path`. Pure. */\nexport function setError(errors: FormErrors, path: FormPath, message: string): FormErrors {\n // SAFETY: `structuredClone` returns a fresh deep copy that nothing else\n // references, and `MutableNode` is `FormErrors` minus `readonly`, so\n // mutating the copy in place cannot be observed through `errors`.\n const next = structuredClone(errors) as MutableNode;\n insert(next, path, message);\n return next;\n}\n\n/** Combine two error trees into one (left then right). Pure. */\nexport function mergeErrors(a: FormErrors, b: FormErrors): FormErrors {\n return issuesToErrors([...flattenErrors(a), ...flattenErrors(b)]);\n}\n\n/** The empty error tree. */\nexport const noErrors: FormErrors = {};\n\n/**\n * Remap the array-index segments of every message under `arrayPath` through\n * `map` (see {@link module:effect-forms/Path.IndexMap}), so an error recorded\n * against a row follows that row through a remove / insert / swap / move.\n * Messages whose index maps to `null` are dropped. Pure.\n */\nexport function remapIndices(errors: FormErrors, arrayPath: FormPath, map: IndexMap): FormErrors {\n return issuesToErrors(\n Arr.flatMap(flattenErrors(errors), (issue) => {\n const path = remapIndex(issue.path, arrayPath, map);\n return path === null ? [] : [{ path, message: issue.message }];\n }),\n );\n}\n\n/**\n * Return a new tree with the messages *directly at* `path` replaced by\n * `messages` (removing the key when `messages` is empty), leaving every other\n * path — including children of a branch `path` — untouched. Unlike\n * {@link setError} (which appends one message), this is the \"this field's\n * validator just ran, here is its complete result\" operation a per-field\n * validator needs: it can clear a field back to valid without disturbing\n * sibling or nested messages. Pure.\n */\nexport function replaceAt(\n errors: FormErrors,\n path: FormPath,\n messages: readonly string[],\n): FormErrors {\n // SAFETY: `structuredClone` returns a fresh deep copy that nothing else\n // references, and `MutableNode` is `FormErrors` minus `readonly`, so\n // mutating the copy in place cannot be observed through `errors`.\n const next = structuredClone(errors) as MutableNode;\n setLeaf(next, path, messages);\n return next;\n}\n\nconst setLeaf = (root: MutableNode, path: FormPath, messages: readonly string[]): void => {\n if (!Arr.isReadonlyArrayNonEmpty(path)) {\n setOwnMessages(root, messages);\n return;\n }\n const parent = descendClearing(root, Arr.initNonEmpty(path), messages);\n if (parent === undefined) return; // an absent branch on the clearing path — nothing to clear\n setLeafMessages(parent, String(Arr.lastNonEmpty(path)), messages);\n};\n\n/** Set or clear a node's own {@link ROOT_ERROR_KEY} messages. */\nconst setOwnMessages = (node: MutableNode, messages: readonly string[]): void => {\n if (Arr.isReadonlyArrayEmpty(messages)) delete node[ROOT_ERROR_KEY];\n else node[ROOT_ERROR_KEY] = [...messages];\n};\n\n/**\n * Walk down the `parents` segments (a path minus its last segment) to the\n * leaf's parent branch, creating branches as needed. When\n * `messages` is empty (a clear, not a set) a missing branch means there is\n * nothing to clear — descent stops early and returns `undefined` rather than\n * creating empty branches just to immediately no-op inside them.\n */\nconst descendClearing = (\n root: MutableNode,\n parents: FormPath,\n messages: readonly string[],\n): MutableNode | undefined =>\n // Once a clear meets an absent branch the accumulator is `undefined`, and\n // every later segment leaves the tree untouched.\n Arr.reduce<string | number, MutableNode | undefined>(parents, root, (node, segment) => {\n if (node === undefined) return undefined;\n const key = String(segment);\n if (node[key] === undefined && Arr.isReadonlyArrayEmpty(messages)) return undefined;\n return branchAt(node, key);\n });\n\n/** Set or clear `node[key]`'s messages — a leaf's list, or (if it's already a branch) its own {@link ROOT_ERROR_KEY}. */\nconst setLeafMessages = (node: MutableNode, key: string, messages: readonly string[]): void => {\n const existing = node[key];\n if (existing === undefined) {\n if (Arr.isReadonlyArrayNonEmpty(messages)) node[key] = [...messages];\n return;\n }\n if (isMessageList(existing)) {\n if (Arr.isReadonlyArrayEmpty(messages)) delete node[key];\n else node[key] = [...messages];\n return;\n }\n // Already a branch (has nested children) — replace its own messages,\n // preserving the children.\n setOwnMessages(existing, messages);\n};\n\n/**\n * Where a message in a {@link FormErrors} tree came from. `\"server\"` messages\n * come from the last submission response; `\"client\"` messages come from\n * browser-side validation (schema re-run, per-field validators, client-run\n * `refineEffect` refinements). Kept as parallel trees (see {@link\n * SourcedErrors}) rather than tagging each message, so a UI can style sources\n * differently and client revalidation can clear only its own tree, leaving\n * server-reported failures in place until their field actually changes.\n */\nexport type ErrorSource = \"client\" | \"server\";\n\n/** Parallel error trees, one per {@link ErrorSource}, plus their {@link mergeSourced} view. */\nexport interface SourcedErrors {\n readonly client: FormErrors;\n readonly server: FormErrors;\n}\n\n/** Both source trees empty. */\nexport const emptySourced: SourcedErrors = { client: noErrors, server: noErrors };\n\n/** Combine both source trees into the single tree a UI reads (server first, then client). */\nexport function mergeSourced(sourced: SourcedErrors): FormErrors {\n return mergeErrors(sourced.server, sourced.client);\n}\n\n/**\n * Return new {@link SourcedErrors} with `source`'s tree updated at `path` via\n * {@link replaceAt} (the other source's tree is untouched). Pure.\n */\nexport function setSourceAt(\n sourced: SourcedErrors,\n source: ErrorSource,\n path: FormPath,\n messages: readonly string[],\n): SourcedErrors {\n return { ...sourced, [source]: replaceAt(sourced[source], path, messages) };\n}\n\n/** Clear `source`'s messages at `path` (and nowhere else). Shorthand for {@link setSourceAt} with `[]`. */\nexport function clearSourceAt(\n sourced: SourcedErrors,\n source: ErrorSource,\n path: FormPath,\n): SourcedErrors {\n return setSourceAt(sourced, source, path, []);\n}\n","/**\n * Field definitions for the {@link module:effect-forms/FormBuilder}.\n *\n * A {@link FormBuilder} composes a form one field at a time. Each field is\n * either a **scalar** {@link FieldDef} (bound to a leaf schema) or an\n * **array** {@link ArrayFieldDef} (bound to an item schema — the field is a\n * list of that item). This module is the small vocabulary the builder is made\n * of: the two field shapes, their guards, the type-level projections that turn\n * a record of fields into the form's encoded / decoded data types, and the\n * default-value derivation used to seed a blank form (or a freshly-appended\n * array item).\n *\n * Defaults are *encoded* — an HTML form holds strings, so a scalar seeds to\n * `\"\"`, an array to `[]`, and a nested struct recurses. Coercion back to the\n * decoded type is the schema's job at validate time (see\n * {@link module:effect-forms/Coerce}).\n *\n * @copyright Thomas Foster\n * @module effect-forms/Field\n */\n\nimport * as Arr from \"effect/Array\";\nimport * as Option from \"effect/Option\";\nimport * as Predicate from \"effect/Predicate\";\nimport * as Rec from \"effect/Record\";\nimport type * as Schema from \"effect/Schema\";\nimport * as SchemaAST from \"effect/SchemaAST\";\nimport type { FormPath } from \"./Issues.ts\";\n\n/** A scalar field: a key bound to a leaf (or nested struct) schema. */\nexport interface FieldDef<K extends string, S extends Schema.Top> {\n readonly _tag: \"field\";\n readonly key: K;\n readonly schema: S;\n}\n\n/** An array field: a key bound to an item schema; the field is a list of items. */\nexport interface ArrayFieldDef<K extends string, S extends Schema.Top> {\n readonly _tag: \"array\";\n readonly key: K;\n readonly itemSchema: S;\n}\n\n/** Either field shape, with the key / schema types erased. */\nexport type AnyFieldDef = FieldDef<string, Schema.Top> | ArrayFieldDef<string, Schema.Top>;\n\n/** A record of fields, keyed by field name — the builder's accumulated shape. */\nexport type FieldsRecord = Record<string, AnyFieldDef>;\n\n/** Whether `def` is an {@link ArrayFieldDef}. */\nexport const isArrayFieldDef = (def: AnyFieldDef): def is ArrayFieldDef<string, Schema.Top> =>\n Predicate.isTagged(def, \"array\");\n\n/** Whether `def` is a scalar {@link FieldDef}. */\nexport const isFieldDef = (def: AnyFieldDef): def is FieldDef<string, Schema.Top> =>\n Predicate.isTagged(def, \"field\");\n\n/** Construct a scalar {@link FieldDef}. */\nexport const makeField = <K extends string, S extends Schema.Top>(\n key: K,\n schema: S,\n): FieldDef<K, S> => ({ _tag: \"field\", key, schema });\n\n/** Construct an {@link ArrayFieldDef}. */\nexport const makeArrayField = <K extends string, S extends Schema.Top>(\n key: K,\n itemSchema: S,\n): ArrayFieldDef<K, S> => ({ _tag: \"array\", key, itemSchema });\n\n/** The encoded (form-input) data type for a record of fields. */\nexport type EncodedFromFields<T extends FieldsRecord> = {\n readonly [K in keyof T]: T[K] extends FieldDef<string, infer S>\n ? S[\"Encoded\"]\n : T[K] extends ArrayFieldDef<string, infer S>\n ? ReadonlyArray<S[\"Encoded\"]>\n : never;\n};\n\n/** The decoded (validated) data type for a record of fields. */\nexport type DecodedFromFields<T extends FieldsRecord> = {\n readonly [K in keyof T]: T[K] extends FieldDef<string, infer S>\n ? S[\"Type\"]\n : T[K] extends ArrayFieldDef<string, infer S>\n ? ReadonlyArray<S[\"Type\"]>\n : never;\n};\n\n/**\n * Derive a blank *encoded* default from a schema AST: a struct recurses into\n * its properties, an array seeds to `[]`, and any scalar seeds to `\"\"` (an\n * HTML input's empty value). Conservative by design — a value that will not\n * decode cleanly is still a valid *starting* value the user edits.\n */\nexport function defaultValue(schema: Schema.Top): unknown {\n return fromAst(schema.ast);\n}\n\n/**\n * Unwrap a union to its first structured (struct / array) member — an\n * `optional` / `NullOr` wrapper (`Struct | undefined` / `Struct | null`)\n * still resolves to the wrapped struct or array's AST rather than a scalar\n * fallback. Non-unions, and unions with no structured member, pass through.\n */\nfunction unwrapToStructured(ast: SchemaAST.AST): SchemaAST.AST {\n if (!SchemaAST.isUnion(ast)) return ast;\n return Arr.findFirst(ast.types, (member) => {\n const inner = unwrapToStructured(member);\n return SchemaAST.isObjects(inner) || SchemaAST.isArrays(inner)\n ? Option.some(inner)\n : Option.none();\n }).pipe(Option.getOrElse(() => ast));\n}\n\n/** A seeded blank value: `\"\"` for a leaf, `[]` for an array, a struct of blanks for a struct. */\ntype BlankValue = \"\" | Array<never> | BlankStruct;\n\n/** A struct of {@link BlankValue}s, one per property. */\ninterface BlankStruct {\n readonly [key: string]: BlankValue;\n}\n\nfunction fromAst(ast: SchemaAST.AST): BlankValue {\n const unwrapped = unwrapToStructured(ast);\n if (SchemaAST.isObjects(unwrapped)) {\n return Rec.fromEntries(\n Arr.map(unwrapped.propertySignatures, (ps) => [String(ps.name), fromAst(ps.type)] as const),\n );\n }\n if (SchemaAST.isArrays(unwrapped)) return [];\n return \"\";\n}\n\n/** The item AST of an {@link SchemaAST.Arrays} node: the rest (spread) type, or the first tuple element. */\nfunction arrayElementAst(ast: SchemaAST.Arrays): SchemaAST.AST | undefined {\n return ast.rest[0] ?? ast.elements[0];\n}\n\n/** Descend one array-index path segment from an (already-unwrapped) array AST. */\nfunction stepIntoArray(ast: SchemaAST.AST): SchemaAST.AST | undefined {\n if (!SchemaAST.isArrays(ast)) return undefined;\n const item = arrayElementAst(ast);\n return item === undefined ? undefined : unwrapToStructured(item);\n}\n\n/** Descend one struct-property path segment from an (already-unwrapped) struct AST. */\nfunction stepIntoStruct(ast: SchemaAST.AST, key: string): SchemaAST.AST | undefined {\n if (!SchemaAST.isObjects(ast)) return undefined;\n const ps = ast.propertySignatures.find((p) => String(p.name) === key);\n return ps === undefined ? undefined : unwrapToStructured(ps.type);\n}\n\n/**\n * Walk `schema`'s AST along `path` — through struct properties and array\n * elements alike — and return the AST found there, or `undefined` if `path`\n * doesn't resolve (a missing property, or a non-array indexed). Unlike a\n * {@link FieldsRecord} lookup, this works for any nesting depth and for\n * schemas built directly with `Form.make` (no builder involved).\n */\nexport function schemaAt(schema: Schema.Top, path: FormPath): SchemaAST.AST | undefined {\n // Once a step finds no matching AST the accumulator is `undefined`, and\n // stays so for every later segment.\n return Arr.reduce<string | number, SchemaAST.AST | undefined>(\n path,\n unwrapToStructured(schema.ast),\n (ast, segment) =>\n ast === undefined\n ? undefined\n : Predicate.isNumber(segment)\n ? stepIntoArray(ast)\n : stepIntoStruct(ast, segment),\n );\n}\n\n/**\n * Derive a blank encoded item for the array field at `path` (any nesting\n * depth) by walking `schema`'s AST there — the nested-array counterpart to\n * {@link defaultValue}. Returns `\"\"` when `path` doesn't resolve to an array.\n */\nexport function defaultItemAt(schema: Schema.Top, path: FormPath): unknown {\n const ast = schemaAt(schema, path);\n const item = ast !== undefined && SchemaAST.isArrays(ast) ? arrayElementAst(ast) : undefined;\n return item === undefined ? \"\" : fromAst(item);\n}\n\n/** Seed a blank encoded value for every field: `[]` for arrays, `\"\"` otherwise. */\nexport function defaultEncodedValues(fields: FieldsRecord): Record<string, unknown> {\n return Rec.map(fields, (def) => (isArrayFieldDef(def) ? [] : defaultValue(def.schema)));\n}\n","/**\n * Form issues — the flat, path-addressed representation of a single\n * validation failure, plus the walker that turns an Effect Schema issue\n * tree into a list of them.\n *\n * An Effect Schema decode failure is a recursive {@link SchemaIssue.Issue}\n * tree: `Pointer` nodes carry path segments, `Composite` / `AnyOf` group\n * sibling failures, `Filter` / `Encoding` wrap a single inner failure, and\n * the leaves (`InvalidType`, `InvalidValue`, `MissingKey`, …) describe the\n * actual problem. {@link schemaIssuesToFormIssues} flattens that tree into\n * one {@link FormIssue} per leaf, accumulating the `Pointer.path` segments\n * into a `FormPath`. This is the framework-neutral\n * flattened error list and the same walk the repo's JSON:API materialiser\n * performs — here it stays UI-shaped (`path` + `message`) rather than\n * JSON:API-pointer-shaped.\n *\n * @copyright Thomas Foster\n * @module effect-forms/Issues\n */\n\nimport * as Arr from \"effect/Array\";\nimport * as Match from \"effect/Match\";\nimport * as Predicate from \"effect/Predicate\";\nimport type * as Schema from \"effect/Schema\";\nimport * as SchemaIssue from \"effect/SchemaIssue\";\n\n/**\n * A path into the form's data. String segments address object keys; number\n * segments address array indices. The empty path addresses the form root\n * (a whole-form / cross-field message).\n */\nexport type FormPath = ReadonlyArray<string | number>;\n\n/** One validation failure: where it happened ({@link FormPath}) and why. */\nexport interface FormIssue {\n readonly path: FormPath;\n readonly message: string;\n}\n\n/**\n * Render one terminal issue into a human-readable message. The default is\n * Effect Schema's own formatter (`SchemaIssue.makeFormatterDefault`), which\n * renders a sentence such as `Expected string`. (An issue's `toString` is not\n * a readable rendering in Effect 4.0.0: it prints `[object Object]`.)\n * Override it to localise or to map specific `_tag`s to custom copy.\n *\n * A failed schema check reaches the formatter as the `Filter` node that\n * wraps its leaf, because the check's own message (e.g. `Expected a value\n * with a length of at least 2`) lives on the `Filter`, not on the leaf.\n */\nexport type LeafFormatter = (issue: SchemaIssue.Issue) => string;\n\n/** The default leaf formatter — Effect Schema's own rendering of the issue. */\nexport const defaultLeafFormatter: LeafFormatter = SchemaIssue.makeFormatterDefault();\n\n/**\n * Normalise a `Pointer.path` segment into a {@link FormPath} segment: integer\n * keys become numbers (array indices), everything else becomes a string.\n */\nconst normaliseSegment = (segment: PropertyKey): string | number => {\n if (Predicate.isNumber(segment)) return segment;\n if (Predicate.isSymbol(segment)) return segment.toString();\n return /^\\d+$/.test(segment) ? Number(segment) : segment;\n};\n\n/** Whether `issue` is a terminal leaf: it wraps no further issues. */\nconst isTerminal: (issue: SchemaIssue.Issue) => boolean = Match.type<SchemaIssue.Issue>().pipe(\n Match.tag(\"Pointer\", \"Composite\", \"Filter\", \"Encoding\", () => false),\n Match.tag(\"AnyOf\", (issue) => Arr.isReadonlyArrayEmpty(issue.issues)),\n Match.orElse(() => true),\n);\n\n/**\n * Walk an Effect Schema issue tree, emitting one {@link FormIssue} per leaf\n * failure. Path segments accumulate through `Pointer` nodes; `Composite` and\n * `AnyOf` fan out to siblings; `Filter` / `Encoding` are transparent\n * wrappers. An `AnyOf` with no recorded member failures (a bare union\n * mismatch) is itself a leaf.\n */\nexport function schemaIssuesToFormIssues(\n issue: SchemaIssue.Issue,\n format: LeafFormatter = defaultLeafFormatter,\n): FormIssue[] {\n const walk = (current: SchemaIssue.Issue, path: Array<string | number>): Array<FormIssue> => {\n const leaf = (node: SchemaIssue.Issue): Array<FormIssue> => [{ path, message: format(node) }];\n const walkAll = (children: ReadonlyArray<SchemaIssue.Issue>): Array<FormIssue> =>\n Arr.flatMap(children, (child) => walk(child, path));\n return Match.value(current).pipe(\n Match.tag(\"Pointer\", (pointer) =>\n walk(pointer.issue, [...path, ...Arr.map(pointer.path, normaliseSegment)]),\n ),\n Match.tag(\"Composite\", (composite) => walkAll(composite.issues)),\n Match.tag(\"AnyOf\", (anyOf) =>\n Arr.match(anyOf.issues, { onEmpty: () => leaf(anyOf), onNonEmpty: walkAll }),\n ),\n // A check that fails on the value itself: its message sits on the\n // `Filter` node, so format that rather than the bare leaf inside.\n Match.tag(\"Filter\", (filter) =>\n isTerminal(filter.issue) ? leaf(filter) : walk(filter.issue, path),\n ),\n Match.tag(\"Encoding\", (encoding) => walk(encoding.issue, path)),\n Match.orElse(leaf),\n );\n };\n\n return walk(issue, []);\n}\n\n/**\n * Normalise a filter predicate's {@link Schema.FilterOutput} into a flat list of\n * {@link FormIssue}s (empty on success). Shared by whole-form refinements\n * ({@link module:effect-forms/FormBuilder}) and per-field validators\n * ({@link module:effect-forms/Validate}) so both fold into the same nested\n * error tree as schema-decode failures.\n */\nexport function filterOutputToFormIssues(output: Schema.FilterOutput): FormIssue[] {\n if (output === undefined || output === true) return [];\n if (output === false) return [{ path: [], message: \"Invalid value\" }];\n if (isFilterIssueList(output)) return Arr.flatMap(output, filterIssueToFormIssues);\n return filterIssueToFormIssues(output);\n}\n\n/** `Array.isArray` as a guard that also narrows away a `ReadonlyArray` member. */\nconst isFilterIssueList = (\n output: Schema.FilterIssue | ReadonlyArray<Schema.FilterIssue>,\n): output is ReadonlyArray<Schema.FilterIssue> => Arr.isArray(output);\n\nfunction filterIssueToFormIssues(issue: Schema.FilterIssue): FormIssue[] {\n if (Predicate.isString(issue)) return [{ path: [], message: issue }];\n if (\"path\" in issue && \"issue\" in issue) {\n const prefix = Arr.map(issue.path, (s) => (Predicate.isSymbol(s) ? s.toString() : s));\n const inner = Predicate.isString(issue.issue)\n ? [{ path: [], message: issue.issue }]\n : schemaIssuesToFormIssues(issue.issue);\n return inner.map((i) => ({ path: [...prefix, ...i.path], message: i.message }));\n }\n return schemaIssuesToFormIssues(issue);\n}\n","/**\n * Compose a form schema one field at a time, with cross-field refinements.\n *\n * {@link make} (aliased {@link empty}) starts an empty builder;\n * {@link FormBuilder.addField} adds a scalar or array field, {@link\n * FormBuilder.merge} folds another builder in, and {@link FormBuilder.refine} /\n * {@link FormBuilder.refineEffect} attach cross-field validations. The point is\n * incremental composition — assemble a large form from small, reusable\n * field-group builders — plus a home for whole-form rules (`password ===\n * confirmPassword`) that a single field schema can't express.\n *\n * {@link buildSchema} collapses the accumulated fields into an ordinary\n * `Schema.Struct` (array fields become `Schema.Array(item)`) and applies every\n * *synchronous* refinement as a struct-level `check`, so the result decodes,\n * derives constraints, and validates exactly like a hand-written schema — hand\n * it straight to {@link module:effect-forms/Form}'s `make`. *Asynchronous*\n * refinements can't be schema checks (Effect Schema has no effectful filter),\n * so they're carried on the builder and run by the Effect-returning validate\n * path — {@link asyncRefinements} exposes them and {@link runAsyncRefinements}\n * runs them into a {@link FormIssue} list.\n *\n * @copyright Thomas Foster\n * @module effect-forms/FormBuilder\n */\n\nimport * as Arr from \"effect/Array\";\nimport * as Effect from \"effect/Effect\";\nimport * as Predicate from \"effect/Predicate\";\nimport * as Rec from \"effect/Record\";\nimport * as Schema from \"effect/Schema\";\nimport type {\n AnyFieldDef,\n ArrayFieldDef,\n DecodedFromFields,\n EncodedFromFields,\n FieldDef,\n FieldsRecord,\n} from \"./Field.ts\";\nimport * as Field from \"./Field.ts\";\nimport type * as Issues from \"./Issues.ts\";\nimport type { FormIssue } from \"./Issues.ts\";\nimport { filterOutputToFormIssues as issuesFromFilterOutput } from \"./Issues.ts\";\n\n/** Brand for a {@link FormBuilder}. */\nexport const TypeId: unique symbol = Symbol.for(\"@thomasfosterau/effect-forms/FormBuilder\");\nexport type TypeId = typeof TypeId;\n\n/** A synchronous cross-field refinement — becomes a struct-level schema check. */\ninterface SyncRefinement {\n readonly _tag: \"sync\";\n readonly fn: (values: unknown) => Schema.FilterOutput;\n}\n\n/** An asynchronous cross-field refinement — run by the Effect validate path. */\ninterface AsyncRefinement {\n readonly _tag: \"async\";\n readonly fn: (values: unknown) => Effect.Effect<Schema.FilterOutput, never, unknown>;\n}\n\ntype Refinement = SyncRefinement | AsyncRefinement;\n\n/**\n * A form assembled field by field. `TFields` accumulates the field record;\n * `R` accumulates the schemas' decoding-service requirements. Immutable — every\n * method returns a new builder.\n */\nexport interface FormBuilder<TFields extends FieldsRecord, R> {\n readonly [TypeId]: TypeId;\n readonly fields: TFields;\n readonly refinements: ReadonlyArray<Refinement>;\n readonly _R?: R;\n\n /** Add a scalar field from a {@link FieldDef}. */\n addField<K extends string, S extends Schema.Top>(\n field: FieldDef<K, S>,\n ): FormBuilder<TFields & { readonly [key in K]: FieldDef<K, S> }, R | S[\"DecodingServices\"]>;\n /** Add an array field from an {@link ArrayFieldDef}. */\n addField<K extends string, S extends Schema.Top>(\n field: ArrayFieldDef<K, S>,\n ): FormBuilder<TFields & { readonly [key in K]: ArrayFieldDef<K, S> }, R | S[\"DecodingServices\"]>;\n /** Add a scalar field from a key and schema. */\n addField<K extends string, S extends Schema.Top>(\n key: K,\n schema: S,\n ): FormBuilder<TFields & { readonly [key in K]: FieldDef<K, S> }, R | S[\"DecodingServices\"]>;\n\n /** Fold another builder's fields and refinements into this one. */\n merge<TFields2 extends FieldsRecord, R2>(\n other: FormBuilder<TFields2, R2>,\n ): FormBuilder<TFields & TFields2, R | R2>;\n\n /**\n * Attach a synchronous whole-form validation. The predicate sees the decoded\n * values and returns a {@link Schema.FilterOutput} (`undefined` / `true` to\n * pass, a message / `{ path, issue }` / list to fail).\n */\n refine(\n predicate: (values: DecodedFromFields<TFields>) => Schema.FilterOutput,\n ): FormBuilder<TFields, R>;\n\n /** Attach an asynchronous whole-form validation (run by the validate path). */\n refineEffect<RD>(\n predicate: (\n values: DecodedFromFields<TFields>,\n ) => Effect.Effect<Schema.FilterOutput, never, RD>,\n ): FormBuilder<TFields, R | RD>;\n}\n\n/** `addField`'s overloads as one argument list: a field definition, or a key with its schema. */\ntype AddFieldArgs = readonly [field: AnyFieldDef] | readonly [key: string, schema: Schema.Top];\n\n/** Whether `addField` was called with the `(key, schema)` overload — the only one whose first argument is a string. */\nconst isKeyedArgs = (args: AddFieldArgs): args is readonly [key: string, schema: Schema.Top] =>\n Predicate.isString(args[0]);\n\nconst proto = {\n [TypeId]: TypeId,\n addField(\n this: FormBuilder<FieldsRecord, unknown>,\n ...args: AddFieldArgs\n ): FormBuilder<FieldsRecord, unknown> {\n const field = isKeyedArgs(args) ? Field.makeField(...args) : args[0];\n return build({ ...this.fields, [field.key]: field }, this.refinements);\n },\n merge(\n this: FormBuilder<FieldsRecord, unknown>,\n other: FormBuilder<FieldsRecord, unknown>,\n ): FormBuilder<FieldsRecord, unknown> {\n return build({ ...this.fields, ...other.fields }, [...this.refinements, ...other.refinements]);\n },\n refine(\n this: FormBuilder<FieldsRecord, unknown>,\n predicate: (values: unknown) => Schema.FilterOutput,\n ): FormBuilder<FieldsRecord, unknown> {\n return build(this.fields, [...this.refinements, { _tag: \"sync\", fn: predicate }]);\n },\n refineEffect(\n this: FormBuilder<FieldsRecord, unknown>,\n predicate: (values: unknown) => Effect.Effect<Schema.FilterOutput, never, unknown>,\n ): FormBuilder<FieldsRecord, unknown> {\n return build(this.fields, [...this.refinements, { _tag: \"async\", fn: predicate }]);\n },\n};\n\nfunction build(\n fields: FieldsRecord,\n refinements: ReadonlyArray<Refinement>,\n): FormBuilder<FieldsRecord, unknown> {\n const self = Object.create(proto);\n self.fields = fields;\n self.refinements = refinements;\n return self;\n}\n\n/** Whether `u` is a {@link FormBuilder}. */\nexport const isFormBuilder = (u: unknown): u is FormBuilder<FieldsRecord, unknown> =>\n Predicate.isObjectOrArray(u) && Predicate.hasProperty(u, TypeId);\n\n/** The empty builder — the starting point for {@link FormBuilder.addField}. */\n// SAFETY: a builder with no fields and no refinements is exactly\n// `FormBuilder<{}, never>`; `build` merely erases the field-record and `R`\n// types its shared implementation works over.\nexport const make = build({}, []) as FormBuilder<Record<never, never>, never>;\n\n/** Alias of {@link make} — the empty builder. */\nexport const empty = make;\n\n/**\n * Collapse a builder into an Effect Schema: a `Schema.Struct` of the fields\n * (array fields as `Schema.Array(item)`) with every synchronous refinement\n * applied as a struct-level check. Ready for {@link module:effect-forms/Form}.\n */\nexport function buildSchema<TFields extends FieldsRecord, R>(\n self: FormBuilder<TFields, R>,\n): Schema.Codec<DecodedFromFields<TFields>, EncodedFromFields<TFields>, R> {\n const structFields = Rec.fromEntries(\n Arr.flatMap(Rec.toEntries(self.fields), ([key, def]): Array<readonly [string, Schema.Top]> => {\n if (Field.isArrayFieldDef(def)) return [[key, Schema.Array(def.itemSchema)]];\n if (Field.isFieldDef(def)) return [[key, def.schema]];\n return [];\n }),\n );\n\n const struct: Schema.Top = Schema.Struct(structFields);\n const schema = Arr.reduce(self.refinements, struct, (refined, refinement) =>\n Predicate.isTagged(refinement, \"sync\")\n ? refined.check(Schema.makeFilter(refinement.fn))\n : refined,\n );\n\n // SAFETY: the struct has exactly one property per entry of `self.fields` —\n // a scalar field's schema, or an array field's item schema under\n // `Schema.Array` — which is the shape `DecodedFromFields` /\n // `EncodedFromFields` compute; checks leave `Type` and `Encoded` unchanged;\n // and `R` is by construction the union of those schemas' decoding services.\n return schema as Schema.Codec<DecodedFromFields<TFields>, EncodedFromFields<TFields>, R>;\n}\n\n/** The asynchronous refinements carried on a builder, as bare predicates. */\nexport function asyncRefinements<TFields extends FieldsRecord, R>(\n self: FormBuilder<TFields, R>,\n): ReadonlyArray<\n (values: DecodedFromFields<TFields>) => Effect.Effect<Schema.FilterOutput, never, R>\n> {\n return Arr.flatMap(self.refinements, (refinement) => {\n if (!Predicate.isTagged(refinement, \"async\")) return [];\n // SAFETY: `refineEffect` stores a predicate typed against this builder's\n // decoded values, with its requirements folded into `R`; the builder keeps\n // it as `(values: unknown) => Effect<…, unknown>` only to share one\n // `Refinement` type across builders, so this restores the original type.\n const fn = refinement.fn as (\n values: DecodedFromFields<TFields>,\n ) => Effect.Effect<Schema.FilterOutput, never, R>;\n return [fn];\n });\n}\n\n/**\n * Normalise a filter predicate's `Schema.FilterOutput` into a flat list of\n * {@link FormIssue}s (empty on success).\n *\n * @deprecated Use `Issues.filterOutputToFormIssues`; this re-export remains\n * only for callers that imported it from `FormBuilder`.\n */\nexport const filterOutputToFormIssues: typeof Issues.filterOutputToFormIssues =\n issuesFromFilterOutput;\n\n/**\n * Run every asynchronous refinement on `self` against `values` and collect\n * their failures as {@link FormIssue}s (empty when all pass). Runs them\n * concurrently; requirements propagate on the `R` channel.\n */\nexport function runAsyncRefinements<TFields extends FieldsRecord, R>(\n self: FormBuilder<TFields, R>,\n values: DecodedFromFields<TFields>,\n): Effect.Effect<ReadonlyArray<FormIssue>, never, R> {\n return Arr.match(asyncRefinements(self), {\n onEmpty: (): Effect.Effect<ReadonlyArray<FormIssue>, never, R> => Effect.succeed([]),\n onNonEmpty: (refinements) =>\n Effect.forEach(refinements, (fn) => fn(values), { concurrency: \"unbounded\" }).pipe(\n Effect.map((outputs) => Arr.flatMap(outputs, issuesFromFilterOutput)),\n ),\n });\n}\n","/**\n * {@link FormState} — the serialisable snapshot of a form.\n *\n * This is the single object a\n * server produces (validate a submission, or seed an empty form) and hands to\n * the client to hydrate from. It is plain JSON so it survives the SSR → client\n * boundary, and {@link FormStateSchema} gives it an Effect Schema so it can be\n * decoded back on arrival.\n *\n * @copyright Thomas Foster\n * @module effect-forms/State\n */\n\nimport * as Schema from \"effect/Schema\";\nimport type * as Types from \"effect/Types\";\nimport type { InputConstraints } from \"./Constraints.ts\";\nimport { type FormErrors, hasErrors, noErrors, setError } from \"./Errors.ts\";\nimport type { FormPath } from \"./Issues.ts\";\n\n/**\n * A form snapshot.\n *\n * - `id` distinguishes multiple forms on one page.\n * - `valid` is whether the last validation succeeded.\n * - `posted` is whether this snapshot came from a submission (vs. a seeded\n * empty form) — drives \"show errors only after submit\" UIs.\n * - `data` is the current values (typed output on success; posted values\n * merged over defaults on failure, so the form re-renders what the user\n * typed).\n * - `errors` is the nested {@link FormErrors} tree.\n * - `message` is an optional status payload (a free-form status payload).\n * - `constraints` is the optional derived {@link InputConstraints} tree.\n */\nexport interface FormState<A, Message = unknown> {\n readonly id: string;\n readonly valid: boolean;\n readonly posted: boolean;\n readonly data: A;\n readonly errors: FormErrors;\n readonly message?: Message | undefined;\n readonly constraints?: InputConstraints | undefined;\n}\n\n/** Options for {@link make}. */\nexport interface MakeFormStateOptions<Message> {\n /** The form's id, distinguishing several forms on one page. Defaults to `\"default\"`. */\n readonly id?: string;\n /** Whether the state is valid. Defaults to `true` exactly when `errors` is empty. */\n readonly valid?: boolean;\n /** Whether the state comes from a submission. Defaults to `false`. */\n readonly posted?: boolean;\n /** The nested error tree. Defaults to no errors. */\n readonly errors?: FormErrors;\n /** A status message to render with the form. Omitted by default. */\n readonly message?: Message;\n /** Native input constraints. Omitted by default. */\n readonly constraints?: InputConstraints;\n}\n\n/** Build a {@link FormState} from data plus overrides. `valid` defaults to \"no errors\". */\nexport function make<A, Message = unknown>(\n data: A,\n options: MakeFormStateOptions<Message> = {},\n): FormState<A, Message> {\n const errors = options.errors ?? noErrors;\n const state: Types.Mutable<FormState<A, Message>> = {\n id: options.id ?? \"default\",\n valid: options.valid ?? !hasErrors(errors),\n posted: options.posted ?? false,\n data,\n errors,\n };\n if (options.message !== undefined) state.message = options.message;\n if (options.constraints !== undefined) state.constraints = options.constraints;\n return state;\n}\n\n/** Return a new state carrying `message` (and not otherwise changed). */\nexport function withMessage<A, Message>(\n state: FormState<A, Message>,\n message: Message,\n): FormState<A, Message> {\n return { ...state, message };\n}\n\n/**\n * Return a new state with `message` added at `path` and `valid` forced to\n * `false` — for attaching a\n * server-side validation failure to a specific field after the schema passed.\n */\nexport function withError<A, Message>(\n state: FormState<A, Message>,\n path: FormPath,\n message: string,\n): FormState<A, Message> {\n return { ...state, valid: false, errors: setError(state.errors, path, message) };\n}\n\n/** Whether the state is valid. */\nexport const isValid = <A, Message>(state: FormState<A, Message>): boolean => state.valid;\n\n/** Effect Schema describing the JSON shape of {@link FormState} for transport. */\nexport function FormStateSchema<DataSchema extends Schema.Top>(data: DataSchema) {\n return Schema.Struct({\n id: Schema.String,\n valid: Schema.Boolean,\n posted: Schema.Boolean,\n data,\n errors: Schema.Record(Schema.String, Schema.Unknown),\n message: Schema.optional(Schema.Unknown),\n constraints: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),\n });\n}\n","/**\n * {@link make} — the core entry point: bind an Effect Schema to a form and\n * get back a validator and an empty-state factory.\n *\n * This is the framework-neutral validate-and-seed entry point,\n * expressed as a small object bound to one schema:\n *\n * - `validate(input)` parses an HTML form body (or a plain object) and decodes\n * it through the schema, returning a {@link FormState} — valid with the\n * decoded data, or invalid with the nested {@link FormErrors} and the posted\n * values preserved so the form re-renders what the user typed. It is an\n * `Effect`, so a schema requiring decoding services composes naturally; it\n * has no error channel — validation failures live in the returned state.\n * - `empty()` seeds an unposted, invalid-by-default form from the supplied\n * defaults — what a `load` returns to render a blank create form.\n *\n * Coercion (strings → numbers / booleans / dates) is the schema's job: build\n * the schema from coercing leaves (`Schema.FiniteFromString`, the\n * {@link module:effect-forms/Coerce} helpers, …) so a multipart body decodes\n * cleanly.\n *\n * @copyright Thomas Foster\n * @module effect-forms/Form\n */\n\nimport * as Arr from \"effect/Array\";\nimport * as Effect from \"effect/Effect\";\nimport type * as Schema from \"effect/Schema\";\nimport * as SchemaParser from \"effect/SchemaParser\";\nimport { constraintsFromSchema, type InputConstraints } from \"./Constraints.ts\";\nimport { defaultEncodedValues } from \"./Field.ts\";\nimport { issuesToErrors } from \"./Errors.ts\";\nimport { buildSchema, type FormBuilder, runAsyncRefinements } from \"./FormBuilder.ts\";\nimport type { DecodedFromFields, EncodedFromFields, FieldsRecord } from \"./Field.ts\";\nimport { formDataToObject, requestToObject } from \"./FormData.ts\";\nimport { type LeafFormatter, schemaIssuesToFormIssues } from \"./Issues.ts\";\nimport { type FormState, make as makeFormState, type MakeFormStateOptions } from \"./State.ts\";\n\n/** Everything {@link Form.validate} accepts. `null` / `undefined` mean \"no submission\". */\nexport type FormInput =\n | FormData\n | URLSearchParams\n | Request\n | Record<string, unknown>\n | null\n | undefined;\n\n/** Options for {@link make}. */\nexport interface MakeFormOptions<S extends Schema.Top> {\n /** Distinguishes multiple forms on a page. Defaults to `\"default\"`. */\n readonly id?: string;\n /** The blank-form values — what {@link Form.empty} seeds and the merge base on failure. */\n readonly defaults: S[\"Type\"];\n /** Override the per-leaf message rendering. */\n readonly leafFormatter?: LeafFormatter;\n /** Constraint overrides deep-merged over the AST-derived tree. */\n readonly constraints?: InputConstraints;\n /** Derive {@link InputConstraints} from the schema AST (default `true`). */\n readonly deriveConstraints?: boolean;\n}\n\n/** A schema bound to a form: a validator plus an empty-state factory. */\nexport interface Form<S extends Schema.Top, Message = unknown> {\n readonly schema: S;\n readonly id: string;\n readonly constraints: InputConstraints;\n /** Seed a blank, unposted form from the defaults. */\n empty(overrides?: MakeFormStateOptions<Message>): FormState<S[\"Type\"], Message>;\n /** Parse a form body (no decode) into a structured object. */\n parse(input: FormData | URLSearchParams | Record<string, unknown>): Record<string, unknown>;\n /**\n * Validate a submission into a {@link FormState}. Never fails on the error\n * channel — schema failures become `errors` on the returned (invalid) state.\n */\n validate(\n input: FormInput,\n ): Effect.Effect<FormState<S[\"Type\"], Message>, never, S[\"DecodingServices\"]>;\n}\n\nconst toStructured = (input: FormInput): Effect.Effect<Record<string, unknown> | null> => {\n if (input === null || input === undefined) return Effect.succeed(null);\n if (input instanceof Request) {\n // A body that can't be read as a form (e.g. a wrong content-type) is a\n // defect, not a validation failure: `validate` never fails, so the\n // rejection reason dies as-is.\n return Effect.tryPromise({ try: () => requestToObject(input), catch: (cause) => cause }).pipe(\n Effect.orDie,\n );\n }\n if (input instanceof FormData || input instanceof URLSearchParams) {\n return Effect.succeed(formDataToObject(input));\n }\n return Effect.succeed(input);\n};\n\n/**\n * Bind `schema` to a form: `validate(input)` decodes a submission into a\n * {@link FormState} (it never fails — validation failures live in the state;\n * its requirements are the schema's decoding services) and `empty()` seeds a\n * blank form from `defaults`. The returned {@link Form} is stateless and\n * reusable across requests.\n *\n * @example\n * ```ts\n * import * as Effect from \"effect/Effect\"\n * import * as Schema from \"effect/Schema\"\n * import { Form } from \"@thomasfosterau/effect-forms\"\n *\n * const signup = Form.make(\n * Schema.Struct({ name: Schema.String.check(Schema.isMinLength(2)) }),\n * { id: \"signup\", defaults: { name: \"\" } },\n * )\n *\n * const state = Effect.runSync(signup.validate(new URLSearchParams({ name: \"A\" })))\n * console.log(state.valid) // false\n * console.log(state.data) // { name: \"A\" } — the posted value, kept for re-render\n * ```\n */\nexport function make<S extends Schema.Top, Message = unknown>(\n schema: S,\n options: MakeFormOptions<S>,\n): Form<S, Message> {\n const id = options.id ?? \"default\";\n const { defaults, leafFormatter } = options;\n const constraints =\n options.deriveConstraints === false\n ? (options.constraints ?? {})\n : constraintsFromSchema(schema, options.constraints ?? {});\n // `errors: \"all\"` collects every field failure (collect-all) rather\n // than stopping at the first, so the whole form lights up at once.\n const decode = SchemaParser.decodeUnknownEffect(schema, { errors: \"all\" });\n\n const empty: Form<S, Message>[\"empty\"] = (overrides = {}) =>\n makeFormState<S[\"Type\"], Message>(defaults, {\n id,\n valid: false,\n posted: false,\n constraints,\n ...overrides,\n });\n\n const parse: Form<S, Message>[\"parse\"] = (input) =>\n input instanceof FormData || input instanceof URLSearchParams ? formDataToObject(input) : input;\n\n const validate: Form<S, Message>[\"validate\"] = (input) =>\n toStructured(input).pipe(\n Effect.flatMap((object) => {\n if (object === null) return Effect.succeed(empty());\n return decode(object).pipe(\n Effect.matchEffect({\n onSuccess: (data) =>\n Effect.succeed(\n makeFormState<S[\"Type\"], Message>(data, {\n id,\n valid: true,\n posted: true,\n constraints,\n }),\n ),\n onFailure: (issue) => {\n // SAFETY: `FormState.data` on a failed decode is, by its documented\n // contract, the posted values merged over the defaults (so the form\n // re-renders what the user typed), typed as `S[\"Type\"]` though not\n // decoded. `defaults` is widened to `object` only so it can be\n // spread; spreading copies its own enumerable properties either way.\n const posted = { ...(defaults as object), ...object } as S[\"Type\"];\n return Effect.succeed(\n makeFormState<S[\"Type\"], Message>(posted, {\n id,\n valid: false,\n posted: true,\n constraints,\n errors: issuesToErrors(schemaIssuesToFormIssues(issue, leafFormatter)),\n }),\n );\n },\n }),\n );\n }),\n );\n\n return { schema, id, constraints, empty, parse, validate };\n}\n\n/** Options for {@link fromBuilder}. Like {@link MakeFormOptions} but `defaults` is optional. */\nexport interface FromBuilderOptions<TFields extends FieldsRecord> {\n /** Distinguishes multiple forms on a page. Defaults to `\"default\"`. */\n readonly id?: string;\n /** Blank-form values. Defaults to a schema-derived encoded blank per field. */\n readonly defaults?: EncodedFromFields<TFields>;\n /** Override the per-leaf message rendering. */\n readonly leafFormatter?: LeafFormatter;\n /** Constraint overrides deep-merged over the AST-derived tree. */\n readonly constraints?: InputConstraints;\n /** Derive {@link InputConstraints} from the schema AST (default `true`). */\n readonly deriveConstraints?: boolean;\n}\n\n/**\n * Bind a {@link FormBuilder} to a {@link Form}. Collapses the builder to a\n * schema (see {@link buildSchema}), then behaves like {@link make} — except its\n * `validate` also runs the builder's *asynchronous* refinements after a\n * successful decode, folding any failures into the same nested error tree. When\n * `defaults` is omitted it is derived from the fields (blank encoded values).\n */\nexport function fromBuilder<TFields extends FieldsRecord, R, Message = unknown>(\n builder: FormBuilder<TFields, R>,\n options: FromBuilderOptions<TFields> = {},\n): Form<Schema.Codec<DecodedFromFields<TFields>, EncodedFromFields<TFields>, R>, Message> {\n const schema = buildSchema(builder);\n // SAFETY: `defaultEncodedValues` seeds exactly one entry per field of\n // `builder.fields` — `[]` for an array field, the schema's blank form input\n // otherwise — the documented \"schema-derived encoded blank per field\" that\n // stands in for `EncodedFromFields<TFields>`.\n const defaults =\n options.defaults ?? (defaultEncodedValues(builder.fields) as EncodedFromFields<TFields>);\n const base = make<typeof schema, Message>(schema, { ...options, defaults });\n\n const decode = SchemaParser.decodeUnknownEffect(schema, { errors: \"all\" });\n\n const validate: Form<typeof schema, Message>[\"validate\"] = (input) =>\n toStructured(input).pipe(\n Effect.flatMap((object) => {\n if (object === null) return Effect.succeed(base.empty());\n // SAFETY: `FormState.data` on a failed validation is, by its documented\n // contract, the posted values merged over the defaults (so the form\n // re-renders what the user typed), typed as the decoded type though\n // not decoded.\n const posted = { ...defaults, ...object } as DecodedFromFields<TFields>;\n return decode(object).pipe(\n Effect.matchEffect({\n onSuccess: (data) =>\n runAsyncRefinements(builder, data).pipe(\n Effect.map((issues) =>\n Arr.match(issues, {\n onEmpty: () =>\n makeFormState<DecodedFromFields<TFields>, Message>(data, {\n id: base.id,\n valid: true,\n posted: true,\n constraints: base.constraints,\n }),\n onNonEmpty: (failures) =>\n makeFormState<DecodedFromFields<TFields>, Message>(posted, {\n id: base.id,\n valid: false,\n posted: true,\n constraints: base.constraints,\n errors: issuesToErrors(failures),\n }),\n }),\n ),\n ),\n onFailure: (issue) =>\n Effect.succeed(\n makeFormState<DecodedFromFields<TFields>, Message>(posted, {\n id: base.id,\n valid: false,\n posted: true,\n constraints: base.constraints,\n errors: issuesToErrors(schemaIssuesToFormIssues(issue, options.leafFormatter)),\n }),\n ),\n }),\n );\n }),\n );\n\n return { ...base, validate };\n}\n","/**\n * When a form validates (and, optionally, auto-submits).\n *\n * A reactive form has to decide *when* to run validation as the user types.\n * The three modes mirror the common form-library vocabulary:\n *\n * - **`onSubmit`** (the default) — validate only when the form is submitted (or\n * validation is triggered explicitly). The quietest mode.\n * - **`onBlur`** — validate a field when it loses focus.\n * - **`onChange`** — validate on every change, optionally `debounce`d.\n *\n * `onBlur` / `onChange` may also `autoSubmit`, turning the form into a\n * live-saving surface (a settings panel, a filter bar). This module is the\n * framework-neutral half: {@link parse} normalises the loose {@link FormMode}\n * a caller supplies into a fully-resolved {@link ParsedMode} the reactive\n * store drives from. The store owns the actual scheduling.\n *\n * A separate `revalidate` dimension governs what happens *after a submit*: an\n * `onSubmit`-mode form is otherwise silent until the next submit, which\n * leaves a failed post's errors frozen even after the user fixes the\n * offending field. `revalidate` says which trigger (`\"onChange\"` /\n * `\"onBlur\"` — independent of the primary `validation` trigger) re-validates\n * once the form has been posted at least once; `\"never\"` keeps the classic\n * frozen-until-resubmit behaviour.\n *\n * @copyright Thomas Foster\n * @module effect-forms/Mode\n */\n\nimport * as Duration from \"effect/Duration\";\n\n/** What re-validates a posted form as the user keeps editing. See {@link ParsedMode.revalidate}. */\nexport type RevalidateMode = \"onChange\" | \"onBlur\" | \"never\";\n\n/**\n * How a form decides when to validate, and whether it auto-submits.\n *\n * - `onSubmit` (the default) cannot auto-submit or debounce.\n * - `onBlur` may auto-submit; debounce is not meaningful.\n * - `onChange` may auto-submit and may debounce.\n *\n * `revalidate` is independent of `validation` and applies to every mode —\n * see the module docs.\n */\nexport type FormMode = (\n | { readonly validation?: \"onSubmit\"; readonly autoSubmit?: false; readonly debounce?: never }\n | { readonly validation: \"onBlur\"; readonly autoSubmit?: boolean; readonly debounce?: never }\n | {\n readonly validation: \"onChange\";\n readonly debounce?: Duration.Input;\n readonly autoSubmit?: boolean;\n }\n) & { readonly revalidate?: RevalidateMode };\n\n/** The fully-resolved form of a {@link FormMode} — every field decided. */\nexport interface ParsedMode {\n readonly validation: \"onSubmit\" | \"onBlur\" | \"onChange\";\n /** Debounce in milliseconds, or `null` for no debounce. */\n readonly debounce: number | null;\n readonly autoSubmit: boolean;\n /** What re-validates the form once it has been posted at least once. Defaults to `\"onChange\"`. */\n readonly revalidate: RevalidateMode;\n}\n\n/**\n * Normalise a {@link FormMode} into a {@link ParsedMode}. An absent mode is\n * `onSubmit`. `debounce` is resolved to milliseconds and only applies in\n * `onChange`; `autoSubmit` is only honoured for `onBlur` / `onChange`;\n * `revalidate` defaults to `\"onChange\"` (TanStack's / RHF's default\n * post-submit behaviour) regardless of the primary `validation` mode.\n */\nexport function parse(mode?: FormMode): ParsedMode {\n const validation = mode?.validation ?? \"onSubmit\";\n const revalidate = mode?.revalidate ?? \"onChange\";\n\n if (validation === \"onBlur\") {\n return {\n validation: \"onBlur\",\n debounce: null,\n autoSubmit: mode?.autoSubmit === true,\n revalidate,\n };\n }\n\n if (validation === \"onChange\") {\n const debounce = mode?.debounce === undefined ? null : Duration.toMillis(mode.debounce);\n return { validation: \"onChange\", debounce, autoSubmit: mode?.autoSubmit === true, revalidate };\n }\n\n return { validation: \"onSubmit\", debounce: null, autoSubmit: false, revalidate };\n}\n","/**\n * {@link FormActionResult} — the framework-neutral outcome of a form\n * submission handler.\n *\n * SvelteKit's `ActionResult` (`success` / `failure` / `redirect` / `error`) is\n * Svelte-specific; this is the same idea expressed as a plain tagged union so\n * a server handler in any framework can describe what happened, and a client\n * adapter can interpret it uniformly. A Svelte adapter maps this onto\n * SvelteKit's `ActionResult`; a fetch client reads the JSON shape directly.\n *\n * @copyright Thomas Foster\n * @module effect-forms/Result\n */\n\nimport type { FormState } from \"./State.ts\";\n\n/** The submission validated and the command ran; carries the resulting state. */\nexport interface FormSuccess<A, Message> {\n readonly _tag: \"Success\";\n readonly status: number;\n readonly state: FormState<A, Message>;\n}\n\n/** The submission was rejected (validation or a user-fixable command failure). */\nexport interface FormFailure<A, Message> {\n readonly _tag: \"Failure\";\n readonly status: number;\n readonly state: FormState<A, Message>;\n}\n\n/** The handler asks the client to navigate elsewhere (post-success redirect). */\nexport interface FormRedirect {\n readonly _tag: \"Redirect\";\n readonly status: number;\n readonly location: string;\n}\n\n/** An unexpected (non-user-fixable) error: surface it, don't bind it to fields. */\nexport interface FormErrorResult {\n readonly _tag: \"Error\";\n readonly status: number;\n readonly message: string;\n}\n\n/** The outcome of a form submission. */\nexport type FormActionResult<A, Message = unknown> =\n | FormSuccess<A, Message>\n | FormFailure<A, Message>\n | FormRedirect\n | FormErrorResult;\n\n/**\n * The data a server form action returns to the client on a non-redirect\n * outcome — the wire contract a client adapter applies back to its store. A\n * validation failure carries the {@link FormState} (`form`); an unexpected\n * error carries a root `message`.\n */\nexport interface FormActionData<A = unknown, Message = unknown> {\n /** The form state (nested errors + posted values) for the client to apply. */\n readonly form?: FormState<A, Message>;\n /** A root-level message for an unexpected (non-field) failure. */\n readonly message?: string;\n}\n\n/** Construct a {@link FormSuccess} (default status 200). */\nexport const success = <A, Message>(\n state: FormState<A, Message>,\n status = 200,\n): FormActionResult<A, Message> => ({ _tag: \"Success\", status, state });\n\n/** Construct a {@link FormFailure} (default status 400). */\nexport const failure = <A, Message>(\n state: FormState<A, Message>,\n status = 400,\n): FormActionResult<A, Message> => ({ _tag: \"Failure\", status, state });\n\n/** Construct a {@link FormRedirect} (default status 303). */\nexport const redirect = <A, Message>(\n location: string,\n status = 303,\n): FormActionResult<A, Message> => ({ _tag: \"Redirect\", status, location });\n\n/** Construct a {@link FormErrorResult} (default status 500). */\nexport const errorResult = <A, Message>(\n message: string,\n status = 500,\n): FormActionResult<A, Message> => ({ _tag: \"Error\", status, message });\n","/**\n * Low-level validation helpers — decode a value through a schema and get back\n * the {@link FormErrors} tree (or the list of {@link FormIssue}s) instead of a\n * raised failure. `Form.make` is the high-level entry; these are for ad\n * hoc field- or value-level validation (e.g. a Svelte client validating a\n * single field on blur).\n *\n * The second half of the module is per-field validators: {@link\n * SyncFieldValidator} / {@link AsyncFieldValidator} (bundled as {@link\n * FieldValidators}) are the framework-neutral shape a reactive store attaches\n * to one field, mirroring TanStack Form's `validators.onChange(Async)` /\n * `onBlur(Async)`. {@link runSyncFieldValidator} runs one synchronously;\n * {@link runAsyncFieldValidator} builds the debounced Effect an async one\n * runs as — the store is responsible for forking it into a fiber per field\n * and interrupting the previous fiber on every new run (see that function's\n * docs for why that alone gives debounce + \"latest wins\" cancellation).\n *\n * @copyright Thomas Foster\n * @module effect-forms/Validate\n */\n\nimport * as Duration from \"effect/Duration\";\nimport * as Effect from \"effect/Effect\";\nimport type * as Schema from \"effect/Schema\";\nimport * as SchemaParser from \"effect/SchemaParser\";\nimport { type FormErrors, issuesToErrors } from \"./Errors.ts\";\nimport {\n filterOutputToFormIssues,\n type FormIssue,\n type LeafFormatter,\n schemaIssuesToFormIssues,\n} from \"./Issues.ts\";\n\n/** The outcome of validating a value against a schema. */\nexport type ValidationResult<A> =\n | { readonly valid: true; readonly data: A }\n | {\n readonly valid: false;\n readonly errors: FormErrors;\n readonly issues: ReadonlyArray<FormIssue>;\n };\n\n/**\n * Decode `input` through `schema`, returning a {@link ValidationResult} on the\n * success channel (decode failures become `valid: false`, not an Effect\n * failure). Decoding-service requirements propagate on the `R` channel.\n */\nexport function validateValue<S extends Schema.Top>(\n schema: S,\n input: unknown,\n format?: LeafFormatter,\n): Effect.Effect<ValidationResult<S[\"Type\"]>, never, S[\"DecodingServices\"]> {\n return SchemaParser.decodeUnknownEffect(schema, { errors: \"all\" })(input).pipe(\n Effect.matchEffect({\n onSuccess: (data): Effect.Effect<ValidationResult<S[\"Type\"]>> =>\n Effect.succeed({ valid: true, data }),\n onFailure: (issue): Effect.Effect<ValidationResult<S[\"Type\"]>> => {\n const issues = schemaIssuesToFormIssues(issue, format);\n return Effect.succeed({ valid: false, errors: issuesToErrors(issues), issues });\n },\n }),\n );\n}\n\n/**\n * Synchronous {@link validateValue} for schemas with no decoding-service\n * requirements (`R = never`) and no async decoding — the common case for plain\n * form schemas. {@link validateValue} never fails on the error channel, so\n * running it with {@link Effect.runSync} is total.\n */\nexport function validateValueSync<S extends Schema.Top>(\n schema: S,\n input: unknown,\n format?: LeafFormatter,\n): ValidationResult<S[\"Type\"]> {\n // SAFETY: `validateValue` never fails on the error channel; the cast drops\n // the decoding-service requirement, which this function's documented\n // contract has the caller assert is `never` (the common case for plain form\n // schemas — a service-requiring schema must use the async {@link validateValue}).\n return Effect.runSync(\n validateValue(schema, input, format) as Effect.Effect<ValidationResult<S[\"Type\"]>>,\n );\n}\n\n/**\n * A synchronous per-field validator — TanStack Form's `validators.onChange` /\n * `onBlur` shape. Sees the field's decoded value and returns a {@link\n * Schema.FilterOutput} (`undefined` / `true` to pass, a message / issue /\n * list to fail), the same vocabulary a schema `check` or a {@link\n * module:effect-forms/FormBuilder} `refine` predicate uses.\n */\nexport type SyncFieldValidator<A> = (value: A) => Schema.FilterOutput;\n\n/**\n * An asynchronous per-field validator — TanStack Form's `onChangeAsync` /\n * `onBlurAsync`. An Effect (not a `Promise`) so its `R` channel can demand\n * services (an API client, a cache) the way a {@link\n * module:effect-forms/FormBuilder} `refineEffect` refinement does; a caller\n * running this client-side supplies `R` via a `Layer` / runtime, or keeps it\n * `never` for a plain `fetch`-based check.\n */\nexport type AsyncFieldValidator<A, R = never> = (\n value: A,\n) => Effect.Effect<Schema.FilterOutput, never, R>;\n\n/**\n * The validators one field declares, mirroring TanStack Form's per-field\n * `validators` option. `asyncDebounceMs` (default `0`, meaning no debounce)\n * governs both async variants; see {@link runAsyncFieldValidator} for how the\n * debounce and cancellation actually work.\n */\nexport interface FieldValidators<A, R = never> {\n readonly onChange?: SyncFieldValidator<A>;\n readonly onBlur?: SyncFieldValidator<A>;\n readonly onChangeAsync?: AsyncFieldValidator<A, R>;\n readonly onBlurAsync?: AsyncFieldValidator<A, R>;\n readonly asyncDebounceMs?: number;\n}\n\n/**\n * Run a {@link SyncFieldValidator}, folding its {@link Schema.FilterOutput}\n * into {@link FormIssue}s (empty on success). Total — a sync field validator\n * cannot fail the Effect error channel.\n */\nexport function runSyncFieldValidator<A>(\n validator: SyncFieldValidator<A>,\n value: A,\n): ReadonlyArray<FormIssue> {\n return filterOutputToFormIssues(validator(value));\n}\n\n/**\n * Build the Effect an {@link AsyncFieldValidator} run is: sleep `debounceMs`\n * (a no-op at `0`, the default), then run `validator` and fold its output\n * into {@link FormIssue}s.\n *\n * The debounce is a plain {@link Effect.sleep}, so it rides Effect's `Clock`\n * — deterministic and instantly-advanceable under `TestClock` in tests, real\n * time otherwise (no `setTimeout` bookkeeping for a caller to manage).\n * \"Latest wins\" falls out of ordinary fiber interruption: a caller re-running\n * this on every keystroke forks a fresh fiber per call and interrupts the\n * previous one before forking the next — interrupting mid-sleep skips the\n * validator entirely (a true debounce, not just a discarded stale result),\n * and interrupting mid-validator aborts whatever the validator was doing\n * (e.g. an in-flight `fetch` wrapped as an Effect).\n */\nexport function runAsyncFieldValidator<A, R>(\n validator: AsyncFieldValidator<A, R>,\n value: A,\n debounceMs = 0,\n): Effect.Effect<ReadonlyArray<FormIssue>, never, R> {\n return Effect.gen(function* () {\n if (debounceMs > 0) yield* Effect.sleep(Duration.millis(debounceMs));\n const output = yield* validator(value);\n return filterOutputToFormIssues(output);\n });\n}\n\n/** Which trigger a {@link FieldValidators} sync/async pair reacts to. */\nexport type FieldTrigger = \"change\" | \"blur\";\n\n/** The validator(s) (if any) that apply for one field trigger — see {@link resolveFieldTrigger}. */\nexport interface ResolvedFieldTrigger<A, R = never> {\n readonly sync: SyncFieldValidator<A> | undefined;\n readonly async: AsyncFieldValidator<A, R> | undefined;\n readonly asyncDebounceMs: number;\n}\n\n/**\n * Pick `validators`' sync/async pair for `trigger` (TanStack's `onChange` /\n * `onBlur` vs `onChangeAsync` / `onBlurAsync`). A reactive store calls this\n * once per trigger instead of repeating the trigger ternary at each call\n * site — the pure decision half of what {@link module:effect-forms-svelte/FormStore}'s\n * field-trigger dispatch runs.\n */\nexport function resolveFieldTrigger<A, R = never>(\n validators: FieldValidators<A, R> | undefined,\n trigger: FieldTrigger,\n): ResolvedFieldTrigger<A, R> {\n return {\n sync: trigger === \"change\" ? validators?.onChange : validators?.onBlur,\n async: trigger === \"change\" ? validators?.onChangeAsync : validators?.onBlurAsync,\n asyncDebounceMs: validators?.asyncDebounceMs ?? 0,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["replaceAt","filterOutputToFormIssues","Field.makeField","make","Field.isArrayFieldDef","Field.isFieldDef","issuesFromFilterOutput","make","makeFormState","filterOutputToFormIssues"],"sources":["../src/Coerce.ts","../src/Command.ts","../src/Constraints.ts","../src/FormData.ts","../src/Path.ts","../src/Errors.ts","../src/Field.ts","../src/Issues.ts","../src/FormBuilder.ts","../src/State.ts","../src/Form.ts","../src/Mode.ts","../src/Result.ts","../src/Validate.ts"],"sourcesContent":["/**\n * Tri-state coercers for the HTML-form boundary.\n *\n * An HTML form submits every value as a string, and an absent optional field\n * arrives as `undefined` (or an empty string). Mapping that onto a\n * value / `null` / `undefined` update contract — where a value **sets**, `null`\n * **clears** a nullable field, and `undefined` **skips** (carry-forward) — is\n * the same boundary discipline the host app applies, lifted here so any\n * consumer of the core gets it without a framework dependency.\n *\n * These are plain, total functions: they never throw and never reach for a\n * framework's `error()` helper (validation failures belong on the schema, not\n * in a coercer).\n *\n * @copyright Thomas Foster\n * @module effect-forms/Coerce\n */\n\n/** Empty string / `undefined` → `undefined`; otherwise the value (skip semantics). */\nexport function emptyToUndefined<T>(value: T | \"\" | undefined): T | undefined {\n return value === \"\" || value === undefined ? undefined : value;\n}\n\n/**\n * `undefined` stays `undefined` (skip); empty string / `null` → `null`\n * (clear a nullable field); otherwise the value (set). The three-state coercer\n * for nullable update fields.\n */\nexport function emptyToNull<T>(value: T | \"\" | null | undefined): T | null | undefined {\n if (value === undefined) return undefined;\n if (value === \"\" || value === null) return null;\n return value;\n}\n\n/** Trim, then {@link emptyToUndefined}: whitespace-only collapses to `undefined` (skip). */\nexport function trimToUndefined(value: string | undefined): string | undefined {\n return emptyToUndefined(value?.trim());\n}\n\n/** Trim, then {@link emptyToNull}: whitespace-only / blank clears (`null`). */\nexport function trimToNull(value: string | undefined): string | null | undefined {\n return emptyToNull(value?.trim());\n}\n\n/**\n * Map a numeric text field onto the value / `null` / `undefined` contract.\n * Blank clears on the nullable path and skips otherwise; a present value\n * parses with `Number` (`NaN` for non-numeric input — validate downstream).\n */\nexport function numberFromField(\n raw: string | undefined,\n options: { nullable: false },\n): number | undefined;\nexport function numberFromField(\n raw: string | undefined,\n options: { nullable: true },\n): number | null | undefined;\nexport function numberFromField(\n raw: string | undefined,\n { nullable }: { nullable: boolean },\n): number | null | undefined {\n const coerced = nullable ? emptyToNull(raw) : emptyToUndefined(raw);\n return coerced === undefined || coerced === null ? coerced : Number(coerced);\n}\n\n/**\n * Interpret a checkbox field. An HTML checkbox submits its `value` (default\n * `\"on\"`) only when checked and omits the field entirely when unchecked, so a\n * present field is `true` and an absent one is `false`. The truthy strings\n * `\"on\"` / `\"true\"` / `\"1\"` / `\"yes\"` all read as `true`; an explicit\n * `\"false\"` / `\"0\"` / `\"off\"` / `\"no\"` reads as `false`.\n */\nexport function booleanFromCheckbox(raw: string | undefined): boolean {\n if (raw === undefined) return false;\n const normalised = raw.trim().toLowerCase();\n return !(\n normalised === \"\" ||\n normalised === \"false\" ||\n normalised === \"0\" ||\n normalised === \"off\" ||\n normalised === \"no\"\n );\n}\n","/**\n * Command-struct introspection and form derivation.\n *\n * A host app's write surface is a pair of flat command `Schema.Struct`s per\n * entity — a create struct (required fields bare, the rest `optional`) and an\n * update struct (keyed by a required `id`, every field `optional`, clearable\n * fields `optional(NullOr(...))` carrying the value / `null` / `undefined`\n * tri-state). This module reads that pair into a plain-data {@link CommandPlan}\n * and derives the three per-entity form artefacts that were previously\n * hand-written per entity: the all-string Form-Input schema fields, the blank\n * defaults, and the tri-state adapters that map a validated multipart input\n * onto the encoded command payload.\n *\n * Classification leads with behavioural decode probes — date codecs are\n * transformations and branded ids erase to `String`, so \"what does this\n * schema accept\" is the only robust signal for those (the same approach the\n * host app's model walkers use). Two kinds fall back to AST matching instead,\n * because no decode probe can name what they are: `enum`'s literal option set\n * (needed anyway for the renderer, not just the classification) and\n * `structured`'s \"this is a nested object, not a scalar\" shape. Neither\n * shadows a probe-classified kind — see {@link classify}.\n *\n * The derived adapters emit the **encoded** command payload (dates stay\n * `YYYY-MM-DD` strings, ids stay plain strings) — the command's canonical\n * decode downstream owns branding and validation. Genuinely irregular fields\n * (a create-time default, an asymmetric coercion) stay expressible via the\n * per-field {@link AdaptOverrides} escape hatch instead of forcing ejection.\n *\n * @copyright Thomas Foster\n * @module effect-forms/Command\n */\n\nimport * as Arr from \"effect/Array\";\nimport * as Exit from \"effect/Exit\";\nimport * as Match from \"effect/Match\";\nimport * as Predicate from \"effect/Predicate\";\nimport * as Rec from \"effect/Record\";\nimport * as Schema from \"effect/Schema\";\nimport * as SchemaAST from \"effect/SchemaAST\";\nimport * as SchemaParser from \"effect/SchemaParser\";\nimport {\n emptyToNull,\n emptyToUndefined,\n numberFromField,\n trimToNull,\n trimToUndefined,\n} from \"./Coerce.ts\";\n\n/** How a command field renders and coerces at the form boundary. */\nexport type FieldKind =\n | \"text\"\n | \"id\"\n | \"number\"\n | \"date\"\n | \"jsonArray\"\n | \"boolean\"\n | \"enum\"\n | \"structured\";\n\n/** One command field's derived plan, as plain data. */\nexport interface FieldPlan {\n readonly key: string;\n readonly kind: FieldKind;\n /** Present on the create struct. */\n readonly inCreate: boolean;\n /** Bare (non-optional) on the create struct. */\n readonly createRequired: boolean;\n /** Present on the update struct. */\n readonly inUpdate: boolean;\n /** `optional(NullOr(...))` on the update struct — blank clears (`null`). */\n readonly updateClearable: boolean;\n /**\n * The field's literal option values, read off the AST — present only when\n * `kind` is `\"enum\"`. In declaration order, stringified (a `<select>`'s\n * option values are always strings at the form boundary, whatever the\n * decoded literal's own type).\n */\n readonly options?: readonly string[];\n}\n\n/** The derived plan for one entity's create/update command pair. */\nexport interface CommandPlan {\n /** Every field except `id`, in update-struct declaration order (create-only fields appended). */\n readonly fields: readonly FieldPlan[];\n /** The create struct carries an optional client-generated `id` (idempotent retries). */\n readonly clientId: boolean;\n}\n\n/** Per-field adapter overrides — the escape hatch for genuinely irregular coercion. */\nexport type AdaptOverrides = Readonly<Record<string, (raw: string | undefined) => unknown>>;\n\n/** A validated all-string multipart input, as the Form-Input schemas produce it. */\nexport type FormInputRecord = Readonly<Record<string, string | undefined>>;\n\n// ── errors ────────────────────────────────────────────────────────────\n\n/** A command field {@link plan} cannot classify into any {@link FieldKind}. */\nclass UnclassifiableFieldError extends Schema.TaggedError<UnclassifiableFieldError>()(\n \"UnclassifiableFieldError\",\n { message: Schema.String },\n) {}\n\n/** A malformed `jsonArray` field payload, as {@link jsonRowsFromField} rejects it. */\nclass InvalidJsonRowsError extends Schema.TaggedError<InvalidJsonRowsError>()(\n \"InvalidJsonRowsError\",\n { message: Schema.String },\n) {}\n\n// ── probes ────────────────────────────────────────────────────────────\n\n/**\n * Run one decode probe: whether the schema accepts the input, plus the decoded\n * value for instance checks (the date probe needs to see a `Date` come out,\n * not just a successful decode).\n */\nconst probe = (schema: Schema.Top, input: unknown): Exit.Exit<unknown, unknown> => {\n // SAFETY: the cast only drops `schema`'s decoding-service requirement. The\n // decode runs to an `Exit`, so a schema that does need a service (or decodes\n // asynchronously, or throws) yields a failed `Exit` — which a probe reads as\n // \"does not decode\", exactly as it reads an ordinary schema issue.\n const decoder = schema as Schema.Codec<unknown>;\n return SchemaParser.decodeUnknownExit(decoder)(input);\n};\n\n/** True when a schema decodes the given input without failing. */\nconst decodes = (schema: Schema.Top, input: unknown): boolean =>\n Exit.isSuccess(probe(schema, input));\n\n/** A syntactically valid UUID no other field kind accepts as non-text. */\nconst PROBE_UUID = \"9f8c1c2e-4b3a-4c5d-8e6f-0a1b2c3d4e5f\";\n\n/**\n * Peel exactly the `optional` / `NullOr` wrapper layers off a property's type\n * AST, stopping at the first union that isn't itself a pure wrapper. A wrapper\n * union carries exactly one non-`null`/`undefined` member (the wrapped type);\n * a union with zero or several such members is a real value-level union (an\n * `enum` field's own literal set, say) rather than another layer to peel, so\n * the loop stops there. Unlike {@link module:effect-forms/Constraints}'s\n * `unwrapOptional` (which only ever needs the *first* real member), this\n * walks every wrapper layer and preserves a multi-member union intact —\n * `enumOptions` below needs the full literal set, not just one branch of it.\n */\nfunction unwrapWrapperUnion(ast: SchemaAST.AST): SchemaAST.AST {\n if (!SchemaAST.isUnion(ast)) return ast;\n const real = Arr.filter(ast.types, (t) => !SchemaAST.isUndefined(t) && !SchemaAST.isNull(t));\n if (real.length !== 1 || real.length === ast.types.length) return ast;\n return unwrapWrapperUnion(real[0]);\n}\n\n/**\n * The literal option values of a field shaped like `Schema.Literal(...)` or\n * `Schema.Literals([...])` (optionally `optional` / `NullOr`-wrapped) —\n * `undefined` when the field isn't literal-shaped. Stringified: a `<select>`'s\n * option values are strings at the form boundary regardless of the literal's\n * own type.\n */\nfunction enumOptions(ast: SchemaAST.AST): readonly string[] | undefined {\n const inner = unwrapWrapperUnion(ast);\n if (SchemaAST.isLiteral(inner)) return [String(inner.literal)];\n if (SchemaAST.isUnion(inner)) {\n const literals = Arr.filter(inner.types, SchemaAST.isLiteral);\n if (Arr.isReadonlyArrayNonEmpty(literals) && literals.length === inner.types.length) {\n return Arr.map(literals, (literal) => String(literal.literal));\n }\n }\n return undefined;\n}\n\n/**\n * Whether a field is itself an object/struct schema (optionally `optional` /\n * `NullOr`-wrapped) — the `structured` kind's shape (inline / block ProseKit\n * JSON: `{ type, content }`). Mirrors {@link module:effect-forms/Field}'s and\n * {@link module:effect-forms/Constraints}'s own `SchemaAST.isObjects` struct\n * check. A `Schema.declare`d type (`UploadedDocument`, `Image`) has no\n * `Objects` AST node, so it is never caught here — the default-throw the\n * derivation relies on stays intact.\n */\nfunction isStructured(ast: SchemaAST.AST): boolean {\n return SchemaAST.isObjects(unwrapWrapperUnion(ast));\n}\n\n/** One field's classification: its {@link FieldKind}, plus `options` for `\"enum\"`. */\ninterface Classification {\n readonly kind: FieldKind;\n readonly options?: readonly string[];\n}\n\n/**\n * The scalar kinds classified purely by what a schema decodes, with no AST\n * inspection: array → boolean → number → date → id → text. `undefined` when\n * the schema accepts none of them, so {@link classify} can fall through to\n * its AST-based checks (`enum`, `structured`).\n */\nconst probeKind = (schema: Schema.Top): FieldKind | undefined => {\n if (decodes(schema, [])) return \"jsonArray\";\n if (decodes(schema, true)) return \"boolean\";\n if (decodes(schema, 42)) return \"number\";\n const date = probe(schema, \"2024-01-02T03:04:05.000Z\");\n if (Exit.isSuccess(date) && date.value instanceof Date) return \"date\";\n if (decodes(schema, PROBE_UUID) && !decodes(schema, \"not-a-uuid\")) return \"id\";\n if (decodes(schema, \"plain text\")) return \"text\";\n return undefined;\n};\n\n/**\n * Classify one field schema: by its AST shape first, then by what it accepts.\n * `enum` is checked before {@link probeKind} rather than after, since a\n * decode probe can't tell \"the literal set an enum accepts\" from any other\n * shape (worse, a numeric-literal enum whose set happens to include the `42`\n * probe value would otherwise misclassify as `number`) — reading the literal\n * set off the AST is unambiguous, and it's needed anyway for the renderer.\n * `structured` is checked last, after every probe has failed: an object\n * schema never decodes any of `probeKind`'s scalar probes, so there is no\n * real ordering hazard, but putting the broadest, AST-only check last keeps\n * every more specific kind first in line. A field that matches nothing\n * (`Schema.declare`d — `UploadedDocument`, `Image`) throws at module load so\n * the gap surfaces when the plan is declared, not on a live submission.\n */\nconst classify = (key: string, schema: Schema.Top): Classification => {\n const options = enumOptions(schema.ast);\n if (options !== undefined) return { kind: \"enum\", options };\n const probed = probeKind(schema);\n if (probed !== undefined) return { kind: probed };\n if (isStructured(schema.ast)) return { kind: \"structured\" };\n throw new UnclassifiableFieldError({\n message: `Command.plan: field \"${key}\" matches no known kind (text/id/number/date/jsonArray/boolean/enum/structured) — a Schema.declare'd type needs an explicit form-level treatment before it can derive`,\n });\n};\n\n// ── plan ──────────────────────────────────────────────────────────────\n\n/** Minimal runtime shape of a command `Schema.Struct`. */\ninterface StructLike {\n readonly fields: Record<string, Schema.Top>;\n}\n\n/**\n * Read a create/update command struct pair into a {@link CommandPlan}.\n * Runs once at module load — probe cost is a handful of tiny decodes per\n * field, never per render or per request.\n */\nexport const plan = (create: StructLike, update: StructLike): CommandPlan => {\n const createFields = create.fields;\n const updateFields = update.fields;\n const keys = [\n ...Arr.filter(Rec.keys(updateFields), (key) => key !== \"id\"),\n ...Arr.filter(Rec.keys(createFields), (key) => key !== \"id\" && !(key in updateFields)),\n ];\n\n const fields = Arr.map(keys, (key): FieldPlan => {\n const createField = createFields[key];\n const updateField = updateFields[key];\n const { kind, options } = classify(key, createField ?? updateField);\n const fieldPlan: FieldPlan = {\n key,\n kind,\n inCreate: createField !== undefined,\n createRequired: createField !== undefined && !decodes(createField, undefined),\n inUpdate: updateField !== undefined,\n updateClearable: updateField !== undefined && decodes(updateField, null),\n };\n return options === undefined ? fieldPlan : { ...fieldPlan, options };\n });\n\n return { fields, clientId: \"id\" in createFields };\n};\n\n// ── Form-Input schema fields ──────────────────────────────────────────\n\n/**\n * Derive the all-string Form-Input schema fields for one mode — every value is\n * a string at the multipart boundary. Create-required fields get a min-length\n * check so blankness surfaces as an inline field error; the update `id` gets\n * the UUID check. Spread into `Schema.Struct({ ...Command.formInputFields(plan,\n * \"create\") })`, with `overrides` spread last for irregular fields.\n */\nexport const formInputFields = (\n commandPlan: CommandPlan,\n mode: \"create\" | \"update\",\n overrides: Readonly<Record<string, Schema.Top>> = {},\n): Record<string, Schema.Top> => {\n const idField: Array<readonly [string, Schema.Top]> =\n mode === \"update\"\n ? [[\"id\", Schema.String.check(Schema.isUUID())]]\n : commandPlan.clientId\n ? [[\"id\", Schema.optional(Schema.String)]]\n : [];\n const planFields = Arr.flatMap(\n commandPlan.fields,\n (field): Array<readonly [string, Schema.Top]> => {\n if (mode === \"create\" ? !field.inCreate : !field.inUpdate) return [];\n return [\n [\n field.key,\n mode === \"create\" && field.createRequired\n ? Schema.String.check(Schema.isMinLength(1))\n : Schema.optional(Schema.String),\n ],\n ];\n },\n );\n // Overrides come last, so an override replaces a derived field in place.\n return Rec.fromEntries([...idField, ...planFields, ...Rec.toEntries(overrides)]);\n};\n\n/**\n * Blank string defaults for one mode's Form-Input shape. Client-id entities\n * override `id` at the call site (`{ ...blankDefaults(plan, \"create\"), id:\n * crypto.randomUUID() }`) so retries stay idempotent.\n */\nexport const blankDefaults = (\n commandPlan: CommandPlan,\n mode: \"create\" | \"update\",\n): Record<string, string> => {\n const keys = [\n ...(mode === \"update\" || commandPlan.clientId ? [\"id\"] : []),\n ...Arr.flatMap(commandPlan.fields, (field) =>\n (mode === \"create\" ? field.inCreate : field.inUpdate) ? [field.key] : [],\n ),\n ];\n return Rec.fromEntries(Arr.map(keys, (key) => [key, \"\"] as const));\n};\n\n// ── adapters ──────────────────────────────────────────────────────────\n\n/**\n * Parse a JSON-encoded row list (a contributor list) from a multipart form\n * field. A blank or absent field yields `undefined` (skip — leave the existing\n * list untouched); an empty JSON array (`\"[]\"`) clears the list. Rows keep\n * `id` / `creditedName` when present; `orderingPosition` falls back to the\n * row's array index. Throws a plain `Error` on a malformed payload — mapping\n * that onto a framework response is the caller's concern.\n */\nexport const jsonRowsFromField = (\n raw: string | undefined,\n label: string,\n):\n | {\n id?: string | undefined;\n agentId: string;\n creditedName?: string | undefined;\n orderingPosition: number;\n }[]\n | undefined => {\n if (raw === undefined || raw.trim() === \"\") return undefined;\n const decoded = decodeJson(raw);\n if (Exit.isFailure(decoded)) {\n throw new InvalidJsonRowsError({ message: `Invalid ${label} payload — expected JSON array` });\n }\n const parsed = decoded.value;\n if (!Arr.isArray(parsed)) {\n throw new InvalidJsonRowsError({ message: `Invalid ${label} payload — expected array` });\n }\n return Arr.map(parsed, (row, index) => {\n if (!Predicate.isObjectOrArray(row)) {\n throw new InvalidJsonRowsError({ message: `Invalid ${label} entry at index ${index}` });\n }\n // `row[key]`, `undefined` when absent — what a plain property read gives.\n const propertyOf = (key: string): unknown =>\n Predicate.hasProperty(row, key) ? row[key] : undefined;\n const agentId = propertyOf(\"agentId\");\n if (!Predicate.isString(agentId)) {\n throw new InvalidJsonRowsError({\n message: `${label} entry at index ${index} missing agentId`,\n });\n }\n const id = propertyOf(\"id\");\n const creditedName = propertyOf(\"creditedName\");\n const orderingPosition = propertyOf(\"orderingPosition\");\n return {\n id: Predicate.isString(id) ? id : undefined,\n agentId,\n creditedName: Predicate.isString(creditedName) ? creditedName : undefined,\n orderingPosition: Predicate.isNumber(orderingPosition) ? orderingPosition : index,\n };\n });\n};\n\n/** Parse a JSON text (`JSON.parse` semantics) to a success or a failed `Exit`. */\nconst decodeJson = SchemaParser.decodeUnknownExit(Schema.fromJsonString(Schema.Unknown));\n\n/** Coercion context for one field in one mode. */\ninterface CoerceContext {\n /** Required at create: emit the raw value's canonical form, never skip. */\n readonly required: boolean;\n /** Blank clears (`null`) rather than skips (`undefined`). */\n readonly clearable: boolean;\n /** Trim before the create-mode blank check (dates trim only at create). */\n readonly trimCreate: boolean;\n}\n\n/** Text fields: trim always; required create fields never skip. */\nconst coerceText = (ctx: CoerceContext, raw: string | undefined): unknown =>\n ctx.required ? (raw ?? \"\").trim() : ctx.clearable ? trimToNull(raw) : trimToUndefined(raw);\n\n/** Id fields: no trimming (pickers emit `\"\"` or a UUID); required create FKs pass through raw. */\nconst coerceId = (ctx: CoerceContext, raw: string | undefined): unknown =>\n ctx.required ? raw : ctx.clearable ? emptyToNull(raw) : emptyToUndefined(raw);\n\n/** Numeric text fields: blank clears on the clearable path, skips otherwise. */\nconst coerceNumber = (ctx: CoerceContext, raw: string | undefined): unknown =>\n ctx.clearable\n ? numberFromField(raw, { nullable: true })\n : numberFromField(raw, { nullable: false });\n\n/** Date fields stay encoded (`YYYY-MM-DD` strings); create trims, update passes through. */\nconst coerceDate = (ctx: CoerceContext, raw: string | undefined): unknown => {\n const value = ctx.trimCreate ? raw?.trim() : raw;\n return ctx.clearable ? emptyToNull(value) : emptyToUndefined(value);\n};\n\n/**\n * Boolean fields: a `<select>`'s value set, not a checkbox — the only truthy\n * raw value is the literal string `\"true\"`, everything else (`\"false\"`,\n * blank, absent) reads as `false`. Create defaults an absent/blank field to\n * `false` (never skips); update skips absent/blank unless clearable, in which\n * case it clears (`null`) instead — the create-defaults / update-skips\n * asymmetry the Content family already hand-writes.\n */\nconst coerceBoolean = (ctx: CoerceContext, raw: string | undefined): unknown => {\n const trimmed = raw?.trim();\n if (trimmed === undefined || trimmed === \"\") {\n return ctx.required ? false : ctx.clearable ? null : undefined;\n }\n return trimmed === \"true\";\n};\n\n/**\n * Parse a JSON-encoded structured document (inline / block ProseKit JSON)\n * from a multipart field. Blank or absent skips (`undefined`) or, on the\n * clearable path, clears (`null`); a present value is `JSON.parse`d and\n * passed through as-is — shape validation is the command's own decode's job,\n * exactly as {@link jsonRowsFromField} leaves row-shape validation to itself\n * rather than duplicating it here. `JSON.parse` throws a `SyntaxError` on\n * malformed JSON, same as {@link jsonRowsFromField} does on a malformed row\n * list — mapping that onto a framework response is the caller's concern.\n */\nconst structuredFromField = (raw: string | undefined, clearable: boolean): unknown => {\n const trimmed = raw?.trim();\n if (trimmed === undefined || trimmed === \"\") return clearable ? null : undefined;\n return JSON.parse(trimmed);\n};\n\n/** Build one field's {@link CoerceContext} for one mode. */\nconst coerceContextFor = (field: FieldPlan, mode: \"create\" | \"update\"): CoerceContext => ({\n required: mode === \"create\" && field.createRequired,\n clearable: mode === \"update\" && field.updateClearable,\n trimCreate: mode === \"create\",\n});\n\n/** The default coercion for one field in one mode. */\nconst coerce = (field: FieldPlan, mode: \"create\" | \"update\", raw: string | undefined): unknown => {\n const ctx = coerceContextFor(field, mode);\n return Match.value(field.kind).pipe(\n Match.when(Match.is(\"text\", \"enum\"), () => coerceText(ctx, raw)),\n Match.when(\"id\", () => coerceId(ctx, raw)),\n Match.when(\"number\", () => coerceNumber(ctx, raw)),\n Match.when(\"date\", () => coerceDate(ctx, raw)),\n Match.when(\"jsonArray\", () => jsonRowsFromField(raw, field.key)),\n Match.when(\"boolean\", () => coerceBoolean(ctx, raw)),\n Match.when(\"structured\", () => structuredFromField(raw, ctx.clearable)),\n Match.exhaustive,\n );\n};\n\n/**\n * Adapt every field of `commandPlan` present in `mode`, in plan order, to a\n * `[key, value]` entry: through the field's override when it has one, else\n * the default {@link coerce}.\n */\nconst adaptFields = (\n commandPlan: CommandPlan,\n overrides: AdaptOverrides,\n mode: \"create\" | \"update\",\n input: FormInputRecord,\n): Array<readonly [string, unknown]> =>\n Arr.flatMap(commandPlan.fields, (field): Array<readonly [string, unknown]> => {\n if (mode === \"create\" ? !field.inCreate : !field.inUpdate) return [];\n const override = overrides[field.key];\n const raw = input[field.key];\n return [[field.key, override ? override(raw) : coerce(field, mode, raw)]];\n });\n\n/**\n * Derive the create adapter: validated all-string input → encoded create\n * command payload. `overrides` replace the default per-field coercion for\n * genuinely irregular fields (e.g. a create-time fallback default).\n */\nexport const createAdapter =\n (commandPlan: CommandPlan, overrides: AdaptOverrides = {}) =>\n (input: FormInputRecord): Record<string, unknown> =>\n Rec.fromEntries([\n ...(commandPlan.clientId ? [[\"id\", trimToUndefined(input.id)] as const] : []),\n ...adaptFields(commandPlan, overrides, \"create\", input),\n ]);\n\n/**\n * Derive the update adapter: validated all-string input → encoded update\n * command payload (value sets, `null` clears a clearable field, `undefined`\n * skips). The required `id` passes through as-is — the command's canonical\n * decode brands it.\n */\nexport const updateAdapter =\n (commandPlan: CommandPlan, overrides: AdaptOverrides = {}) =>\n (input: FormInputRecord): Record<string, unknown> =>\n Rec.fromEntries([\n [\"id\", input.id] as const,\n ...adaptFields(commandPlan, overrides, \"update\", input),\n ]);\n","/**\n * HTML input constraints derived from an Effect Schema.\n *\n * Native validation attributes can be read (`required`, `minlength`, …)\n * off the schema so a form can apply them to its inputs for free, layering\n * browser-native validation under the schema-driven validation. This module\n * does the framework-neutral half: walk the schema AST and emit an\n * {@link InputConstraints} tree a UI can spread onto its `<input>`s.\n *\n * `required` comes from a struct field's optionality; `minlength` /\n * `maxlength` / `min` / `max` / `pattern` / `step` are derived from the\n * `representation` annotation Effect Schema's built-in checks carry\n * (`isMinLength`, `isBetween`, `isPattern`, …), keyed by its stable\n * `effect/schema/<check>` id. A check with no recognised representation id\n * (a bare `Schema.filter` predicate, a custom `.check()`) contributes\n * nothing; there is no attribute to guess at,\n * so the derivation stays silent rather than wrong. The walk descends into\n * nested structs and into a homogeneous array's item type (folding the\n * array's own optionality into the item's `required`); a fixed-shape tuple\n * with differently-typed elements has no single item schema to represent as\n * one constraint, so it derives only `required`. Like\n * {@link module:effect-forms/Field}'s `fromAst`, an `optional` / `NullOr`\n * union is unwrapped by taking its first non-`null`/`undefined` member —\n * scoped to that wrapping pattern, not general discriminated unions, whose\n * branches can't be flattened into one constraint either. The `overrides`\n * argument to {@link constraintsFromSchema} remains the seam for anything the\n * walk cannot infer (or should not — a check that's semantically stronger\n * than any HTML attribute).\n *\n * @copyright Thomas Foster\n * @module effect-forms/Constraints\n */\n\nimport * as Arr from \"effect/Array\";\nimport * as Option from \"effect/Option\";\nimport * as Predicate from \"effect/Predicate\";\nimport * as Rec from \"effect/Record\";\nimport type * as Schema from \"effect/Schema\";\nimport * as SchemaAST from \"effect/SchemaAST\";\nimport type * as SchemaRepresentation from \"effect/SchemaRepresentation\";\nimport type { FormPath } from \"./Issues.ts\";\n\n/** Native validation attributes for a single input. */\nexport interface InputConstraint {\n readonly required?: boolean;\n readonly min?: number | string;\n readonly max?: number | string;\n readonly minlength?: number;\n readonly maxlength?: number;\n readonly pattern?: string;\n readonly step?: number;\n}\n\n/** A nested tree of {@link InputConstraint}s mirroring the schema's shape. */\nexport interface InputConstraints {\n readonly [key: string]: InputConstraint | InputConstraints | undefined;\n}\n\n/** Whether a node in an {@link InputConstraints} tree is a leaf ({@link InputConstraint}) rather than a branch. */\nexport const isLeafConstraint = (\n value: InputConstraint | InputConstraints,\n): value is InputConstraint =>\n \"required\" in value ||\n \"min\" in value ||\n \"max\" in value ||\n \"minlength\" in value ||\n \"maxlength\" in value ||\n \"pattern\" in value ||\n \"step\" in value;\n\n/** Whether a property's type AST is optional (so the field is not required). */\nconst isOptional = (type: SchemaAST.AST): boolean => type.context?.isOptional === true;\n\n/**\n * Unwrap an `optional` / `NullOr` union the way {@link module:effect-forms/Field}'s\n * `fromAst` does: recurse into the first member that isn't a bare `undefined`\n * / `null` literal, so a wrapped struct / array / refined leaf still yields\n * its own shape instead of falling through to an empty constraint.\n */\nfunction unwrapOptional(type: SchemaAST.AST): SchemaAST.AST {\n if (!SchemaAST.isUnion(type)) return type;\n return Arr.findFirst(\n type.types,\n (member) => !SchemaAST.isUndefined(member) && !SchemaAST.isNull(member),\n ).pipe(Option.match({ onNone: () => type, onSome: unwrapOptional }));\n}\n\n/** Flatten a `Checks` array (recursing into `FilterGroup`s) into individual filters. */\nfunction flattenChecks(\n checks: ReadonlyArray<SchemaAST.Check<never>> | undefined,\n): Array<SchemaAST.Filter<never>> {\n if (checks === undefined) return [];\n return Arr.flatMap(checks, (check) =>\n Predicate.isTagged(check, \"FilterGroup\") ? flattenChecks(check.checks) : [check],\n );\n}\n\n/** A same-shape, mutable patch of {@link InputConstraint} fields. */\ntype ConstraintPatch = { -readonly [K in keyof InputConstraint]?: InputConstraint[K] };\n\n/**\n * The `representation.payload` shapes of the built-in checks that map onto a\n * native HTML attribute, keyed by the check's stable representation id.\n * Mirrors each check constructor in `effect/Schema` (e.g. `isMinLength`\n * writes `{ id: \"effect/schema/isMinLength\", payload: { minLength } }`).\n */\ninterface CheckPayloads {\n readonly \"effect/schema/isMinLength\": { readonly minLength: number };\n readonly \"effect/schema/isMaxLength\": { readonly maxLength: number };\n readonly \"effect/schema/isBetweenLength\": { readonly minimum: number; readonly maximum: number };\n readonly \"effect/schema/isPattern\": { readonly source: string; readonly flags: string };\n readonly \"effect/schema/isGreaterThan\": { readonly exclusiveMinimum: number };\n readonly \"effect/schema/isGreaterThanOrEqualTo\": { readonly minimum: number };\n readonly \"effect/schema/isLessThan\": { readonly exclusiveMaximum: number };\n readonly \"effect/schema/isLessThanOrEqualTo\": { readonly maximum: number };\n readonly \"effect/schema/isBetween\": { readonly minimum: number; readonly maximum: number };\n readonly \"effect/schema/isMultipleOf\": { readonly divisor: number };\n}\n\n/**\n * One constraint patch per recognised representation id — the built-in\n * checks that map onto a native HTML attribute. An id with no entry here\n * (an unannotated custom filter, or a built-in check with no HTML\n * equivalent) derives nothing. `isInt` isn't a plain patch: it only sets\n * `step` as a fallback when nothing more specific already did, so it's\n * handled separately in {@link constraintFromChecks}.\n */\nconst payloadPatches = {\n \"effect/schema/isMinLength\": (p) => ({ minlength: p.minLength }),\n \"effect/schema/isMaxLength\": (p) => ({ maxlength: p.maxLength }),\n \"effect/schema/isBetweenLength\": (p) => ({ minlength: p.minimum, maxlength: p.maximum }),\n \"effect/schema/isPattern\": (p) => ({ pattern: p.source }),\n \"effect/schema/isGreaterThan\": (p) => ({ min: p.exclusiveMinimum }),\n \"effect/schema/isGreaterThanOrEqualTo\": (p) => ({ min: p.minimum }),\n \"effect/schema/isLessThan\": (p) => ({ max: p.exclusiveMaximum }),\n \"effect/schema/isLessThanOrEqualTo\": (p) => ({ max: p.maximum }),\n \"effect/schema/isBetween\": (p) => ({ min: p.minimum, max: p.maximum }),\n \"effect/schema/isMultipleOf\": (p) => ({ step: p.divisor }),\n} satisfies {\n readonly [K in keyof CheckPayloads]: (payload: CheckPayloads[K]) => ConstraintPatch;\n};\n\n/** Whether `id` names a check {@link payloadPatches} has a patch for (an own key, never an inherited one). */\nconst isPatchedCheckId = (id: string): id is keyof CheckPayloads =>\n Rec.has<string, unknown>(payloadPatches, id);\n\n/**\n * Look up and apply `payloadPatches[representation.id]`. The cast is sound:\n * each payload is authored by the same built-in check constructor its id\n * names, so an entry is never called with a mismatched payload shape.\n */\nfunction applyPayloadPatch(\n representation: SchemaRepresentation.RepresentationAnnotation,\n): ConstraintPatch | undefined {\n const id = representation.id;\n if (!isPatchedCheckId(id)) return undefined;\n // SAFETY: every `effect/schema/is*` id in `CheckPayloads` is the\n // representation id of one of Effect's built-in checks, which publishes its\n // `payload` in exactly the shape `CheckPayloads` records for that id — so\n // the patch for `id` accepts this representation's payload.\n const patch = payloadPatches[id] as (payload: Schema.Json) => ConstraintPatch;\n return patch(representation.payload);\n}\n\n/** Whether a check is `Schema.isInt` (it implies `step=\"1\"`, not an attribute payload). */\nconst isIntCheck = (representation: SchemaRepresentation.RepresentationAnnotation): boolean =>\n representation.id === \"effect/schema/isInt\";\n\n/**\n * Derive an {@link InputConstraint}'s refinement attributes from the checks\n * attached to `outer` (the property's own type, e.g. the union wrapping an\n * optional field) and `inner` (the unwrapped leaf) — `inner`'s checks win on\n * conflict since they're the more specific, unwrapped node. A check whose\n * `representation` annotation has no recognised id (an unannotated custom\n * filter) is skipped: there is no attribute to derive from it.\n */\nfunction constraintFromChecks(outer: SchemaAST.AST, inner: SchemaAST.AST): InputConstraint {\n const filters =\n inner === outer\n ? flattenChecks(outer.checks)\n : [...flattenChecks(outer.checks), ...flattenChecks(inner.checks)];\n const representations = Arr.flatMap(filters, (filter) => {\n const representation = filter.annotations?.representation;\n return representation === undefined ? [] : [representation];\n });\n\n const empty: ConstraintPatch = {};\n const patched = Arr.reduce(representations, empty, (out, representation) =>\n isIntCheck(representation) ? out : { ...out, ...applyPayloadPatch(representation) },\n );\n\n return Arr.some(representations, isIntCheck) && patched.step === undefined\n ? { ...patched, step: 1 }\n : patched;\n}\n\n/**\n * Whether `ast` is a homogeneous array/list — `Schema.Array(item)` or\n * `Schema.NonEmptyArray(item)` — as opposed to a fixed-shape tuple with\n * differently-typed positional elements. Only a homogeneous array has a\n * single item schema to descend into; a tuple's elements each need their own\n * constraint, which this leaf-per-key tree can't represent, so it's left to\n * `overrides`.\n */\nfunction homogeneousItem(ast: SchemaAST.Arrays): SchemaAST.AST | undefined {\n if (ast.rest.length !== 1) return undefined;\n const [item] = ast.rest;\n return Arr.match(ast.elements, {\n onEmpty: () => item,\n onNonEmpty: (elements) => (elements.length === 1 && elements[0] === item ? item : undefined),\n });\n}\n\n/**\n * Derive the constraint (or nested tree) for one property/item type: a\n * struct descends into its properties, a homogeneous array descends into its\n * item type (the array's own length checks have no native equivalent for a\n * repeated field, so they're not derived) while folding the array's own\n * optionality into the item's `required`, and anything else is a leaf whose\n * `required` comes from optionality and whose refinement attributes come\n * from {@link constraintFromChecks}.\n */\nfunction constraintsForType(type: SchemaAST.AST): InputConstraint | InputConstraints {\n const required = !isOptional(type);\n const inner = unwrapOptional(type);\n if (SchemaAST.isObjects(inner)) {\n return constraintsForObjects(inner);\n }\n if (SchemaAST.isArrays(inner)) {\n const item = homogeneousItem(inner);\n if (!item) return { required };\n const itemConstraints = constraintsForType(item);\n return isLeafConstraint(itemConstraints)\n ? { ...itemConstraints, required: required && itemConstraints.required !== false }\n : itemConstraints;\n }\n return { required, ...constraintFromChecks(type, inner) };\n}\n\n/** Build the constraint tree for one object AST node. */\nfunction constraintsForObjects(ast: SchemaAST.Objects): InputConstraints {\n return Rec.fromEntries(\n Arr.map(\n ast.propertySignatures,\n (ps) => [String(ps.name), constraintsForType(ps.type)] as const,\n ),\n );\n}\n\n/** Deep-merge constraint overrides over a derived tree. */\nfunction mergeConstraints(base: InputConstraints, overrides: InputConstraints): InputConstraints {\n const merged = Arr.flatMap(\n Rec.keys<string, InputConstraints[string]>(overrides),\n (key): Array<readonly [string, InputConstraint | InputConstraints]> => {\n const override = overrides[key];\n if (override === undefined) return [];\n const existing = base[key];\n if (existing !== undefined && !isLeafConstraint(existing) && !isLeafConstraint(override)) {\n return [[key, mergeConstraints(existing, override)]];\n }\n if (existing !== undefined && isLeafConstraint(existing) && isLeafConstraint(override)) {\n return [[key, { ...existing, ...override }]];\n }\n return [[key, override]];\n },\n );\n return { ...base, ...Rec.fromEntries(merged) };\n}\n\n/**\n * Derive the {@link InputConstraints} for a struct schema. Non-struct schemas\n * yield an empty tree. `overrides` (deep-merged over the derived tree, and\n * winning on conflict) is the seam for anything the AST walk does not infer.\n *\n * @example\n * ```ts\n * import * as Schema from \"effect/Schema\"\n * import { Constraints } from \"@thomasfosterau/effect-forms\"\n *\n * const Signup = Schema.Struct({\n * name: Schema.String.check(Schema.isMinLength(2)),\n * nickname: Schema.optional(Schema.String),\n * })\n * console.log(Constraints.constraintsFromSchema(Signup))\n * // { name: { required: true, minlength: 2 }, nickname: { required: false } }\n * ```\n */\nexport function constraintsFromSchema(\n schema: Schema.Top,\n overrides: InputConstraints = {},\n): InputConstraints {\n const ast = schema.ast;\n const base = SchemaAST.isObjects(ast) ? constraintsForObjects(ast) : {};\n return mergeConstraints(base, overrides);\n}\n\n/**\n * Read the {@link InputConstraint} leaf at `path` in an {@link InputConstraints}\n * tree, defaulting to `{}` when `path` has no derived constraints. Array\n * indices in `path` are skipped rather than descended into — the tree has no\n * per-item nodes to index into, since {@link constraintsForType} derives one\n * constraint (or branch) per *field*, shared by every item at that field. A\n * bare item path (`tags[0]`) resolves to that shared leaf; a path that\n * continues past the index into a struct item's own field (`items[0].label`)\n * skips the index and resolves straight to `label`'s derived constraint.\n */\nexport function constraintAt(constraints: InputConstraints, path: FormPath): InputConstraint {\n // Index segments are skipped (a list's items share one constraint). Once a\n // string segment runs past a leaf or off the tree the accumulator is\n // `undefined`, and stays so for every later segment.\n const node = Arr.reduce<string | number, InputConstraint | InputConstraints | undefined>(\n path,\n constraints,\n (current, segment) => {\n if (Predicate.isNumber(segment)) return current;\n if (current === undefined || isLeafConstraint(current)) return undefined;\n return current[segment];\n },\n );\n return node !== undefined && isLeafConstraint(node) ? node : {};\n}\n","/**\n * Structured parsing of an HTML form body into a plain object the schema can\n * decode.\n *\n * Browsers flatten structured data into `name=value` pairs using bracket /\n * dot path notation (`author.name`, `author[name]`, `tags[0]`, `tags[]`).\n * {@link formDataToObject} reverses that into a nested object / array tree —\n * the framework-neutral step that turns a form body into structured data, before handing it\n * to a validator. Coercion (strings → numbers / booleans / dates) is then the\n * schema's job: decode the result with a schema built from coercing leaves\n * (`Schema.FiniteFromString`, `Schema.BooleanFromString`, the {@link\n * module:effect-forms/Coerce} helpers, …).\n *\n * `File` values pass through untouched; repeated bare keys collapse into an\n * array. The parser is total and never throws.\n *\n * @copyright Thomas Foster\n * @module effect-forms/FormData\n */\n\nimport * as Arr from \"effect/Array\";\nimport * as Predicate from \"effect/Predicate\";\nimport * as Rec from \"effect/Record\";\n\n/** Anything that can be parsed into a structured form object. */\nexport type FormBodySource =\n | FormData\n | URLSearchParams\n | Iterable<readonly [string, FormDataEntryValue | string]>;\n\ntype Container = Record<string, unknown> | unknown[];\n\n/** A pending path token, dropped when empty. */\nconst nonEmpty = (buffer: string): ReadonlyArray<string> => (buffer === \"\" ? [] : [buffer]);\n\n/**\n * Split a flat field name into its path tokens. Supports dot notation\n * (`a.b`), bracket notation (`a[b]`, `a[0]`), and the trailing append form\n * (`a[]`, represented by an empty-string token). `\"a[b][0]\"` → `[\"a\",\"b\",\"0\"]`;\n * `\"a.b[]\"` → `[\"a\",\"b\",\"\"]`.\n */\nexport function parseFieldPath(name: string): string[] {\n const scanned = Arr.reduce(\n Arr.fromIterable(name),\n { tokens: Arr.empty<string>(), buffer: \"\", inBracket: false },\n ({ tokens, buffer, inBracket }, char) => {\n if (char === \"[\") {\n return { tokens: [...tokens, ...nonEmpty(buffer)], buffer: \"\", inBracket: true };\n }\n if (char === \"]\") return { tokens: [...tokens, buffer], buffer: \"\", inBracket: false };\n if (char === \".\" && !inBracket) {\n return { tokens: [...tokens, ...nonEmpty(buffer)], buffer: \"\", inBracket };\n }\n return { tokens, buffer: buffer + char, inBracket };\n },\n );\n const tokens = [...scanned.tokens, ...nonEmpty(scanned.buffer)];\n // A leading empty token can appear for names like `[0]`; drop it.\n return tokens.length > 1 && tokens[0] === \"\" ? tokens.slice(1) : tokens;\n}\n\nconst isIndexToken = (token: string): boolean => token === \"\" || /^\\d+$/.test(token);\n\n/** Create the container a token should live in: array for index/append tokens. */\nconst makeContainer = (nextToken: string): Container => (isIndexToken(nextToken) ? [] : {});\n\nconst setOnContainer = (container: Container, token: string, value: unknown): void => {\n if (Arr.isArray(container)) {\n if (token === \"\") container.push(value);\n else container[Number(token)] = value;\n } else {\n container[token] = value;\n }\n};\n\nconst getFromContainer = (container: Container, token: string): unknown =>\n Arr.isArray(container) ? container[Number(token)] : container[token];\n\n/** Assign `value` deep into `root` along `tokens`, creating containers as needed. */\nfunction assign(root: Container, tokens: Arr.NonEmptyReadonlyArray<string>, value: unknown): void {\n const last = Arr.lastNonEmpty(tokens);\n // Walk every token but the last, descending into (or creating) the\n // container each one names; `tokens[i + 1]` is the token after `token`.\n const node = Arr.reduce(Arr.initNonEmpty(tokens), root, (parent, token, i): Container => {\n const child = token === \"\" ? undefined : getFromContainer(parent, token);\n if (Predicate.isObjectOrArray(child)) return child;\n const created = makeContainer(tokens[i + 1]);\n setOnContainer(parent, token, created);\n return created;\n });\n // A repeated bare key (no brackets) collapses into an array.\n if (last !== \"\" && !Array.isArray(node)) {\n const existing = getFromContainer(node, last);\n if (existing !== undefined) {\n const arr = Array.isArray(existing) ? existing : [existing];\n arr.push(value);\n setOnContainer(node, last, arr);\n return;\n }\n }\n setOnContainer(node, last, value);\n}\n\n/** Normalise a source into `[name, value]` entries. */\nfunction entriesOf(source: FormBodySource): Iterable<readonly [string, unknown]> {\n if (source instanceof FormData || source instanceof URLSearchParams) return source.entries();\n return source;\n}\n\n/**\n * Parse a form body into a nested object / array tree. Field names use dot or\n * bracket path notation (see {@link parseFieldPath}); repeated bare keys\n * become arrays; `File` values pass through. The result is ready to hand to\n * `Schema.decodeUnknown` with a schema whose leaves coerce the string inputs.\n *\n * @example\n * ```ts\n * import { FormData } from \"@thomasfosterau/effect-forms\"\n *\n * const body = new URLSearchParams([\n * [\"author.name\", \"Ada\"],\n * [\"tags[]\", \"math\"],\n * [\"tags[]\", \"engines\"],\n * ])\n * console.log(FormData.formDataToObject(body))\n * // { author: { name: \"Ada\" }, tags: [\"math\", \"engines\"] }\n * ```\n */\nexport function formDataToObject(source: FormBodySource): Record<string, unknown> {\n return Arr.reduce(entriesOf(source), Rec.empty<string, unknown>(), (root, [name, value]) => {\n const tokens = parseFieldPath(name);\n if (Arr.isReadonlyArrayNonEmpty(tokens)) assign(root, tokens, value);\n return root;\n });\n}\n\n/** Read and parse a `Request`'s form body. Errors (e.g. wrong content-type) reject. */\nexport async function requestToObject(request: Request): Promise<Record<string, unknown>> {\n const formData = await request.formData();\n return formDataToObject(formData);\n}\n","/**\n * Nested get / set along a {@link FormPath}.\n *\n * A form's data is a tree, and the reactive bindings address it by path:\n * `[\"author\", \"name\"]`, `[\"tags\", 0]`. This module reads and writes a value\n * at such a path — {@link getAt} descends, {@link setAt} returns a new tree\n * with the value replaced (structural sharing, never a mutation) so a rune /\n * atom store can diff cheaply. It is the framework-neutral half of the\n * reactive field bindings (the Svelte store leans on it for\n * `setValue` and the array operations).\n *\n * Paths are {@link FormPath}s — the same array-of-segments the {@link\n * module:effect-forms/Errors} tree and {@link module:effect-forms/Issues}\n * walker already speak — so string keys address object properties and number\n * segments address array indices. {@link toString} / {@link fromString}\n * convert to and from the flat `author.name` / `tags[0]` notation the HTML\n * body parser ({@link module:effect-forms/FormData}) uses.\n *\n * @copyright Thomas Foster\n * @module effect-forms/Path\n */\n\nimport * as Arr from \"effect/Array\";\nimport * as Predicate from \"effect/Predicate\";\nimport * as Rec from \"effect/Record\";\nimport { parseFieldPath } from \"./FormData.ts\";\nimport type { FormPath } from \"./Issues.ts\";\n\n/**\n * Read the value at `path`. Returns `undefined` if any segment is missing or\n * traverses a non-object. The empty path returns the object itself.\n */\nexport function getAt(obj: unknown, path: FormPath): unknown {\n // Once a segment lands on a non-object the accumulator is `undefined`, which\n // is itself a non-object, so every later segment keeps it `undefined`.\n // A missing key reads as `undefined` either way, so guarding on `in` first\n // matches a plain property read on any object or array.\n return Arr.reduce(path, obj, (current, segment) =>\n Predicate.isObjectOrArray(current) && Predicate.hasProperty(current, segment)\n ? current[segment]\n : undefined,\n );\n}\n\n/**\n * Return a new tree with the value at `path` replaced by `value`. Objects and\n * arrays along the path are cloned (structural sharing elsewhere); missing\n * containers are created as arrays for numeric segments and objects otherwise.\n * The empty path returns `value`. Pure — `obj` is never mutated.\n *\n * @example\n * ```ts\n * import { Path } from \"@thomasfosterau/effect-forms\"\n *\n * const before = { authors: [{ name: \"Ada\" }] }\n * const after = Path.setAt(before, [\"authors\", 0, \"name\"], \"Grace\")\n * console.log(Path.getAt(after, [\"authors\", 0, \"name\"])) // \"Grace\"\n * console.log(before.authors[0]?.name) // \"Ada\" — the input is untouched\n * ```\n */\nexport function setAt<T>(obj: T, path: FormPath, value: unknown): T {\n return Arr.match(path, {\n // SAFETY: the empty path addresses the whole tree, so `value` is the new\n // root; `setAt`'s documented contract is that the caller writes a value of\n // the shape `T` expects at `path`, which here is `T` itself.\n onEmpty: () => value as T,\n onNonEmpty: ([head, ...rest]) => {\n // `Object(obj)` boxes a primitive root so the spread type-checks; spreading\n // the box copies exactly what spreading `obj` itself would.\n // SAFETY: `clone` is a fresh shallow copy — an array or a plain object —\n // and both accept a read or write under any string or number key.\n const clone = (Arr.isArray(obj) ? [...obj] : { ...Object(obj) }) as Record<\n PropertyKey,\n unknown\n >;\n const child = clone[head];\n clone[head] = Arr.match(rest, {\n onEmpty: () => value,\n onNonEmpty: (tail) =>\n setAt(\n Predicate.isObjectOrArray(child) ? child : Predicate.isNumber(tail[0]) ? [] : {},\n tail,\n value,\n ),\n });\n // SAFETY: `clone` is a shallow copy of `obj` (an array when `obj` is\n // one, a plain object otherwise) differing only at `head`, where the\n // caller's `value` (by `setAt`'s contract, of the type `T` holds at\n // `path`) was written.\n return clone as T;\n },\n });\n}\n\n/** Whether `path` is `root` or nested beneath it. */\nexport function isUnder(path: FormPath, root: FormPath): boolean {\n return path.length >= root.length && Arr.every(root, (segment, i) => path[i] === segment);\n}\n\n/**\n * Render a {@link FormPath} as flat field-name notation: string segments join\n * with `.` and number segments become `[n]` (`[\"tags\", 0]` → `\"tags[0]\"`).\n * The inverse of {@link fromString}.\n */\nexport function toString(path: FormPath): string {\n return Arr.reduce(path, \"\", (out, segment) =>\n Predicate.isNumber(segment) ? `${out}[${segment}]` : out === \"\" ? segment : `${out}.${segment}`,\n );\n}\n\n/**\n * Parse flat field-name notation into a {@link FormPath}: integer tokens become\n * number segments (array indices), everything else stays a string. The inverse\n * of {@link toString}.\n */\nexport function fromString(name: string): FormPath {\n return parseFieldPath(name).map((token) => (/^\\d+$/.test(token) ? Number(token) : token));\n}\n\n/**\n * The deep-key type: every valid {@link toString} path into `T`, as a union of\n * string literals (`\"author.name\"`, `\"tags[0]\"`, `\"tags[0].label\"`, …). This is\n * the type-checked counterpart of a hand-written {@link FormPath} — a typo'd\n * segment or a path into a field that doesn't exist is a compile error. Bounded\n * to a fixed recursion depth ({@link Prev}'s length) so a struct with cycles\n * (there shouldn't be any — form data is JSON-shaped) can't blow up inference.\n *\n * Used by {@link module:effect-forms-svelte/FormStore}'s `field(path)` — see\n * the trade-off recorded in `docs/decisions/` for why this was chosen over the\n * core's (now-removed) opaque `FieldRef`.\n */\nexport type FieldPath<T, D extends number = 8> = T extends object\n ? [D] extends [never]\n ? never\n : {\n [K in Extract<keyof T, string>]: `${K}${PathSuffix<T[K], Prev[D]>}`;\n }[Extract<keyof T, string>]\n : never;\n\n/** Recursion-depth countdown used by {@link FieldPath} / {@link PathValue}. */\ntype Prev = [never, 0, 1, 2, 3, 4, 5, 6, 7];\n\n/**\n * The continuation after a key: nothing, an array index (the bracket closes\n * immediately — `[0]`, not `[0.label]` — then {@link PathSuffix} recurses on\n * the element type for whatever comes after), or `.key`.\n */\ntype PathSuffix<T, D extends number> = [D] extends [never]\n ? \"\"\n : T extends ReadonlyArray<infer E>\n ? \"\" | `[${number}]${PathSuffix<E, Prev[D]>}`\n : T extends object\n ?\n | \"\"\n | { [K in Extract<keyof T, string>]: `.${K}${PathSuffix<T[K], Prev[D]>}` }[Extract<\n keyof T,\n string\n >]\n : \"\";\n\n/** The base key of a single dot-segment (`\"tags[0]\"` → `\"tags\"`). */\ntype SegmentBase<S extends string> = S extends `${infer Base}[${string}` ? Base : S;\n\n/** The bracket indices of a single dot-segment (`\"tags[0][1]\"` → `[\"0\", \"1\"]`), untyped past presence. */\ntype SegmentIndices<S extends string> = S extends `${string}[${infer Idx}]${infer Rest}`\n ? [Idx, ...SegmentIndicesRest<Rest>]\n : [];\n\n/** Continuation of {@link SegmentIndices} once the leading (non-bracket) prefix has been consumed. */\ntype SegmentIndicesRest<S extends string> = S extends `[${infer Idx}]${infer Rest}`\n ? [Idx, ...SegmentIndicesRest<Rest>]\n : [];\n\n/** Step through an array type once per entry in `Idxs`. */\ntype ApplyIndices<T, Idxs extends readonly unknown[]> = Idxs extends [unknown, ...infer Rest]\n ? T extends ReadonlyArray<infer E>\n ? ApplyIndices<E, Rest>\n : never\n : T;\n\n/** Resolve a single dot-segment (base key plus any bracket indices) against `T`. */\ntype ResolveSegment<T, S extends string> =\n SegmentBase<S> extends keyof T ? ApplyIndices<T[SegmentBase<S>], SegmentIndices<S>> : never;\n\n/**\n * The value type at a {@link FieldPath} `P` into `T` — the leaf type `field(path)`\n * resolves to. `PathValue<{ tags: string[] }, \"tags[0]\">` is `string`.\n */\nexport type PathValue<T, P extends string> = P extends `${infer Head}.${infer Rest}`\n ? PathValue<ResolveSegment<T, Head>, Rest>\n : ResolveSegment<T, P>;\n\n/**\n * Maps an array index that existed before an index-shifting mutation to where\n * it lives afterwards, or to `null` when the item at that index no longer\n * exists (its per-index state should be dropped). Built by {@link removeAt} /\n * {@link insertAt} / {@link swapAt} / {@link moveAt} / {@link replaceAt}, and\n * consumed by {@link remapIndex} / {@link remapKeyedRecord} and\n * `Errors.remapIndices` to keep per-index state (touched flags, error trees)\n * in sync with the row it was recorded against.\n */\nexport type IndexMap = (index: number) => number | null;\n\n/** The {@link IndexMap} for removing the item at `index`: shifts later indices down by one. */\nexport function removeAt(index: number): IndexMap {\n return (i) => (i === index ? null : i > index ? i - 1 : i);\n}\n\n/** The {@link IndexMap} for inserting an item at `index`: shifts `index` and later up by one. */\nexport function insertAt(index: number): IndexMap {\n return (i) => (i >= index ? i + 1 : i);\n}\n\n/** The {@link IndexMap} for swapping the items at `a` and `b`. */\nexport function swapAt(a: number, b: number): IndexMap {\n return (i) => (i === a ? b : i === b ? a : i);\n}\n\n/** The {@link IndexMap} for moving the item at `from` to `to`, shifting the items between. */\nexport function moveAt(from: number, to: number): IndexMap {\n return (i) => {\n if (i === from) return to;\n if (from < to) return i > from && i <= to ? i - 1 : i;\n return i >= to && i < from ? i + 1 : i;\n };\n}\n\n/** The {@link IndexMap} for replacing the item at `index` in place: drops only that index's state. */\nexport function replaceAt(index: number): IndexMap {\n return (i) => (i === index ? null : i);\n}\n\n/** The {@link IndexMap} for clearing every item in the array: drops all per-index state. */\nexport const clearAll: IndexMap = () => null;\n\n/**\n * Remap `path`'s index segment immediately under `arrayPath` through `map`.\n * Paths outside `arrayPath`, and `arrayPath` itself (a whole-array message),\n * pass through unchanged; a path whose index segment maps to `null` is\n * dropped (returns `null`) — its item no longer exists. Pure.\n */\nexport function remapIndex(path: FormPath, arrayPath: FormPath, map: IndexMap): FormPath | null {\n if (!isUnder(path, arrayPath) || path.length === arrayPath.length) return path;\n const segment = path[arrayPath.length];\n if (!Predicate.isNumber(segment)) return path;\n const mapped = map(segment);\n if (mapped === null) return null;\n if (mapped === segment) return path;\n return [...path.slice(0, arrayPath.length), mapped, ...path.slice(arrayPath.length + 1)];\n}\n\n/**\n * Remap every entry of a flat, path-keyed record (a {@link toString} key per\n * entry — e.g. touched flags) through {@link remapIndex}. Entries whose path\n * maps to `null` are dropped. Pure.\n */\nexport function remapKeyedRecord<T>(\n record: Record<string, T>,\n arrayPath: FormPath,\n map: IndexMap,\n): Record<string, T> {\n return Rec.fromEntries(\n Arr.flatMap(Rec.toEntries(record), ([key, value]) => {\n const remapped = remapIndex(fromString(key), arrayPath, map);\n return remapped === null ? [] : [[toString(remapped), value] as const];\n }),\n );\n}\n","/**\n * The nested form-error tree and its operations.\n *\n * {@link FormErrors} is a nested error tree so a UI can read\n * `errors.author.name` or `errors.tags[0]` directly. The representation is:\n *\n * - a **leaf** field's messages are a `string[]` keyed by the field name\n * (`{ title: [\"Required\"] }`);\n * - a **branch** (nested object / array) is a nested `FormErrors`\n * (`{ author: { name: [\"Required\"] } }`); array elements are keyed by their\n * stringified index (`{ tags: { \"0\": [\"Too short\"] } }`);\n * - a message attached to a **branch itself** (a cross-field or\n * whole-array message) lives under that branch's `_errors` key.\n *\n * The tree is plain JSON — it serialises across the SSR boundary unchanged —\n * and the operations here ({@link issuesToErrors}, {@link flattenErrors},\n * {@link setError}, {@link mergeErrors}, …) are pure and return new trees.\n *\n * @copyright Thomas Foster\n * @module effect-forms/Errors\n */\n\nimport * as Arr from \"effect/Array\";\nimport * as Predicate from \"effect/Predicate\";\nimport * as Rec from \"effect/Record\";\nimport type { FormIssue, FormPath } from \"./Issues.ts\";\nimport { type IndexMap, remapIndex } from \"./Path.ts\";\n\n/** The root key under which a branch's own (non-field) messages are stored. */\nexport const ROOT_ERROR_KEY = \"_errors\";\n\n/**\n * A nested tree of validation messages. Leaves are `string[]`; branches are\n * nested {@link FormErrors}; a branch's own messages live under\n * {@link ROOT_ERROR_KEY}. Always JSON-serialisable.\n */\nexport interface FormErrors {\n readonly [ROOT_ERROR_KEY]?: readonly string[];\n readonly [key: string]: FormErrors | readonly string[] | undefined;\n}\n\n/**\n * A {@link FormErrors} tree typed to the shape of `A`, so fields are accessed\n * by name with type-safety: for `A = { name: string; author: { bio: string } }`,\n * `errors.name` is `readonly string[] | undefined` and `errors.author?.bio`\n * likewise. A branch's own (cross-field) messages live under `_errors`. The\n * runtime value is an ordinary {@link FormErrors} — this only refines the type.\n */\nexport type TypedFormErrors<A> = [NonNullable<A>] extends [ReadonlyArray<infer E>]\n ? {\n readonly [index: number]: TypedFormErrors<E> | undefined;\n readonly _errors?: readonly string[];\n }\n : [NonNullable<A>] extends [object]\n ? { readonly [K in keyof A]?: TypedFormErrors<A[K]> } & { readonly _errors?: readonly string[] }\n : readonly string[];\n\n/** Mutable mirror of {@link FormErrors} used while building a tree. */\ninterface MutableNode {\n [key: string]: string[] | MutableNode | undefined;\n}\n\nconst isMessageList = <T>(value: T): value is Extract<T, readonly string[]> =>\n Arr.isArray(value) && Arr.every(value, Predicate.isString);\n\nconst ownErrors = (node: MutableNode): string[] => {\n const existing = node[ROOT_ERROR_KEY];\n if (isMessageList(existing)) return existing;\n const created: string[] = [];\n node[ROOT_ERROR_KEY] = created;\n return created;\n};\n\n/**\n * Return the branch at `node[key]`, making one in place if needed: an absent\n * key gets a new empty branch, and a leaf's message list is converted into a\n * branch carrying those same messages as its own {@link ROOT_ERROR_KEY} (a\n * path used as a leaf is now needed as a branch too). Shared by {@link insert}\n * and {@link setLeaf}'s path-descent, the one piece of their branch-walking\n * that's identical.\n */\nconst branchAt = (node: MutableNode, key: string): MutableNode => {\n const existing = node[key];\n if (existing === undefined) {\n const created: MutableNode = {};\n node[key] = created;\n return created;\n }\n if (isMessageList(existing)) {\n const promoted: MutableNode = { [ROOT_ERROR_KEY]: existing };\n node[key] = promoted;\n return promoted;\n }\n return existing;\n};\n\n/**\n * Insert one message at `path` into a mutable error tree. A leaf becomes a\n * `string[]`; if the same path is later used as a branch the existing list is\n * preserved under {@link ROOT_ERROR_KEY}; if a branch later receives its own\n * message it lands under {@link ROOT_ERROR_KEY}. The empty path targets the\n * tree root's {@link ROOT_ERROR_KEY}.\n */\nconst insert = (root: MutableNode, path: FormPath, message: string): void => {\n if (!Arr.isReadonlyArrayNonEmpty(path)) {\n ownErrors(root).push(message);\n return;\n }\n\n const node = Arr.reduce(Arr.initNonEmpty(path), root, (parent, segment) =>\n branchAt(parent, String(segment)),\n );\n\n const last = String(Arr.lastNonEmpty(path));\n const existing = node[last];\n if (existing === undefined) {\n node[last] = [message];\n } else if (isMessageList(existing)) {\n existing.push(message);\n } else {\n // The leaf key is already a branch — attach to its own `_errors`.\n ownErrors(existing).push(message);\n }\n};\n\n/** Build a nested {@link FormErrors} tree from a flat list of {@link FormIssue}s. */\nexport function issuesToErrors(issues: ReadonlyArray<FormIssue>): FormErrors {\n const root: MutableNode = {};\n Arr.forEach(issues, (issue) => insert(root, issue.path, issue.message));\n return root;\n}\n\n/**\n * Flatten a {@link FormErrors} tree back into a list of {@link FormIssue}s, in\n * depth-first order. The inverse of {@link issuesToErrors} (modulo message\n * ordering within a single field).\n */\nexport function flattenErrors(errors: FormErrors): FormIssue[] {\n const walk = (node: FormErrors, path: Array<string | number>): Array<FormIssue> =>\n Arr.flatMap(Rec.keys<string, FormErrors[string]>(node), (key): Array<FormIssue> => {\n if (key === ROOT_ERROR_KEY) {\n return Arr.map(node[ROOT_ERROR_KEY] ?? [], (message) => ({ path, message }));\n }\n const value = node[key];\n if (value === undefined) return [];\n const segment: string | number = /^\\d+$/.test(key) ? Number(key) : key;\n if (isMessageList(value)) {\n return Arr.map(value, (message) => ({ path: [...path, segment], message }));\n }\n return walk(value, [...path, segment]);\n });\n\n return walk(errors, []);\n}\n\n/** Whether the tree carries any message anywhere. */\nexport function hasErrors(errors: FormErrors): boolean {\n return Arr.isReadonlyArrayNonEmpty(flattenErrors(errors));\n}\n\n/**\n * Read the messages attached directly to `path` (a leaf's list, or a branch's\n * own {@link ROOT_ERROR_KEY}). Returns `undefined` when the path has no direct\n * messages.\n */\nexport function getError(errors: FormErrors, path: FormPath): readonly string[] | undefined {\n // Once the walk leaves the tree (an absent key, or a leaf with path left\n // over) the accumulator is `undefined`, and stays so for every later segment.\n const node = Arr.reduce<string | number, FormErrors | readonly string[] | undefined>(\n path,\n errors,\n (current, segment) =>\n current === undefined || isMessageList(current) ? undefined : current[String(segment)],\n );\n if (node === undefined) return undefined;\n if (isMessageList(node)) return node;\n return node[ROOT_ERROR_KEY];\n}\n\n/** Return a new tree with `message` added at `path`. Pure. */\nexport function setError(errors: FormErrors, path: FormPath, message: string): FormErrors {\n // SAFETY: `structuredClone` returns a fresh deep copy that nothing else\n // references, and `MutableNode` is `FormErrors` minus `readonly`, so\n // mutating the copy in place cannot be observed through `errors`.\n const next = structuredClone(errors) as MutableNode;\n insert(next, path, message);\n return next;\n}\n\n/** Combine two error trees into one (left then right). Pure. */\nexport function mergeErrors(a: FormErrors, b: FormErrors): FormErrors {\n return issuesToErrors([...flattenErrors(a), ...flattenErrors(b)]);\n}\n\n/** The empty error tree. */\nexport const noErrors: FormErrors = {};\n\n/**\n * Remap the array-index segments of every message under `arrayPath` through\n * `map` (see {@link module:effect-forms/Path.IndexMap}), so an error recorded\n * against a row follows that row through a remove / insert / swap / move.\n * Messages whose index maps to `null` are dropped. Pure.\n */\nexport function remapIndices(errors: FormErrors, arrayPath: FormPath, map: IndexMap): FormErrors {\n return issuesToErrors(\n Arr.flatMap(flattenErrors(errors), (issue) => {\n const path = remapIndex(issue.path, arrayPath, map);\n return path === null ? [] : [{ path, message: issue.message }];\n }),\n );\n}\n\n/**\n * Return a new tree with the messages *directly at* `path` replaced by\n * `messages` (removing the key when `messages` is empty), leaving every other\n * path — including children of a branch `path` — untouched. Unlike\n * {@link setError} (which appends one message), this is the \"this field's\n * validator just ran, here is its complete result\" operation a per-field\n * validator needs: it can clear a field back to valid without disturbing\n * sibling or nested messages. Pure.\n */\nexport function replaceAt(\n errors: FormErrors,\n path: FormPath,\n messages: readonly string[],\n): FormErrors {\n // SAFETY: `structuredClone` returns a fresh deep copy that nothing else\n // references, and `MutableNode` is `FormErrors` minus `readonly`, so\n // mutating the copy in place cannot be observed through `errors`.\n const next = structuredClone(errors) as MutableNode;\n setLeaf(next, path, messages);\n return next;\n}\n\nconst setLeaf = (root: MutableNode, path: FormPath, messages: readonly string[]): void => {\n if (!Arr.isReadonlyArrayNonEmpty(path)) {\n setOwnMessages(root, messages);\n return;\n }\n const parent = descendClearing(root, Arr.initNonEmpty(path), messages);\n if (parent === undefined) return; // an absent branch on the clearing path — nothing to clear\n setLeafMessages(parent, String(Arr.lastNonEmpty(path)), messages);\n};\n\n/** Set or clear a node's own {@link ROOT_ERROR_KEY} messages. */\nconst setOwnMessages = (node: MutableNode, messages: readonly string[]): void => {\n if (Arr.isReadonlyArrayEmpty(messages)) delete node[ROOT_ERROR_KEY];\n else node[ROOT_ERROR_KEY] = [...messages];\n};\n\n/**\n * Walk down the `parents` segments (a path minus its last segment) to the\n * leaf's parent branch, creating branches as needed. When\n * `messages` is empty (a clear, not a set) a missing branch means there is\n * nothing to clear — descent stops early and returns `undefined` rather than\n * creating empty branches just to immediately no-op inside them.\n */\nconst descendClearing = (\n root: MutableNode,\n parents: FormPath,\n messages: readonly string[],\n): MutableNode | undefined =>\n // Once a clear meets an absent branch the accumulator is `undefined`, and\n // every later segment leaves the tree untouched.\n Arr.reduce<string | number, MutableNode | undefined>(parents, root, (node, segment) => {\n if (node === undefined) return undefined;\n const key = String(segment);\n if (node[key] === undefined && Arr.isReadonlyArrayEmpty(messages)) return undefined;\n return branchAt(node, key);\n });\n\n/** Set or clear `node[key]`'s messages — a leaf's list, or (if it's already a branch) its own {@link ROOT_ERROR_KEY}. */\nconst setLeafMessages = (node: MutableNode, key: string, messages: readonly string[]): void => {\n const existing = node[key];\n if (existing === undefined) {\n if (Arr.isReadonlyArrayNonEmpty(messages)) node[key] = [...messages];\n return;\n }\n if (isMessageList(existing)) {\n if (Arr.isReadonlyArrayEmpty(messages)) delete node[key];\n else node[key] = [...messages];\n return;\n }\n // Already a branch (has nested children) — replace its own messages,\n // preserving the children.\n setOwnMessages(existing, messages);\n};\n\n/**\n * Where a message in a {@link FormErrors} tree came from. `\"server\"` messages\n * come from the last submission response; `\"client\"` messages come from\n * browser-side validation (schema re-run, per-field validators, client-run\n * `refineEffect` refinements). Kept as parallel trees (see {@link\n * SourcedErrors}) rather than tagging each message, so a UI can style sources\n * differently and client revalidation can clear only its own tree, leaving\n * server-reported failures in place until their field actually changes.\n */\nexport type ErrorSource = \"client\" | \"server\";\n\n/** Parallel error trees, one per {@link ErrorSource}, plus their {@link mergeSourced} view. */\nexport interface SourcedErrors {\n readonly client: FormErrors;\n readonly server: FormErrors;\n}\n\n/** Both source trees empty. */\nexport const emptySourced: SourcedErrors = { client: noErrors, server: noErrors };\n\n/** Combine both source trees into the single tree a UI reads (server first, then client). */\nexport function mergeSourced(sourced: SourcedErrors): FormErrors {\n return mergeErrors(sourced.server, sourced.client);\n}\n\n/**\n * Return new {@link SourcedErrors} with `source`'s tree updated at `path` via\n * {@link replaceAt} (the other source's tree is untouched). Pure.\n */\nexport function setSourceAt(\n sourced: SourcedErrors,\n source: ErrorSource,\n path: FormPath,\n messages: readonly string[],\n): SourcedErrors {\n return { ...sourced, [source]: replaceAt(sourced[source], path, messages) };\n}\n\n/** Clear `source`'s messages at `path` (and nowhere else). Shorthand for {@link setSourceAt} with `[]`. */\nexport function clearSourceAt(\n sourced: SourcedErrors,\n source: ErrorSource,\n path: FormPath,\n): SourcedErrors {\n return setSourceAt(sourced, source, path, []);\n}\n","/**\n * Field definitions for the {@link module:effect-forms/FormBuilder}.\n *\n * A {@link FormBuilder} composes a form one field at a time. Each field is\n * either a **scalar** {@link FieldDef} (bound to a leaf schema) or an\n * **array** {@link ArrayFieldDef} (bound to an item schema — the field is a\n * list of that item). This module is the small vocabulary the builder is made\n * of: the two field shapes, their guards, the type-level projections that turn\n * a record of fields into the form's encoded / decoded data types, and the\n * default-value derivation used to seed a blank form (or a freshly-appended\n * array item).\n *\n * Defaults are *encoded* — an HTML form holds strings, so a scalar seeds to\n * `\"\"`, an array to `[]`, and a nested struct recurses. Coercion back to the\n * decoded type is the schema's job at validate time (see\n * {@link module:effect-forms/Coerce}).\n *\n * @copyright Thomas Foster\n * @module effect-forms/Field\n */\n\nimport * as Arr from \"effect/Array\";\nimport * as Option from \"effect/Option\";\nimport * as Predicate from \"effect/Predicate\";\nimport * as Rec from \"effect/Record\";\nimport type * as Schema from \"effect/Schema\";\nimport * as SchemaAST from \"effect/SchemaAST\";\nimport type { FormPath } from \"./Issues.ts\";\n\n/** A scalar field: a key bound to a leaf (or nested struct) schema. */\nexport interface FieldDef<K extends string, S extends Schema.Top> {\n readonly _tag: \"field\";\n readonly key: K;\n readonly schema: S;\n}\n\n/** An array field: a key bound to an item schema; the field is a list of items. */\nexport interface ArrayFieldDef<K extends string, S extends Schema.Top> {\n readonly _tag: \"array\";\n readonly key: K;\n readonly itemSchema: S;\n}\n\n/** Either field shape, with the key / schema types erased. */\nexport type AnyFieldDef = FieldDef<string, Schema.Top> | ArrayFieldDef<string, Schema.Top>;\n\n/** A record of fields, keyed by field name — the builder's accumulated shape. */\nexport type FieldsRecord = Record<string, AnyFieldDef>;\n\n/** Whether `def` is an {@link ArrayFieldDef}. */\nexport const isArrayFieldDef = (def: AnyFieldDef): def is ArrayFieldDef<string, Schema.Top> =>\n Predicate.isTagged(def, \"array\");\n\n/** Whether `def` is a scalar {@link FieldDef}. */\nexport const isFieldDef = (def: AnyFieldDef): def is FieldDef<string, Schema.Top> =>\n Predicate.isTagged(def, \"field\");\n\n/** Construct a scalar {@link FieldDef}. */\nexport const makeField = <K extends string, S extends Schema.Top>(\n key: K,\n schema: S,\n): FieldDef<K, S> => ({ _tag: \"field\", key, schema });\n\n/** Construct an {@link ArrayFieldDef}. */\nexport const makeArrayField = <K extends string, S extends Schema.Top>(\n key: K,\n itemSchema: S,\n): ArrayFieldDef<K, S> => ({ _tag: \"array\", key, itemSchema });\n\n/** The encoded (form-input) data type for a record of fields. */\nexport type EncodedFromFields<T extends FieldsRecord> = {\n readonly [K in keyof T]: T[K] extends FieldDef<string, infer S>\n ? S[\"Encoded\"]\n : T[K] extends ArrayFieldDef<string, infer S>\n ? ReadonlyArray<S[\"Encoded\"]>\n : never;\n};\n\n/** The decoded (validated) data type for a record of fields. */\nexport type DecodedFromFields<T extends FieldsRecord> = {\n readonly [K in keyof T]: T[K] extends FieldDef<string, infer S>\n ? S[\"Type\"]\n : T[K] extends ArrayFieldDef<string, infer S>\n ? ReadonlyArray<S[\"Type\"]>\n : never;\n};\n\n/**\n * Derive a blank *encoded* default from a schema AST: a struct recurses into\n * its properties, an array seeds to `[]`, and any scalar seeds to `\"\"` (an\n * HTML input's empty value). Conservative by design — a value that will not\n * decode cleanly is still a valid *starting* value the user edits.\n */\nexport function defaultValue(schema: Schema.Top): unknown {\n return fromAst(schema.ast);\n}\n\n/**\n * Unwrap a union to its first structured (struct / array) member — an\n * `optional` / `NullOr` wrapper (`Struct | undefined` / `Struct | null`)\n * still resolves to the wrapped struct or array's AST rather than a scalar\n * fallback. Non-unions, and unions with no structured member, pass through.\n */\nfunction unwrapToStructured(ast: SchemaAST.AST): SchemaAST.AST {\n if (!SchemaAST.isUnion(ast)) return ast;\n return Arr.findFirst(ast.types, (member) => {\n const inner = unwrapToStructured(member);\n return SchemaAST.isObjects(inner) || SchemaAST.isArrays(inner)\n ? Option.some(inner)\n : Option.none();\n }).pipe(Option.getOrElse(() => ast));\n}\n\n/** A seeded blank value: `\"\"` for a leaf, `[]` for an array, a struct of blanks for a struct. */\ntype BlankValue = \"\" | Array<never> | BlankStruct;\n\n/** A struct of {@link BlankValue}s, one per property. */\ninterface BlankStruct {\n readonly [key: string]: BlankValue;\n}\n\nfunction fromAst(ast: SchemaAST.AST): BlankValue {\n const unwrapped = unwrapToStructured(ast);\n if (SchemaAST.isObjects(unwrapped)) {\n return Rec.fromEntries(\n Arr.map(unwrapped.propertySignatures, (ps) => [String(ps.name), fromAst(ps.type)] as const),\n );\n }\n if (SchemaAST.isArrays(unwrapped)) return [];\n return \"\";\n}\n\n/** The item AST of an {@link SchemaAST.Arrays} node: the rest (spread) type, or the first tuple element. */\nfunction arrayElementAst(ast: SchemaAST.Arrays): SchemaAST.AST | undefined {\n return ast.rest[0] ?? ast.elements[0];\n}\n\n/** Descend one array-index path segment from an (already-unwrapped) array AST. */\nfunction stepIntoArray(ast: SchemaAST.AST): SchemaAST.AST | undefined {\n if (!SchemaAST.isArrays(ast)) return undefined;\n const item = arrayElementAst(ast);\n return item === undefined ? undefined : unwrapToStructured(item);\n}\n\n/** Descend one struct-property path segment from an (already-unwrapped) struct AST. */\nfunction stepIntoStruct(ast: SchemaAST.AST, key: string): SchemaAST.AST | undefined {\n if (!SchemaAST.isObjects(ast)) return undefined;\n const ps = ast.propertySignatures.find((p) => String(p.name) === key);\n return ps === undefined ? undefined : unwrapToStructured(ps.type);\n}\n\n/**\n * Walk `schema`'s AST along `path` — through struct properties and array\n * elements alike — and return the AST found there, or `undefined` if `path`\n * doesn't resolve (a missing property, or a non-array indexed). Unlike a\n * {@link FieldsRecord} lookup, this works for any nesting depth and for\n * schemas built directly with `Form.make` (no builder involved).\n */\nexport function schemaAt(schema: Schema.Top, path: FormPath): SchemaAST.AST | undefined {\n // Once a step finds no matching AST the accumulator is `undefined`, and\n // stays so for every later segment.\n return Arr.reduce<string | number, SchemaAST.AST | undefined>(\n path,\n unwrapToStructured(schema.ast),\n (ast, segment) =>\n ast === undefined\n ? undefined\n : Predicate.isNumber(segment)\n ? stepIntoArray(ast)\n : stepIntoStruct(ast, segment),\n );\n}\n\n/**\n * Derive a blank encoded item for the array field at `path` (any nesting\n * depth) by walking `schema`'s AST there — the nested-array counterpart to\n * {@link defaultValue}. Returns `\"\"` when `path` doesn't resolve to an array.\n */\nexport function defaultItemAt(schema: Schema.Top, path: FormPath): unknown {\n const ast = schemaAt(schema, path);\n const item = ast !== undefined && SchemaAST.isArrays(ast) ? arrayElementAst(ast) : undefined;\n return item === undefined ? \"\" : fromAst(item);\n}\n\n/** Seed a blank encoded value for every field: `[]` for arrays, `\"\"` otherwise. */\nexport function defaultEncodedValues(fields: FieldsRecord): Record<string, unknown> {\n return Rec.map(fields, (def) => (isArrayFieldDef(def) ? [] : defaultValue(def.schema)));\n}\n","/**\n * Form issues — the flat, path-addressed representation of a single\n * validation failure, plus the walker that turns an Effect Schema issue\n * tree into a list of them.\n *\n * An Effect Schema decode failure is a recursive {@link SchemaIssue.Issue}\n * tree: `Pointer` nodes carry path segments, `Composite` / `AnyOf` group\n * sibling failures, `Filter` / `Encoding` wrap a single inner failure, and\n * the leaves (`InvalidType`, `InvalidValue`, `MissingKey`, …) describe the\n * actual problem. {@link schemaIssuesToFormIssues} flattens that tree into\n * one {@link FormIssue} per leaf, accumulating the `Pointer.path` segments\n * into a `FormPath`. This is the framework-neutral\n * flattened error list and the same walk the repo's JSON:API materialiser\n * performs — here it stays UI-shaped (`path` + `message`) rather than\n * JSON:API-pointer-shaped.\n *\n * @copyright Thomas Foster\n * @module effect-forms/Issues\n */\n\nimport * as Arr from \"effect/Array\";\nimport * as Match from \"effect/Match\";\nimport * as Predicate from \"effect/Predicate\";\nimport type * as Schema from \"effect/Schema\";\nimport * as SchemaIssue from \"effect/SchemaIssue\";\n\n/**\n * A path into the form's data. String segments address object keys; number\n * segments address array indices. The empty path addresses the form root\n * (a whole-form / cross-field message).\n */\nexport type FormPath = ReadonlyArray<string | number>;\n\n/** One validation failure: where it happened ({@link FormPath}) and why. */\nexport interface FormIssue {\n readonly path: FormPath;\n readonly message: string;\n}\n\n/**\n * Render one terminal issue into a human-readable message. The default is\n * Effect Schema's own formatter (`SchemaIssue.makeFormatterDefault`), which\n * renders a sentence such as `Expected string`. (An issue's `toString` is not\n * a readable rendering in Effect 4.0.0: it prints `[object Object]`.)\n * Override it to localise or to map specific `_tag`s to custom copy.\n *\n * A failed schema check reaches the formatter as the `Filter` node that\n * wraps its leaf, because the check's own message (e.g. `Expected a value\n * with a length of at least 2`) lives on the `Filter`, not on the leaf.\n */\nexport type LeafFormatter = (issue: SchemaIssue.Issue) => string;\n\n/** The default leaf formatter — Effect Schema's own rendering of the issue. */\nexport const defaultLeafFormatter: LeafFormatter = SchemaIssue.makeFormatterDefault();\n\n/**\n * Normalise a `Pointer.path` segment into a {@link FormPath} segment: integer\n * keys become numbers (array indices), everything else becomes a string.\n */\nconst normaliseSegment = (segment: PropertyKey): string | number => {\n if (Predicate.isNumber(segment)) return segment;\n if (Predicate.isSymbol(segment)) return segment.toString();\n return /^\\d+$/.test(segment) ? Number(segment) : segment;\n};\n\n/** Whether `issue` is a terminal leaf: it wraps no further issues. */\nconst isTerminal: (issue: SchemaIssue.Issue) => boolean = Match.type<SchemaIssue.Issue>().pipe(\n Match.tag(\"Pointer\", \"Composite\", \"Filter\", \"Encoding\", () => false),\n Match.tag(\"AnyOf\", (issue) => Arr.isReadonlyArrayEmpty(issue.issues)),\n Match.orElse(() => true),\n);\n\n/**\n * Walk an Effect Schema issue tree, emitting one {@link FormIssue} per leaf\n * failure. Path segments accumulate through `Pointer` nodes; `Composite` and\n * `AnyOf` fan out to siblings; `Filter` / `Encoding` are transparent\n * wrappers. An `AnyOf` with no recorded member failures (a bare union\n * mismatch) is itself a leaf.\n */\nexport function schemaIssuesToFormIssues(\n issue: SchemaIssue.Issue,\n format: LeafFormatter = defaultLeafFormatter,\n): FormIssue[] {\n const walk = (current: SchemaIssue.Issue, path: Array<string | number>): Array<FormIssue> => {\n const leaf = (node: SchemaIssue.Issue): Array<FormIssue> => [{ path, message: format(node) }];\n const walkAll = (children: ReadonlyArray<SchemaIssue.Issue>): Array<FormIssue> =>\n Arr.flatMap(children, (child) => walk(child, path));\n return Match.value(current).pipe(\n Match.tag(\"Pointer\", (pointer) =>\n walk(pointer.issue, [...path, ...Arr.map(pointer.path, normaliseSegment)]),\n ),\n Match.tag(\"Composite\", (composite) => walkAll(composite.issues)),\n Match.tag(\"AnyOf\", (anyOf) =>\n Arr.match(anyOf.issues, { onEmpty: () => leaf(anyOf), onNonEmpty: walkAll }),\n ),\n // A check that fails on the value itself: its message sits on the\n // `Filter` node, so format that rather than the bare leaf inside.\n Match.tag(\"Filter\", (filter) =>\n isTerminal(filter.issue) ? leaf(filter) : walk(filter.issue, path),\n ),\n Match.tag(\"Encoding\", (encoding) => walk(encoding.issue, path)),\n Match.orElse(leaf),\n );\n };\n\n return walk(issue, []);\n}\n\n/**\n * Normalise a filter predicate's {@link Schema.FilterOutput} into a flat list of\n * {@link FormIssue}s (empty on success). Shared by whole-form refinements\n * ({@link module:effect-forms/FormBuilder}) and per-field validators\n * ({@link module:effect-forms/Validate}) so both fold into the same nested\n * error tree as schema-decode failures.\n */\nexport function filterOutputToFormIssues(output: Schema.FilterOutput): FormIssue[] {\n if (output === undefined || output === true) return [];\n if (output === false) return [{ path: [], message: \"Invalid value\" }];\n if (isFilterIssueList(output)) return Arr.flatMap(output, filterIssueToFormIssues);\n return filterIssueToFormIssues(output);\n}\n\n/** `Array.isArray` as a guard that also narrows away a `ReadonlyArray` member. */\nconst isFilterIssueList = (\n output: Schema.FilterIssue | ReadonlyArray<Schema.FilterIssue>,\n): output is ReadonlyArray<Schema.FilterIssue> => Arr.isArray(output);\n\nfunction filterIssueToFormIssues(issue: Schema.FilterIssue): FormIssue[] {\n if (Predicate.isString(issue)) return [{ path: [], message: issue }];\n if (\"path\" in issue && \"issue\" in issue) {\n const prefix = Arr.map(issue.path, (s) => (Predicate.isSymbol(s) ? s.toString() : s));\n const inner = Predicate.isString(issue.issue)\n ? [{ path: [], message: issue.issue }]\n : schemaIssuesToFormIssues(issue.issue);\n return inner.map((i) => ({ path: [...prefix, ...i.path], message: i.message }));\n }\n return schemaIssuesToFormIssues(issue);\n}\n","/**\n * Compose a form schema one field at a time, with cross-field refinements.\n *\n * {@link make} (aliased {@link empty}) starts an empty builder;\n * {@link FormBuilder.addField} adds a scalar or array field, {@link\n * FormBuilder.merge} folds another builder in, and {@link FormBuilder.refine} /\n * {@link FormBuilder.refineEffect} attach cross-field validations. The point is\n * incremental composition — assemble a large form from small, reusable\n * field-group builders — plus a home for whole-form rules (`password ===\n * confirmPassword`) that a single field schema can't express.\n *\n * {@link buildSchema} collapses the accumulated fields into an ordinary\n * `Schema.Struct` (array fields become `Schema.Array(item)`) and applies every\n * *synchronous* refinement as a struct-level `check`, so the result decodes,\n * derives constraints, and validates exactly like a hand-written schema — hand\n * it straight to {@link module:effect-forms/Form}'s `make`. *Asynchronous*\n * refinements can't be schema checks (Effect Schema has no effectful filter),\n * so they're carried on the builder and run by the Effect-returning validate\n * path — {@link asyncRefinements} exposes them and {@link runAsyncRefinements}\n * runs them into a {@link FormIssue} list.\n *\n * @copyright Thomas Foster\n * @module effect-forms/FormBuilder\n */\n\nimport * as Arr from \"effect/Array\";\nimport * as Effect from \"effect/Effect\";\nimport * as Predicate from \"effect/Predicate\";\nimport * as Rec from \"effect/Record\";\nimport * as Schema from \"effect/Schema\";\nimport type {\n AnyFieldDef,\n ArrayFieldDef,\n DecodedFromFields,\n EncodedFromFields,\n FieldDef,\n FieldsRecord,\n} from \"./Field.ts\";\nimport * as Field from \"./Field.ts\";\nimport type * as Issues from \"./Issues.ts\";\nimport type { FormIssue } from \"./Issues.ts\";\nimport { filterOutputToFormIssues as issuesFromFilterOutput } from \"./Issues.ts\";\n\n/** Brand for a {@link FormBuilder}. */\nexport const TypeId: unique symbol = Symbol.for(\"@thomasfosterau/effect-forms/FormBuilder\");\nexport type TypeId = typeof TypeId;\n\n/** A synchronous cross-field refinement — becomes a struct-level schema check. */\ninterface SyncRefinement {\n readonly _tag: \"sync\";\n readonly fn: (values: unknown) => Schema.FilterOutput;\n}\n\n/** An asynchronous cross-field refinement — run by the Effect validate path. */\ninterface AsyncRefinement {\n readonly _tag: \"async\";\n readonly fn: (values: unknown) => Effect.Effect<Schema.FilterOutput, never, unknown>;\n}\n\ntype Refinement = SyncRefinement | AsyncRefinement;\n\n/**\n * A form assembled field by field. `TFields` accumulates the field record;\n * `R` accumulates the schemas' decoding-service requirements. Immutable — every\n * method returns a new builder.\n */\nexport interface FormBuilder<TFields extends FieldsRecord, R> {\n readonly [TypeId]: TypeId;\n readonly fields: TFields;\n readonly refinements: ReadonlyArray<Refinement>;\n readonly _R?: R;\n\n /** Add a scalar field from a {@link FieldDef}. */\n addField<K extends string, S extends Schema.Top>(\n field: FieldDef<K, S>,\n ): FormBuilder<TFields & { readonly [key in K]: FieldDef<K, S> }, R | S[\"DecodingServices\"]>;\n /** Add an array field from an {@link ArrayFieldDef}. */\n addField<K extends string, S extends Schema.Top>(\n field: ArrayFieldDef<K, S>,\n ): FormBuilder<TFields & { readonly [key in K]: ArrayFieldDef<K, S> }, R | S[\"DecodingServices\"]>;\n /** Add a scalar field from a key and schema. */\n addField<K extends string, S extends Schema.Top>(\n key: K,\n schema: S,\n ): FormBuilder<TFields & { readonly [key in K]: FieldDef<K, S> }, R | S[\"DecodingServices\"]>;\n\n /** Fold another builder's fields and refinements into this one. */\n merge<TFields2 extends FieldsRecord, R2>(\n other: FormBuilder<TFields2, R2>,\n ): FormBuilder<TFields & TFields2, R | R2>;\n\n /**\n * Attach a synchronous whole-form validation. The predicate sees the decoded\n * values and returns a {@link Schema.FilterOutput} (`undefined` / `true` to\n * pass, a message / `{ path, issue }` / list to fail).\n */\n refine(\n predicate: (values: DecodedFromFields<TFields>) => Schema.FilterOutput,\n ): FormBuilder<TFields, R>;\n\n /** Attach an asynchronous whole-form validation (run by the validate path). */\n refineEffect<RD>(\n predicate: (\n values: DecodedFromFields<TFields>,\n ) => Effect.Effect<Schema.FilterOutput, never, RD>,\n ): FormBuilder<TFields, R | RD>;\n}\n\n/** `addField`'s overloads as one argument list: a field definition, or a key with its schema. */\ntype AddFieldArgs = readonly [field: AnyFieldDef] | readonly [key: string, schema: Schema.Top];\n\n/** Whether `addField` was called with the `(key, schema)` overload — the only one whose first argument is a string. */\nconst isKeyedArgs = (args: AddFieldArgs): args is readonly [key: string, schema: Schema.Top] =>\n Predicate.isString(args[0]);\n\nconst proto = {\n [TypeId]: TypeId,\n addField(\n this: FormBuilder<FieldsRecord, unknown>,\n ...args: AddFieldArgs\n ): FormBuilder<FieldsRecord, unknown> {\n const field = isKeyedArgs(args) ? Field.makeField(...args) : args[0];\n return build({ ...this.fields, [field.key]: field }, this.refinements);\n },\n merge(\n this: FormBuilder<FieldsRecord, unknown>,\n other: FormBuilder<FieldsRecord, unknown>,\n ): FormBuilder<FieldsRecord, unknown> {\n return build({ ...this.fields, ...other.fields }, [...this.refinements, ...other.refinements]);\n },\n refine(\n this: FormBuilder<FieldsRecord, unknown>,\n predicate: (values: unknown) => Schema.FilterOutput,\n ): FormBuilder<FieldsRecord, unknown> {\n return build(this.fields, [...this.refinements, { _tag: \"sync\", fn: predicate }]);\n },\n refineEffect(\n this: FormBuilder<FieldsRecord, unknown>,\n predicate: (values: unknown) => Effect.Effect<Schema.FilterOutput, never, unknown>,\n ): FormBuilder<FieldsRecord, unknown> {\n return build(this.fields, [...this.refinements, { _tag: \"async\", fn: predicate }]);\n },\n};\n\nfunction build(\n fields: FieldsRecord,\n refinements: ReadonlyArray<Refinement>,\n): FormBuilder<FieldsRecord, unknown> {\n const self = Object.create(proto);\n self.fields = fields;\n self.refinements = refinements;\n return self;\n}\n\n/** Whether `u` is a {@link FormBuilder}. */\nexport const isFormBuilder = (u: unknown): u is FormBuilder<FieldsRecord, unknown> =>\n Predicate.isObjectOrArray(u) && Predicate.hasProperty(u, TypeId);\n\n/** The empty builder — the starting point for {@link FormBuilder.addField}. */\n// SAFETY: a builder with no fields and no refinements is exactly\n// `FormBuilder<{}, never>`; `build` merely erases the field-record and `R`\n// types its shared implementation works over.\nexport const make = build({}, []) as FormBuilder<Record<never, never>, never>;\n\n/** Alias of {@link make} — the empty builder. */\nexport const empty = make;\n\n/**\n * Collapse a builder into an Effect Schema: a `Schema.Struct` of the fields\n * (array fields as `Schema.Array(item)`) with every synchronous refinement\n * applied as a struct-level check. Ready for {@link module:effect-forms/Form}.\n */\nexport function buildSchema<TFields extends FieldsRecord, R>(\n self: FormBuilder<TFields, R>,\n): Schema.Codec<DecodedFromFields<TFields>, EncodedFromFields<TFields>, R> {\n const structFields = Rec.fromEntries(\n Arr.flatMap(Rec.toEntries(self.fields), ([key, def]): Array<readonly [string, Schema.Top]> => {\n if (Field.isArrayFieldDef(def)) return [[key, Schema.Array(def.itemSchema)]];\n if (Field.isFieldDef(def)) return [[key, def.schema]];\n return [];\n }),\n );\n\n const struct: Schema.Top = Schema.Struct(structFields);\n const schema = Arr.reduce(self.refinements, struct, (refined, refinement) =>\n Predicate.isTagged(refinement, \"sync\")\n ? refined.check(Schema.makeFilter(refinement.fn))\n : refined,\n );\n\n // SAFETY: the struct has exactly one property per entry of `self.fields` —\n // a scalar field's schema, or an array field's item schema under\n // `Schema.Array` — which is the shape `DecodedFromFields` /\n // `EncodedFromFields` compute; checks leave `Type` and `Encoded` unchanged;\n // and `R` is by construction the union of those schemas' decoding services.\n return schema as Schema.Codec<DecodedFromFields<TFields>, EncodedFromFields<TFields>, R>;\n}\n\n/** The asynchronous refinements carried on a builder, as bare predicates. */\nexport function asyncRefinements<TFields extends FieldsRecord, R>(\n self: FormBuilder<TFields, R>,\n): ReadonlyArray<\n (values: DecodedFromFields<TFields>) => Effect.Effect<Schema.FilterOutput, never, R>\n> {\n return Arr.flatMap(self.refinements, (refinement) => {\n if (!Predicate.isTagged(refinement, \"async\")) return [];\n // SAFETY: `refineEffect` stores a predicate typed against this builder's\n // decoded values, with its requirements folded into `R`; the builder keeps\n // it as `(values: unknown) => Effect<…, unknown>` only to share one\n // `Refinement` type across builders, so this restores the original type.\n const fn = refinement.fn as (\n values: DecodedFromFields<TFields>,\n ) => Effect.Effect<Schema.FilterOutput, never, R>;\n return [fn];\n });\n}\n\n/**\n * Normalise a filter predicate's `Schema.FilterOutput` into a flat list of\n * {@link FormIssue}s (empty on success).\n *\n * @deprecated Use `Issues.filterOutputToFormIssues`; this re-export remains\n * only for callers that imported it from `FormBuilder`.\n */\nexport const filterOutputToFormIssues: typeof Issues.filterOutputToFormIssues =\n issuesFromFilterOutput;\n\n/**\n * Run every asynchronous refinement on `self` against `values` and collect\n * their failures as {@link FormIssue}s (empty when all pass). Runs them\n * concurrently; requirements propagate on the `R` channel.\n */\nexport function runAsyncRefinements<TFields extends FieldsRecord, R>(\n self: FormBuilder<TFields, R>,\n values: DecodedFromFields<TFields>,\n): Effect.Effect<ReadonlyArray<FormIssue>, never, R> {\n return Arr.match(asyncRefinements(self), {\n onEmpty: (): Effect.Effect<ReadonlyArray<FormIssue>, never, R> => Effect.succeed([]),\n onNonEmpty: (refinements) =>\n Effect.forEach(refinements, (fn) => fn(values), { concurrency: \"unbounded\" }).pipe(\n Effect.map((outputs) => Arr.flatMap(outputs, issuesFromFilterOutput)),\n ),\n });\n}\n","/**\n * {@link FormState} — the serialisable snapshot of a form.\n *\n * This is the single object a\n * server produces (validate a submission, or seed an empty form) and hands to\n * the client to hydrate from. It is plain JSON so it survives the SSR → client\n * boundary, and {@link FormStateSchema} gives it an Effect Schema so it can be\n * decoded back on arrival.\n *\n * @copyright Thomas Foster\n * @module effect-forms/State\n */\n\nimport * as Schema from \"effect/Schema\";\nimport type * as Types from \"effect/Types\";\nimport type { InputConstraints } from \"./Constraints.ts\";\nimport { type FormErrors, hasErrors, noErrors, setError } from \"./Errors.ts\";\nimport type { FormPath } from \"./Issues.ts\";\n\n/**\n * A form snapshot.\n *\n * - `id` distinguishes multiple forms on one page.\n * - `valid` is whether the last validation succeeded.\n * - `posted` is whether this snapshot came from a submission (vs. a seeded\n * empty form) — drives \"show errors only after submit\" UIs.\n * - `data` is the current values (typed output on success; posted values\n * merged over defaults on failure, so the form re-renders what the user\n * typed).\n * - `errors` is the nested {@link FormErrors} tree.\n * - `message` is an optional status payload (a free-form status payload).\n * - `constraints` is the optional derived {@link InputConstraints} tree.\n */\nexport interface FormState<A, Message = unknown> {\n readonly id: string;\n readonly valid: boolean;\n readonly posted: boolean;\n readonly data: A;\n readonly errors: FormErrors;\n readonly message?: Message | undefined;\n readonly constraints?: InputConstraints | undefined;\n}\n\n/** Options for {@link make}. */\nexport interface MakeFormStateOptions<Message> {\n /** The form's id, distinguishing several forms on one page. Defaults to `\"default\"`. */\n readonly id?: string;\n /** Whether the state is valid. Defaults to `true` exactly when `errors` is empty. */\n readonly valid?: boolean;\n /** Whether the state comes from a submission. Defaults to `false`. */\n readonly posted?: boolean;\n /** The nested error tree. Defaults to no errors. */\n readonly errors?: FormErrors;\n /** A status message to render with the form. Omitted by default. */\n readonly message?: Message;\n /** Native input constraints. Omitted by default. */\n readonly constraints?: InputConstraints;\n}\n\n/** Build a {@link FormState} from data plus overrides. `valid` defaults to \"no errors\". */\nexport function make<A, Message = unknown>(\n data: A,\n options: MakeFormStateOptions<Message> = {},\n): FormState<A, Message> {\n const errors = options.errors ?? noErrors;\n const state: Types.Mutable<FormState<A, Message>> = {\n id: options.id ?? \"default\",\n valid: options.valid ?? !hasErrors(errors),\n posted: options.posted ?? false,\n data,\n errors,\n };\n if (options.message !== undefined) state.message = options.message;\n if (options.constraints !== undefined) state.constraints = options.constraints;\n return state;\n}\n\n/** Return a new state carrying `message` (and not otherwise changed). */\nexport function withMessage<A, Message>(\n state: FormState<A, Message>,\n message: Message,\n): FormState<A, Message> {\n return { ...state, message };\n}\n\n/**\n * Return a new state with `message` added at `path` and `valid` forced to\n * `false` — for attaching a\n * server-side validation failure to a specific field after the schema passed.\n */\nexport function withError<A, Message>(\n state: FormState<A, Message>,\n path: FormPath,\n message: string,\n): FormState<A, Message> {\n return { ...state, valid: false, errors: setError(state.errors, path, message) };\n}\n\n/** Whether the state is valid. */\nexport const isValid = <A, Message>(state: FormState<A, Message>): boolean => state.valid;\n\n/** Effect Schema describing the JSON shape of {@link FormState} for transport. */\nexport function FormStateSchema<DataSchema extends Schema.Top>(data: DataSchema) {\n return Schema.Struct({\n id: Schema.String,\n valid: Schema.Boolean,\n posted: Schema.Boolean,\n data,\n errors: Schema.Record(Schema.String, Schema.Unknown),\n message: Schema.optional(Schema.Unknown),\n constraints: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),\n });\n}\n","/**\n * {@link make} — the core entry point: bind an Effect Schema to a form and\n * get back a validator and an empty-state factory.\n *\n * This is the framework-neutral validate-and-seed entry point,\n * expressed as a small object bound to one schema:\n *\n * - `validate(input)` parses an HTML form body (or a plain object) and decodes\n * it through the schema, returning a {@link FormState} — valid with the\n * decoded data, or invalid with the nested {@link FormErrors} and the posted\n * values preserved so the form re-renders what the user typed. It is an\n * `Effect`, so a schema requiring decoding services composes naturally; it\n * has no error channel — validation failures live in the returned state.\n * - `empty()` seeds an unposted, invalid-by-default form from the supplied\n * defaults — what a `load` returns to render a blank create form.\n *\n * Coercion (strings → numbers / booleans / dates) is the schema's job: build\n * the schema from coercing leaves (`Schema.FiniteFromString`, the\n * {@link module:effect-forms/Coerce} helpers, …) so a multipart body decodes\n * cleanly.\n *\n * @copyright Thomas Foster\n * @module effect-forms/Form\n */\n\nimport * as Arr from \"effect/Array\";\nimport * as Effect from \"effect/Effect\";\nimport type * as Schema from \"effect/Schema\";\nimport * as SchemaParser from \"effect/SchemaParser\";\nimport { constraintsFromSchema, type InputConstraints } from \"./Constraints.ts\";\nimport { defaultEncodedValues } from \"./Field.ts\";\nimport { issuesToErrors } from \"./Errors.ts\";\nimport { buildSchema, type FormBuilder, runAsyncRefinements } from \"./FormBuilder.ts\";\nimport type { DecodedFromFields, EncodedFromFields, FieldsRecord } from \"./Field.ts\";\nimport { formDataToObject, requestToObject } from \"./FormData.ts\";\nimport { type LeafFormatter, schemaIssuesToFormIssues } from \"./Issues.ts\";\nimport { type FormState, make as makeFormState, type MakeFormStateOptions } from \"./State.ts\";\n\n/** Everything {@link Form.validate} accepts. `null` / `undefined` mean \"no submission\". */\nexport type FormInput =\n | FormData\n | URLSearchParams\n | Request\n | Record<string, unknown>\n | null\n | undefined;\n\n/** Options for {@link make}. */\nexport interface MakeFormOptions<S extends Schema.Top> {\n /** Distinguishes multiple forms on a page. Defaults to `\"default\"`. */\n readonly id?: string;\n /** The blank-form values — what {@link Form.empty} seeds and the merge base on failure. */\n readonly defaults: S[\"Type\"];\n /** Override the per-leaf message rendering. */\n readonly leafFormatter?: LeafFormatter;\n /** Constraint overrides deep-merged over the AST-derived tree. */\n readonly constraints?: InputConstraints;\n /** Derive {@link InputConstraints} from the schema AST (default `true`). */\n readonly deriveConstraints?: boolean;\n}\n\n/** A schema bound to a form: a validator plus an empty-state factory. */\nexport interface Form<S extends Schema.Top, Message = unknown> {\n readonly schema: S;\n readonly id: string;\n readonly constraints: InputConstraints;\n /** Seed a blank, unposted form from the defaults. */\n empty(overrides?: MakeFormStateOptions<Message>): FormState<S[\"Type\"], Message>;\n /** Parse a form body (no decode) into a structured object. */\n parse(input: FormData | URLSearchParams | Record<string, unknown>): Record<string, unknown>;\n /**\n * Validate a submission into a {@link FormState}. Never fails on the error\n * channel — schema failures become `errors` on the returned (invalid) state.\n */\n validate(\n input: FormInput,\n ): Effect.Effect<FormState<S[\"Type\"], Message>, never, S[\"DecodingServices\"]>;\n}\n\n/** {@link Form.parse}: flatten a posted body, pass a plain record through. */\nconst parseInput: Form<Schema.Top>[\"parse\"] = (input) =>\n input instanceof FormData || input instanceof URLSearchParams ? formDataToObject(input) : input;\n\nconst toStructured = (input: FormInput): Effect.Effect<Record<string, unknown> | null> => {\n if (input === null || input === undefined) return Effect.succeed(null);\n if (input instanceof Request) {\n // A body that can't be read as a form (e.g. a wrong content-type) is a\n // defect, not a validation failure: `validate` never fails, so the\n // rejection reason dies as-is.\n return Effect.tryPromise({ try: () => requestToObject(input), catch: (cause) => cause }).pipe(\n Effect.orDie,\n );\n }\n if (input instanceof FormData || input instanceof URLSearchParams) {\n return Effect.succeed(formDataToObject(input));\n }\n return Effect.succeed(input);\n};\n\n/**\n * Bind `schema` to a form: `validate(input)` decodes a submission into a\n * {@link FormState} (it never fails — validation failures live in the state;\n * its requirements are the schema's decoding services) and `empty()` seeds a\n * blank form from `defaults`. The returned {@link Form} is stateless and\n * reusable across requests.\n *\n * @example\n * ```ts\n * import * as Effect from \"effect/Effect\"\n * import * as Schema from \"effect/Schema\"\n * import { Form } from \"@thomasfosterau/effect-forms\"\n *\n * const signup = Form.make(\n * Schema.Struct({ name: Schema.String.check(Schema.isMinLength(2)) }),\n * { id: \"signup\", defaults: { name: \"\" } },\n * )\n *\n * const state = Effect.runSync(signup.validate(new URLSearchParams({ name: \"A\" })))\n * console.log(state.valid) // false\n * console.log(state.data) // { name: \"A\" } — the posted value, kept for re-render\n * ```\n */\nexport function make<S extends Schema.Top, Message = unknown>(\n schema: S,\n options: MakeFormOptions<S>,\n): Form<S, Message> {\n const id = options.id ?? \"default\";\n const { defaults, leafFormatter } = options;\n const constraints =\n options.deriveConstraints === false\n ? (options.constraints ?? {})\n : constraintsFromSchema(schema, options.constraints ?? {});\n // `errors: \"all\"` collects every field failure (collect-all) rather\n // than stopping at the first, so the whole form lights up at once.\n const decode = SchemaParser.decodeUnknownEffect(schema, { errors: \"all\" });\n\n const empty: Form<S, Message>[\"empty\"] = (overrides = {}) =>\n makeFormState<S[\"Type\"], Message>(defaults, {\n id,\n valid: false,\n posted: false,\n constraints,\n ...overrides,\n });\n\n const parse: Form<S, Message>[\"parse\"] = parseInput;\n\n const validate: Form<S, Message>[\"validate\"] = (input) =>\n toStructured(input).pipe(\n Effect.flatMap((object) => {\n if (object === null) return Effect.succeed(empty());\n return decode(object).pipe(\n Effect.matchEffect({\n onSuccess: (data) =>\n Effect.succeed(\n makeFormState<S[\"Type\"], Message>(data, {\n id,\n valid: true,\n posted: true,\n constraints,\n }),\n ),\n onFailure: (issue) => {\n // SAFETY: `FormState.data` on a failed decode is, by its documented\n // contract, the posted values merged over the defaults (so the form\n // re-renders what the user typed), typed as `S[\"Type\"]` though not\n // decoded. `defaults` is widened to `object` only so it can be\n // spread; spreading copies its own enumerable properties either way.\n const posted = { ...(defaults as object), ...object } as S[\"Type\"];\n return Effect.succeed(\n makeFormState<S[\"Type\"], Message>(posted, {\n id,\n valid: false,\n posted: true,\n constraints,\n errors: issuesToErrors(schemaIssuesToFormIssues(issue, leafFormatter)),\n }),\n );\n },\n }),\n );\n }),\n );\n\n return { schema, id, constraints, empty, parse, validate };\n}\n\n/** Options for {@link fromBuilder}. Like {@link MakeFormOptions} but `defaults` is optional. */\nexport interface FromBuilderOptions<TFields extends FieldsRecord> {\n /** Distinguishes multiple forms on a page. Defaults to `\"default\"`. */\n readonly id?: string;\n /** Blank-form values. Defaults to a schema-derived encoded blank per field. */\n readonly defaults?: EncodedFromFields<TFields>;\n /** Override the per-leaf message rendering. */\n readonly leafFormatter?: LeafFormatter;\n /** Constraint overrides deep-merged over the AST-derived tree. */\n readonly constraints?: InputConstraints;\n /** Derive {@link InputConstraints} from the schema AST (default `true`). */\n readonly deriveConstraints?: boolean;\n}\n\n/**\n * Bind a {@link FormBuilder} to a {@link Form}. Collapses the builder to a\n * schema (see {@link buildSchema}), then behaves like {@link make} — except its\n * `validate` also runs the builder's *asynchronous* refinements after a\n * successful decode, folding any failures into the same nested error tree. When\n * `defaults` is omitted it is derived from the fields (blank encoded values).\n */\nexport function fromBuilder<TFields extends FieldsRecord, R, Message = unknown>(\n builder: FormBuilder<TFields, R>,\n options: FromBuilderOptions<TFields> = {},\n): Form<Schema.Codec<DecodedFromFields<TFields>, EncodedFromFields<TFields>, R>, Message> {\n const schema = buildSchema(builder);\n // SAFETY: `defaultEncodedValues` seeds exactly one entry per field of\n // `builder.fields` — `[]` for an array field, the schema's blank form input\n // otherwise — the documented \"schema-derived encoded blank per field\" that\n // stands in for `EncodedFromFields<TFields>`.\n const defaults =\n options.defaults ?? (defaultEncodedValues(builder.fields) as EncodedFromFields<TFields>);\n const base = make<typeof schema, Message>(schema, { ...options, defaults });\n\n const decode = SchemaParser.decodeUnknownEffect(schema, { errors: \"all\" });\n\n const validate: Form<typeof schema, Message>[\"validate\"] = (input) =>\n toStructured(input).pipe(\n Effect.flatMap((object) => {\n if (object === null) return Effect.succeed(base.empty());\n // SAFETY: `FormState.data` on a failed validation is, by its documented\n // contract, the posted values merged over the defaults (so the form\n // re-renders what the user typed), typed as the decoded type though\n // not decoded.\n const posted = { ...defaults, ...object } as DecodedFromFields<TFields>;\n return decode(object).pipe(\n Effect.matchEffect({\n onSuccess: (data) =>\n runAsyncRefinements(builder, data).pipe(\n Effect.map((issues) =>\n Arr.match(issues, {\n onEmpty: () =>\n makeFormState<DecodedFromFields<TFields>, Message>(data, {\n id: base.id,\n valid: true,\n posted: true,\n constraints: base.constraints,\n }),\n onNonEmpty: (failures) =>\n makeFormState<DecodedFromFields<TFields>, Message>(posted, {\n id: base.id,\n valid: false,\n posted: true,\n constraints: base.constraints,\n errors: issuesToErrors(failures),\n }),\n }),\n ),\n ),\n onFailure: (issue) =>\n Effect.succeed(\n makeFormState<DecodedFromFields<TFields>, Message>(posted, {\n id: base.id,\n valid: false,\n posted: true,\n constraints: base.constraints,\n errors: issuesToErrors(schemaIssuesToFormIssues(issue, options.leafFormatter)),\n }),\n ),\n }),\n );\n }),\n );\n\n return { ...base, validate };\n}\n","/**\n * When a form validates (and, optionally, auto-submits).\n *\n * A reactive form has to decide *when* to run validation as the user types.\n * The three modes mirror the common form-library vocabulary:\n *\n * - **`onSubmit`** (the default) — validate only when the form is submitted (or\n * validation is triggered explicitly). The quietest mode.\n * - **`onBlur`** — validate a field when it loses focus.\n * - **`onChange`** — validate on every change, optionally `debounce`d.\n *\n * `onBlur` / `onChange` may also `autoSubmit`, turning the form into a\n * live-saving surface (a settings panel, a filter bar). This module is the\n * framework-neutral half: {@link parse} normalises the loose {@link FormMode}\n * a caller supplies into a fully-resolved {@link ParsedMode} the reactive\n * store drives from. The store owns the actual scheduling.\n *\n * A separate `revalidate` dimension governs what happens *after a submit*: an\n * `onSubmit`-mode form is otherwise silent until the next submit, which\n * leaves a failed post's errors frozen even after the user fixes the\n * offending field. `revalidate` says which trigger (`\"onChange\"` /\n * `\"onBlur\"` — independent of the primary `validation` trigger) re-validates\n * once the form has been posted at least once; `\"never\"` keeps the classic\n * frozen-until-resubmit behaviour.\n *\n * @copyright Thomas Foster\n * @module effect-forms/Mode\n */\n\nimport * as Duration from \"effect/Duration\";\n\n/** What re-validates a posted form as the user keeps editing. See {@link ParsedMode.revalidate}. */\nexport type RevalidateMode = \"onChange\" | \"onBlur\" | \"never\";\n\n/**\n * How a form decides when to validate, and whether it auto-submits.\n *\n * - `onSubmit` (the default) cannot auto-submit or debounce.\n * - `onBlur` may auto-submit; debounce is not meaningful.\n * - `onChange` may auto-submit and may debounce.\n *\n * `revalidate` is independent of `validation` and applies to every mode —\n * see the module docs.\n */\nexport type FormMode = (\n | { readonly validation?: \"onSubmit\"; readonly autoSubmit?: false; readonly debounce?: never }\n | { readonly validation: \"onBlur\"; readonly autoSubmit?: boolean; readonly debounce?: never }\n | {\n readonly validation: \"onChange\";\n readonly debounce?: Duration.Input;\n readonly autoSubmit?: boolean;\n }\n) & { readonly revalidate?: RevalidateMode };\n\n/** The fully-resolved form of a {@link FormMode} — every field decided. */\nexport interface ParsedMode {\n readonly validation: \"onSubmit\" | \"onBlur\" | \"onChange\";\n /** Debounce in milliseconds, or `null` for no debounce. */\n readonly debounce: number | null;\n readonly autoSubmit: boolean;\n /** What re-validates the form once it has been posted at least once. Defaults to `\"onChange\"`. */\n readonly revalidate: RevalidateMode;\n}\n\n/**\n * Normalise a {@link FormMode} into a {@link ParsedMode}. An absent mode is\n * `onSubmit`. `debounce` is resolved to milliseconds and only applies in\n * `onChange`; `autoSubmit` is only honoured for `onBlur` / `onChange`;\n * `revalidate` defaults to `\"onChange\"` (TanStack's / RHF's default\n * post-submit behaviour) regardless of the primary `validation` mode.\n */\nexport function parse(mode?: FormMode): ParsedMode {\n const validation = mode?.validation ?? \"onSubmit\";\n const revalidate = mode?.revalidate ?? \"onChange\";\n\n if (validation === \"onBlur\") {\n return {\n validation: \"onBlur\",\n debounce: null,\n autoSubmit: mode?.autoSubmit === true,\n revalidate,\n };\n }\n\n if (validation === \"onChange\") {\n const debounce = mode?.debounce === undefined ? null : Duration.toMillis(mode.debounce);\n return { validation: \"onChange\", debounce, autoSubmit: mode?.autoSubmit === true, revalidate };\n }\n\n return { validation: \"onSubmit\", debounce: null, autoSubmit: false, revalidate };\n}\n","/**\n * {@link FormActionResult} — the framework-neutral outcome of a form\n * submission handler.\n *\n * SvelteKit's `ActionResult` (`success` / `failure` / `redirect` / `error`) is\n * Svelte-specific; this is the same idea expressed as a plain tagged union so\n * a server handler in any framework can describe what happened, and a client\n * adapter can interpret it uniformly. A Svelte adapter maps this onto\n * SvelteKit's `ActionResult`; a fetch client reads the JSON shape directly.\n *\n * @copyright Thomas Foster\n * @module effect-forms/Result\n */\n\nimport type { FormState } from \"./State.ts\";\n\n/** The submission validated and the command ran; carries the resulting state. */\nexport interface FormSuccess<A, Message> {\n readonly _tag: \"Success\";\n readonly status: number;\n readonly state: FormState<A, Message>;\n}\n\n/** The submission was rejected (validation or a user-fixable command failure). */\nexport interface FormFailure<A, Message> {\n readonly _tag: \"Failure\";\n readonly status: number;\n readonly state: FormState<A, Message>;\n}\n\n/** The handler asks the client to navigate elsewhere (post-success redirect). */\nexport interface FormRedirect {\n readonly _tag: \"Redirect\";\n readonly status: number;\n readonly location: string;\n}\n\n/** An unexpected (non-user-fixable) error: surface it, don't bind it to fields. */\nexport interface FormErrorResult {\n readonly _tag: \"Error\";\n readonly status: number;\n readonly message: string;\n}\n\n/** The outcome of a form submission. */\nexport type FormActionResult<A, Message = unknown> =\n | FormSuccess<A, Message>\n | FormFailure<A, Message>\n | FormRedirect\n | FormErrorResult;\n\n/**\n * The data a server form action returns to the client on a non-redirect\n * outcome — the wire contract a client adapter applies back to its store. A\n * validation failure carries the {@link FormState} (`form`); an unexpected\n * error carries a root `message`.\n */\nexport interface FormActionData<A = unknown, Message = unknown> {\n /** The form state (nested errors + posted values) for the client to apply. */\n readonly form?: FormState<A, Message>;\n /** A root-level message for an unexpected (non-field) failure. */\n readonly message?: string;\n}\n\n/** Construct a {@link FormSuccess} (default status 200). */\nexport const success = <A, Message>(\n state: FormState<A, Message>,\n status = 200,\n): FormActionResult<A, Message> => ({ _tag: \"Success\", status, state });\n\n/** Construct a {@link FormFailure} (default status 400). */\nexport const failure = <A, Message>(\n state: FormState<A, Message>,\n status = 400,\n): FormActionResult<A, Message> => ({ _tag: \"Failure\", status, state });\n\n/** Construct a {@link FormRedirect} (default status 303). */\nexport const redirect = <A, Message>(\n location: string,\n status = 303,\n): FormActionResult<A, Message> => ({ _tag: \"Redirect\", status, location });\n\n/** Construct a {@link FormErrorResult} (default status 500). */\nexport const errorResult = <A, Message>(\n message: string,\n status = 500,\n): FormActionResult<A, Message> => ({ _tag: \"Error\", status, message });\n","/**\n * Low-level validation helpers — decode a value through a schema and get back\n * the {@link FormErrors} tree (or the list of {@link FormIssue}s) instead of a\n * raised failure. `Form.make` is the high-level entry; these are for ad\n * hoc field- or value-level validation (e.g. a Svelte client validating a\n * single field on blur).\n *\n * The second half of the module is per-field validators: {@link\n * SyncFieldValidator} / {@link AsyncFieldValidator} (bundled as {@link\n * FieldValidators}) are the framework-neutral shape a reactive store attaches\n * to one field, mirroring TanStack Form's `validators.onChange(Async)` /\n * `onBlur(Async)`. {@link runSyncFieldValidator} runs one synchronously;\n * {@link runAsyncFieldValidator} builds the debounced Effect an async one\n * runs as — the store is responsible for forking it into a fiber per field\n * and interrupting the previous fiber on every new run (see that function's\n * docs for why that alone gives debounce + \"latest wins\" cancellation).\n *\n * @copyright Thomas Foster\n * @module effect-forms/Validate\n */\n\nimport * as Duration from \"effect/Duration\";\nimport * as Effect from \"effect/Effect\";\nimport type * as Schema from \"effect/Schema\";\nimport * as SchemaParser from \"effect/SchemaParser\";\nimport { type FormErrors, issuesToErrors } from \"./Errors.ts\";\nimport {\n filterOutputToFormIssues,\n type FormIssue,\n type LeafFormatter,\n schemaIssuesToFormIssues,\n} from \"./Issues.ts\";\n\n/** The outcome of validating a value against a schema. */\nexport type ValidationResult<A> =\n | { readonly valid: true; readonly data: A }\n | {\n readonly valid: false;\n readonly errors: FormErrors;\n readonly issues: ReadonlyArray<FormIssue>;\n };\n\n/**\n * Decode `input` through `schema`, returning a {@link ValidationResult} on the\n * success channel (decode failures become `valid: false`, not an Effect\n * failure). Decoding-service requirements propagate on the `R` channel.\n */\nexport function validateValue<S extends Schema.Top>(\n schema: S,\n input: unknown,\n format?: LeafFormatter,\n): Effect.Effect<ValidationResult<S[\"Type\"]>, never, S[\"DecodingServices\"]> {\n return SchemaParser.decodeUnknownEffect(schema, { errors: \"all\" })(input).pipe(\n Effect.matchEffect({\n onSuccess: (data): Effect.Effect<ValidationResult<S[\"Type\"]>> =>\n Effect.succeed({ valid: true, data }),\n onFailure: (issue): Effect.Effect<ValidationResult<S[\"Type\"]>> => {\n const issues = schemaIssuesToFormIssues(issue, format);\n return Effect.succeed({ valid: false, errors: issuesToErrors(issues), issues });\n },\n }),\n );\n}\n\n/**\n * Synchronous {@link validateValue} for schemas with no decoding-service\n * requirements (`R = never`) and no async decoding — the common case for plain\n * form schemas. {@link validateValue} never fails on the error channel, so\n * running it with {@link Effect.runSync} is total.\n */\nexport function validateValueSync<S extends Schema.Top>(\n schema: S,\n input: unknown,\n format?: LeafFormatter,\n): ValidationResult<S[\"Type\"]> {\n // SAFETY: `validateValue` never fails on the error channel; the cast drops\n // the decoding-service requirement, which this function's documented\n // contract has the caller assert is `never` (the common case for plain form\n // schemas — a service-requiring schema must use the async {@link validateValue}).\n return Effect.runSync(\n validateValue(schema, input, format) as Effect.Effect<ValidationResult<S[\"Type\"]>>,\n );\n}\n\n/**\n * A synchronous per-field validator — TanStack Form's `validators.onChange` /\n * `onBlur` shape. Sees the field's decoded value and returns a {@link\n * Schema.FilterOutput} (`undefined` / `true` to pass, a message / issue /\n * list to fail), the same vocabulary a schema `check` or a {@link\n * module:effect-forms/FormBuilder} `refine` predicate uses.\n */\nexport type SyncFieldValidator<A> = (value: A) => Schema.FilterOutput;\n\n/**\n * An asynchronous per-field validator — TanStack Form's `onChangeAsync` /\n * `onBlurAsync`. An Effect (not a `Promise`) so its `R` channel can demand\n * services (an API client, a cache) the way a {@link\n * module:effect-forms/FormBuilder} `refineEffect` refinement does; a caller\n * running this client-side supplies `R` via a `Layer` / runtime, or keeps it\n * `never` for a plain `fetch`-based check.\n */\nexport type AsyncFieldValidator<A, R = never> = (\n value: A,\n) => Effect.Effect<Schema.FilterOutput, never, R>;\n\n/**\n * The validators one field declares, mirroring TanStack Form's per-field\n * `validators` option. `asyncDebounceMs` (default `0`, meaning no debounce)\n * governs both async variants; see {@link runAsyncFieldValidator} for how the\n * debounce and cancellation actually work.\n */\nexport interface FieldValidators<A, R = never> {\n readonly onChange?: SyncFieldValidator<A>;\n readonly onBlur?: SyncFieldValidator<A>;\n readonly onChangeAsync?: AsyncFieldValidator<A, R>;\n readonly onBlurAsync?: AsyncFieldValidator<A, R>;\n readonly asyncDebounceMs?: number;\n}\n\n/**\n * Run a {@link SyncFieldValidator}, folding its {@link Schema.FilterOutput}\n * into {@link FormIssue}s (empty on success). Total — a sync field validator\n * cannot fail the Effect error channel.\n */\nexport function runSyncFieldValidator<A>(\n validator: SyncFieldValidator<A>,\n value: A,\n): ReadonlyArray<FormIssue> {\n return filterOutputToFormIssues(validator(value));\n}\n\n/**\n * Build the Effect an {@link AsyncFieldValidator} run is: sleep `debounceMs`\n * (a no-op at `0`, the default), then run `validator` and fold its output\n * into {@link FormIssue}s.\n *\n * The debounce is a plain {@link Effect.sleep}, so it rides Effect's `Clock`\n * — deterministic and instantly-advanceable under `TestClock` in tests, real\n * time otherwise (no `setTimeout` bookkeeping for a caller to manage).\n * \"Latest wins\" falls out of ordinary fiber interruption: a caller re-running\n * this on every keystroke forks a fresh fiber per call and interrupts the\n * previous one before forking the next — interrupting mid-sleep skips the\n * validator entirely (a true debounce, not just a discarded stale result),\n * and interrupting mid-validator aborts whatever the validator was doing\n * (e.g. an in-flight `fetch` wrapped as an Effect).\n */\nexport function runAsyncFieldValidator<A, R>(\n validator: AsyncFieldValidator<A, R>,\n value: A,\n debounceMs = 0,\n): Effect.Effect<ReadonlyArray<FormIssue>, never, R> {\n return Effect.gen(function* () {\n if (debounceMs > 0) yield* Effect.sleep(Duration.millis(debounceMs));\n const output = yield* validator(value);\n return filterOutputToFormIssues(output);\n });\n}\n\n/** Which trigger a {@link FieldValidators} sync/async pair reacts to. */\nexport type FieldTrigger = \"change\" | \"blur\";\n\n/** The validator(s) (if any) that apply for one field trigger — see {@link resolveFieldTrigger}. */\nexport interface ResolvedFieldTrigger<A, R = never> {\n readonly sync: SyncFieldValidator<A> | undefined;\n readonly async: AsyncFieldValidator<A, R> | undefined;\n readonly asyncDebounceMs: number;\n}\n\n/**\n * Pick `validators`' sync/async pair for `trigger` (TanStack's `onChange` /\n * `onBlur` vs `onChangeAsync` / `onBlurAsync`). A reactive store calls this\n * once per trigger instead of repeating the trigger ternary at each call\n * site — the pure decision half of what {@link module:effect-forms-svelte/FormStore}'s\n * field-trigger dispatch runs.\n */\nexport function resolveFieldTrigger<A, R = never>(\n validators: FieldValidators<A, R> | undefined,\n trigger: FieldTrigger,\n): ResolvedFieldTrigger<A, R> {\n return {\n sync: trigger === \"change\" ? validators?.onChange : validators?.onBlur,\n async: trigger === \"change\" ? validators?.onChangeAsync : validators?.onBlurAsync,\n asyncDebounceMs: validators?.asyncDebounceMs ?? 0,\n };\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- //#region \0rolldown/runtime.js
1
+ // \0rolldown/runtime.js
2
2
  var __defProp = Object.defineProperty;
3
3
  var __exportAll = (all, no_symbols) => {
4
4
  let target = {};
@@ -9,5 +9,4 @@ var __exportAll = (all, no_symbols) => {
9
9
  if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
10
10
  return target;
11
11
  };
12
- //#endregion
13
12
  export { __exportAll as t };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thomasfosterau/effect-forms",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "Framework-neutral, Effect-native form validation and state — schema-driven validation, nested errors, posted-value retention, and native constraints, on Effect Schema.",
5
5
  "keywords": [
6
6
  "effect",
@@ -42,7 +42,7 @@
42
42
  "access": "public"
43
43
  },
44
44
  "peerDependencies": {
45
- "effect": ">=4.0.0"
45
+ "effect": "^4.0.0"
46
46
  },
47
47
  "engines": {
48
48
  "node": "^20.19.0 || >=22.12.0"